``
This commit is contained in:
parent
9c0d5a65a8
commit
90f8bfe677
128
src/api/system/mrp.js
Normal file
128
src/api/system/mrp.js
Normal file
@ -0,0 +1,128 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询MRP运算结果复查列表
|
||||
export function listMrp(query) {
|
||||
return request({
|
||||
url: '/system/mrp/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询MRP运算结果复查详细
|
||||
export function getMrp(id) {
|
||||
return request({
|
||||
url: '/system/mrp/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增MRP运算结果复查
|
||||
export function addMrp(data) {
|
||||
return request({
|
||||
url: '/system/mrp',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改MRP运算结果复查
|
||||
export function updateMrp(data) {
|
||||
return request({
|
||||
url: '/system/mrp',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除MRP运算结果复查
|
||||
export function delMrp(id) {
|
||||
return request({
|
||||
url: '/system/mrp/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出MRP运算结果复查列表
|
||||
export function exportMrp(query) {
|
||||
return request({
|
||||
url: '/system/mrp/export',
|
||||
method: 'post',
|
||||
data: query,
|
||||
responseType: 'blob',
|
||||
timeout: 5 * 60 * 1000 // 导出可能需要较长时间,设置5分钟超时
|
||||
})
|
||||
}
|
||||
|
||||
// ========== MRP2 相关方法 ==========
|
||||
|
||||
// 查询MRP2运算结果列表
|
||||
export function getMRP2Results(query) {
|
||||
return request({
|
||||
url: '/system/mrp2/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询MRP2运算结果详细
|
||||
export function getMRP2Result(id) {
|
||||
return request({
|
||||
url: '/system/mrp2/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增MRP2运算结果
|
||||
export function addMRP2Result(data) {
|
||||
return request({
|
||||
url: '/system/mrp2',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改MRP2运算结果
|
||||
export function updateMRP2Result(data) {
|
||||
return request({
|
||||
url: '/system/mrp2',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除MRP2运算结果
|
||||
export function delMRP2Result(id) {
|
||||
return request({
|
||||
url: '/system/mrp2/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出MRP2运算结果
|
||||
export function exportMRP2Data(query) {
|
||||
return request({
|
||||
url: '/system/mrp2/export',
|
||||
method: 'post',
|
||||
data: query,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
// 执行MRP2运算
|
||||
export function executeMRP2Calculation(data) {
|
||||
return request({
|
||||
url: '/system/mrp2/calculate',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取MRP2统计信息
|
||||
export function getMRP2Stats(query) {
|
||||
return request({
|
||||
url: '/system/mrp2/stats',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
67
src/api/system/proPlan.js
Normal file
67
src/api/system/proPlan.js
Normal file
@ -0,0 +1,67 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询方案管理列表
|
||||
export function listProPlan(query) {
|
||||
return request({
|
||||
url: '/system/proPlan/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}// 查询方案管理列表
|
||||
export function listProPlan2(query) {
|
||||
return request({
|
||||
url: '/system/proPlan/list2',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询超期/临期项目列表
|
||||
export function listProPlanOverdue() {
|
||||
return request({
|
||||
url: '/system/proPlan/overdue',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询临期项目列表
|
||||
export function listProPlanExpirys() {
|
||||
return request({
|
||||
url: '/system/proPlan/expiryProjects',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询方案管理详细
|
||||
export function getProPlan(id) {
|
||||
return request({
|
||||
url: '/system/proPlan/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增方案管理
|
||||
export function addProPlan(data) {
|
||||
return request({
|
||||
url: '/system/proPlan',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改方案管理
|
||||
export function updateProPlan(data) {
|
||||
return request({
|
||||
url: '/system/proPlan',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除方案管理
|
||||
export function delProPlan(id) {
|
||||
return request({
|
||||
url: '/system/proPlan/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
1050
src/views/indexDaping.vue
Normal file
1050
src/views/indexDaping.vue
Normal file
File diff suppressed because it is too large
Load Diff
945
src/views/system/mrp2/index.vue
Normal file
945
src/views/system/mrp2/index.vue
Normal file
@ -0,0 +1,945 @@
|
||||
<template>
|
||||
<div class="mrp2-standalone-container">
|
||||
<!-- 独立页面头部 -->
|
||||
<div class="standalone-header">
|
||||
<div class="header-content">
|
||||
<div class="header-left">
|
||||
<h1 style="margin: 0; color: #303133; font-size: 24px;">
|
||||
<i class="el-icon-data-analysis" style="margin-right: 12px; color: #409EFF;"></i>
|
||||
项目供需查询(仅供参考)
|
||||
</h1>
|
||||
<p style="margin: 8px 0 0 0; color: #909399; font-size: 14px;">
|
||||
物料需求计划运算结果查询
|
||||
</p>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-button type="primary" icon="el-icon-refresh" @click="handleRefreshAll">
|
||||
刷新页面
|
||||
</el-button>
|
||||
<el-button type="info" icon="el-icon-back" @click="goBack">
|
||||
返回
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面内容 -->
|
||||
<div class="page-content">
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<el-card class="box-card" style="margin-bottom: 20px;">
|
||||
<div slot="header" class="clearfix">
|
||||
<span style="font-weight: 600; color: #303133;">
|
||||
<i class="el-icon-search" style="margin-right: 8px; color: #409EFF;"></i>
|
||||
查询条件
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="生产令号" prop="productionOrderNo">
|
||||
<el-select
|
||||
v-model="productionObj"
|
||||
placeholder="请选择生产令号"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 400px"
|
||||
@change="handleProductionOrderChange"
|
||||
value-key="id"
|
||||
:loading="productionOrderLoading"
|
||||
:filter-method="filterProductionOrders"
|
||||
remote
|
||||
reserve-keyword
|
||||
popper-class="production-order-select-dropdown"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in filteredProductionOrderList"
|
||||
:key="item.id"
|
||||
:value="item"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; min-height: 40px; padding: 4px 0;">
|
||||
<div style="flex: 1; min-width: 0;">
|
||||
<div style="font-weight: 600; color: #303133; font-size: 14px; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" :title="item.productionOrderNo">
|
||||
{{ item.productionOrderNo }}
|
||||
</div>
|
||||
<div style="color: #909399; font-size: 12px; margin-top: 2px; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" :title="item.productionName">
|
||||
{{ item.productionName }}
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right; margin-left: 12px; flex-shrink: 0; display: flex; flex-direction: column; align-items: flex-end;">
|
||||
<el-tag
|
||||
v-if="item.drawingNo"
|
||||
size="mini"
|
||||
type="info"
|
||||
style="margin-bottom: 2px; max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
|
||||
:title="item.drawingNo"
|
||||
>
|
||||
{{ item.drawingNo }}
|
||||
</el-tag>
|
||||
<div style="color: #67c23a; font-size: 11px; line-height: 1.2; max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" :title="item.createdBy || '系统'">
|
||||
{{ item.createdBy || '系统' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-option>
|
||||
</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-card>
|
||||
|
||||
<!-- 当前选择的生产令号信息 -->
|
||||
<el-card v-if="productionObj" class="box-card production-order-info" style="margin-bottom: 20px;">
|
||||
<div slot="header" class="clearfix">
|
||||
<span style="font-weight: 600; color: #303133;">
|
||||
<i class="el-icon-document" style="margin-right: 8px; color: #409EFF;"></i>
|
||||
当前选择的生产令号
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 20px; padding: 10px 0;">
|
||||
<div style="flex: 1;">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<el-tag type="primary" size="medium" style="font-weight: 600;">
|
||||
{{ productionObj.productionOrderNo }}
|
||||
</el-tag>
|
||||
<span style="font-size: 16px; font-weight: 600; color: #303133;">
|
||||
{{ productionObj.productionName }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="margin-top: 8px; display: flex; gap: 16px; font-size: 13px; color: #606266;">
|
||||
<span v-if="productionObj.drawingNo">
|
||||
<i class="el-icon-document-copy" style="margin-right: 4px;"></i>
|
||||
图号: {{ productionObj.drawingNo }}
|
||||
</span>
|
||||
<span v-if="productionObj.createdBy">
|
||||
<i class="el-icon-user" style="margin-right: 4px;"></i>
|
||||
创建人: {{ productionObj.createdBy }}
|
||||
</span>
|
||||
<span v-if="productionObj.createTime">
|
||||
<i class="el-icon-time" style="margin-right: 4px;"></i>
|
||||
创建时间: {{ formatDateTime(productionObj.createTime) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-refresh"
|
||||
@click="handleRefreshMrpData"
|
||||
:loading="mrpLoading"
|
||||
>
|
||||
刷新数据
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- MRP运算结果 -->
|
||||
<el-card class="box-card mrp-result-card" style="margin-bottom: 20px;">
|
||||
<div slot="header" class="clearfix">
|
||||
<span style="font-weight: 600; color: #303133;">
|
||||
<i class="el-icon-data-analysis" style="margin-right: 8px; color: #409EFF;"></i>
|
||||
运算结果
|
||||
</span>
|
||||
<div style="float: right; display: flex; align-items: center; gap: 12px;">
|
||||
<!-- 运算状态显示 -->
|
||||
<div v-if="loadingMRP" class="mrp-status-indicator">
|
||||
<i class="el-icon-loading" style="margin-right: 6px;"></i>
|
||||
<span>{{ mrpOperationStatus || '运算进行中...' }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 运算结果按钮 -->
|
||||
<el-button
|
||||
type="primary"
|
||||
size="medium"
|
||||
:loading="loadingMRP"
|
||||
:disabled="loadingMRP"
|
||||
@click="getMRPResults()"
|
||||
class="mrp-calculate-btn"
|
||||
>
|
||||
{{ loadingMRP ? '核算中...' : '开始核算' }}
|
||||
</el-button>
|
||||
|
||||
<!-- 搜索输入框 -->
|
||||
<el-input
|
||||
v-model="mrpSearchKeyword"
|
||||
placeholder="搜索物料名称或编码"
|
||||
style="width: 220px;"
|
||||
size="small"
|
||||
clearable
|
||||
@input="handleMrpSearch"
|
||||
@clear="clearMrpSearch"
|
||||
class="mrp-search-input"
|
||||
>
|
||||
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
||||
</el-input>
|
||||
|
||||
<!-- 导出按钮 -->
|
||||
<el-button
|
||||
type="success"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="handleExportMrp"
|
||||
:loading="exportLoading"
|
||||
:disabled="!productionObj || !productionObj.id"
|
||||
class="mrp-export-btn"
|
||||
:title="productionObj && productionObj.id ? `导出${productionObj.productionOrderNo}的MRP数据` : '请先选择生产令号'"
|
||||
>
|
||||
导出Excel
|
||||
</el-button>
|
||||
|
||||
<el-button type="text" @click="handleRefreshMrpData">
|
||||
<i class="el-icon-refresh"></i> 刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计信息 -->
|
||||
<div
|
||||
v-if="productionObj"
|
||||
style="margin-bottom: 15px; padding: 15px; background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); border-radius: 8px; border-left: 4px solid #409EFF;">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div style="text-align: center;">
|
||||
<div style="font-size: 24px; font-weight: bold; color: #409EFF;">{{ mrpStats.totalMaterials }}</div>
|
||||
<div style="font-size: 12px; color: #606266;">总物料数</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div style="text-align: center;">
|
||||
<div style="font-size: 24px; font-weight: bold; color: #67c23a;">{{ mrpStats.positiveStock }}</div>
|
||||
<div style="font-size: 12px; color: #606266;">正库存</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div style="text-align: center;">
|
||||
<div style="font-size: 24px; font-weight: bold; color: #f56c6c;">{{ mrpStats.negativeStock }}</div>
|
||||
<div style="font-size: 12px; color: #606266;">负库存</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div style="text-align: center;">
|
||||
<div style="font-size: 24px; font-weight: bold; color: #e6a23c;">{{ mrpStats.zeroStock }}</div>
|
||||
<div style="font-size: 12px; color: #606266;">零库存</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 提示信息 -->
|
||||
<div v-if="!productionObj" style="text-align: center; padding: 40px; color: #909399;">
|
||||
<i class="el-icon-info" style="font-size: 48px; margin-bottom: 16px;"></i>
|
||||
<p style="font-size: 16px; margin: 0;">请先选择生产令号以查看运算结果</p>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-else
|
||||
:data="mrpResultData"
|
||||
style="width: 100%"
|
||||
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
|
||||
:row-class-name="getMrpRowClass"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
v-loading="mrpLoading"
|
||||
>
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="120" show-overflow-tooltip/>
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="100" show-overflow-tooltip/>
|
||||
<el-table-column prop="purchaseNotInStock" label="项目本批数" width="130" align="right">
|
||||
<template slot-scope="scope">
|
||||
<span style="color: #e6a23c">{{ scope.row.purchaseNotInStock.toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="availableStock" label="本项目可用库存" width="120" align="right">
|
||||
<template slot-scope="scope">
|
||||
<span :style="{ color: scope.row.availableStock < 0 ? '#f56c6c' : '#67c23a' }">
|
||||
{{ scope.row.availableStock.toFixed(3) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="realTimeStock" label="当前可用库存" width="100" align="right">
|
||||
<template slot-scope="scope">
|
||||
<span :style="{ color: scope.row.realTimeStock < 0 ? '#f56c6c' : '#67c23a' }">
|
||||
{{ scope.row.realTimeStock.toFixed(3) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="prodNotInStock" label="本次生产未入库" width="130" align="right">
|
||||
<template slot-scope="scope">
|
||||
<span style="color: #e6a23c">{{ scope.row.prodNotInStock.toFixed(3) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="purchaseRequestQty" label="本项目采购申请" width="130" align="right">
|
||||
<template slot-scope="scope">
|
||||
<span style="color: #409eff">{{ scope.row.purchaseRequestQty.toFixed(3) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="childNotPickedQty" label="子项未领料数量" width="130" align="right">
|
||||
<template slot-scope="scope">
|
||||
<span style="color: #f56c6c">{{ scope.row.childNotPickedQty.toFixed(3) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="160" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ formatDateTime(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="updateTime" label="更新时间" width="160" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ formatDateTime(scope.row.updateTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
v-if="productionObj"
|
||||
@size-change="handleMrpSizeChange"
|
||||
@current-change="handleMrpCurrentChange"
|
||||
:current-page="mrpQueryParams.pageNum"
|
||||
:page-sizes="[10, 50, 100, 500]"
|
||||
:page-size="mrpQueryParams.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="mrpTotal"
|
||||
style="margin-top: 20px; text-align: right;"
|
||||
/>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listMrp, exportMrp } from '@/api/system/mrp'
|
||||
import { listOrderPro, getMRPResults } from '@/api/system/orderPro'
|
||||
import dayjs from "dayjs"
|
||||
|
||||
export default {
|
||||
name: "MRPIndex",
|
||||
data() {
|
||||
return {
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
productionOrderNo: null,
|
||||
materialCode: null,
|
||||
materialName: null
|
||||
},
|
||||
showSearch: true,
|
||||
|
||||
// 生产令号对象(模拟orderPro中的productionObj)
|
||||
productionObj: null,
|
||||
productionOrderList: [], // 生产令号列表
|
||||
filteredProductionOrderList: [], // 过滤后的生产令号列表
|
||||
productionOrderLoading: false, // 生产令号加载状态
|
||||
|
||||
// MRP相关数据(与orderPro中的MRP数据完全一致)
|
||||
mrpResultData: [],
|
||||
mrpLoading: false,
|
||||
mrpTotal: 0,
|
||||
mrpSearchKeyword: '',
|
||||
mrpQueryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
materialName: '',
|
||||
materialCode: '',
|
||||
orderProId: null
|
||||
},
|
||||
loadingMRP: false,
|
||||
mrpOperationStatus: '', // MRP运算状态
|
||||
exportLoading: false, // 导出加载状态
|
||||
mrpStats: {
|
||||
totalMaterials: 0,
|
||||
positiveStock: 0,
|
||||
negativeStock: 0,
|
||||
zeroStock: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getProductionOrderList()
|
||||
},
|
||||
methods: {
|
||||
/** 获取生产令号列表 */
|
||||
async getProductionOrderList() {
|
||||
this.productionOrderLoading = true
|
||||
try {
|
||||
const response = await listOrderPro({
|
||||
pageNum: 1,
|
||||
pageSize: 1000 // 获取所有生产令号
|
||||
})
|
||||
if (response && response.rows) {
|
||||
this.productionOrderList = response.rows
|
||||
this.filteredProductionOrderList = response.rows
|
||||
} else {
|
||||
this.productionOrderList = []
|
||||
this.filteredProductionOrderList = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取生产令号列表失败:', error)
|
||||
this.$message.error('获取生产令号列表失败')
|
||||
this.productionOrderList = []
|
||||
this.filteredProductionOrderList = []
|
||||
} finally {
|
||||
this.productionOrderLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
/** 过滤生产令号 */
|
||||
filterProductionOrders(query) {
|
||||
if (!query) {
|
||||
this.filteredProductionOrderList = this.productionOrderList
|
||||
return
|
||||
}
|
||||
|
||||
const keyword = query.toLowerCase()
|
||||
this.filteredProductionOrderList = this.productionOrderList.filter(item =>
|
||||
item.productionOrderNo.toLowerCase().includes(keyword) ||
|
||||
item.productionName.toLowerCase().includes(keyword) ||
|
||||
(item.drawingNo && item.drawingNo.toLowerCase().includes(keyword)) ||
|
||||
(item.createdBy && item.createdBy.toLowerCase().includes(keyword))
|
||||
)
|
||||
},
|
||||
|
||||
/** 处理生产令号变化 */
|
||||
handleProductionOrderChange(selectedOrder) {
|
||||
if (selectedOrder) {
|
||||
this.productionObj = selectedOrder
|
||||
this.mrpQueryParams.orderProId = selectedOrder.id
|
||||
// 选择生产令号后自动获取MRP数据
|
||||
this.getMrpData()
|
||||
} else {
|
||||
this.productionObj = null
|
||||
this.mrpQueryParams.orderProId = null
|
||||
this.mrpResultData = []
|
||||
this.mrpTotal = 0
|
||||
this.mrpStats = {
|
||||
totalMaterials: 0,
|
||||
positiveStock: 0,
|
||||
negativeStock: 0,
|
||||
zeroStock: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** 查询MRP结果列表 */
|
||||
getList() {
|
||||
if (!this.productionObj) {
|
||||
this.$message.warning('请先选择生产令号')
|
||||
return
|
||||
}
|
||||
this.getMrpData()
|
||||
},
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.mrpQueryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm")
|
||||
this.handleQuery()
|
||||
},
|
||||
|
||||
/** 重置表单 */
|
||||
resetForm(refName) {
|
||||
if (this.$refs[refName]) {
|
||||
this.$refs[refName].resetFields()
|
||||
}
|
||||
},
|
||||
|
||||
/** MRP运算结果复查相关方法 */
|
||||
// 获取MRP运算结果
|
||||
async getMRPResults() {
|
||||
if (!this.productionObj || !this.productionObj.id) {
|
||||
this.$message.error('请先选择生产令号');
|
||||
return;
|
||||
}
|
||||
|
||||
// 只显示一次开始提示,不重复弹窗
|
||||
this.$message({
|
||||
message: '运算已开始,由于数据量较大,可能需要较长时间,请耐心等待...',
|
||||
type: 'info',
|
||||
duration: 6000, // 减少到6秒
|
||||
showClose: true
|
||||
});
|
||||
|
||||
this.loadingMRP = true;
|
||||
this.mrpOperationStatus = '运算已开始,正在处理数据...';
|
||||
|
||||
// 只更新状态,不弹窗提醒
|
||||
const progressTimer = setInterval(() => {
|
||||
this.mrpOperationStatus = '运算仍在进行中,请继续等待...';
|
||||
// 移除弹窗提示,只更新状态显示
|
||||
}, 30000);
|
||||
|
||||
try {
|
||||
|
||||
const response = await getMRPResults(this.productionObj.id);
|
||||
|
||||
|
||||
// 清除定时器
|
||||
clearInterval(progressTimer);
|
||||
|
||||
if (response && response.code === 200) {
|
||||
this.$message.success('MRP运算完成!');
|
||||
// 运算完成后刷新MRP数据
|
||||
await this.getMrpData();
|
||||
} else {
|
||||
console.error('运算失败,响应:', response);
|
||||
this.$message.error(response?.msg || '运算失败');
|
||||
}
|
||||
} catch (error) {
|
||||
// 清除定时器
|
||||
clearInterval(progressTimer);
|
||||
|
||||
console.error('运算异常:', error);
|
||||
console.error('错误详情:', {
|
||||
message: error.message,
|
||||
code: error.code,
|
||||
response: error.response,
|
||||
status: error.response?.status,
|
||||
data: error.response?.data
|
||||
});
|
||||
|
||||
// 简化错误提示,避免重复弹窗
|
||||
if (error.code === 'ECONNABORTED' || error.message.includes('timeout')) {
|
||||
this.$message.error('运算超时,请稍后重试');
|
||||
} else if (error.response?.status === 404) {
|
||||
this.$message.error('运算接口不存在,请检查后端服务');
|
||||
} else if (error.response?.status === 500) {
|
||||
this.$message.error('运算服务器内部错误,请稍后重试');
|
||||
} else {
|
||||
this.$message.error(`运算失败: ${error.message || '未知错误'}`);
|
||||
}
|
||||
} finally {
|
||||
this.loadingMRP = false;
|
||||
this.mrpOperationStatus = ''; // 清空状态
|
||||
}
|
||||
},
|
||||
|
||||
// 获取MRP数据
|
||||
async getMrpData() {
|
||||
this.mrpLoading = true;
|
||||
try {
|
||||
this.mrpQueryParams.orderProId = this.productionObj.id;
|
||||
const response = await listMrp(this.mrpQueryParams);
|
||||
|
||||
if (response && response.rows) {
|
||||
// 统一转Number,避免 toFixed 报错
|
||||
const processedRows = response.rows.map(item => ({
|
||||
...item,
|
||||
availableStock: Number(item.availableStock) || 0,
|
||||
realTimeStock: Number(item.realTimeStock) || 0,
|
||||
prodNotInStock: Number(item.prodNotInStock) || 0,
|
||||
purchaseRequestQty: Number(item.purchaseRequestQty) || 0,
|
||||
purchaseNotInStock: Number(item.purchaseNotInStock) || 0,
|
||||
childNotPickedQty: Number(item.childNotPickedQty) || 0,
|
||||
createTime: item.createTime ? dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') : '',
|
||||
updateTime: item.updateTime ? dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss') : ''
|
||||
}));
|
||||
this.mrpResultData = processedRows;
|
||||
this.mrpTotal = response.total || processedRows.length;
|
||||
// 统计计算也用处理过的数字数据
|
||||
this.calculateMrpStats(processedRows);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
this.$message.error('获取数据失败');
|
||||
} finally {
|
||||
this.mrpLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// MRP分页大小改变
|
||||
handleMrpSizeChange(size) {
|
||||
this.mrpQueryParams.pageSize = size;
|
||||
this.mrpQueryParams.pageNum = 1;
|
||||
this.getMrpData();
|
||||
},
|
||||
|
||||
// MRP当前页改变
|
||||
handleMrpCurrentChange(page) {
|
||||
this.mrpQueryParams.pageNum = page;
|
||||
this.getMrpData();
|
||||
},
|
||||
|
||||
// 格式化日期时间
|
||||
formatDateTime(date) {
|
||||
if (!date) return '-';
|
||||
if (typeof date === 'string') {
|
||||
date = new Date(date);
|
||||
}
|
||||
if (isNaN(date.getTime())) return '-';
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
},
|
||||
|
||||
// MRP搜索处理
|
||||
handleMrpSearch() {
|
||||
this.mrpQueryParams.pageNum = 1;
|
||||
this.mrpQueryParams.materialCode = this.mrpSearchKeyword;
|
||||
console.log('搜索参数:', this.mrpQueryParams);
|
||||
this.getMrpData();
|
||||
},
|
||||
|
||||
// 清空MRP搜索
|
||||
clearMrpSearch() {
|
||||
this.mrpSearchKeyword = '';
|
||||
this.mrpQueryParams.materialCode = '';
|
||||
this.mrpQueryParams.pageNum = 1;
|
||||
this.getMrpData();
|
||||
},
|
||||
|
||||
// 过滤MRP数据(前端过滤,作为备用方案)
|
||||
filterMrpData(data) {
|
||||
if (!this.mrpSearchKeyword) {
|
||||
return data;
|
||||
}
|
||||
|
||||
const keyword = this.mrpSearchKeyword.toLowerCase();
|
||||
return data.filter(item =>
|
||||
item.materialName.toLowerCase().includes(keyword) ||
|
||||
item.materialCode.toLowerCase().includes(keyword)
|
||||
);
|
||||
},
|
||||
|
||||
// 计算MRP统计信息
|
||||
calculateMrpStats(data) {
|
||||
const total = data.length;
|
||||
const positive = data.filter(item => item.availableStock > 0).length;
|
||||
const negative = data.filter(item => item.availableStock < 0).length;
|
||||
const zero = data.filter(item => item.availableStock === 0).length;
|
||||
|
||||
this.mrpStats = {
|
||||
totalMaterials: total,
|
||||
positiveStock: positive,
|
||||
negativeStock: negative,
|
||||
zeroStock: zero
|
||||
};
|
||||
},
|
||||
|
||||
// 刷新MRP数据
|
||||
handleRefreshMrpData() {
|
||||
this.mrpQueryParams.pageNum = 1;
|
||||
this.getMrpData();
|
||||
},
|
||||
|
||||
// 导出MRP数据
|
||||
async handleExportMrp() {
|
||||
// 增强参数验证
|
||||
if (!this.productionObj) {
|
||||
this.$message.error('请先选择生产令号');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.productionObj.id) {
|
||||
this.$message.error('生产令号ID无效,请重新选择');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.productionObj.productionOrderNo) {
|
||||
this.$message.error('生产令号信息不完整,请重新选择');
|
||||
return;
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
console.log('productionObj:', this.productionObj);
|
||||
console.log('productionObj.id:', this.productionObj.id);
|
||||
console.log('mrpResultData.length:', this.mrpResultData?.length || 0);
|
||||
|
||||
// 显示导出确认信息
|
||||
const exportInfo = `导出生产令号"${this.productionObj.productionOrderNo}"的数据`;
|
||||
const searchInfo = this.mrpSearchKeyword ? `,包含搜索关键词"${this.mrpSearchKeyword}"` : '';
|
||||
const dataInfo = this.mrpResultData && this.mrpResultData.length > 0
|
||||
? `,当前显示${this.mrpResultData.length}条记录`
|
||||
: '(后台将查询所有相关数据)';
|
||||
|
||||
try {
|
||||
await this.$confirm(
|
||||
`${exportInfo}${searchInfo}${dataInfo}。\n\n确定要导出吗?`,
|
||||
'确认导出',
|
||||
{
|
||||
confirmButtonText: '确定导出',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info'
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
// 用户取消导出
|
||||
return;
|
||||
}
|
||||
|
||||
this.exportLoading = true;
|
||||
|
||||
try {
|
||||
// 准备导出参数,主要传递orderProId给后台
|
||||
const exportParams = {
|
||||
orderProId: this.productionObj.id, // 核心参数:生产令号ID
|
||||
materialCode: this.mrpSearchKeyword || '', // 可选的物料编码搜索
|
||||
materialName: '', // 清空名称搜索,避免冲突
|
||||
pageSize: 9999, // 后台查询时获取所有数据
|
||||
pageNum: 1
|
||||
};
|
||||
|
||||
|
||||
const response = await exportMrp(exportParams);
|
||||
|
||||
// 创建下载链接
|
||||
const blob = new Blob([response], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
});
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `MRP运算结果_${this.productionObj.productionOrderNo}_${new Date().getTime()}.xlsx`;
|
||||
link.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
this.$message.success(`导出成功!文件已下载:运算结果_${this.productionObj.productionOrderNo}_${new Date().getTime()}.xlsx`);
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error);
|
||||
this.$message.error('导出失败: ' + (error.message || error));
|
||||
} finally {
|
||||
this.exportLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 获取MRP行样式类
|
||||
getMrpRowClass({ row, rowIndex }) {
|
||||
if (row.availableStock < 0) {
|
||||
return 'negative-stock-row'
|
||||
} else if (row.availableStock === 0) {
|
||||
return 'zero-stock-row'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
|
||||
// 刷新整个页面
|
||||
handleRefreshAll() {
|
||||
this.getProductionOrderList()
|
||||
if (this.productionObj) {
|
||||
this.getMrpData()
|
||||
}
|
||||
},
|
||||
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
if (window.history.length > 1) {
|
||||
this.$router.go(-1)
|
||||
} else {
|
||||
// 如果没有历史记录,可以跳转到首页或其他页面
|
||||
this.$router.push('/')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mrp2-standalone-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.standalone-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 20px 0;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-left h1 {
|
||||
color: white !important;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.header-left p {
|
||||
color: rgba(255, 255, 255, 0.8) !important;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.mrp-result-card {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.mrp-status-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #b3d8ff;
|
||||
border-radius: 4px;
|
||||
color: #409eff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mrp-calculate-btn {
|
||||
background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
|
||||
border: none;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.mrp-calculate-btn:hover {
|
||||
background: linear-gradient(135deg, #66b1ff 0%, #409eff 100%);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(64, 158, 255, 0.4);
|
||||
}
|
||||
|
||||
.mrp-search-input {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.mrp-export-btn {
|
||||
background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
|
||||
border: none;
|
||||
box-shadow: 0 2px 4px rgba(103, 194, 58, 0.3);
|
||||
}
|
||||
|
||||
.mrp-export-btn:hover {
|
||||
background: linear-gradient(135deg, #85ce61 0%, #67c23a 100%);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(103, 194, 58, 0.4);
|
||||
}
|
||||
|
||||
/* 表格行样式 */
|
||||
:deep(.negative-stock-row) {
|
||||
background-color: #fef0f0 !important;
|
||||
}
|
||||
|
||||
:deep(.negative-stock-row:hover > td) {
|
||||
background-color: #fde2e2 !important;
|
||||
}
|
||||
|
||||
:deep(.zero-stock-row) {
|
||||
background-color: #fdf6ec !important;
|
||||
}
|
||||
|
||||
:deep(.zero-stock-row:hover > td) {
|
||||
background-color: #faecd8 !important;
|
||||
}
|
||||
|
||||
/* 生产令号选择器优化样式 */
|
||||
.production-order-select-dropdown {
|
||||
min-width: 400px !important;
|
||||
max-width: 600px !important;
|
||||
max-height: 400px !important;
|
||||
}
|
||||
|
||||
.production-order-select-dropdown .el-select-dropdown__item {
|
||||
height: auto !important;
|
||||
min-height: 40px !important;
|
||||
padding: 8px 16px !important;
|
||||
line-height: 1.4 !important;
|
||||
white-space: normal !important;
|
||||
overflow: visible !important;
|
||||
border-bottom: 1px solid #f0f0f0 !important;
|
||||
}
|
||||
|
||||
.production-order-select-dropdown .el-select-dropdown__item:last-child {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.production-order-select-dropdown .el-select-dropdown__item:hover {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
|
||||
.production-order-select-dropdown .el-select-dropdown__item.selected {
|
||||
background-color: #ecf5ff !important;
|
||||
color: #409eff !important;
|
||||
}
|
||||
|
||||
.production-order-select-dropdown .el-select-dropdown__item.is-disabled {
|
||||
color: #c0c4cc !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
/* 全局下拉选择器样式优化 */
|
||||
:deep(.el-select-dropdown__item) {
|
||||
height: auto !important;
|
||||
padding: 12px 20px !important;
|
||||
line-height: 1.4 !important;
|
||||
white-space: normal !important;
|
||||
}
|
||||
|
||||
:deep(.el-select-dropdown__item:hover) {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
|
||||
:deep(.el-select-dropdown__item.selected) {
|
||||
background-color: #ecf5ff !important;
|
||||
color: #409eff !important;
|
||||
}
|
||||
|
||||
/* 生产令号信息卡片样式 */
|
||||
.production-order-info {
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
border-left: 4px solid #409eff;
|
||||
}
|
||||
|
||||
.production-order-info .el-card__header {
|
||||
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
||||
border-bottom: 1px solid #b3d8ff;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.app-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mrp-status-indicator {
|
||||
font-size: 12px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.production-order-info .el-card__body {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user