Compare commits

...

3 Commits

Author SHA1 Message Date
FCD 01fce620fa 手动同步物料 2 months ago
FCD 25aaa8b4f3 备件新增字段 2 months ago
FCD fc27855f67 生产产线看板、白坯看板 2 months ago

@ -47,3 +47,11 @@ export function getProductionNumberPLC(data){
data: data
});
}
export function getProductionLineData(data){
return request({
url: '/mes/mesborad/getProductionLineData',
method: 'get',
data: data
});
}

@ -111,3 +111,12 @@ export function getKBTest() {
method: 'post'
});
}
// 白坯每月不合格数量
export function getBpMonthNoOk(data) {
return request({
url: '/quality/qcInterface/getBpMonthNoOk',
method: 'get',
data: data
});
}

@ -73,3 +73,11 @@ export function addProductAttached(data) {
data: data
});
}
// 手动同步物料
export function syncProduct(data) {
return request({
url: '/wms/product/sync',
method: 'post',
data: data
});
}

@ -156,6 +156,11 @@ export const constantRoutes = [
component: () => import("@/views/kanban/Dryingprocess/index"),
hidden: true,
},
{
path: "/kanban/productLine",
component: () => import("@/views/kanban/productLine/index"),
hidden: true,
},
// {
// path: "/report/board/LeadershipViewCheart",
// component: () => import("@/views/energy/LeadershipView/LeadershipViewChearts"),

@ -207,6 +207,12 @@
prop="materialDesc"
width="120"
/>
<el-table-column
label="库位编码"
align="center"
prop="wlCode"
width="120"
/>
<el-table-column
label="所属设备名称"
align="center"
@ -226,6 +232,32 @@
prop="spareMode"
width="200"
/>
<el-table-column
width="120"
label="备件类型"
align="center"
prop="spareType"
>
<template slot-scope="scope">
<dict-tag
:options="dict.type.spare_part_type"
:value="scope.row.spareType"
/>
</template>
</el-table-column>
<el-table-column
width="120"
label="备件品类"
align="center"
prop="spareCategory"
>
<template slot-scope="scope">
<dict-tag
:options="categoryList"
:value="scope.row.spareCategory"
/>
</template>
</el-table-column>
<el-table-column
label="单机装配数量"
align="center"
@ -396,115 +428,7 @@
<!-- 添加或修改备品备件台账管理对话框 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<!--
<el-form-item label="唯一序列" prop="storageId">
<el-input v-model="form.storageId" placeholder="请输入唯一序列" />
</el-form-item>
<el-form-item label="仓库编码" prop="whCode">
<el-input v-model="form.whCode" placeholder="请输入仓库编码" />
</el-form-item>
<el-form-item label="区域编号" prop="regionCode">
<el-input v-model="form.regionCode" placeholder="请输入区域编号" />
</el-form-item>
<el-form-item label="库区编码" prop="waCode">
<el-input v-model="form.waCode" placeholder="请输入库区编码" />
</el-form-item>
<el-form-item label="库位编码" prop="wlCode">
<el-input v-model="form.wlCode" placeholder="请输入库位编码" />
</el-form-item>
<el-form-item label="物料号" prop="materialCode">
<el-input v-model="form.materialCode" placeholder="请输入物料号" />
</el-form-item>
<el-form-item label="物料描述" prop="materialDesc">
<el-input v-model="form.materialDesc" placeholder="请输入物料描述" />
</el-form-item>
-->
<!-- <el-form-item label="占用数量" prop="occupyAmount">
<el-input v-model="form.occupyAmount" placeholder="请输入占用数量" />
</el-form-item>
<el-form-item label="LPN" prop="lpn">
<el-input v-model="form.lpn" placeholder="请输入LPN" />
</el-form-item>
<el-form-item label="入库批次号" prop="productBatch">
<el-input v-model="form.productBatch" placeholder="请输入入库批次号" />
</el-form-item>
<el-form-item label="入库时间x" prop="receiveDate">
<el-date-picker clearable
v-model="form.receiveDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择入库时间x">
</el-date-picker>
</el-form-item>
<el-form-item label="生产时间" prop="productDate">
<el-date-picker clearable
v-model="form.productDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择生产时间">
</el-date-picker>
</el-form-item>
<el-form-item label="SAP库位" prop="userDefined2">
<el-input v-model="form.userDefined2" placeholder="请输入SAP库位" />
</el-form-item>
<el-form-item label="备用3" prop="userDefined3">
<el-input v-model="form.userDefined3" placeholder="请输入备用3" />
</el-form-item>
<el-form-item label="备用4" prop="userDefined4">
<el-input v-model="form.userDefined4" placeholder="请输入备用4" />
</el-form-item>
<el-form-item label="备用5" prop="userDefined5">
<el-input v-model="form.userDefined5" placeholder="请输入备用5" />
</el-form-item>
<el-form-item label="备用6" prop="userDefined6">
<el-input v-model="form.userDefined6" placeholder="请输入备用6" />
</el-form-item>
<el-form-item label="备用7" prop="userDefined7">
<el-input v-model="form.userDefined7" placeholder="请输入备用7" />
</el-form-item>
<el-form-item label="备用8" prop="userDefined8">
<el-input v-model="form.userDefined8" placeholder="请输入备用8" />
</el-form-item>
<el-form-item label="备用9" prop="userDefined9">
<el-input v-model="form.userDefined9" placeholder="请输入备用9" />
</el-form-item>
<el-form-item label="备用10" prop="userDefined10">
<el-input v-model="form.userDefined10" placeholder="请输入备用10" />
</el-form-item>
<el-form-item label="创建时间" prop="gmtCreate">
<el-date-picker clearable
v-model="form.gmtCreate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择创建时间">
</el-date-picker>
</el-form-item>
<el-form-item label="最后更新人" prop="lastModifiedBy">
<el-input v-model="form.lastModifiedBy" placeholder="请输入最后更新人" />
</el-form-item>
<el-form-item label="最后更新时间" prop="gmtModified">
<el-date-picker clearable
v-model="form.gmtModified"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择最后更新时间">
</el-date-picker>
</el-form-item>
<el-form-item label="有效标记" prop="activeFlag">
<el-input v-model="form.activeFlag" placeholder="请输入有效标记" />
</el-form-item>
<el-form-item label="工厂号" prop="factoryCode">
<el-input v-model="form.factoryCode" placeholder="请输入工厂号" />
</el-form-item>
<el-form-item label="SAP工厂号" prop="sapFactoryCode">
<el-input v-model="form.sapFactoryCode" placeholder="请输入SAP工厂号" />
</el-form-item>
<el-form-item label="库位名称" prop="wlName">
<el-input v-model="form.wlName" placeholder="请输入库位名称" />
</el-form-item>
<el-form-item label="0存在" prop="delFlag">
<el-input v-model="form.delFlag" placeholder="请输入0存在" />
</el-form-item> -->
<el-row :gutter="10" class="mb8">
<el-col :span="12">
<el-form-item label="备件编码" prop="materialCode">
@ -524,6 +448,39 @@
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备件类型" prop="spareType">
<el-select v-model="form.spareType" placeholder="备件类型" clearable @change="changeType">
<el-option
v-for="dict in dict.type.spare_part_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备件品类" prop="spareCategory">
<el-select v-model="form.spareCategory" placeholder="备件品类" clearable>
<el-option
v-for="dict in categoryList"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="库位编码" prop="wlCode">
<el-input
v-model="form.wlCode"
placeholder="请输入库位编码"
style="width: 250px"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属设备编码" prop="ownEquipmentCode">
<el-input
@ -782,10 +739,11 @@ import {
} from "@/api/device/sparePartsLedger";
import { getToken } from "@/utils/auth";
import QRCode from 'qrcode';
import dict from "@/utils/dict";
export default {
name: "SparePartsLedger",
dicts: ['safety_stock'],
dicts: ['safety_stock', 'spare_part_type', 'fastening_sealing_parts','general_pipeline_parts','process_consumables','electric_control','pneumatic_components','hydraulic_components','power_transmission_parts'],
data() {
return {
//
@ -924,14 +882,43 @@ export default {
procurementMethod: [
{ required: true, message: "采购方式不能为空", trigger: "blur" },
],
spareType: [
{ required: true, message: "备件类型不能为空", trigger: "blur" },
],
spareCategory: [
{ required: true, message: "备件品类不能为空", trigger: "blur" },
],
wlCode: [
{ required: true, message: "库位不能为空", trigger: "blur" },
{ validator: this.validateWlCodeBlur, trigger: ["blur"] }
],
},
categoryList:[]
};
},
created() {
this.getList();
},
mounted(){
this.$nextTick(() => {
this.initCategoryList();
});
},
methods: {
dict,
validateWlCodeBlur(rule, value, callback){
if (typeof value !== 'string') {
callback(new Error('请输入正确的库位编码'));
}else {
const reg = /^[a-zA-Z0-9]+$/;
if (reg.test(value)){
callback();
}else {
callback(new Error('请输入正确的库位编码'));
}
}
},
//
indexMethod(index) {
return index + 1;
@ -1172,6 +1159,43 @@ export default {
`sparePartsLedger_template_${new Date().getTime()}.xlsx`
);
},
initCategoryList(){
//
this.$watch(
() => [
this.dict.type.power_transmission_parts,
this.dict.type.hydraulic_components,
this.dict.type.pneumatic_components,
this.dict.type.electric_control,
this.dict.type.process_consumables,
this.dict.type.general_pipeline_parts,
this.dict.type.fastening_sealing_parts
],
([val1, val2, val3, val4, val5, val6, val7,]) => {
// undefined
if (val1 && val2 && val3 && val4 && val5 && val6 && val7) {
this.categoryList = [
...val1,
...val2,
...val3,
...val4,
...val5,
...val6,
...val7
]
}
},
{ immediate: true, deep: true }
);
},
changeType(res){
if (!res){
this.initCategoryList()
this.form.spareCategory = ''
}else {
this.categoryList = this.dict.type[res]
}
},
},
};
</script>

@ -0,0 +1,420 @@
<template>
<!-- 屏幕适配容器基于1920*1080设计稿防溢出+完整适配 -->
<v-scale-screen width="1920" height="1080" :fullScreen="false">
<div class="app-container home">
<!-- 头部区域标题+看板时间 -->
<div class="head">
<div class="head-content">
<div class="title">产线生产看板</div>
<div class="board-time">{{ currentTime }}</div>
</div>
</div>
<!-- 产线统计概览核心数据总览 -->
<div class="overview-card">
<div class="overview-item">
<span class="overview-label">总产线数</span>
<span class="overview-value">18</span>
</div>
<div class="overview-item">
<span class="overview-label">生产产线</span>
<span class="overview-value success">{{ productionLines.length }}</span>
</div>
<div class="overview-item">
<span class="overview-label">计划总产量</span>
<span class="overview-value">{{ planTotal }}</span>
</div>
<div class="overview-item">
<span class="overview-label">实际完成产量</span>
<span class="overview-value" :class="{ 'overview-warning': completeRate < 80 }">{{ completeTotal }}</span>
</div>
<div class="overview-item">
<span class="overview-label">完成比例</span>
<span class="overview-value" :class="{ 'overview-warning': completeRate < 80 }">{{ completeRate }} %</span>
</div>
</div>
<!-- 产线网格6列3行适配18条产线优化内边距避免挤压 -->
<div class="production-grid" :class="{ 'production-grid-mini': productionLines.length < 13 }">
<div
class="production-card"
v-for="line in productionLines"
:key="line.lineNo"
:class="{ 'line-warning': line.completionRate < 80 }"
>
<div class="line-number"> {{ line.equipmentName }}</div>
<!-- 修复1Vue中title绑定语法错误改为v-bind:title -->
<div class="product-name" >{{ line.productName }}</div>
<div class="data-group">
<div class="data-item">
<span class="label">计划产量</span>
<span class="value plan-value">{{ line.planTotal }}</span>
</div>
<div class="data-item">
<span class="label">当前产量</span>
<span class="value current-value" :class="{ 'reach': line.currentOutput >= line.planOutput }">
{{ line.completTotal || 0 }}
</span>
</div>
<div class="data-item efficiency-item">
<div class="efficiency-top">
<span class="label">产量完成率</span>
<span class="value efficiency-value" :class="{ 'reach': line.completionRate >= 80 }">
{{ line.completionRate }}%
</span>
</div>
<div class="efficiency-progress">
<div
class="progress-bar"
:style="{ width: line.completionRate + '%' }"
:class="{ 'progress-reach': line.completionRate >= 80 }"
></div>
</div>
</div>
</div>
</div>
</div>
</div>
</v-scale-screen>
</template>
<script>
import {getProductionLineData} from "@/api/kanban/Packagingline";
export default {
name: 'ProductionBoard',
data() {
return {
productionLines: [],
currentTime: '',
planTotal:0,
completeTotal: 0,
completeRate: 0
}
},
created() {
this.initProductionData();
setInterval(() => {
this.initProductionData();
}, 5000);
setInterval(() => {
this.updateCurrentTime();
}, 1000);
},
methods: {
// 线1
initProductionData() {
getProductionLineData().then(res=>{
let planTotal = 0
let completeTotal = 0
for (const line of res.data){
if (line.planTotal){
planTotal = planTotal + line.planTotal
}
if (line.completTotal){
completeTotal = completeTotal + line.completTotal
line.completionRate = (line.completTotal / line.planTotal * 100).toFixed(1)
}else {
line.completionRate = 0
}
}
let completeRate = ((completeTotal/planTotal) * 100 ).toFixed(1)
this.planTotal = planTotal
this.completeTotal = completeTotal
this.completeRate = completeRate
this.productionLines = res.data
})
},
// YYYY-MM-DD HH:mm:ss
updateCurrentTime() {
const now = new Date()
this.currentTime = now.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).replace(/\//g, '-')
},
}
}
</script>
<style scoped>
/* 全局容器修复高度100%继承配合v-scale-screen满屏展示无溢出 */
.app-container {
padding: 0;
width: 100%;
box-sizing: border-box;
overflow: hidden; /* 核心:防止页面整体溢出滚动 */
}
.home {
width: 100%;
background: url("../../../assets/images/bg1.jpg") no-repeat center center;
background-size: cover;
background-color: #050711;
display: flex;
flex-direction: column;
gap: 16px; /* 区域间距,避免内容挤压 */
padding: 16px 24px; /* 全局内边距,防止内容贴边 */
box-sizing: border-box;
}
/* 头部区域:固定高度,避免占用过多空间,内容垂直居中 */
.head {
width: 100%;
height: 74px; /* 降低高度,为下方内容释放空间 */
position: relative;
display: flex;
align-items: center;
flex-shrink: 0; /* 禁止头部压缩,确保高度固定 */
}
/* .head-content 保留原有样式,无需修改 */
.head-content {
width: 100%;
height: 100%;
background: url("../../../assets/images/bg-head.png") no-repeat center center;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
position: relative;
}
/* 标题字体放大28px → 36px醒目且不挤压 */
.title {
position: absolute;
font-size: 36px;
color: #ffffff;
font-weight: 500;
margin: 0;
top: 0;
text-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
/* 时间字体同步放大16px → 20px与标题比例协调 */
.board-time {
position: absolute;
right: 20px;
top: 70%;
transform: translateY(-50%);
font-size: 20px;
color: #e0e0e0;
margin: 0;
}
/* 概览卡片:降低高度和内边距,紧凑布局,释放垂直空间 */
.overview-card {
width: 100%;
display: flex;
justify-content: space-around;
align-items: center;
border-radius: 8px;
padding: 20px 0; /* 减少上下内边距 */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
flex-shrink: 0; /* 禁止概览区压缩 */
height: 120px; /* 固定高度,避免占用过多空间 */
}
.overview-item {
text-align: center;
padding: 0 16px;
}
.overview-label {
display: block;
font-size: 24px;
color: #95a5a6;
margin-bottom: 4px; /* 减少间距,紧凑布局 */
}
.overview-value {
font-size: 26px; /* 降低字体,适配紧凑布局 */
color: #ffffff;
font-weight: 600;
line-height: 1.2;
}
.overview-value.success {
color: #2ecc71;
}
.overview-warning{
color: #f39c12;
}
/* 产线网格:核心修复显示不全,优化间距/高度,确保内容完整展示 */
.production-grid {
display: grid;
grid-template-columns: repeat(6, 1fr); /* 6列3行完美适配18条产线 */
grid-template-rows: repeat(3, 1fr); /* 等分剩余高度,无多余空白 */
gap: 20px; /* 减小网格间距,避免间距占用过多垂直空间 */
flex: 1; /* 填充父容器剩余高度,核心布局属性 */
box-sizing: border-box;
padding: 0; /* 移除多余内边距,释放空间 */
overflow: hidden; /* 防止网格内部内容溢出 */
}
.production-grid-mini{
grid-template-columns: repeat(4, 1fr); /* 5列4行完美适配18条产线 */
grid-template-rows: repeat(4, 1fr); /* 等分剩余高度,无多余空白 */
}
/* 单个产线卡片:彻底修复内容挤压,优化内边距/字体/间距 */
.production-card {
background: rgba(14, 28, 56, 0.9);
border-radius: 6px;
padding: 10px 12px; /* 适中内边距,内容不贴边也不挤压 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
box-sizing: border-box;
width: 100%;
height: 100%; /* 卡片满网格单元格高度,无空白 */
display: flex;
flex-direction: column;
gap: 6px; /* 减小内部元素间距,紧凑但不拥挤 */
}
/* 卡片悬浮效果:细腻交互,不影响布局 */
.production-card:hover {
box-shadow: 0 6px 16px rgba(0, 183, 255, 0.2);
transform: translateY(-2px);
border-color: rgba(0, 183, 255, 0.4);
}
/* 产量未达标标识:宽度适中,醒目不占空间 */
.line-warning {
border-left: 3px solid #f39c12;
}
/* 产线编号:字体适中,分隔线细腻 */
.line-number {
font-size: 24px;
font-weight: 600;
color: #ffffff;
padding-bottom: 4px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
line-height: 1.2;
white-space: nowrap;
margin: 0;
}
/* 产品名称:强制换行+高度自适应,超长名称自动折行显示 */
.product-name {
font-size: 22px;
color: #a0b4c8;
word-wrap: break-word !important; /* 强制换行,兼容所有浏览器 */
word-break: break-all !important; /* 中英文/数字混合也能正常换行,无截断 */
white-space: normal !important; /* 核心:取消原有不换行限制,覆盖所有冲突样式 */
min-height: 24px; /* 最小高度,避免内容过少时挤压下方 */
overflow: hidden; /* 超出最大高度时隐藏,不影响布局 */
cursor: help;
}
/* 数据组容器:弹性填充卡片剩余高度,内容垂直居中 */
.data-group {
display: flex;
flex-direction: column;
gap: 8px; /* 数据项间距适中,阅读舒适 */
flex: 1; /* 填充卡片剩余高度,无空白 */
justify-content: center; /* 内容垂直居中,避免挤压 */
box-sizing: border-box;
margin: 0;
}
/* 单个数据项:字体缩小,行高优化,紧凑布局 */
.data-item {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 20px; /* 核心:降低字体大小,适配小卡片,避免内容溢出 */
width: 100%;
line-height: 1.3; /* 行高适中,不重叠 */
margin: 0;
}
/* 标签和数值:颜色适配深色主题,对比度适中 */
.label {
color: #8fa4b8;
font-weight: 400;
}
.value {
color: #e0efff;
font-weight: 600;
}
/* 计划产量颜色:浅蓝色,清晰区分 */
.plan-value {
color: #4fc3f7;
}
/* 达标状态统一绿色,未达标浅橙色,视觉区分明确 */
.current-value.reach,
.completion-rate.reach,
.efficiency-value.reach {
color: #2ecc71;
}
.current-value,
.completion-rate {
color: #ffb74d;
}
/* 效率项:布局紧凑,进度条适配 */
.efficiency-item {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px; /* 减小间距,紧凑布局 */
width: 100%;
margin: 0;
}
.efficiency-top {
display: flex;
justify-content: space-between;
width: 100%;
align-items: center;
}
.efficiency-value {
font-size: 20px !important;
color: #ffb74d;
}
/* 效率进度条:高度适中,不占空间,样式细腻 */
.efficiency-progress {
width: 100%;
height: 5px; /* 降低进度条高度,紧凑布局 */
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
box-sizing: border-box;
}
.progress-bar {
height: 100%;
background: #f39c12;
border-radius: 2px;
transition: width 0.8s ease-in-out;
}
.progress-bar.progress-reach {
background: #2ecc71;
}
</style>

@ -601,7 +601,7 @@ export default {
};
myChart3.setOption(option3);
// console.log(_this.selectxt)
getProMonthNoOk({
getBpMonthNoOk({
typeCode: "produce",
factoryCode: "ds_" + _this.selectxt,
}).then((response) => {

@ -130,6 +130,28 @@
>同步物料工艺
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-download"
size="mini"
@click="handleSyncSingleProduct"
v-hasPermi="['wms:product:edit']"
>同步单个物料信息
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-download"
size="mini"
@click="handleSyncMultipleProduct"
v-hasPermi="['wms:product:edit']"
>同步所有物料信息
</el-button>
</el-col>
<right-toolbar
:showSearch.sync="showSearch"
@queryTable="getList"
@ -687,7 +709,7 @@ import {
addProduct,
updateProduct,
updateProductAttached,
addProductAttached,
addProductAttached, syncProduct,
} from "@/api/wms/product";
import { syncProductSAP } from "@/api/technology/proroute";
@ -1054,6 +1076,54 @@ export default {
this.form.height = null;
}
},
handleSyncSingleProduct(){
this.$prompt(
'请输入物料编码',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
inputPlaceholder: '请输入物料编码...',
inputPattern: /^.{11}$|^.{18}$/,
inputErrorMessage: '请输入正确的物料编码!',
closeOnClickModal: false,
}
).then((result) => {
let inputValue = result.value;
if (inputValue.length < 18){
inputValue = "0000000"+inputValue
}
syncProduct({
matnr:inputValue,
werks:localStorage.getItem('USER_POOL_NAME_CURRENT').replace("ds_","")
}).then(res=>{
if (res.code === 200){
this.$message.success(`操作成功!`);
}else {
this.$message.error(`操作失败!`+res.msg);
}
})
}).catch(() => {
});
},
handleSyncMultipleProduct(){
this.$modal
.confirm(
'是否同步所有物料?'
)
.then((res) => {
console.log(res)
if (res === "confirm"){
syncProduct({
werks:localStorage.getItem('USER_POOL_NAME_CURRENT').replace("ds_","")
}).then(()=>{
this.$message.success(`操作成功!`);
})
}
})
.catch(() => {});
}
},
};
</script>

Loading…
Cancel
Save