chore: update project files

This commit is contained in:
meswork764
2026-06-05 18:05:35 +08:00
parent 6ecd0b5bd5
commit 6f9f8fe148
13 changed files with 2501 additions and 561 deletions

View File

@@ -0,0 +1,489 @@
<template>
<div class="app-container">
<el-card style="height: 850px">
<div style="margin-top: 7px; margin-bottom: 7px">
<span class="show-text">生产订单:</span>
<el-input
v-model="orderNo"
placeholder="请输入生产订单"
clearable
style="width: 150px; margin-right: 10px"
@change="searchTable()"
/>
<span class="show-text">计划号:</span>
<el-input
v-model="plannum"
placeholder="请输入生产订单"
clearable
style="width: 150px; margin-right: 10px"
/>
<span class="show-text">物料编码:</span>
<el-input
v-model="productCode"
placeholder="请输入物料编码"
clearable
style="width: 150px; margin-right: 10px"
/>
<span class="show-text">物料名称:</span>
<el-input
v-model="itemName"
placeholder="请输入物料名称"
clearable
style="width: 150px; margin-right: 10px"
/>
<span class="show-text">工序名称:</span>
<el-input
v-model="processName"
placeholder="请输入工序名称"
clearable
style="width: 150px; margin-right: 10px"
/>
<span class="show-text">执行状态:</span>
<el-select v-model="dostate" placeholder="请选择">
<el-option
v-for="item in dostateoptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-button
icon="el-icon-search"
plain
type="primary"
@click="searchTable()"
>查询</el-button
>
</div>
<el-table
:data="mergedTableData"
highlight-current-row
border
size="small"
style="margin-top: 10px"
height="75vh"
@selection-change="handleSelectionChange"
@row-click="handleRowClick">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="序号" type="index" align="center" width="50"/>
<el-table-column label="生产订单" align="center" prop="订单号" width="80" />
<el-table-column label="计划号" align="center" prop="计划号" width="80" />
<el-table-column label="工序名称" align="center" prop="工序名称" width="120"/>
<el-table-column label="物料名称" align="center" prop="产品名称" width="200"/>
<el-table-column label="同步状态" align="center" width="100">
<template slot-scope="scope">
{{scope.row.派工状态 === 1 ? '已同步':'未同步'}}
</template>
</el-table-column>
<el-table-column label="上序合格数" align="center" prop="上序合格数" width="100"/>
<el-table-column label="指派数量" align="center" prop="指派数量" width="80"/>
<el-table-column label="打印数量" align="center" prop="打印数量" width="80"/>
<el-table-column label="发料数量" align="center" prop="收料数量" width="80"/>
<el-table-column label="收货数量" align="center" prop="完成数量" width="80"/>
<el-table-column label="备注" align="center" prop="外协备注" width="200"/>
<el-table-column label="单重" align="center" prop="单重" width="80"/>
<el-table-column label="物料编码" align="center" prop="产品编码" width="200"/>
<el-table-column label="打印次数" align="center" prop="外协打印次数" width="120"/>
<el-table-column label="计划开始时间" align="center" prop="计划开始时间" width="120">
<template slot-scope="scope">
{{formatDate(scope.row.计划开始时间)}}
</template>
</el-table-column>
<el-table-column label="计划完成时间" align="center" prop="计划完成时间" width="120">
<template slot-scope="scope">
{{formatDate(scope.row.计划完成时间)}}
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { postB1 } from '@/api/b1s';
import $ from 'jquery'
import QRCode from 'qrcode'
import { set } from 'nprogress';
import { MESDownFile } from '@/utils/request'
import { seepdf, initPDFKeyboardShortcuts, setZoom } from '@/utils/pdf';
export default {
data() {
return {
downPDF:MESDownFile,
seeVisible:false,
qrCodeDataUrl:'',
printDataList2:[],
historyVisible:false,
printdata2:[],
historydata:[],
dialogFormVisible1: false,
tableData: [], // 原始数据
mergedTableData: [], // 合并后的数据
selectedRow:'',
selectedRow2:'',
productCode:'',
orderNo:'',
plannum:'',
processName:'',
itemName:'',
sessionId:'',
currentTime:'',
receiveVisible:false,
receiveData: {},
receiveNum: 0,
doVisible:false,
doData: {},
doNum: 0,
// 新增:存储合并数据的原始信息
mergedDataMap: new Map(), // 用于存储合并数据对应的原始数据组
workstate:0,
orderstate:0,
dostate:1,
workstateoptions:[{
value: 0,
label: '未同步'
}, {
value: 1,
label: '已同步',
disabled: true
}],
stateoptions:[{
value: 0,
label: '未完成',
disabled: true
}, {
value: 1,
label: '已完成',
}],
dostateoptions:[{
value: 0,
label: '全部',
disabled: true
}, {
value: 1,
label: '可执行',
},{
value: 2,
label: '不可执行',
}],
multipleSelection: [],
printDataList: [] ,// 新增:存储多个打印数据
numname:'',
textweight:'',
textsum:'',
textcontent:'',
textData:{},
textVisible:false,
qrCodeCache: new Map(),
qrCodeUrl: '',
}
},
computed: {
...mapGetters([
'id',
'token'
]),
},
created() {
this.searchTable()
setInterval(this.getCurrentTime, 1000);
},
methods: {
handleSelectionChange(val) {
this.multipleSelection = val;
},
getCurrentTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
const seconds = String(now.getSeconds()).padStart(2, "0");
this.currentTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
handleRowClick(row) {
this.selectedRow = row
},
handleRowClick2(row) {
this.selectedRow2 = row
},
searchTable() {
this.loading = true
var param = []
param.push(["订单号",this.orderNo])
param.push(["产品编码",this.productCode])
param.push(["产品名称",this.itemName])
param.push(["工序名称",this.processName])
param.push(["计划号",this.plannum])
var Data = this.CreateData('11', '异常提醒_外协及时开工_查询',param)
this.ExecDatabase(Data).then(response => {
this.tableData = response.data
this.mergeTableData() // 处理数据合并
})
},
// 合并表格数据的方法
mergeTableData() {
if (!this.tableData || this.tableData.length === 0) {
this.mergedTableData = [];
this.mergedDataMap.clear();
return;
}
const groupedByOrder = {};
this.tableData.forEach(item => {
const groupKey = `${item.订单号}_${item.拆分内码}`;
if (!groupedByOrder[groupKey]) {
groupedByOrder[groupKey] = [];
}
groupedByOrder[groupKey].push(item);
});
const mergedData = [];
this.mergedDataMap.clear();
Object.keys(groupedByOrder).forEach(orderNo => {
const orderItems = groupedByOrder[orderNo];
orderItems.sort((a, b) => a.订单行号 - b.订单行号);
let currentGroup = [orderItems[0]];
for (let i = 1; i < orderItems.length; i++) {
const currentItem = orderItems[i];
const lastItem = orderItems[i - 1];
if (currentItem.订单行号 === lastItem.订单行号 + 1 && currentItem.外协分组 === lastItem.外协分组 && currentItem.拆分内码 === lastItem.拆分内码) {
currentGroup.push(currentItem);
} else {
// 检查组内最小计划开始时间是否小于今天
if (this.checkGroupPlanStartValid(currentGroup)) {
if (this.checkGroupAllZero(currentGroup)) {
const mergedItem = this.mergeGroup(currentGroup);
if (this.shouldIncludeByExecutionStatus(mergedItem)) {
mergedData.push(mergedItem);
this.mergedDataMap.set(mergedItem.uniqueKey, currentGroup);
}
}
}
currentGroup = [currentItem];
}
}
// 处理最后一组
if (currentGroup.length > 0) {
if (this.checkGroupPlanStartValid(currentGroup)) {
if (this.checkGroupAllZero(currentGroup)) {
const mergedItem = this.mergeGroup(currentGroup);
if (this.shouldIncludeByExecutionStatus(mergedItem)) {
mergedData.push(mergedItem);
this.mergedDataMap.set(mergedItem.uniqueKey, currentGroup);
}
}
}
}
});
this.mergedTableData = mergedData;
},
// 新增:检查组内最小计划开始时间是否小于今天
checkGroupPlanStartValid(group) {
const today = new Date();
today.setHours(0, 0, 0, 0);
// 获取组内最小的计划开始时间
const minPlanStart = group.reduce((min, item) => {
if (!item.计划开始时间) return min;
const planDate = new Date(item.计划开始时间);
planDate.setHours(0, 0, 0, 0);
return planDate < min ? planDate : min;
}, new Date('2099-12-31'));
return minPlanStart < today;
},
// 新增检查组内所有数据的收料数量是否都为0
checkGroupAllZero(group) {
// 只有组内所有数据的收料数量都等于0才返回true
return group.every(item => {
const 收料数量 = Number(item.收料数量) || 0;
return 收料数量 === 0;
});
},
// 新增:根据执行状态筛选数据(纯前端筛选)
shouldIncludeByExecutionStatus(mergedItem) {
const 执行状态 = this.dostate; // 0=全部, 1=可执行, 2=不可执行
if (执行状态 === 0) {
return true; // 全部,不筛选
}
const itemExecutionStatus = mergedItem.执行状态 || 1; // 默认可执行
if (执行状态 === 1) {
return itemExecutionStatus === 1; // 只显示可执行
} else if (执行状态 === 2) {
return itemExecutionStatus === 2; // 只显示不可执行
}
return true;
},
// 合并单个组的数据
mergeGroup(group) {
if (group.length === 1) {
const item = group[0];
item.uniqueKey = `${item.订单号}_${item.订单行号}_${item.TaskAID}`;
// 确保TaskAID是字符串类型
item.TaskAID = String(item.TaskAID);
// 计算单条记录的执行状态
const 上序合格数 = item.上序合格数 || 0;
const 订单行号 = item.订单行号;
item.执行状态 = (订单行号 === 1 || 上序合格数 > 0) ? 1 : 2;
return item;
}
const mergedItem = { ...group[0] };
// 生成唯一标识key
mergedItem.uniqueKey = group.map(item => `${item.订单号}_${item.订单行号}_${item.TaskAID}`).join('|');
// 拼接工序名称(逗号间隔)
const processNames = group.map(item => item.工序名称);
mergedItem.工序名称 = processNames.join(',');
// 拼接TaskAID逗号间隔
const taskAIDs = [...new Set(group.map(item => String(item.TaskAID)))];
mergedItem.TaskAID = taskAIDs.join(',');
// 取计划开始时间的最小值
const startTimes = group.map(item => new Date(item.计划开始时间));
mergedItem.计划开始时间 = new Date(Math.min(...startTimes)).toISOString().slice(0, 19).replace('T', ' ');
// 取计划完成时间的最大值
const endTimes = group.map(item => new Date(item.计划完成时间));
mergedItem.计划完成时间 = new Date(Math.max(...endTimes)).toISOString().slice(0, 19).replace('T', ' ');
// 取最大值
const wcnum = group.map(item => item.完成数量);
mergedItem.完成数量 = Math.max(...wcnum);
const printnum = group.map(item => item.外协打印次数);
mergedItem.外协打印次数 = Math.max(...printnum);
const slnum = group.map(item => item.收料数量);
mergedItem.收料数量 = Math.max(...slnum);
// 计算上序合格数(取组内最大值)
const shxnum = group.map(item => item.上序合格数 || 0);
mergedItem.上序合格数 = Math.max(...shxnum);
// 计算执行状态:只要一组内包含订单行号=1 或者包含上序合格数>0都属于可执行
const hasFirstProcess = group.some(item => item.订单行号 === 1);
const hasPreviousQualified = group.some(item => (item.上序合格数 || 0) > 0);
mergedItem.执行状态 = (hasFirstProcess || hasPreviousQualified) ? 1 : 2;
// 存储原始组数据引用
mergedItem.originalGroup = group;
return mergedItem;
},
// 获取订单行号最大的TaskAID
getMaxOrderLineTaskAID(row) {
// 如果是单条数据直接返回TaskAID
if (typeof row.TaskAID === 'number' || !row.TaskAID.includes(',')) {
return String(row.TaskAID);
}
// 如果是合并数据从原始数据组中找订单行号最大的TaskAID
if (row.originalGroup) {
const maxOrderLineItem = row.originalGroup.reduce((max, item) => {
return item.订单行号 > max.订单行号 ? item : max;
}, row.originalGroup[0]);
return String(maxOrderLineItem.TaskAID);
}
// 如果没有原始组数据从TaskAID字符串中取最后一个
const taskAIDs = row.TaskAID.split(',');
return taskAIDs[taskAIDs.length - 1];
},
// 获取订单行号最小的TaskAID
getMinOrderLineTaskAID(row) {
// 如果是单条数据直接返回TaskAID
if (typeof row.TaskAID === 'number' || !row.TaskAID.includes(',')) {
return String(row.TaskAID);
}
// 如果是合并数据从原始数据组中找订单行号最大的TaskAID
console.log(row)
if (row.originalGroup) {
const minOrderLineItem = row.originalGroup.reduce((min, item) => {
return item.订单行号 < min.订单行号 ? item : min;
}, row.originalGroup[0]);
return String(minOrderLineItem.TaskAID);
}
// 如果没有原始组数据从TaskAID字符串中取最后一个
const taskAIDs = row.TaskAID.split(',');
return taskAIDs[taskAIDs.length - 1];
},
formatDate(date) {
if (!date) return ''
return date.split(' ').length > 1 ? date.split(' ')[0] : date
},
}
}
</script>
<style>
.field-container {
border: 1px solid #000;
border-bottom:0px
}
.field-row {
display: flex;
width: 100%;
border-bottom: 1px solid #000;
}
.field-group {
display: flex;
width: 25%;
align-items: stretch;
border-right: 1px solid #000;
}
.field-group:last-child {
border-right: none;
}
.field-label {
width: 40%;
color: #000;
text-align: right;
padding: 12px 5px;
border-right: 1px solid #000;
display: flex;
align-items: center;
justify-content: flex-end;
}
.field-value {
width: 60%;
color: #303133;
word-break: break-word;
overflow-wrap: break-word;
padding: 12px 10px;
display: flex;
align-items: center;
}
</style>

View File

@@ -64,6 +64,14 @@
<el-option label="已开工" value="2" />
<el-option label="已完成" value="4" />
</el-select>
<el-switch
v-model="stationFilterMode"
active-text="工位正选"
inactive-text="工位反选"
active-value="include"
inactive-value="exclude"
style="margin-left: 20px;"
/>
<el-button
:disabled="loading"
icon="el-icon-search"
@@ -76,6 +84,7 @@
<el-checkbox v-for="item in Doingoptions" :key="item.工位号" :label="item.指南工位号" :value="item.工位号"> </el-checkbox>
</el-checkbox-group>
</div>
<el-table
v-loading="loading"
@@ -320,6 +329,7 @@ export default {
],
Doing: [],
Doingoptions: [],
stationFilterMode: 'include',
// DoingData: {
// 订单编号: "",
// 合同号: "",
@@ -549,6 +559,7 @@ export default {
param.push(["生产状态", this.productionStatus2]||-1);
param.push(["工位名称", this.Doing]);
param.push(["工位筛选模式", this.stationFilterMode]);
param.push(["用户ID" , this.$store.state.user.id])
param.push(["计划号" , this.plannum])
param.push(["销售订单" , this.salenum])

View File

@@ -18,7 +18,7 @@
/>
&nbsp;&nbsp;
<span class="show-text">设备:</span>
<el-select v-model="equipmentType" placeholder="请选择设备" filterable size="small" style="width:200px" @change="searchTable" >
<el-select v-model="equipmentType" placeholder="请选择设备" filterable size="small" style="width:100px" @change="searchTable" >
<el-option
v-for="item in basedatalx"
:key="item.id"
@@ -43,7 +43,7 @@
</el-table-column>
<el-table-column label="参数值" align="center">
<template slot-scope="scope">
{{ scope.row.参数值 }}
{{ scope.row.参数值 }}{{ scope.row.参数单位 }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100px">
@@ -53,7 +53,7 @@
</el-table-column>
</el-table>
</div>
<div style="width: 600px;margin-left: 5px">
<div style="width: 800px;margin-left: 5px">
<el-table v-if="dataType === 1" :data="tableData1" v-loading="loading" border size="small" stripe highlight-current-row height="740px" >
<el-table-column label="开始时间" prop="Zone" align="center" width="150px" >
<template slot-scope="scope">
@@ -70,29 +70,130 @@
{{ scope.row.状态名称 }}
</template>
</el-table-column>
<el-table-column label="时长(m)" align="center">
<el-table-column label="时长(min)" align="center">
<template slot-scope="scope">
{{ scope.row.累计分钟数 }}
</template>
</el-table-column>
</el-table>
<el-table v-if="dataType === 2" :data="tableData1" border size="small" stripe highlight-current-row height="740px" >
<el-table-column label="订单编号" prop="Zone" align="center" width="150px" >
<div v-if="dataType === 2">
<span class="show-text">订单编号:</span>
<el-input
v-model="orderNo"
placeholder="请输入订单编号"
clearable
style="width: 150px; margin-right: 10px"
/>
<el-button type="primary" icon="el-icon-search" size="small" style="margin-left: 20px" plain @click="handleSearch(currentRow)">查询</el-button>
<el-table :data="tableData1" border size="small" stripe highlight-current-row height="700px" style="margin-top: 5px" >
<el-table-column label="订单编号" prop="Zone" align="center" width="100px" >
<template slot-scope="scope">
{{ scope.row.订单编号 }}
</template>
</el-table-column>
<el-table-column label="加工时长(min)" prop="TagName" align="center">
<template slot-scope="scope">
{{ scope.row.循环时间 }}
</template>
</el-table-column>
<el-table-column label="换件时长(min)" prop="TagName" align="center">
<template slot-scope="scope">
{{ scope.row.换件时间 }}
</template>
</el-table-column>
<el-table-column label="采集时间" align="center" width="150px" >
<template slot-scope="scope">
{{ scope.row.采集时间 }}
</template>
</el-table-column>
</el-table>
<div class="block" style="margin-top: 10px;">
<el-pagination
:current-page="pageCurrent"
:page-sizes="[20, 40, 50, 100]"
:page-size="pageSize"
:total="total"
:small="true"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChanges"
@current-change="handleCurrentChanges"/>
</div>
</div>
<el-table v-if="dataType === 3" :data="tableData1" border size="small" stripe highlight-current-row height="740px" >
<el-table-column label="订单编号" prop="Zone" align="center" width="100px" >
<template slot-scope="scope">
{{ scope.row.订单编号 }}
</template>
</el-table-column>
<el-table-column label="加工时长" prop="TagName" align="center">
<el-table-column label="程序时长(min)" prop="Zone" align="center" width="120px" >
<template slot-scope="scope">
{{ scope.row.循环时间 }}
<span style="color: #0ba9e3">{{ scope.row.程序时长 }}</span>
</template>
</el-table-column>
<el-table-column label="采集时间" align="center" width="150px" >
<el-table-column label="最小时长(min)" prop="Zone" align="center" width="120px" >
<template slot-scope="scope">
{{ scope.row.采集时间 }}
{{ scope.row.最小时长 }}
</template>
</el-table-column>
<el-table-column label="最大时长(min)" prop="Zone" align="center" width="120px" >
<template slot-scope="scope">
{{ scope.row.最大时长 }}
</template>
</el-table-column>
<!-- <el-table-column label="频次/总数" prop="Zone" align="center" >-->
<!-- <template slot-scope="scope">-->
<!-- {{ scope.row.频次 }} / {{ scope.row.总记录数 }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column label="产品编码" prop="Zone" align="center">
<template slot-scope="scope">
{{ scope.row.产品编码 }}
</template>
</el-table-column>
<el-table-column label="产品名称" prop="Zone" align="center" >
<template slot-scope="scope">
{{ scope.row.产品名称 }}
</template>
</el-table-column>
</el-table>
<el-table v-if="dataType === 4" :data="tableData1" border size="small" stripe highlight-current-row height="740px" >
<el-table-column label="订单编号" prop="Zone" align="center" width="100px" >
<template slot-scope="scope">
{{ scope.row.订单编号 }}
</template>
</el-table-column>
<el-table-column label="换件时长(min)" prop="Zone" align="center" width="120px" >
<template slot-scope="scope">
<span style="color: #0ba9e3">{{ scope.row.换件时长 }}</span>
</template>
</el-table-column>
<el-table-column label="最小时长(min)" prop="Zone" align="center" width="120px" >
<template slot-scope="scope">
{{ scope.row.最小时长 }}
</template>
</el-table-column>
<el-table-column label="最大时长(min)" prop="Zone" align="center" width="120px" >
<template slot-scope="scope">
{{ scope.row.最大时长 }}
</template>
</el-table-column>
<!-- <el-table-column label="频次/总数" prop="Zone" align="center" >-->
<!-- <template slot-scope="scope">-->
<!-- {{ scope.row.频次 }} / {{ scope.row.总记录数 }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column label="产品编码" prop="Zone" align="center" >
<template slot-scope="scope">
{{ scope.row.产品编码 }}
</template>
</el-table-column>
<el-table-column label="产品名称" prop="Zone" align="center" >
<template slot-scope="scope">
{{ scope.row.产品名称 }}
</template>
</el-table-column>
</el-table>
</div>
</div>
@@ -120,6 +221,7 @@ export default {
completionTime: [],
routineCheckType: [],
dataType: 0,
orderNo: '',
loading: false,
dialogFormVisible: false,
title: '',
@@ -146,6 +248,7 @@ export default {
设备类型: [{ required: true, message: '请选择' }]
},
original: '',
currentRow: null,
total: 0, // 分页总数
tableData: [], // 设备信息表
tableData1: [], // 设备信息表
@@ -274,16 +377,31 @@ export default {
this.loading = true
this.tableData1 = []
this.dataType = row.数据类型
this.currentRow = row
var param = []
param.push(['PageCurrent', this.pageCurrent])
param.push(['PageSize', this.pageSize])
param.push(['PageCount', '1111', 'int', '1'])
param.push(['ItemCount', '1111', 'int', '1'])
param.push(['开始时间', this.completionTime[0]])
param.push(['结束时间', this.completionTime[1]])
param.push(['设备名称', this.equipmentType])
param.push(['参数名称', row.参数名称])
param.push(['数据类型', row.数据类型])
param.push(['订单编号', this.orderNo])
var Data = this.CreateData('11', '设备管理_设备数据_统计数据查询_通过类型', param)
this.ExecDatabase(Data).then(response => {
this.loading = false
this.tableData1 = row.数据类型 === 1 ? this.mergeContinuousStatus(response.data) : response.data
console.log(response.data)
if (row.数据类型 === 1) {
this.tableData1 = this.mergeContinuousStatus(response.data.result)
} else if (row.数据类型 === 2) {
this.tableData1 = response.data.result
this.total = parseInt(response.data.output[0].ItemCount)
} else {
this.tableData1 = response.data.result
}
// this.tableData = response.data.result
// this.total = parseInt(response.data.output[0].ItemCount)
}).catch(() => {
@@ -293,11 +411,11 @@ export default {
handleSizeChanges(val) {
this.pageCurrent = 1
this.pageSize = val
this.searchTable()
this.handleSearch(this.currentRow)
},
handleCurrentChanges(val) {
this.pageCurrent = val
this.searchTable()
this.handleSearch(this.currentRow)
}
}
}

View File

@@ -15,12 +15,12 @@
<el-input v-model="doneprocessName" placeholder="请输入工序名称" clearable style="width: 150px;margin-right: 10px" />
<br>
<br>
<span class="show-text">排产状态:</span>
<!-- <span class="show-text">排产状态:</span>
<el-select v-model="doneproductionStatus" clearable placeholder="请选择状态" style="width: 150px;margin-right: 10px">
<el-option label="全部" value="全部" />
<el-option label="已完成" value="已完成" />
<el-option label="未完成" value="未完成" />
</el-select>
</el-select> -->
<span class="show-text">要求完工日期:</span>
<el-date-picker
v-model="completionTime"
@@ -37,25 +37,20 @@
<el-date-picker
v-model="planStartTime"
:clearable="false"
end-placeholder="结束时间"
format="yyyy-MM-dd"
popper-class="virtual-cell-time-picker"
range-separator="-"
start-placeholder="开始时间"
style="width: 280px;vertical-align: top"
type="datetimerange"
placeholder="计划开始时间"
style="width: 150px;
vertical-align: top"
type="date"
value-format="yyyy-MM-dd" />
<span class="show-text">计划完成:</span>
<el-date-picker
v-model="planendTime"
:clearable="false"
end-placeholder="结束时间"
format="yyyy-MM-dd"
popper-class="virtual-cell-time-picker"
range-separator="-"
start-placeholder="开始时间"
style="width: 280px;vertical-align: top"
type="datetimerange"
placeholder="计划完成时间"
style="width: 150px;
vertical-align: top"
type="date"
value-format="yyyy-MM-dd" />
<el-button :disabled="loading" icon="el-icon-search" plain type="primary" @click="donesearchTable()">查询</el-button>
</div>
@@ -375,8 +370,8 @@ export default {
station:'全部',
completids:[],
completionTime: [],
planStartTime: [],
planendTime: [],
planStartTime: '',
planendTime: '',
loading: false,
tableData: [],
donetableData: [],
@@ -770,13 +765,11 @@ export default {
// param.push(['性质', this.nature])
param.push(['要求完工日期1', this.completionTime[0]])
param.push(['要求完工日期2', this.completionTime[1]])
param.push(['计划开始1', this.planStartTime[0]])
param.push(['计划开始2', this.planStartTime[1]])
param.push(['计划完成1', this.planendTime[0]])
param.push(['计划完成2', this.planendTime[1]])
param.push(['计划开始', this.planStartTime])
param.push(['计划完成', this.planendTime])
param.push(['排产状态', this.productionStatus])
param.push(['指派对象', this.station])
var Data = this.CreateData('11', 'MES_OrderPlanned_Query', param,this.pageSize, this.pageCurrent)
var Data = this.CreateData('11', '机加已排产_获取任务列表', param,this.pageSize, this.pageCurrent)
this.ExecDatabase(Data).then(response => {
this.loading = false
@@ -825,16 +818,13 @@ export default {
param.push(['产品编码', this.doneproductCode])
param.push(['产品名称', this.doneproductName])
param.push(['工序名称', this.doneprocessName])
// param.push(['性质', this.donenature])
param.push(['要求完工日期1', this.completionTime[0]])
param.push(['要求完工日期2', this.completionTime[1]])
param.push(['计划开始1', this.planStartTime[0]])
param.push(['计划开始2', this.planStartTime[1]])
param.push(['计划完成1', this.planendTime[0]])
param.push(['计划完成2', this.planendTime[1]])
param.push(['计划开始', this.planStartTime])
param.push(['计划完成', this.planendTime])
param.push(['排产状态', this.doneproductionStatus])
param.push(['指派对象', this.station])
var Data = this.CreateData('11', 'MES_OrderPlanned_Query', param,this.pageSize, this.pageCurrent)
var Data = this.CreateData('11', '机加已排产_获取任务列表', param,this.pageSize, this.pageCurrent)
this.ExecDatabase(Data).then(response => {
this.loading = false

File diff suppressed because it is too large Load Diff

View File

@@ -259,6 +259,14 @@
prop="工序名称"
label="工序名称"
width="80"
/>
<el-table-column
align="center"
prop="上序合格数"
label="上序合格数"
width="80"
/>
<!-- <el-table-column align="center" label="开工时间" width="100">
<template slot-scope="scope">{{formatDate(scope.row.开工时间)}}</template>

View File

@@ -87,6 +87,7 @@
@row-click="handleRowClick">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="序号" type="index" align="center" width="50"/>
<el-table-column label="状态" align="center" prop="单据状态" width="80" />
<el-table-column label="生产订单" align="center" prop="订单号" width="80" />
<el-table-column label="计划号" align="center" prop="计划号" width="80" />
<el-table-column label="工序名称" align="center" prop="工序名称" width="120"/>

View File

@@ -32,6 +32,12 @@
<el-option label="加急" value="加急" />
<el-option label="正常" value="正常" />
</el-select>
<span class="show-text">单据状态:</span>
<el-select v-model="orderStatus" clearable placeholder="请选择单据状态" style="width: 150px;margin-right: 10px">
<el-option label="全部" value="全部" />
<el-option label="关闭" value="关闭" />
<el-option label="正常" value="正常" />
</el-select>
<!-- 其他搜索条件保持不变 -->
<el-button :disabled="loading" icon="el-icon-search" plain type="primary" @click="searchTable()">查询</el-button>
@@ -86,7 +92,7 @@
<!-- 主表列定义 -->
<el-table-column align="center" label="序号" type="index" width="50" />
<el-table-column align="center" prop="合同号" label="销售订单" width="120" />
<el-table-column align="center" prop="计划号" label="计划号" width="120" />
<el-table-column align="center" prop="订单号" label="生产订单" width="100" />
<el-table-column align="center" prop="产品编码" label="产品编码" width="220" />
<el-table-column align="center" prop="产品名称" label="产品名称" width="190" />
@@ -143,6 +149,7 @@
</el-table-column>
<el-table-column align="center" prop="当前序" label="当前序" width="80" />
<el-table-column align="center" prop="工艺路线" label="工艺路线" width="180" />
<el-table-column align="center" prop="计划号" label="计划号" width="120" />
</el-table>
</el-card>
<el-dialog
@@ -214,6 +221,7 @@ export default {
// 新增过滤字段
outsourceStatus: '全部', // 外协状态
urgentStatus: '全部', // 加急状态
orderStatus:'正常',
completionTime: [],
loading: false,
tableData: [],
@@ -477,6 +485,7 @@ export default {
param.push(['指派对象', '全部'])
param.push(['计划号', this.plannum])
param.push(['加急状态', this.urgentStatus])
param.push(['单据状态', this.orderStatus])
// // 添加外协状态过滤条件
// if (this.outsourceStatus !== '全部') {
// param.push(['是否外协', this.outsourceStatus === '是' ? 1 : 0])

View File

@@ -97,6 +97,7 @@
@row-click="handleRowClick">
<el-table-column type="selection" width="50"></el-table-column>
<!-- <el-table-column label="序号" type="index" align="center" width="50"/> -->
<el-table-column label="单据状态" align="center" prop="单据状态" width="80" />
<el-table-column label="生产订单" align="center" prop="订单号" width="80" />
<!-- <el-table-column label="计划号" align="center" prop="计划号" width="80" /> -->
<el-table-column label="物料名称" align="center" prop="产品名称" width="120"/>

View File

@@ -221,6 +221,18 @@
width="80"
prop="工位名称"
/>
<el-table-column
align="center"
label="供应商"
width="80"
prop="供应商"
/>
<el-table-column
align="center"
label="采购收货单"
width="80"
prop="采购收货单"
/>
<el-table-column
align="center"
label="加急数量"

View File

@@ -35,8 +35,16 @@
<section class="panel metric-panel late-panel">
<div class="panel-title">未及时开工</div>
<div class="metric-content">
<img :src="icons.calendar" alt="" />
<strong class="orange">{{ summary.lateStart }}</strong>
<!-- <img :src="icons.calendar" alt="" />
<strong class="orange">{{ summary.lateStart }}</strong> -->
<div class="finish-stat2">
<span>机加</span>
<strong class="orange">{{ summary.lateStart }}</strong>
</div>
<div class="finish-stat2">
<span>外协</span>
<strong class="red">{{ summary.outlateStart}}</strong>
</div>
</div>
</section>
@@ -51,7 +59,6 @@
<section class="panel finished-panel">
<div class="panel-title">已完工作务 / 合格率</div>
<div class="finish-content">
<!-- <img :src="icons.clipboard" alt="" /> -->
<div class="finish-stat">
<span>已完工</span>
<strong>{{ summary.finishedTask }}</strong>
@@ -77,24 +84,12 @@
</div>
</section>
<!-- <section class="panel oee-chart-panel">
<div class="panel-title">设备 OEE TOP10</div>
<div ref="oeeChart" class="bar-chart"></div>
</section> -->
<section class="panel late-detail-panel">
<div class="panel-title">未及时开工明细</div>
<div class="table-box datav-table">
<dv-scroll-board :config="lateStartBoardConfig" />
</div>
</section>
<section class="panel defect-detail-panel">
<div class="panel-title">不良品明细</div>
<div class="table-box datav-table">
<dv-scroll-board :config="defectBoardConfig" />
</div>
</section>
<section class="panel assignee-summary-panel">
<div class="panel-title">未及时开工汇总(工位)</div>
@@ -105,6 +100,13 @@
</div>
</div>
</section>
<section class="panel out-late-detail-panel">
<div class="panel-title">外协未及时开工明细</div>
<div class="table-box datav-table">
<dv-scroll-board :config="outlateStartBoardConfig" />
</div>
</section>
</main>
</div>
</template>
@@ -112,9 +114,13 @@
<script setup>
import * as echarts from 'echarts'
import { computed, inject, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
const tableData = ref([])
const mergedTableData = ref([])
const mergedDataMap = ref(new Map())
const CreateData = inject('CreateData')
const ExecDatabase = inject('ExecDatabase')
defineOptions({
name: 'MachiningOperationDashboard'
})
@@ -160,11 +166,13 @@ let resizeHandler = null
const summary = reactive({
equipmentTotal: 18,
running: 10,
running2: 0,
standby: 4,
alarm: 2,
shutdown: 2,
planRate: 92.5,
lateStart: 12,
outlateStart: 0,
defectTotal: 18,
finishedTask: 156,
passRate: 98.6
@@ -178,6 +186,7 @@ const runStatusCards = computed(() => [
{ label: '报警', value: summary.alarm, icon: icons.alarm, className: 'red', valueClass: 'red-text' },
{ label: '关机', value: summary.shutdown, icon: icons.power, className: 'gray', valueClass: 'gray-text' }
])
const equipmentRows = ref([
{ name: '数 1', status: '运行', auto: 'power', auxiliary: 'run', utilization: 86.1, performance: 94.1, passRate: 98.5, oee: 79.8 },
{ name: '数 3', status: '运行', auto: 'run', auxiliary: 'run', utilization: 87.2, performance: 94.7, passRate: 98.8, oee: 81.1 },
@@ -232,18 +241,9 @@ const defectRows = ref([
{ productionOrder: 'MO260422-014', salesOrder: 'SO260408-026', materialName: '箱体', materialCode: 'MP-BOX-001', processName: '粗铣', defectQty: 1, assignee: '数 1' }
])
let lateStartRows = ref([
{ productionOrder: 'MO260430-001', salesOrder: 'SO260415-021', materialName: '箱体壳体', materialCode: 'MP-BOX-001', processName: '粗铣', assignQty: 20, prevPassQty: 0, urgentQty: 10, assignee: '数 5' },
{ productionOrder: 'MO260430-002', salesOrder: 'SO260416-033', materialName: '轴套', materialCode: 'MP-SHAFT-002', processName: '钻孔', assignQty: 15, prevPassQty: 0, urgentQty: 5, assignee: '普 2' },
{ productionOrder: 'MO260429-015', salesOrder: 'SO260414-012', materialName: '端盖', materialCode: 'MP-CAP-003', processName: '攻丝', assignQty: 30, prevPassQty: 0, urgentQty: 0, assignee: '普 1' },
{ productionOrder: 'MO260429-017', salesOrder: 'SO260414-018', materialName: '支架', materialCode: 'MP-BRKT-004', processName: '平面铣', assignQty: 12, prevPassQty: 0, urgentQty: 6, assignee: '加 2' },
{ productionOrder: 'MO260429-020', salesOrder: 'SO260415-007', materialName: '导轨座', materialCode: 'MP-RAIL-005', processName: '铣槽', assignQty: 8, prevPassQty: 0, urgentQty: 3, assignee: '数 3' },
{ productionOrder: 'MO260429-021', salesOrder: 'SO260415-009', materialName: '盖板', materialCode: 'MP-COVER-006', processName: '钻孔', assignQty: 25, prevPassQty: 0, urgentQty: 0, assignee: '数 1' },
{ productionOrder: 'MO260428-022', salesOrder: 'SO260414-030', materialName: '法兰盘', materialCode: 'MP-FLANGE-003', processName: '精铣', assignQty: 18, prevPassQty: 0, urgentQty: 4, assignee: '加 1' },
{ productionOrder: 'MO260428-025', salesOrder: 'SO260414-035', materialName: '齿轮轴', materialCode: 'MP-GEAR-002', processName: '滚齿', assignQty: 10, prevPassQty: 0, urgentQty: 2, assignee: '数 2' },
{ productionOrder: 'MO260428-027', salesOrder: 'SO260413-018', materialName: '轴承座', materialCode: 'MP-HOUS-001', processName: '精铣', assignQty: 16, prevPassQty: 0, urgentQty: 4, assignee: '数 6' },
{ productionOrder: 'MO260427-030', salesOrder: 'SO260413-025', materialName: '支撑座', materialCode: 'MP-BRKT-006', processName: '钻孔', assignQty: 22, prevPassQty: 0, urgentQty: 0, assignee: '普 4' }
])
const lateStartRows = ref([])
const outlateStartRows = ref([]) // 存储合并过滤后的数据
const completedRows = ref([
{ productionOrder: 'MO260428-003', materialName: '箱体壳体', materialCode: 'MP-BOX-001', processName: '精铣', passRate: 99.1 },
@@ -293,7 +293,6 @@ const equipmentBoardConfig = computed(() => ({
const defectBoardConfig = computed(() => ({
...boardBaseConfig,
rowNum: 7,
// columnWidth: [124, 124, 70, 128, 62, 54, 54],
header: ['生产订单', '销售订单', '物料名称', '物品编码', '工序名称', '不良数量', '指派对象'],
data: defectRows.value.map(row => [
row.productionOrder,
@@ -309,7 +308,6 @@ const defectBoardConfig = computed(() => ({
const lateStartBoardConfig = computed(() => ({
...boardBaseConfig,
rowNum: 7,
// columnWidth: [108, 108, 82, 102, 62, 68, 72, 68, 52],
header: ['生产订单', '销售订单', '物料名称', '物料编码', '工序名称', '分配数量', '上序合格数', '加急数量', '指派对象'],
data: lateStartRows.value.map(row => [
row.productionOrder,
@@ -324,6 +322,23 @@ const lateStartBoardConfig = computed(() => ({
])
}))
const outlateStartBoardConfig = computed(() => ({
...boardBaseConfig,
rowNum: 7,
header: ['生产订单', '销售订单', '物料名称', '物料编码', '工序名称', '分配数量', '上序合格数', '加急数量', '指派对象'],
data: outlateStartRows.value.map(row => [
row.productionOrder,
row.salesOrder,
row.materialName,
row.materialCode,
row.processName,
row.assignQty,
row.prevPassQty,
row.urgentQty,
row.assignee
])
}))
const completedBoardConfig = computed(() => ({
...boardBaseConfig,
rowNum: 8,
@@ -350,55 +365,179 @@ const lateStartGroupedByAssignee = computed(() => {
return Object.values(groupMap)
})
onMounted(() => {
refreshClock()
clockTimer = window.setInterval(refreshClock, 1000)
setScale()
resizeHandler = () => {
setScale()
resizeCharts()
}
window.addEventListener('resize', resizeHandler)
nextTick(() => {
initCharts()
})
searchDashboardData()
})
// ==================== 数据合并相关函数(从 Vue 2 转换而来) ====================
onUnmounted(() => {
if (clockTimer) {
window.clearInterval(clockTimer)
clockTimer = null
// 检查计划开始时间是否有效(最小计划开始时间小于今天)
const checkGroupPlanStartValid = (group) => {
const today = new Date()
today.setHours(0, 0, 0, 0)
const minPlanStart = group.reduce((min, item) => {
if (!item.计划开始时间) return min
const planDate = new Date(item.计划开始时间)
planDate.setHours(0, 0, 0, 0)
return planDate < min ? planDate : min
}, new Date('2099-12-31'))
return minPlanStart < today
}
// 检查组内所有数据的收料数量是否都为0
const checkGroupAllZero = (group) => {
return group.every(item => {
const 收料数量 = Number(item.收料数量) || 0
return 收料数量 === 0
})
}
// 根据执行状态判断是否应该包含
const shouldIncludeByExecutionStatus = (mergedItem) => {
// 执行状态为1可执行的包含
return mergedItem.执行状态 === 1
}
// 合并组数据
const mergeGroup = (group) => {
if (group.length === 1) {
const item = { ...group[0] }
item.uniqueKey = `${item.订单号}_${item.订单行号}_${item.TaskAID}`
item.TaskAID = String(item.TaskAID)
const 上序合格数 = item.上序合格数 || 0
const 订单行号 = item.订单行号
item.执行状态 = (订单行号 === 1 || 上序合格数 > 0) ? 1 : 2
return item
}
if (resizeHandler) {
window.removeEventListener('resize', resizeHandler)
resizeHandler = null
const mergedItem = { ...group[0] }
// 生成唯一标识key
mergedItem.uniqueKey = group.map(item => `${item.订单号}_${item.订单行号}_${item.TaskAID}`).join('|')
// 拼接工序名称(逗号间隔)
const processNames = group.map(item => item.工序名称)
mergedItem.工序名称 = processNames.join(',')
// 拼接TaskAID逗号间隔
const taskAIDs = [...new Set(group.map(item => String(item.TaskAID)))]
mergedItem.TaskAID = taskAIDs.join(',')
// 取计划开始时间的最小值
const startTimes = group.map(item => new Date(item.计划开始时间))
mergedItem.计划开始时间 = new Date(Math.min(...startTimes)).toISOString().slice(0, 19).replace('T', ' ')
// 取计划完成时间的最大值
const endTimes = group.map(item => new Date(item.计划完成时间))
mergedItem.计划完成时间 = new Date(Math.max(...endTimes)).toISOString().slice(0, 19).replace('T', ' ')
// 取最大值
const wcnum = group.map(item => item.完成数量)
mergedItem.完成数量 = Math.max(...wcnum)
const printnum = group.map(item => item.外协打印次数)
mergedItem.外协打印次数 = Math.max(...printnum)
const slnum = group.map(item => item.收料数量)
mergedItem.收料数量 = Math.max(...slnum)
// 计算上序合格数(取组内最大值)
const shxnum = group.map(item => item.上序合格数 || 0)
mergedItem.上序合格数 = Math.max(...shxnum)
// 计算执行状态:只要一组内包含订单行号=1 或者包含上序合格数>0都属于可执行
const hasFirstProcess = group.some(item => item.订单行号 === 1)
const hasPreviousQualified = group.some(item => (item.上序合格数 || 0) > 0)
mergedItem.执行状态 = (hasFirstProcess || hasPreviousQualified) ? 1 : 2
// 存储原始组数据引用
mergedItem.originalGroup = group
return mergedItem
}
// 合并表格数据并返回合并后的结果
const mergeTableData = (rawData) => {
if (!rawData || rawData.length === 0) {
return []
}
Object.keys(charts).forEach(key => {
if (charts[key]) {
charts[key].dispose()
charts[key] = null
const groupedByOrder = {}
rawData.forEach(item => {
const groupKey = `${item.订单号}_${item.拆分内码}`
if (!groupedByOrder[groupKey]) {
groupedByOrder[groupKey] = []
}
groupedByOrder[groupKey].push(item)
})
const mergedData = []
Object.keys(groupedByOrder).forEach(orderNo => {
const orderItems = groupedByOrder[orderNo]
orderItems.sort((a, b) => a.订单行号 - b.订单行号)
let currentGroup = [orderItems[0]]
for (let i = 1; i < orderItems.length; i++) {
const currentItem = orderItems[i]
const lastItem = orderItems[i - 1]
if (currentItem.订单行号 === lastItem.订单行号 + 1 &&
currentItem.外协分组 === lastItem.外协分组 &&
currentItem.拆分内码 === lastItem.拆分内码) {
currentGroup.push(currentItem)
} else {
// 检查组内最小计划开始时间是否小于今天
if (checkGroupPlanStartValid(currentGroup)) {
if (checkGroupAllZero(currentGroup)) {
const mergedItem = mergeGroup(currentGroup)
if (shouldIncludeByExecutionStatus(mergedItem)) {
mergedData.push(mergedItem)
}
}
}
currentGroup = [currentItem]
}
}
// 处理最后一组
if (currentGroup.length > 0) {
if (checkGroupPlanStartValid(currentGroup)) {
if (checkGroupAllZero(currentGroup)) {
const mergedItem = mergeGroup(currentGroup)
if (shouldIncludeByExecutionStatus(mergedItem)) {
mergedData.push(mergedItem)
}
}
}
}
})
})
return mergedData
}
// ==================== 数据查询相关函数 ====================
const queryDashboardData = (procedureName, applyData) => {
let data
if (typeof CreateData !== 'function' || typeof ExecDatabase !== 'function') {
initCharts()
return
}
data = CreateData('11', procedureName, [])
const data = CreateData('11', procedureName, [])
ExecDatabase(data).then(response => {
const rows = getResponseRows(response)
if (rows.length > 0) {
tableData.value = rows
applyData(rows)
nextTick(() => {
initCharts()
})
}
}).catch(error => {
console.error('查询数据失败:', procedureName, error)
})
}
const getResponseRows = response => {
if (response && Array.isArray(response.data)) {
return response.data
@@ -408,108 +547,6 @@ const getResponseRows = response => {
}
return []
}
const searchDashboardData = () => {
searchDeviceStatusData()
searchNotStartedData()
searchTodayDoneSummaryData()
searchTodayDoneDetailData()
searchunqualifiedData()
searchPlanExecuteData()
searchdeviceData()
}
const searchdeviceData = () => {
queryDashboardData('机加设备生产运营看板_设备任务查询', applydeviceData)
}
const searchunqualifiedData = () => {
queryDashboardData('机加设备生产运营看板_不良品查询', applyunqualifiedData)
}
const searchTodayDoneSummaryData = () => {
queryDashboardData('质量任务看板_当天已完成汇总', applyTodayDoneSummaryData)
}
const searchTodayDoneDetailData = () => {
queryDashboardData('机加设备生产运营看板_当天已完成明细', applyTodayDoneDetailData)
}
const searchDeviceStatusData = () => {
queryDashboardData('机加设备生产运营看板_设备运行状态查询', applyDashboardData)
}
const searchNotStartedData = () => {
queryDashboardData('机加设备生产运营看板_未及时开工查询', applyNotStartData)
}
const searchPlanExecuteData = () => {
queryDashboardData('机加设备生产运营看板_计划执行率', applyPlanExecuteData)
}
const applyDashboardData = rows => {
const first = getFirstRow(rows)
console.log(first)
// summary.equipmentTotal = first.总数
summary.equipmentTotal = 18
// summary.running = first.运行数量
summary.running = first.运行数量
summary.standby = first.待机数量
summary.alarm = first.报警数量
summary.shutdown = first.关机数量
}
const applydeviceData = rows => {
const first = getFirstRow(rows)
console.log(first)
summary.running2 = first.总记录数
}
const applyNotStartData = rows => {
const first = getFirstRow(rows)
summary.lateStart = first.总记录数
// 假设 rows 是从存储过程返回的未及时开工数据
// 如果返回的数据字段名与表格需要的一致,直接映射即可
lateStartRows.value = rows.map(row => ({
productionOrder: row.订单号,
salesOrder: row.合同号,
materialName: row.零件名称,
materialCode: row.零件编码,
processName: row.工序名称,
assignQty: row.分配数 || 0,
prevPassQty: row.上序合格数 || row.上序合格数量 || 0,
urgentQty: row.加急数量 || row.紧急数量 || 0,
assignee: row.指派对象
}))
}
const applyTodayDoneSummaryData = rows => {
const first = getFirstRow(rows)
summary.finishedTask = first.totalCount
summary.passRate = ((first.passCount / (first.passCount + first.failCount)) * 100).toFixed(2)
}
const applyTodayDoneDetailData = rows => {
const first = getFirstRow(rows)
completedRows.value = rows.map(row => ({
productionOrder: row.orderNo,
materialName: row.materialName,
materialCode: row.materialCode,
processName: row.processName,
passRate: ((row.passQty / (row.passQty + row.failQty)) * 100).toFixed(2)
}))
}
const applyunqualifiedData = rows => {
const first = getFirstRow(rows)
summary.defectTotal = first.总记录数
defectRows.value = rows.map(row => ({
productionOrder: row.计划号,
salesOrder: row.内部合同号,
materialName: row.零件名称,
materialCode: row.零件编码,
processName: row.工序名称,
defectQty: row.不合格数,
assignee:row.指派对象 || ''
}))
}
const applyPlanExecuteData = rows => {
const first = getFirstRow(rows)
summary.planRate = first.计划执行率_百分数
}
const getFirstRow = rows => {
if (Array.isArray(rows) && rows.length > 0) {
@@ -517,6 +554,217 @@ const getFirstRow = rows => {
}
return {}
}
const searchDashboardData = () => {
searchDeviceStatusData()
searchNotStartedData()
searchTodayDoneSummaryData()
searchTodayDoneDetailData()
searchPlanExecuteData()
searchdeviceData()
searchoutNotStartedData()
}
const searchdeviceData = () => {
queryDashboardData('机加设备生产运营看板_设备任务查询', applydeviceData)
}
const searchTodayDoneSummaryData = () => {
queryDashboardData('质量任务看板_当天已完成汇总', applyTodayDoneSummaryData)
}
const searchTodayDoneDetailData = () => {
queryDashboardData('机加设备生产运营看板_当天已完成明细', applyTodayDoneDetailData)
}
const searchDeviceStatusData = () => {
queryDashboardData('机加设备生产运营看板_设备运行状态查询', applyDashboardData)
}
const searchNotStartedData = () => {
queryDashboardData('机加设备生产运营看板_未及时开工查询', applyNotStartData)
}
const searchoutNotStartedData = () => {
queryDashboardData('机加设备生产运营看板_外协未及时开工查询', outapplyNotStartData)
}
const searchPlanExecuteData = () => {
queryDashboardData('机加设备生产运营看板_计划执行率', applyPlanExecuteData)
}
const applyDashboardData = rows => {
const first = getFirstRow(rows)
summary.equipmentTotal = 18
summary.running = first.运行数量 || 0
summary.standby = first.待机数量 || 0
summary.alarm = first.报警数量 || 0
summary.shutdown = first.关机数量 || 0
}
const applydeviceData = rows => {
const first = getFirstRow(rows)
summary.running2 = first.总记录数 || 0
}
const applyNotStartData = rows => {
const first = getFirstRow(rows)
summary.lateStart = first.总记录数 || 0
lateStartRows.value = rows.map(row => ({
productionOrder: row.订单号,
salesOrder: row.合同号,
materialName: row.零件名称,
materialCode: row.零件编码,
processName: row.工序名称,
assignQty: row.分配数 || 0,
prevPassQty: row.上序合格数 || row.上序合格数量 || 0,
urgentQty: row.加急数量 || row.紧急数量 || 0,
assignee: row.指派对象
}))
}
// 外协未及时开工数据处理 - 应用合并和过滤逻辑
const outapplyNotStartData = rows => {
// 1. 先处理原始数据,统一字段名
const processedRows = rows.map(row => ({
...row,
订单号: row.订单号,
合同号: row.合同号,
零件名称: row.零件名称,
零件编码: row.零件编码,
工序名称: row.工序名称,
分配数: row.分配数 || 0,
上序合格数: row.上序合格数 || row.上序合格数量 || 0,
加急数量: row.加急数量 || row.紧急数量 || 0,
指派对象: row.指派对象,
拆分内码: row.拆分内码,
外协分组: row.外协分组,
订单行号: row.订单行号,
计划开始时间: row.计划开始时间,
计划完成时间: row.计划完成时间,
完成数量: row.完成数量 || 0,
外协打印次数: row.外协打印次数 || 0,
收料数量: row.收料数量 || 0,
TaskAID: row.TaskAID
}))
// 2. 调用合并过滤函数,获取所有合并后的数据(不过滤执行状态)
const allMergedData = mergeAllTableData(processedRows)
// 3. 总数 = 所有合并后的数据条数包括执行状态为1和2的
summary.outlateStart = allMergedData.length
// 4. 明细数据只显示执行状态 === 1 的数据
const filteredMergedData = allMergedData.filter(item => item.执行状态 === 1)
// 5. 转换为表格显示格式并赋值给 outlateStartRows
outlateStartRows.value = convertMergedDataToRows(filteredMergedData)
console.log('原始数据条数:', rows.length)
console.log('合并后总数包括状态1和2:', allMergedData.length)
console.log('明细显示条数(执行状态=1:', filteredMergedData.length)
}
// 合并表格数据并返回所有合并后的结果(不过滤执行状态)
const mergeAllTableData = (rawData) => {
if (!rawData || rawData.length === 0) {
return []
}
const groupedByOrder = {}
rawData.forEach(item => {
const groupKey = `${item.订单号}_${item.拆分内码}`
if (!groupedByOrder[groupKey]) {
groupedByOrder[groupKey] = []
}
groupedByOrder[groupKey].push(item)
})
const mergedData = []
Object.keys(groupedByOrder).forEach(orderNo => {
const orderItems = groupedByOrder[orderNo]
orderItems.sort((a, b) => a.订单行号 - b.订单行号)
let currentGroup = [orderItems[0]]
for (let i = 1; i < orderItems.length; i++) {
const currentItem = orderItems[i]
const lastItem = orderItems[i - 1]
if (currentItem.订单行号 === lastItem.订单行号 + 1 &&
currentItem.外协分组 === lastItem.外协分组 &&
currentItem.拆分内码 === lastItem.拆分内码) {
currentGroup.push(currentItem)
} else {
// 检查组内最小计划开始时间是否小于今天
if (checkGroupPlanStartValid(currentGroup)) {
if (checkGroupAllZero(currentGroup)) {
const mergedItem = mergeGroup(currentGroup)
// 不再过滤执行状态,保存所有数据
mergedData.push(mergedItem)
}
}
currentGroup = [currentItem]
}
}
// 处理最后一组
if (currentGroup.length > 0) {
if (checkGroupPlanStartValid(currentGroup)) {
if (checkGroupAllZero(currentGroup)) {
const mergedItem = mergeGroup(currentGroup)
// 不再过滤执行状态,保存所有数据
mergedData.push(mergedItem)
}
}
}
})
return mergedData
}
// 将合并后的数据转换为表格显示格式
const convertMergedDataToRows = (mergedData) => {
return mergedData.map(item => ({
productionOrder: item.订单号 || '',
salesOrder: item.合同号 || '',
materialName: item.零件名称 || '',
materialCode: item.零件编码 || '',
processName: item.工序名称 || '',
assignQty: item.分配数 || 0,
prevPassQty: item.上序合格数 || 0,
urgentQty: item.加急数量 || item.紧急数量 || 0,
assignee: item.指派对象 || ''
}))
}
const applyTodayDoneSummaryData = rows => {
const first = getFirstRow(rows)
summary.finishedTask = first.totalCount || 0
const passCount = first.passCount || 0
const failCount = first.failCount || 0
summary.passRate = ((passCount / (passCount + failCount)) * 100).toFixed(2)
}
const applyTodayDoneDetailData = rows => {
completedRows.value = rows.map(row => ({
productionOrder: row.orderNo,
materialName: row.materialName,
materialCode: row.materialCode,
processName: row.processName,
passRate: ((row.passQty / (row.passQty + row.failQty)) * 100).toFixed(2)
}))
}
const applyPlanExecuteData = rows => {
const first = getFirstRow(rows)
summary.planRate = first.计划执行率_百分数 || 0
}
// ==================== 图表相关函数 ====================
const initCharts = () => {
if (planRateChart.value && !charts.planRate) {
charts.planRate = echarts.init(planRateChart.value)
@@ -636,6 +884,8 @@ const renderBarChart = (chart, rows) => {
})
}
// ==================== 辅助函数 ====================
const refreshClock = () => {
const now = new Date()
const pad = value => String(value).padStart(2, '0')
@@ -659,18 +909,39 @@ const resizeCharts = () => {
})
}
const stateClass = status => {
if (status === '运行') return 'state-run'
if (status === '待机') return 'state-standby'
if (status === '报警') return 'state-alarm'
return 'state-off'
}
// ==================== 生命周期 ====================
const pointClass = value => {
if (value === 'run') return 'point point-run'
if (value === 'power') return 'point point-power'
return 'point point-none'
}
onMounted(() => {
refreshClock()
clockTimer = window.setInterval(refreshClock, 1000)
setScale()
resizeHandler = () => {
setScale()
resizeCharts()
}
window.addEventListener('resize', resizeHandler)
nextTick(() => {
initCharts()
})
searchDashboardData()
})
onUnmounted(() => {
if (clockTimer) {
window.clearInterval(clockTimer)
clockTimer = null
}
if (resizeHandler) {
window.removeEventListener('resize', resizeHandler)
resizeHandler = null
}
Object.keys(charts).forEach(key => {
if (charts[key]) {
charts[key].dispose()
charts[key] = null
}
})
})
</script>
<style scoped>
@@ -1005,18 +1276,12 @@ const pointClass = value => {
.finish-content {
height: calc(100% - 38px);
display: grid;
/* grid-template-columns: 82px 1fr 1fr; */
grid-template-columns: repeat(2, 1fr);
align-items: center;
gap: 8px;
padding: 7px 14px 15px;
}
.finish-content img {
width: 80px;
height: 80px;
}
.finish-stat {
min-width: 0;
text-align: center;
@@ -1052,51 +1317,24 @@ const pointClass = value => {
height: 374px;
}
.oee-chart-panel {
left: 746px;
top: 196px;
width: 520px;
height: 374px;
}
/* .defect-detail-panel {
left: 1278px;
top: 196px;
width: 628px;
height: 374px;
} */
.late-detail-panel {
left: 780px;
top: 196px;
width: 1126px;
height: 374px;
}
.defect-detail-panel {
left: 14px;
top: 584px;
width: 720px;
height: 356px;
}
.plan-chart-panel {
left: 746px;
.out-late-detail-panel {
left: 780px;
top: 584px;
width: 520px;
height: 356px;
}
.complete-detail-panel {
left: 1278px;
top: 584px;
width: 628px;
width: 1126px;
height: 356px;
}
.assignee-summary-panel {
left: 746px;
left: 14px;
top: 584px;
width: 1160px;
width: 756px;
height: 356px;
}
@@ -1253,20 +1491,6 @@ const pointClass = value => {
text-overflow: clip;
}
.defect-detail-panel .datav-table :deep(.header-item:nth-child(6)),
.defect-detail-panel .datav-table :deep(.ceil:nth-child(6)),
.defect-detail-panel .datav-table :deep(.header-item:nth-child(7)),
.defect-detail-panel .datav-table :deep(.ceil:nth-child(7)) {
padding: 0 2px;
}
.defect-detail-panel .datav-table :deep(.header-item:nth-child(1)),
.defect-detail-panel .datav-table :deep(.ceil:nth-child(1)),
.defect-detail-panel .datav-table :deep(.header-item:nth-child(2)),
.defect-detail-panel .datav-table :deep(.ceil:nth-child(2)) {
padding: 0 2px;
}
.datav-table :deep(.rows) {
overflow: hidden;
}
@@ -1330,5 +1554,41 @@ const pointClass = value => {
height: calc(100% - 45px);
margin: 0 11px;
}
.finish-content2 {
height: calc(100% - 38px);
display: grid;
grid-template-columns: repeat(2, 1fr);
align-items: center;
gap: 8px;
padding: 7px 14px 15px;
}
</style>
.finish-stat2 {
min-width: 0;
text-align: center;
}
.finish-stat2 span {
display: block;
margin-bottom: 8px;
color: #dce7fa;
font-size: 20px;
font-weight: 700;
}
.finish-stat2 strong {
color: #20d8ff;
font-size: 50px;
line-height: 1;
font-weight: 900;
letter-spacing: 2px;
text-shadow: 0 0 10px rgba(32, 216, 255, .5);
}
.finish-stat2 small {
margin-left: 2px;
font-size: 24px;
}
</style>

View File

@@ -14,7 +14,6 @@
<main class="dashboard-body">
<section v-for="card in summaryCards" :key="card.title" class="panel summary-card" :class="card.className">
<img class="summary-icon" :src="card.icon" alt="" />
<!-- 特殊在制任务汇总 三列数字 -->
<div v-if="card.subValues" class="summary-content summary-triple-content">
<div class="summary-title">{{ card.title }}</div>
<div class="summary-triple">
@@ -30,7 +29,6 @@
</div>
</div>
</div>
<!-- 其他卡片原有单数字 -->
<div v-else class="summary-content">
<div class="summary-title">{{ card.title }}</div>
<div class="summary-label">总数</div>
@@ -47,43 +45,22 @@
</div>
</section>
<section class="panel work-detail-panel">
<div class="panel-title">在制任务明细</div>
<div class="table-box datav-table main-task-table">
<dv-scroll-board :config="inProcessBoardConfig" />
</div>
</section>
<section class="panel urgent-list-panel">
<div class="panel-title">加急任务列表</div>
<div class="table-box datav-table">
<dv-scroll-board :config="urgentBoardConfig" />
</div>
</section>
<section class="panel process-task-panel">
<div class="panel-title">关键工序任务明细</div>
<div class="table-box datav-table">
<dv-scroll-board :config="keyProcessBoardConfig" />
</div>
</section>
<section class="panel start-task-panel">
<div class="panel-title">正在开工任务</div>
<div class="table-box datav-table">
<dv-scroll-board :config="startedBoardConfig" />
</div>
</section>
</main>
</div>
</template>
<script setup>
// import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'
import { computed, inject, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
const tableData = ref([])
const CreateData = inject('CreateData')
const ExecDatabase = inject('ExecDatabase')
defineOptions({
name: 'MachiningOperationTwoDashboard'
})
@@ -91,8 +68,7 @@ defineOptions({
const clientLogoUrl = './public/ClientLogo.png'
const svgData = svg => 'data:image/svg+xml;utf8,' + encodeURIComponent(svg)
const lineIcon = (color, body, viewBox = '0 0 64 64') => {
const size = parseFloat(viewBox.split(' ')[2]) || 64
const sw = size <= 28 ? 1.4 : 2.0
const sw = parseFloat(viewBox.split(' ')[2]) <= 28 ? 1.4 : 2.0
return svgData(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="' + viewBox + '" fill="none">' +
'<g stroke="' + color + '" stroke-width="' + sw + '" stroke-linecap="round" stroke-linejoin="round">' +
@@ -113,11 +89,11 @@ const style = reactive({
height: 1080,
transform: 'scaleY(1) scaleX(1) translate(-50%, -50%)'
})
const currentTime = ref('')
let clockTimer = null
let resizeHandler = null
// 将 computed 改为 ref
const summaryCards = ref([
{
title: '在制任务汇总',
@@ -163,43 +139,7 @@ const summaryCards = ref([
}
])
const inProcessRows = ref([
])
const urgentRows = ref([
{ productionOrder: 'MO260428-001', salesOrder: 'SO260413-001', materialName: '主轴壳体', materialCode: 'MP-BOX-001', processName: '精铣', assignQty: 120, prevPassQty: 105, urgentQty: 15, assignee: '张伟' },
{ productionOrder: 'MO260428-002', salesOrder: 'SO260413-005', materialName: '法兰盘', materialCode: 'MP-FLANGE-002', processName: '镗孔', assignQty: 80, prevPassQty: 68, urgentQty: 12, assignee: '李强' },
{ productionOrder: 'MO260428-003', salesOrder: 'SO260414-002', materialName: '齿轮轴', materialCode: 'MP-GEAR-001', processName: '磨削', assignQty: 60, prevPassQty: 48, urgentQty: 12, assignee: '王磊' },
{ productionOrder: 'MO260428-004', salesOrder: 'SO260414-003', materialName: '端盖', materialCode: 'MP-COVER-003', processName: '钻孔', assignQty: 90, prevPassQty: 75, urgentQty: 15, assignee: '刘洋' },
{ productionOrder: 'MO260428-005', salesOrder: 'SO260414-007', materialName: '导轨座', materialCode: 'MP-RAIL-002', processName: '精镗', assignQty: 70, prevPassQty: 60, urgentQty: 10, assignee: '赵峰' },
{ productionOrder: 'MO260428-006', salesOrder: 'SO260415-001', materialName: '轴承座', materialCode: 'MP-BEAR-001', processName: '铣平面', assignQty: 50, prevPassQty: 42, urgentQty: 8, assignee: '陈晨' },
{ productionOrder: 'MO260428-007', salesOrder: 'SO260415-003', materialName: '联轴器', materialCode: 'MP-COUP-001', processName: '车削', assignQty: 40, prevPassQty: 32, urgentQty: 8, assignee: '周杰' },
{ productionOrder: 'MO260428-008', salesOrder: 'SO260415-005', materialName: '垫板', materialCode: 'MP-PLATE-001', processName: '钻孔', assignQty: 100, prevPassQty: 88, urgentQty: 12, assignee: '孙鹏' }
])
const keyProcessRows = ref([
{ productionOrder: 'MO260428-201', salesOrder: 'SO260413-001', materialName: '主轴壳体', materialCode: 'MP-BOX-001', processName: '精镗面', assignQty: 120, prevPassQty: 105, urgentQty: 15, assignee: '周检' },
{ productionOrder: 'MO260428-202', salesOrder: 'SO260413-002', materialName: '法兰盘', materialCode: 'MP-FLANGE-002', processName: '镗孔尺寸', assignQty: 80, prevPassQty: 68, urgentQty: 12, assignee: '吕检' },
{ productionOrder: 'MO260428-203', salesOrder: 'SO260413-003', materialName: '齿轮轴', materialCode: 'MP-GEAR-001', processName: '磨削圆度', assignQty: 60, prevPassQty: 48, urgentQty: 12, assignee: '何检' },
{ productionOrder: 'MO260428-204', salesOrder: 'SO260413-004', materialName: '端盖', materialCode: 'MP-COVER-003', processName: '平面度', assignQty: 90, prevPassQty: 75, urgentQty: 15, assignee: '郭检' },
{ productionOrder: 'MO260428-205', salesOrder: 'SO260414-001', materialName: '导轨座', materialCode: 'MP-RAIL-002', processName: '平行度', assignQty: 70, prevPassQty: 60, urgentQty: 10, assignee: '丁检' },
{ productionOrder: 'MO260428-206', salesOrder: 'SO260414-002', materialName: '轴承座', materialCode: 'MP-BEAR-001', processName: '孔距尺寸', assignQty: 50, prevPassQty: 42, urgentQty: 8, assignee: '黄检' },
{ productionOrder: 'MO260428-207', salesOrder: 'SO260414-003', materialName: '联轴器', materialCode: 'MP-COUP-001', processName: '端面跳动', assignQty: 40, prevPassQty: 32, urgentQty: 8, assignee: '蒋检' },
{ productionOrder: 'MO260428-208', salesOrder: 'SO260414-004', materialName: '垫板', materialCode: 'MP-PLATE-001', processName: '厚度', assignQty: 100, prevPassQty: 88, urgentQty: 12, assignee: '韩检' }
])
const startedRows = ref([
{ productionOrder: 'MO260429-301', salesOrder: 'SO260415-101', materialName: '箱体壳体', materialCode: 'MP-BOX-009', processName: '粗铣', assignQty: 30 },
{ productionOrder: 'MO260429-302', salesOrder: 'SO260415-102', materialName: '轴套', materialCode: 'MP-SHAFT-008', processName: '轴车', assignQty: 24 },
{ productionOrder: 'MO260429-303', salesOrder: 'SO260415-103', materialName: '端盖', materialCode: 'MP-CAP-006', processName: '钻孔', assignQty: 20 },
{ productionOrder: 'MO260429-304', salesOrder: 'SO260415-104', materialName: '支架', materialCode: 'MP-BRKT-009', processName: '平面铣', assignQty: 16 },
{ productionOrder: 'MO260429-305', salesOrder: 'SO260415-105', materialName: '导轨座', materialCode: 'MP-RAIL-010', processName: '铣槽', assignQty: 18 },
{ productionOrder: 'MO260429-306', salesOrder: 'SO260415-106', materialName: '盖板', materialCode: 'MP-COVER-011', processName: '攻丝', assignQty: 22 },
{ productionOrder: 'MO260429-307', salesOrder: 'SO260415-107', materialName: '连接板', materialCode: 'MP-PLATE-010', processName: '钻孔', assignQty: 28 },
{ productionOrder: 'MO260429-308', salesOrder: 'SO260415-108', materialName: '滑块', materialCode: 'MP-SLIDER-009', processName: '精磨', assignQty: 14 }
])
const urgentRows = ref([])
const extendRows = (rows, minRows, idKey = 'productionOrder') => {
const result = [...rows]
@@ -232,72 +172,286 @@ const boardBaseConfig = {
index: false
}
const progressText = value => (
'<div class="progress-cell">' +
'<span class="progress-track"><span class="progress-fill" style="width:' + value + '%"></span></span>' +
'<strong>' + Number(value).toFixed(1) + '%</strong>' +
'</div>'
)
const urgentRed = (text, isUrgent) => isUrgent ? `<span style="color:#ff3f37">${text}</span>` : text
// 格式化工序进度详情为HTML
// const formatProcessDetail = (processDetailStr) => {
// if (!processDetailStr || typeof processDetailStr !== 'string') {
// return ''
// }
const inProcessBoardConfig = computed(() => ({
...boardBaseConfig,
rowNum: 12,
columnWidth: [100, 100, 116, 180, 88, 80, 80, 80,170],
header: ['生产订单', '销售订单', '物料名称', '物料编码', '工序名称', '指派对象','分配数量', '完成数量', '进度百分比%'],
data: extendRows(inProcessRows.value, 18).map(row => [
urgentRed(row.productionOrder, row.isUrgent),
urgentRed(row.salesOrder, row.isUrgent),
urgentRed(row.materialName, row.isUrgent),
urgentRed(row.materialCode, row.isUrgent),
urgentRed(row.processName, row.isUrgent),
urgentRed(row.assignee, row.isUrgent),
urgentRed(row.assignQty, row.isUrgent),
urgentRed(row.finishQty, row.isUrgent),
row.isUrgent ? progressText(row.progress).replace(/color:[^;"]+/, 'color:#ff3f37') : progressText(row.progress)
])
}))
// // 处理HTML实体将 -&gt; 转换为 ->
// let cleanedStr = processDetailStr
// .replace(/&gt;/g, '>')
// .replace(/&lt;/g, '<')
// .replace(/&amp;/g, '&')
// .replace(/&quot;/g, '"')
// .replace(/&#39;/g, "'")
// // 按 ' -> ' 分割工序
// const steps = cleanedStr.split(' -> ')
// // 每行最多显示的工序数量
// const maxPerRow = 5
// // 计算需要多少行
// const rows = Math.ceil(steps.length / maxPerRow)
// // 构建表格
// let tableHtml = '<table class="process-table"><tbody>'
// // 为每一行生成名称行和数量行
// for (let row = 0; row < rows; row++) {
// const startIndex = row * maxPerRow
// const endIndex = Math.min(startIndex + maxPerRow, steps.length)
// const rowSteps = steps.slice(startIndex, endIndex)
// // 如果不是第一行,添加分隔行
// if (row > 0) {
// tableHtml += '<tr style="height: 4px;"><td colspan="' + (maxPerRow * 2 - 1) + '" style="padding: 0; line-height: 1;"></td></tr>'
// }
// // 工序名称行
// tableHtml += '<tr>'
// rowSteps.forEach((step, index) => {
// const parts = step.split('|')
// // 关键修改:直接使用原始名称,不进行任何转换
// let name = parts[0] || ''
// const status = parts[1] || '0'
// // 根据状态设置颜色
// let colorStyle = ''
// const statusNum = parseInt(status)
// if (statusNum === 6) {
// colorStyle = 'color: #09a8ff; font-weight: 700;'
// } else if (statusNum === 2 || statusNum === 4) {
// colorStyle = 'color: #28d961; font-weight: 700;'
// }
// tableHtml += `<td style="${colorStyle}">${name}</td>`
// // 在本行内部添加工序间箭头(不包括行尾和不补位)
// if (index < rowSteps.length - 1) {
// tableHtml += '<td class="arrow-cell">→</td>'
// }
// })
// tableHtml += '</tr>'
// // 完成数量行
// tableHtml += '<tr>'
// rowSteps.forEach((step, index) => {
// const parts = step.split('|')
// const count = parts[2] || '0'
// tableHtml += `<td class="count-cell">${count}</td>`
// // 完成数量之间不显示箭头,但保留空列以对齐
// if (index < rowSteps.length - 1) {
// tableHtml += '<td class="arrow-cell-hidden"></td>'
// }
// })
// tableHtml += '</tr>'
// }
// tableHtml += '</tbody></table>'
// return tableHtml
// }
// 格式化工序进度详情为HTML
// 格式化工序进度详情为HTML
// 格式化工序进度详情为HTML
const formatProcessDetail = (processDetailStr) => {
if (!processDetailStr || typeof processDetailStr !== 'string') {
return ''
}
const urgentBoardConfig = computed(() => makeTaskBoard(urgentRows.value))
const keyProcessBoardConfig = computed(() => makeTaskBoard(keyProcessRows.value))
const startedBoardConfig = computed(() => ({
...boardBaseConfig,
rowNum: 8,
// columnWidth: [160, 170, 140, 176, 140, 128],
header: ['生产订单','物料名称', '物料编码', '工序名称', '指派对象','分配数量','完成数量'],
data: extendRows(startedRows.value, 12).map(row => [
row.productionOrder,
// row.salesOrder,
row.materialName,
row.materialCode,
row.processName,
row.assignee,
row.assignQty,
row.finishQty
])
}))
const steps = processDetailStr.split(' -> ')
const maxPerRow = 8
const rows = Math.ceil(steps.length / maxPerRow)
// 外层容器左对齐,内部表格正常
let tableHtml = '<div style="text-align: left; width: 100%;"><table class="process-table"><tbody>'
for (let row = 0; row < rows; row++) {
const startIndex = row * maxPerRow
const endIndex = Math.min(startIndex + maxPerRow, steps.length)
const rowSteps = steps.slice(startIndex, endIndex)
if (row > 0) {
tableHtml += '<tr style="height: 4px;"><td colspan="' + (maxPerRow * 2 - 1) + '" style="padding: 0; line-height: 1;"></td></tr>'
}
// 计划开始时间行
tableHtml += '<tr>'
rowSteps.forEach((step, index) => {
const parts = step.split('|')
const planDate = parts[3] || ''
const status = parts[1] || '0'
const count = parts[2] || '0'
// 获取颜色
const colorStyle = getProcessColor(status, count)
tableHtml += `<td class="plan-date-cell" style="${colorStyle}">${planDate}</td>`
if (index < rowSteps.length - 1) {
tableHtml += '<td class="arrow-cell-hidden"></td>'
}
})
tableHtml += '</tr>'
// 工序名称行
tableHtml += '<tr>'
rowSteps.forEach((step, index) => {
const parts = step.split('|')
const name = parts[0] || ''
const status = parts[1] || '0'
const count = parts[2] || '0'
const colorStyle = getProcessColor(status, count)
tableHtml += `<td style="${colorStyle}">${name}</td>`
if (index < rowSteps.length - 1) {
tableHtml += '<td class="arrow-cell">→</td>'
}
})
tableHtml += '</tr>'
// 完成数量行
tableHtml += '<tr>'
rowSteps.forEach((step, index) => {
const parts = step.split('|')
const count = parts[2] || '0'
const status = parts[1] || '0'
const colorStyle = getProcessColor(status, count)
tableHtml += `<td class="count-cell" style="${colorStyle}">合格${count}</td>`
if (index < rowSteps.length - 1) {
tableHtml += '<td class="arrow-cell-hidden"></td>'
}
})
tableHtml += '</tr>'
}
tableHtml += '</tbody></table></div>'
return tableHtml
}
// 获取颜色函数
const getProcessColor = (status, count) => {
const statusNum = parseInt(status)
const countNum = parseInt(count)
// 加工状态=2或4 绿色
if (statusNum === 2 || statusNum === 4) {
return 'color: #28d961; font-weight: 700;'
}
// 加工状态=6 蓝色
if (statusNum === 6 || statusNum === 3) {
return 'color: #09a8ff; font-weight: 700;'
}
// 完成数量>0 蓝色
if (countNum > 0) {
return 'color: #09a8ff; font-weight: 700;'
}
// 其余红色
return 'color: #ff3f37; font-weight: 700;'
}
function makeTaskBoard(rows) {
return {
...boardBaseConfig,
rowNum: 8,
columnWidth: [122, 132, 132, 152, 92, 82, 92, 94],
header: ['生产订单', '销售订单', '物料名称', '物料编码', '工序名称', '分配数量', '加急数量', '指派对象'],
rowNum: 12,
columnWidth: [150, 132, 200, 82, 750],
header: ['生产订单', '物料名称', '物料编码', '加急数量', '进度'],
data: extendRows(rows, 12).map(row => [
row.productionOrder,
row.salesOrder,
row.materialName,
row.materialCode,
row.processName,
row.assignQty,
row.urgentQty,
row.assignee
formatProcessDetail(row.assignee)
])
}
}
const urgentBoardConfig = computed(() => makeTaskBoard(urgentRows.value))
const queryDashboardData = (procedureName, applyData) => {
if (typeof CreateData !== 'function' || typeof ExecDatabase !== 'function') {
return
}
const data = CreateData('11', procedureName, [])
ExecDatabase(data).then(response => {
const rows = getResponseRows(response)
if (rows.length > 0) {
applyData(rows)
nextTick(() => {})
}
})
}
const getResponseRows = response => {
if (response && Array.isArray(response.data)) {
return response.data
}
if (response && response.data && Array.isArray(response.data.result)) {
return response.data.result
}
return []
}
const searchDashboardData = () => {
searchUrgentData()
}
const searchUrgentData = () => {
queryDashboardData('机加设备生产运营看板_加急任务查询', applyUrgentData)
}
const applyUrgentData = rows => {
const first = getFirstRow(rows)
if (first.加急订单总数 !== undefined) {
summaryCards.value[1].value = first.加急订单总数
}
urgentRows.value = rows.map(row => {
// 处理HTML实体
let processDetail = row.工序进度详情 || ''
const textarea = document.createElement('textarea')
textarea.innerHTML = processDetail
processDetail = textarea.value
return {
productionOrder: row.订单编号 + '-' + row.合同号,
materialName: row.物料描述,
materialCode: row.物料编号,
urgentQty: row.加急数量,
assignee: processDetail,
}
})
}
const getFirstRow = rows => {
if (Array.isArray(rows) && rows.length > 0) {
return rows[0]
}
return {}
}
const refreshClock = () => {
const now = new Date()
const pad = value => String(value).padStart(2, '0')
const weeks = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
currentTime.value = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())} ${weeks[now.getDay()]}`
}
const setScale = () => {
const scale = getScale()
style.transform = `scaleY(${scale.y}) scaleX(${scale.x}) translate(-50%, -50%)`
}
const getScale = () => ({
x: window.innerWidth / style.width,
y: window.innerHeight / style.height
})
onMounted(() => {
refreshClock()
clockTimer = window.setInterval(refreshClock, 1000)
@@ -317,125 +471,6 @@ onUnmounted(() => {
resizeHandler = null
}
})
const queryDashboardData = (procedureName, applyData) => {
let data
if (typeof CreateData !== 'function' || typeof ExecDatabase !== 'function') {
return
}
data = CreateData('11', procedureName, [])
ExecDatabase(data).then(response => {
const rows = getResponseRows(response)
if (rows.length > 0) {
tableData.value = rows
applyData(rows)
nextTick(() => {
})
}
})
}
const getResponseRows = response => {
if (response && Array.isArray(response.data)) {
return response.data
}
if (response && response.data && Array.isArray(response.data.result)) {
return response.data.result
}
return []
}
const searchDashboardData = () => {
searchUrgentData()
searchInProcessData()
searchInProcessData2()
}
const searchUrgentData = () => {
queryDashboardData('机加设备生产运营看板_加急任务查询', applyUrgentData)
}
const searchInProcessData = () => {
queryDashboardData('机加设备生产运营看板_机加在制查询', applyInProcessData)
}
const searchInProcessData2 = () => {
queryDashboardData('机加设备生产运营看板_机加在制查询2', applyInProcessData2)
}
// 修改赋值逻辑
const applyUrgentData = rows => {
const first = getFirstRow(rows)
console.log(first)
// 加急任务汇总卡片是索引1
if (first.加急订单总数 !== undefined) {
summaryCards.value[1].value = first.加急订单总数
}
urgentRows .value = rows.map(row => ({
productionOrder: row.订单编号,
salesOrder:row.合同号,
materialName: row.物料描述,
materialCode: row.物料编号,
assignQty: row.计划数量,
urgentQty: row.加急数量,
prevPassQty:'',
processName:row.工序名称,
assignee:row.指派对象,
}))
}
const applyInProcessData = rows => {
const first = getFirstRow(rows)
startedRows .value = rows.map(row => ({
productionOrder: row.订单号,
salesOrder:row.合同号,
materialName: row.零件名称,
materialCode: row.零件编码,
assignQty: row.分配数,
processName:row.工序名称,
assignee:row.指派对象,
finishQty:row.完成数量
}))
}
const applyInProcessData2 = rows => {
const first = getFirstRow(rows)
summaryCards.value[0].value = first.总记录数
summaryCards.value[0].subValues[0].value = first.计划开始_今天之前_数量 || 0
summaryCards.value[0].subValues[1].value = first.计划开始_今天及之后_数量 || 0
inProcessRows .value = rows.map(row => ({
productionOrder: row.订单号,
salesOrder:row.合同号,
materialName: row.零件名称,
materialCode: row.零件编码,
assignQty: row.分配数,
processName:row.工序名称,
assignee: row.指派对象,
finishQty: row.完成数量,
progress: ((row.完成数量 / row.分配数) * 100).toFixed(2),
isUrgent: row.加急 === '加急'
})).sort((a, b) => (b.isUrgent ? 1 : 0) - (a.isUrgent ? 1 : 0))
}
const getFirstRow = rows => {
if (Array.isArray(rows) && rows.length > 0) {
return rows[0]
}
return {}
}
const refreshClock = () => {
const now = new Date()
const pad = value => String(value).padStart(2, '0')
const weeks = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
currentTime.value = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())} ${weeks[now.getDay()]}`
}
const setScale = () => {
const scale = getScale()
style.transform = `scaleY(${scale.y}) scaleX(${scale.x}) translate(-50%, -50%)`
}
const getScale = () => ({
x: window.innerWidth / style.width,
y: window.innerHeight / style.height
})
</script>
<style scoped>
@@ -658,7 +693,6 @@ const getScale = () => ({
font-weight: 800;
}
/* ---- 在制任务汇总 三列数字 ---- */
.summary-triple-content {
flex: 1;
}
@@ -765,34 +799,13 @@ const getScale = () => ({
transform: rotate(180deg);
}
.work-detail-panel {
.urgent-list-panel {
left: 14px;
top: 149px;
width: 938px;
width: 1350px;
height: 818px;
}
.urgent-list-panel {
left: 968px;
top: 149px;
width: 938px;
height: 270px;
}
.process-task-panel {
left: 968px;
top: 431px;
width: 938px;
height: 270px;
}
.start-task-panel {
left: 968px;
top: 713px;
width: 938px;
height: 254px;
}
.table-box {
position: absolute;
left: 12px;
@@ -831,9 +844,8 @@ const getScale = () => ({
padding: 0 2px;
border-right: 1px solid rgba(0, 164, 255, .55);
border-bottom: 1px solid rgba(0, 164, 255, .42);
white-space: nowrap;
overflow: hidden;
text-overflow: clip;
white-space: wrap;
line-height: 30px;
}
.datav-table :deep(.header-item:last-child),
@@ -862,43 +874,62 @@ const getScale = () => ({
text-align: center;
}
.main-task-table :deep(.row-item) {
.datav-table :deep(.row-item) {
font-size: 13px;
}
.datav-table :deep(.progress-cell) {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
height: 100%;
.process-table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
.datav-table :deep(.progress-track) {
position: relative;
width: 78px;
height: 16px;
border: 1px solid rgba(73, 160, 232, .76);
border-radius: 2px;
overflow: hidden;
background: rgba(14, 41, 67, .92);
box-shadow: inset 0 0 9px rgba(16, 130, 255, .25);
.process-table td {
text-align: left; /* 改为左对齐 */
padding: 2px 4px;
font-size: 11px;
white-space: nowrap;
color: #c2cbe0;
line-height: 1.4;
}
.datav-table :deep(.progress-fill) {
position: absolute;
left: 0;
top: 0;
bottom: 0;
background: linear-gradient(90deg, #314dff 0%, #126dff 55%, #22e6ff 100%);
box-shadow: 0 0 10px rgba(23, 150, 255, .75);
.plan-date-cell {
font-size: 10px !important;
color: #8899aa !important;
padding: 2px 4px !important;
}
.datav-table :deep(.progress-cell strong) {
min-width: 46px;
color: #62c6ec;
font-size: 12px;
font-weight: 500;
text-align: right;
.count-cell {
font-weight: 700;
font-size: 12px !important;
}
</style>
.arrow-cell {
color: rgba(0, 164, 255, 0.6) !important;
font-size: 10px !important;
padding: 2px 1px !important;
width: 12px;
text-align: center !important; /* 箭头居中 */
}
.arrow-cell-hidden {
padding: 2px 1px !important;
width: 12px;
}
/* 滚动表格中的进度列样式优化 */
.datav-table :deep(.ceil .process-table) {
margin: 4px 0;
}
.datav-table :deep(.row-item) {
height: auto !important;
min-height: 50px;
}
.datav-table :deep(.ceil) {
height: auto !important;
min-height: 50px;
line-height: 1.4 !important;
}
</style>