chore: update project changes
This commit is contained in:
69
MES_Manage_View_V20/fix_机加设备生产运营看板_加急任务查询.sql
Normal file
69
MES_Manage_View_V20/fix_机加设备生产运营看板_加急任务查询.sql
Normal file
@@ -0,0 +1,69 @@
|
||||
USE [YL_MESDB]
|
||||
GO
|
||||
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON
|
||||
GO
|
||||
|
||||
ALTER PROCEDURE [dbo].[机加设备生产运营看板_加急任务查询]
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT
|
||||
[订单编号]
|
||||
,[合同号]
|
||||
,[订单类型]
|
||||
,[订单状态]
|
||||
,[物料编号]
|
||||
,[物料描述]
|
||||
,[计划数量]
|
||||
,[计量单位]
|
||||
,[下达日期]
|
||||
,[计划开始时间]
|
||||
,[计划完成]
|
||||
,[自制件属性]
|
||||
,加急总数 as 加急数量
|
||||
,工序名称
|
||||
,指派对象
|
||||
,外协分组
|
||||
,REPLACE(STUFF((
|
||||
SELECT
|
||||
' -> ' + ISNULL(d2.工序名称, '') + '(' + ISNULL(d2.指派对象, '') + ')'
|
||||
+ '|' + ISNULL(CAST(d2.加工状态 AS VARCHAR(10)), '0')
|
||||
+ '|' + ISNULL(CAST(CAST(ISNULL((
|
||||
SELECT SUM(合格数)
|
||||
FROM [dbo].[YL_质量检验_质检记录]
|
||||
WHERE TaskAID = d2.TaskAID
|
||||
), 0) AS INT) AS VARCHAR(10)), '0')
|
||||
+ '|' + ISNULL(CONVERT(VARCHAR(2), MONTH(d2.计划开始时间)) + '月' + CONVERT(VARCHAR(2), DAY(d2.计划开始时间)) + '日', '')
|
||||
+ '|' + ISNULL(CAST(d2.订单行号 AS VARCHAR(10)), '')
|
||||
+ '|' + ISNULL(CAST(d2.外协分组 AS NVARCHAR(50)), '')
|
||||
+ '|' + ISNULL(CAST(CAST(ISNULL((
|
||||
SELECT SUM(操作数值)
|
||||
FROM [dbo].[YL_加工中心_操作记录表]
|
||||
WHERE TaskAID = d2.TaskAID
|
||||
AND 操作类别 = '收料'
|
||||
), 0) AS INT) AS VARCHAR(10)), '0')
|
||||
+ '|' + ISNULL(CAST(CAST(ISNULL((
|
||||
SELECT SUM(不合格数)
|
||||
FROM [dbo].[YL_质量检验_质检记录]
|
||||
WHERE TaskAID = d2.TaskAID
|
||||
), 0) AS INT) AS VARCHAR(10)), '0')
|
||||
FROM [dbo].[View_生产订单_MES] d2
|
||||
WHERE d2.订单编号 = v.订单编号
|
||||
ORDER BY d2.订单行号
|
||||
FOR XML PATH('')
|
||||
), 1, 6, ''), '>', '>') AS 工序进度详情
|
||||
,(SELECT COUNT(DISTINCT 订单编号) FROM [YL_MESDB].[dbo].[View_生产订单_MES] WHERE 加急总数 > 0 AND 指派对象 NOT LIKE '%装配%') AS [加急订单总数]
|
||||
FROM [YL_MESDB].[dbo].[View_生产订单_MES] v
|
||||
WHERE 加急总数 > 0
|
||||
AND 指派对象 NOT LIKE '%装配%'
|
||||
AND 订单行号 = (
|
||||
SELECT MIN(订单行号)
|
||||
FROM [YL_MESDB].[dbo].[View_生产订单_MES]
|
||||
WHERE 订单编号 = v.订单编号
|
||||
)
|
||||
|
||||
END
|
||||
@@ -0,0 +1,861 @@
|
||||
、<template>
|
||||
<div>
|
||||
<el-card>
|
||||
<div class="search-container" @keyup.enter="searchTable">
|
||||
<span class="show-text">生产订单:</span>
|
||||
<el-input v-model="orderNo" placeholder="请输入生产订单" clearable style="width: 150px;margin-right: 10px" />
|
||||
<span class="show-text">销售订单:</span>
|
||||
<el-input v-model="contractNo" 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="plannum" placeholder="请输入计划号" clearable style="width: 150px;margin-right: 10px" />
|
||||
<span class="show-text">自制件属性:</span>
|
||||
<el-select v-model="selfStatus" clearable placeholder="请选择自制件属性" style="width: 150px;margin-right: 10px">
|
||||
<el-option label="全部" value="全部" />
|
||||
<el-option label="机加" value="机加" />
|
||||
<el-option label="装配" value="装配" />
|
||||
</el-select>
|
||||
|
||||
<!-- 新增外协过滤 -->
|
||||
<span class="show-text">外协状态:</span>
|
||||
<el-select v-model="outsourceStatus" clearable placeholder="请选择外协状态" style="width: 150px;margin-right: 10px">
|
||||
<el-option label="全部" value="全部" />
|
||||
<el-option label="是" value="是" />
|
||||
<el-option label="否" value="否" />
|
||||
</el-select>
|
||||
|
||||
<!-- 新增加急过滤 -->
|
||||
<span class="show-text">加急状态:</span>
|
||||
<el-select v-model="urgentStatus" clearable placeholder="请选择加急状态" style="width: 150px;margin-right: 10px">
|
||||
<el-option label="全部" value="全部" />
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" :height="'80vh'" border
|
||||
element-loading-background="rgba(0, 0, 0, 0.2)" element-loading-spinner="el-icon-loading" highlight-current-row
|
||||
size="mini" style="width: 100%;margin-top: 20px;" tooltip-effect="dark" @row-click="handleRowClick">
|
||||
|
||||
<el-table-column type="expand">
|
||||
<template slot-scope="scope">
|
||||
<el-form label-position="left" class="demo-table-expand">
|
||||
<el-table :data="scope.row.子数据" :row-class-name="getHeaderClass" border >
|
||||
<el-table-column align="center" label="序号" type="index" width="50" />
|
||||
<el-table-column align="center" label="工序名称" prop="工序名称" width="100" />
|
||||
<el-table-column align="center" label="计划数量" prop="计划数量" width="100" />
|
||||
<el-table-column align="center" label="指派对象" prop="指派对象" width="100" />
|
||||
<!-- 在子数据中显示外协标识 -->
|
||||
<!-- <el-table-column align="center" label="外协" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="status-light" :class="scope.row.是否外协 ? 'status-light-green' : 'status-light-gray'" />
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column align="center" label="计划开始时间" prop="计划开始时间" width="100">
|
||||
<template slot-scope="scope">{{ formatDate(scope.row.计划开始时间) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="计划结束时间" prop="计划完成时间" width="100">
|
||||
<template slot-scope="scope">{{ formatDate(scope.row.计划完成时间) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="实际开始时间" prop="开始时间" width="100">
|
||||
<template slot-scope="scope">{{ scope.row.开始时间 === '1900-01-01 00:00:00'? '':scope.row.开始时间}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="实际结束时间" width="100">
|
||||
<template slot-scope="scope">{{ scope.row.指派对象 === '外协' ? scope.row.外协结束时间 === '1900-01-01 00:00:00'? '':scope.row.外协结束时间 : scope.row.结束时间 === '1900-01-01 00:00:00'? '':scope.row.结束时间}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="完成进度" prop="进度" width="100">
|
||||
<template slot-scope="scope">{{ scope.row.进度}}%</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="完成数量" prop="完成数量" width="100" />
|
||||
<el-table-column align="center" label="外协发料数量" prop="收料数量" width="100" >
|
||||
<template slot-scope="scope">{{scope.row.指派对象 === '外协' ? scope.row.收料数量:''}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="合格数" prop="收检合格数" width="100" />
|
||||
<el-table-column align="center" label="不良数" prop="收检不合格数" width="100" />
|
||||
<el-table-column align="center" label="任务状态" prop="任务状态" width="100" />
|
||||
<el-table-column align="center" label="加工状态" prop="加工状态" width="100" />
|
||||
</el-table>
|
||||
</el-form>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 主表列定义 -->
|
||||
<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="100" />
|
||||
<el-table-column align="center" prop="产品编码" label="产品编码" width="220" />
|
||||
<el-table-column align="center" prop="产品名称" label="产品名称" width="190" />
|
||||
<el-table-column align="center" prop="计划数量订单" label="计划数量" width="80" />
|
||||
<el-table-column align="center" label="加急数量" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div @click="Urgent(scope.row)" style="color: aqua;">
|
||||
{{ scope.row.加急总数 }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="工艺路线" width="450">
|
||||
<template slot-scope="scope">
|
||||
<div class="process-route" v-html="formatProcessRoute(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="入库数量" label="入库数量" width="80" />
|
||||
<el-table-column align="center" label="要求完工日" width="120">
|
||||
<template slot-scope="scope">{{ formatDate(scope.row.要求完工日期) }}</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 主表显示外协状态 -->
|
||||
<!-- <el-table-column align="center" label="外协" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="status-light" :class="scope.row.是否外协 ? 'status-light-green' : 'status-light-gray'" />
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
|
||||
<!-- 其他状态列保持不变 -->
|
||||
<el-table-column align="center" label="排产" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="status-light" :class="scope.row.排产状态 === 1 ? 'status-light-green' : 'status-light-gray'" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="拣配" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="status-light" :class="Number(scope.row.拣配状态) === 1 ? 'status-light-green' : 'status-light-gray'" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="开工" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="status-light" :class="scope.row.开工时间 === 1 ? 'status-light-green' : 'status-light-gray'" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="终检" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="status-light" :class="scope.row.终检 === '是' ? 'status-light-green' : 'status-light-gray'" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="加急" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="status-light" :class="scope.row.加急状态 === '加急' ? 'status-light-green' : 'status-light-gray'" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="优先级" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="status-light" :class="scope.row.优先级 === '是' ? 'status-light-green' : 'status-light-gray'" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="当前序" label="当前序" width="80" />
|
||||
|
||||
<el-table-column align="center" prop="计划号" label="计划号" width="120" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
<el-dialog
|
||||
:visible.sync="UrgentVisible"
|
||||
title="加急明细"
|
||||
width="1200px"
|
||||
center
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form ref="Urgentarr" :model="Urgentarr" size="mini">
|
||||
<el-form-item v-for="item in Urgentarr" :key="item.订单编号" >
|
||||
<div class="info-container" >
|
||||
<div class="info-item">
|
||||
<span class="info-label" style="width: 80px">生产订单</span>
|
||||
<span class="info-value">
|
||||
<el-input
|
||||
v-model="item.订单编号"
|
||||
size="mini"
|
||||
readonly
|
||||
style="width: 300px"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label" style="width: 80px">加急数量</span>
|
||||
<span class="info-value">
|
||||
<el-input
|
||||
v-model="item.加急数量"
|
||||
size="mini"
|
||||
style="width: 150px"
|
||||
readonly
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label" style="width: 80px">物料编号</span>
|
||||
<span class="info-value">
|
||||
<el-input
|
||||
v-model="item.物料编号"
|
||||
size="mini"
|
||||
style="width: 150px"
|
||||
readonly
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="UrgentVisible = false">关闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
orderNo: '',
|
||||
contractNo: '',
|
||||
productCode: '',
|
||||
plannum: '',
|
||||
productName: '',
|
||||
processName: '',
|
||||
nature: '',
|
||||
productionStatus: '全部',
|
||||
selfStatus: '全部',
|
||||
// 新增过滤字段
|
||||
outsourceStatus: '全部', // 外协状态
|
||||
urgentStatus: '全部', // 加急状态
|
||||
orderStatus:'正常',
|
||||
completionTime: [],
|
||||
loading: false,
|
||||
tableData: [],
|
||||
selectedRow: null,
|
||||
editDialogVisible: false,
|
||||
editSubmitLoading: false,
|
||||
currentRow: {},
|
||||
editForm: {
|
||||
优先级: '',
|
||||
未完成说明: '',
|
||||
派工特殊说明: ''
|
||||
},
|
||||
splitTaskDialogVisible: false,
|
||||
splitTaskSubmitLoading: false,
|
||||
stationOptions: [],
|
||||
splitTaskForm: {
|
||||
分配数量: 0,
|
||||
指派对象: '',
|
||||
指派对象编号: '',
|
||||
派工特殊备注: '',
|
||||
计划开始时间: '',
|
||||
计划结束时间: '',
|
||||
最晚开始时间: '',
|
||||
任务概述: ''
|
||||
},
|
||||
planStartTime: [],
|
||||
planendTime: [],
|
||||
UrgentVisible: false,
|
||||
Urgentarr: {}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
var now = new Date()
|
||||
var start = new Date(Date.UTC(now.getFullYear() - 2, now.getMonth(), now.getDate(), 0, 0, 0)).toISOString().replace('T', ' ').replace('.000Z', '')
|
||||
var end = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)).toISOString().replace('T', ' ').replace('.000Z', '')
|
||||
this.searchTable()
|
||||
},
|
||||
methods: {
|
||||
Urgent(row){
|
||||
var param = []
|
||||
param.push(["物料编号",row.产品编码])
|
||||
this.UrgentVisible = true
|
||||
var Data = this.CreateData("11", "加急订单_加急明细查询",param);
|
||||
this.ExecDatabase(Data).then((response) => {
|
||||
this.Urgentarr = response.data
|
||||
});
|
||||
},
|
||||
getHeaderClass({ row, rowIndex }) {
|
||||
if (row.绿 == 1) {
|
||||
return 'warning-row'
|
||||
} else if (row.蓝 === 1) {
|
||||
return 'error-row'
|
||||
} else if (row.橙 === 1) {
|
||||
return 'success-row'
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
formatDate(date) {
|
||||
if (!date) return ''
|
||||
return date.split(' ').length > 1 ? date.split(' ')[0] : date
|
||||
},
|
||||
toProcessNumber(value) {
|
||||
const num = Number(value)
|
||||
return Number.isFinite(num) ? num : 0
|
||||
},
|
||||
escapeProcessHtml(value) {
|
||||
return String(value == null ? '' : value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
},
|
||||
isOutsourceProcess(process) {
|
||||
return process.指派对象 === '外协' ||
|
||||
String(process.工序名称 || '').includes('(外协)') ||
|
||||
String(process.工序名称 || '').includes('(外协)')
|
||||
},
|
||||
getProcessStatusNum(status) {
|
||||
const statusNum = parseInt(status)
|
||||
if (Number.isFinite(statusNum)) {
|
||||
return statusNum
|
||||
}
|
||||
|
||||
const statusMap = {
|
||||
辅助开始: 2,
|
||||
辅助结束: 3,
|
||||
开始加工: 4,
|
||||
暂停: 5,
|
||||
报工: 6,
|
||||
装配加工: 4
|
||||
}
|
||||
return statusMap[status] || 0
|
||||
},
|
||||
isBlueProcess(process) {
|
||||
const statusNum = this.getProcessStatusNum(process.加工状态)
|
||||
const countNum = parseInt(process.收检合格数)
|
||||
return statusNum === 6 || statusNum === 3 || countNum > 0
|
||||
},
|
||||
isGreenProcess(process) {
|
||||
return this.isOutsourceProcess(process) &&
|
||||
this.toProcessNumber(process.收料数量) > this.toProcessNumber(process.收检合格数) + this.toProcessNumber(process.收检不合格数)
|
||||
},
|
||||
getProcessBaseClass(process) {
|
||||
if (this.isGreenProcess(process)) {
|
||||
return 'process-route-green'
|
||||
}
|
||||
|
||||
if (this.isBlueProcess(process)) {
|
||||
return 'process-route-blue'
|
||||
}
|
||||
|
||||
const statusNum = this.getProcessStatusNum(process.加工状态)
|
||||
if (statusNum === 2 || statusNum === 4) {
|
||||
return 'process-route-green'
|
||||
}
|
||||
|
||||
return 'process-route-red'
|
||||
},
|
||||
getOutsourceGroupClassState(processes) {
|
||||
const classes = new Map()
|
||||
let currentIndexes = []
|
||||
let lastProcess = null
|
||||
|
||||
const flushOutsourceRun = () => {
|
||||
if (currentIndexes.length > 0 && lastProcess) {
|
||||
const className = this.getProcessBaseClass(lastProcess)
|
||||
currentIndexes.forEach(index => classes.set(index, className))
|
||||
}
|
||||
currentIndexes = []
|
||||
lastProcess = null
|
||||
}
|
||||
|
||||
const isSameContinuousGroup = process => {
|
||||
if (!lastProcess) {
|
||||
return true
|
||||
}
|
||||
const lineNo = this.toProcessNumber(process.订单行号)
|
||||
const lastLineNo = this.toProcessNumber(lastProcess.订单行号)
|
||||
return lineNo === lastLineNo + 1 &&
|
||||
String(process.外协分组 || '') === String(lastProcess.外协分组 || '')
|
||||
}
|
||||
|
||||
processes.forEach((process, index) => {
|
||||
if (!this.isOutsourceProcess(process)) {
|
||||
flushOutsourceRun()
|
||||
return
|
||||
}
|
||||
if (!isSameContinuousGroup(process)) {
|
||||
flushOutsourceRun()
|
||||
}
|
||||
currentIndexes.push(index)
|
||||
lastProcess = process
|
||||
})
|
||||
flushOutsourceRun()
|
||||
|
||||
return classes
|
||||
},
|
||||
getProcessClass(process, index, outsourceGroupClasses) {
|
||||
if (this.isOutsourceProcess(process)) {
|
||||
return outsourceGroupClasses.get(index) || this.getProcessBaseClass(process)
|
||||
}
|
||||
return this.getProcessBaseClass(process)
|
||||
},
|
||||
getRouteProcesses(row) {
|
||||
if (Array.isArray(row.子数据) && row.子数据.length > 0) {
|
||||
return row.子数据
|
||||
}
|
||||
|
||||
return String(row.工艺路线 || '')
|
||||
.split(',')
|
||||
.map(name => name.trim())
|
||||
.filter(Boolean)
|
||||
.map(name => ({
|
||||
工序名称: name,
|
||||
加工状态: 0,
|
||||
收检合格数: 0,
|
||||
指派对象: '',
|
||||
订单行号: 0,
|
||||
收料数量: 0,
|
||||
收检不合格数: 0
|
||||
}))
|
||||
},
|
||||
formatProcessRoute(row) {
|
||||
const processes = this.getRouteProcesses(row)
|
||||
if (processes.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const outsourceGroupClasses = this.getOutsourceGroupClassState(processes)
|
||||
return processes.map((process, index) => {
|
||||
const name = process.工序名称 || ''
|
||||
const className = this.getProcessClass(process, index, outsourceGroupClasses)
|
||||
const arrow = index < processes.length - 1 ? '<span class="process-route-arrow">-></span>' : ''
|
||||
return `<span class="${className}">${this.escapeProcessHtml(name)}</span>${arrow}`
|
||||
}).join('')
|
||||
},
|
||||
mergeOrderData(dataList) {
|
||||
// 第一步:先按照订单行号对原始数据进行排序
|
||||
const sortedDataList = [...dataList].sort((a, b) => {
|
||||
const rowA = parseInt(a.订单行号 || 0)
|
||||
const rowB = parseInt(b.订单行号 || 0)
|
||||
return rowA - rowB
|
||||
})
|
||||
|
||||
const orderMap = new Map()
|
||||
|
||||
sortedDataList.forEach((item, index) => {
|
||||
const orderNumber = item.订单号
|
||||
if (!orderMap.has(orderNumber)) {
|
||||
// 判断该工序是否外协
|
||||
const isOutsource = item.指派对象 && item.指派对象.includes('外协')
|
||||
|
||||
orderMap.set(orderNumber, {
|
||||
...item,
|
||||
当前序: '',
|
||||
|
||||
开工时间: 0,
|
||||
序检: item.序检,
|
||||
是否外协: isOutsource, // 新增:记录订单是否包含外协
|
||||
子数据: [{
|
||||
工序名称: item.工序名称,
|
||||
指派对象: item.指派对象,
|
||||
计划完成时间: item.计划完成时间,
|
||||
计划开始时间: item.计划开始时间,
|
||||
进度: item.进度,
|
||||
拆分内码: item.拆分内码,
|
||||
计划数量: item.计划数量,
|
||||
开工时间: item.开工时间,
|
||||
完成数量: item.完成数量,
|
||||
收料数量: item.收料数量,
|
||||
收检合格数: item.收检合格数,
|
||||
收检不合格数: item.收检不合格数,
|
||||
任务状态: item.任务状态,
|
||||
加工状态: item.加工状态,
|
||||
终检: item.终检,
|
||||
是否外协: isOutsource, // 新增:记录该工序是否外协
|
||||
绿: 0,
|
||||
橙: 0,
|
||||
蓝: 0,
|
||||
订单行号: item.订单行号,
|
||||
加急总数:item.加急总数,
|
||||
外协结束时间:item.外协结束时间,
|
||||
开始时间:item.开始时间,
|
||||
外协分组: item.外协分组
|
||||
}]
|
||||
})
|
||||
} else {
|
||||
const existingItem = orderMap.get(orderNumber)
|
||||
|
||||
// 判断该工序是否外协
|
||||
const isOutsource = item.指派对象 && item.指派对象.includes('外协')
|
||||
|
||||
// 如果发现外协工序,更新订单的外协状态
|
||||
if (isOutsource) {
|
||||
existingItem.是否外协 = true
|
||||
}
|
||||
|
||||
// 插入子数据时保持排序
|
||||
existingItem.子数据.push({
|
||||
工序名称: item.工序名称,
|
||||
指派对象: item.指派对象,
|
||||
计划完成时间: item.计划完成时间,
|
||||
计划开始时间: item.计划开始时间,
|
||||
进度: item.进度,
|
||||
拆分内码: item.拆分内码,
|
||||
计划数量: item.计划数量,
|
||||
开工时间: item.开工时间,
|
||||
完成数量: item.完成数量,
|
||||
收料数量: item.收料数量,
|
||||
收检合格数: item.收检合格数,
|
||||
收检不合格数: item.收检不合格数,
|
||||
任务状态: item.任务状态,
|
||||
加工状态: item.加工状态,
|
||||
终检: item.终检,
|
||||
是否外协: isOutsource, // 新增:记录该工序是否外协
|
||||
绿: 0,
|
||||
橙: 0,
|
||||
蓝: 0,
|
||||
订单行号: item.订单行号,
|
||||
加急总数:item.加急总数,
|
||||
开始时间:item.开始时间,
|
||||
结束时间:item.结束时间,
|
||||
外协结束时间:item.外协结束时间,
|
||||
外协分组: item.外协分组
|
||||
})
|
||||
|
||||
// 对子数据按照订单行号排序
|
||||
existingItem.子数据.sort((a, b) => {
|
||||
const rowA = parseInt(a.订单行号 || 0)
|
||||
const rowB = parseInt(b.订单行号 || 0)
|
||||
return rowA - rowB
|
||||
})
|
||||
|
||||
existingItem.子数据.forEach((element, index) => {
|
||||
if (new Date(element.开工时间) > new Date('1900-01-01 00:00:00')) {
|
||||
element.绿 = 1
|
||||
} else {
|
||||
element.绿 = 0
|
||||
}
|
||||
if (element.进度 == 100) {
|
||||
element.蓝 = 1
|
||||
element.绿 = 0
|
||||
} else {
|
||||
element.蓝 = 0
|
||||
}
|
||||
if (index > 0) {
|
||||
if (new Date(element.开工时间) == new Date('1900-01-01 00:00:00') && existingItem.子数据[index - 1].进度 == 100) {
|
||||
element.橙 = 1
|
||||
element.绿 = 0
|
||||
} else {
|
||||
element.橙 = 0
|
||||
}
|
||||
}
|
||||
})
|
||||
if (item.收检合格数 > 0) {
|
||||
|
||||
existingItem.当前序 = item.工序名称
|
||||
}
|
||||
|
||||
if (item.优先级 == '是') {
|
||||
existingItem.优先级 = item.优先级
|
||||
}
|
||||
if (item.终检 == '是') {
|
||||
existingItem.终检 = item.终检
|
||||
}
|
||||
if (new Date(item.开工时间) > new Date('1900-01-01 00:00:00')) {
|
||||
existingItem.开工时间 = 1
|
||||
}
|
||||
|
||||
// 确保加急状态正确传递
|
||||
if (item.加急状态 === '加急') {
|
||||
existingItem.加急状态 = '加急'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 应用过滤条件
|
||||
let filteredData = Array.from(orderMap.values()).map(item => ({
|
||||
订单号: item.订单号,
|
||||
合同号: item.合同号,
|
||||
产品编码: item.产品编码,
|
||||
产品名称: item.产品名称,
|
||||
计划数量订单: item.计划数量订单,
|
||||
要求完工日期: item.要求完工日期,
|
||||
排产状态: item.排产状态,
|
||||
优先级: item.优先级,
|
||||
当前序: item.当前序,
|
||||
工艺路线: item.工艺路线,
|
||||
计划数量: item.计划数量,
|
||||
开工时间: item.开工时间,
|
||||
序检: item.序检,
|
||||
子数据: item.子数据,
|
||||
加急状态: item.加急状态,
|
||||
终检: item.终检,
|
||||
计划号: item.计划号,
|
||||
拣配状态: item.拣配状态,
|
||||
是否外协: item.是否外协, // 新增:是否外协订单
|
||||
加急数量: item.加急数量,
|
||||
加急总数: item.加急总数,
|
||||
入库数量: item.入库数量,
|
||||
}))
|
||||
|
||||
// 应用外协过滤
|
||||
if (this.outsourceStatus !== '全部') {
|
||||
const isOutsource = this.outsourceStatus === '是'
|
||||
filteredData = filteredData.filter(item => item.是否外协 === isOutsource)
|
||||
}
|
||||
|
||||
// 应用加急过滤
|
||||
if (this.urgentStatus !== '全部') {
|
||||
const isUrgent = this.urgentStatus === '加急'
|
||||
filteredData = filteredData.filter(item =>
|
||||
isUrgent ? item.加急状态 === '加急' : (!item.加急状态 || item.加急状态 !== '加急')
|
||||
)
|
||||
}
|
||||
|
||||
this.tableData = filteredData
|
||||
},
|
||||
searchTable() {
|
||||
this.loading = true
|
||||
this.tableData = []
|
||||
var param = []
|
||||
param.push(['订单号', this.orderNo || 0])
|
||||
param.push(['物料类型', '自制件'])
|
||||
param.push(['合同号', this.contractNo])
|
||||
param.push(['产品编码', this.productCode])
|
||||
param.push(['产品名称', this.productName])
|
||||
param.push(['工序名称', this.processName])
|
||||
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(['要求完工日期1', this.completionTime ? this.completionTime[0] : ''])
|
||||
param.push(['要求完工日期2', this.completionTime ? this.completionTime[1] : ''])
|
||||
param.push(['排产状态', this.productionStatus])
|
||||
param.push(['自制件属性', this.selfStatus])
|
||||
param.push(['指派对象', '全部'])
|
||||
param.push(['计划号', this.plannum])
|
||||
param.push(['加急状态', this.urgentStatus])
|
||||
param.push(['单据状态', this.orderStatus])
|
||||
// // 添加外协状态过滤条件
|
||||
// if (this.outsourceStatus !== '全部') {
|
||||
// param.push(['是否外协', this.outsourceStatus === '是' ? 1 : 0])
|
||||
// }
|
||||
|
||||
|
||||
|
||||
var Data = this.CreateData('11', '生产管理_生产计划跟踪', param)
|
||||
this.ExecDatabase(Data).then(response => {
|
||||
this.loading = false
|
||||
if (response.data.length !== 0) {
|
||||
this.mergeOrderData(response.data)
|
||||
}
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 其他方法保持不变...
|
||||
handleRowClick(row) {
|
||||
this.selectedRow = row
|
||||
},
|
||||
editTask(row) {
|
||||
this.currentRow = row
|
||||
this.editForm = {
|
||||
优先级: row.优先级 || '',
|
||||
未完成说明: row.未完成说明 || '',
|
||||
派工特殊说明: row.派工特殊说明 || ''
|
||||
}
|
||||
this.editDialogVisible = true
|
||||
},
|
||||
submitEditForm() {
|
||||
this.editSubmitLoading = true
|
||||
const params = []
|
||||
params.push(['订单号', this.currentRow.订单号])
|
||||
params.push(['订单行号', this.currentRow.订单行号])
|
||||
params.push(['订单拆分内码', this.currentRow.拆分内码])
|
||||
params.push(['优先级', this.editForm.优先级])
|
||||
params.push(['未完成说明', this.editForm.未完成说明])
|
||||
params.push(['派工特殊说明', this.editForm.派工特殊说明])
|
||||
params.push(['最后编辑人', this.$store.state.user.name])
|
||||
|
||||
const Data = this.CreateData('12', 'MES_OrderPlanned_DescriptionEdit', params)
|
||||
this.ExecDatabase(Data).then(() => {
|
||||
this.$message.success('保存成功')
|
||||
this.editDialogVisible = false
|
||||
this.searchTable()
|
||||
}).catch(() => {
|
||||
this.$message.error('保存失败')
|
||||
}).finally(() => {
|
||||
this.editSubmitLoading = false
|
||||
})
|
||||
},
|
||||
handleOutsourceNotify(row) {
|
||||
this.$confirm('确认同计划发起外协通知吗?', '确认', {
|
||||
confirmButtonText: '是',
|
||||
cancelButtonText: '否',
|
||||
type: 'warning',
|
||||
center: true
|
||||
}).then(() => {
|
||||
this.$message.success('外协通知已发送')
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
openSplitTask() {
|
||||
if (!this.selectedRow) {
|
||||
this.$message.warning('请先选择需要操作的行')
|
||||
return
|
||||
}
|
||||
this.getStationList()
|
||||
this.splitTaskForm = {
|
||||
分配数量: this.selectedRow.计划数量 || 0,
|
||||
指派对象: this.selectedRow.指派对象 || '',
|
||||
指派对象编号: this.selectedRow.指派对象编号 || '',
|
||||
派工特殊备注: this.selectedRow.派工特殊说明 || '',
|
||||
计划开始时间: this.selectedRow.计划开始时间 || '',
|
||||
计划结束时间: this.selectedRow.计划完成时间 || '',
|
||||
最晚开始时间: this.selectedRow.最晚开始时间 || '',
|
||||
任务概述: this.selectedRow.计划数量 || ''
|
||||
}
|
||||
this.splitTaskDialogVisible = true
|
||||
},
|
||||
submitSplitTaskForm() {
|
||||
this.splitTaskSubmitLoading = true
|
||||
this.$confirm('确认要拆分子任务吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 空逻辑
|
||||
this.splitTaskSubmitLoading = false
|
||||
this.splitTaskDialogVisible = false
|
||||
this.$message.success('操作成功')
|
||||
this.searchTable()
|
||||
}).catch(() => {
|
||||
this.splitTaskSubmitLoading = false
|
||||
this.$message.info('已取消操作')
|
||||
})
|
||||
},
|
||||
getStationList() {
|
||||
const params = []
|
||||
const Data = this.CreateData('11', 'Prog_MES_LoadStations', params)
|
||||
this.ExecDatabase(Data).then(response => {
|
||||
if (response.data && response.data.length > 0) {
|
||||
this.stationOptions = response.data.map(item => ({
|
||||
label: item.OPName,
|
||||
value: item.OPName,
|
||||
code: item.VisCode
|
||||
}))
|
||||
}
|
||||
})
|
||||
},
|
||||
handleStationChange(value) {
|
||||
const selected = this.stationOptions.find(item => item.value === value)
|
||||
if (selected) {
|
||||
this.splitTaskForm.指派对象编号 = selected.code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
::v-deep .el-table th.el-table__cell>.cell{
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.process{
|
||||
width: 120px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.process-route {
|
||||
line-height: 20px;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
::v-deep .process-route-green {
|
||||
color: #28d961;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
::v-deep .process-route-blue {
|
||||
color: #09a8ff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
::v-deep .process-route-red {
|
||||
color: #ff3f37;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
::v-deep .process-route-arrow {
|
||||
margin: 0 4px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.show-text {
|
||||
margin-right: 5px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.status-light {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.status-light-green {
|
||||
background-color: #67C23A;
|
||||
}
|
||||
|
||||
.status-light-gray {
|
||||
background-color: #909399;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
::v-deep .warning-row {
|
||||
background-color: #67c23a !important;
|
||||
}
|
||||
|
||||
::v-deep .error-row {
|
||||
background-color: #409eff !important;
|
||||
}
|
||||
|
||||
::v-deep .success-row {
|
||||
background-color: #f2a201 !important;
|
||||
}
|
||||
::v-deep .el-table td.el-table__cell{
|
||||
border: 0;
|
||||
}
|
||||
.info-container {
|
||||
border: 1px solid #ccc;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
width: 30%;
|
||||
padding: 8px;
|
||||
background-color: #f5f7fa;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
flex: 1;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.info-label,
|
||||
.info-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -24,7 +24,7 @@
|
||||
<span class="show-text">要求完工日期:</span>
|
||||
<el-date-picker
|
||||
v-model="completionTime"
|
||||
:clearable="false"
|
||||
|
||||
end-placeholder="结束时间"
|
||||
format="yyyy-MM-dd"
|
||||
popper-class="virtual-cell-time-picker"
|
||||
@@ -36,7 +36,7 @@
|
||||
<span class="show-text">计划开始:</span>
|
||||
<el-date-picker
|
||||
v-model="planStartTime"
|
||||
:clearable="false"
|
||||
|
||||
|
||||
placeholder="计划开始时间"
|
||||
style="width: 150px;
|
||||
@@ -46,7 +46,7 @@
|
||||
<span class="show-text">计划完成:</span>
|
||||
<el-date-picker
|
||||
v-model="planendTime"
|
||||
:clearable="false"
|
||||
|
||||
placeholder="计划完成时间"
|
||||
style="width: 150px;
|
||||
vertical-align: top"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -148,7 +148,11 @@
|
||||
</template>
|
||||
</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" label="工艺路线" width="260">
|
||||
<template slot-scope="scope">
|
||||
<div class="process-route" v-html="formatProcessRoute(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="计划号" label="计划号" width="120" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
@@ -284,6 +288,142 @@ export default {
|
||||
if (!date) return ''
|
||||
return date.split(' ').length > 1 ? date.split(' ')[0] : date
|
||||
},
|
||||
toProcessNumber(value) {
|
||||
const num = Number(value)
|
||||
return Number.isFinite(num) ? num : 0
|
||||
},
|
||||
escapeProcessHtml(value) {
|
||||
return String(value == null ? '' : value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
},
|
||||
isOutsourceProcess(process) {
|
||||
return process.指派对象 === '外协' ||
|
||||
String(process.工序名称 || '').includes('(外协)') ||
|
||||
String(process.工序名称 || '').includes('(外协)')
|
||||
},
|
||||
getProcessStatusNum(status) {
|
||||
const statusNum = parseInt(status)
|
||||
if (Number.isFinite(statusNum)) {
|
||||
return statusNum
|
||||
}
|
||||
|
||||
const statusMap = {
|
||||
辅助开始: 2,
|
||||
辅助结束: 3,
|
||||
开始加工: 4,
|
||||
暂停: 5,
|
||||
报工: 6,
|
||||
装配加工: 4
|
||||
}
|
||||
return statusMap[status] || 0
|
||||
},
|
||||
isBlueProcess(process) {
|
||||
const statusNum = this.getProcessStatusNum(process.加工状态)
|
||||
const countNum = parseInt(process.收检合格数)
|
||||
return statusNum === 6 || statusNum === 3 || countNum > 0
|
||||
},
|
||||
isGreenProcess(process) {
|
||||
return this.isOutsourceProcess(process) &&
|
||||
this.toProcessNumber(process.收料数量) > this.toProcessNumber(process.收检合格数) + this.toProcessNumber(process.收检不合格数)
|
||||
},
|
||||
getProcessBaseClass(process) {
|
||||
if (this.isGreenProcess(process)) {
|
||||
return 'process-route-green'
|
||||
}
|
||||
|
||||
if (this.isBlueProcess(process)) {
|
||||
return 'process-route-blue'
|
||||
}
|
||||
|
||||
const statusNum = this.getProcessStatusNum(process.加工状态)
|
||||
if (statusNum === 2 || statusNum === 4) {
|
||||
return 'process-route-green'
|
||||
}
|
||||
|
||||
return 'process-route-red'
|
||||
},
|
||||
getOutsourceGroupClassState(processes) {
|
||||
const classes = new Map()
|
||||
let currentIndexes = []
|
||||
let lastProcess = null
|
||||
|
||||
const flushOutsourceRun = () => {
|
||||
if (currentIndexes.length > 0 && lastProcess) {
|
||||
const className = this.getProcessBaseClass(lastProcess)
|
||||
currentIndexes.forEach(index => classes.set(index, className))
|
||||
}
|
||||
currentIndexes = []
|
||||
lastProcess = null
|
||||
}
|
||||
|
||||
const isSameContinuousGroup = process => {
|
||||
if (!lastProcess) {
|
||||
return true
|
||||
}
|
||||
const lineNo = this.toProcessNumber(process.订单行号)
|
||||
const lastLineNo = this.toProcessNumber(lastProcess.订单行号)
|
||||
return lineNo === lastLineNo + 1 &&
|
||||
String(process.外协分组 || '') === String(lastProcess.外协分组 || '')
|
||||
}
|
||||
|
||||
processes.forEach((process, index) => {
|
||||
if (!this.isOutsourceProcess(process)) {
|
||||
flushOutsourceRun()
|
||||
return
|
||||
}
|
||||
if (!isSameContinuousGroup(process)) {
|
||||
flushOutsourceRun()
|
||||
}
|
||||
currentIndexes.push(index)
|
||||
lastProcess = process
|
||||
})
|
||||
flushOutsourceRun()
|
||||
|
||||
return classes
|
||||
},
|
||||
getProcessClass(process, index, outsourceGroupClasses) {
|
||||
if (this.isOutsourceProcess(process)) {
|
||||
return outsourceGroupClasses.get(index) || this.getProcessBaseClass(process)
|
||||
}
|
||||
return this.getProcessBaseClass(process)
|
||||
},
|
||||
getRouteProcesses(row) {
|
||||
if (Array.isArray(row.子数据) && row.子数据.length > 0) {
|
||||
return row.子数据
|
||||
}
|
||||
|
||||
return String(row.工艺路线 || '')
|
||||
.split(',')
|
||||
.map(name => name.trim())
|
||||
.filter(Boolean)
|
||||
.map(name => ({
|
||||
工序名称: name,
|
||||
加工状态: 0,
|
||||
收检合格数: 0,
|
||||
指派对象: '',
|
||||
订单行号: 0,
|
||||
收料数量: 0,
|
||||
收检不合格数: 0
|
||||
}))
|
||||
},
|
||||
formatProcessRoute(row) {
|
||||
const processes = this.getRouteProcesses(row)
|
||||
if (processes.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const outsourceGroupClasses = this.getOutsourceGroupClassState(processes)
|
||||
return processes.map((process, index) => {
|
||||
const name = process.工序名称 || ''
|
||||
const className = this.getProcessClass(process, index, outsourceGroupClasses)
|
||||
const arrow = index < processes.length - 1 ? '<span class="process-route-arrow">-></span>' : ''
|
||||
return `<span class="${className}">${this.escapeProcessHtml(name)}</span>${arrow}`
|
||||
}).join('')
|
||||
},
|
||||
mergeOrderData(dataList) {
|
||||
// 第一步:先按照订单行号对原始数据进行排序
|
||||
const sortedDataList = [...dataList].sort((a, b) => {
|
||||
@@ -330,7 +470,8 @@ export default {
|
||||
订单行号: item.订单行号,
|
||||
加急总数:item.加急总数,
|
||||
外协结束时间:item.外协结束时间,
|
||||
开始时间:item.开始时间
|
||||
开始时间:item.开始时间,
|
||||
外协分组: item.外协分组
|
||||
}]
|
||||
})
|
||||
} else {
|
||||
@@ -370,6 +511,7 @@ export default {
|
||||
开始时间:item.开始时间,
|
||||
结束时间:item.结束时间,
|
||||
外协结束时间:item.外协结束时间,
|
||||
外协分组: item.外协分组
|
||||
})
|
||||
|
||||
// 对子数据按照订单行号排序
|
||||
@@ -615,6 +757,33 @@ export default {
|
||||
width: 120px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.process-route {
|
||||
line-height: 20px;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
::v-deep .process-route-green {
|
||||
color: #28d961;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
::v-deep .process-route-blue {
|
||||
color: #09a8ff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
::v-deep .process-route-red {
|
||||
color: #ff3f37;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
::v-deep .process-route-arrow {
|
||||
margin: 0 4px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,10 @@
|
||||
</el-select>
|
||||
<el-button :disabled="!!loading" icon="el-icon-search" plain type="primary" @click="searchTable()">查询</el-button>
|
||||
<el-button :disabled="!!multipleSelection.length === 0 || loading" icon="el-icon-check" plain type="success" style="width: 100px;" @click="batchValidate">一键校验</el-button>
|
||||
<el-button :disabled=" uesrname != '付延辉'" icon="el-icon-check" plain type="danger" style="width: 100px;" @click="saveValidationRecord2(multipleSelection)">直接校验</el-button>
|
||||
<el-button :disabled=" uesrname != '付延辉'" icon="el-icon-check" plain type="danger" style="position: absolute;
|
||||
width: 100px;
|
||||
right: 36px;
|
||||
top: 70px;width: 100px;" @click="saveValidationRecord2(multipleSelection)">直接校验</el-button>
|
||||
<!-- <el-button :disabled="!!loading" icon="el-icon-plus" plain type="warning" style="width: 130px;" @click="addWorkTimeRecord">新增工时记录</el-button> -->
|
||||
</div>
|
||||
|
||||
|
||||
Binary file not shown.
@@ -164,7 +164,7 @@ let clockTimer = null
|
||||
let resizeHandler = null
|
||||
|
||||
const summary = reactive({
|
||||
equipmentTotal: 18,
|
||||
equipmentTotal: 23,
|
||||
running: 10,
|
||||
running2: 0,
|
||||
standby: 4,
|
||||
@@ -595,11 +595,14 @@ const searchPlanExecuteData = () => {
|
||||
|
||||
const applyDashboardData = rows => {
|
||||
const first = getFirstRow(rows)
|
||||
summary.equipmentTotal = 18
|
||||
summary.equipmentTotal = 23
|
||||
summary.running = first.运行数量 || 0
|
||||
summary.standby = first.待机数量 || 0
|
||||
summary.alarm = first.报警数量 || 0
|
||||
summary.shutdown = first.关机数量 || 0
|
||||
// summary.standby = first.待机数量 || 0
|
||||
// summary.alarm = first.报警数量 || 0
|
||||
// summary.shutdown = first.关机数量 || 0
|
||||
summary.standby = 0
|
||||
summary.alarm = 0
|
||||
summary.shutdown = 0
|
||||
}
|
||||
|
||||
const applydeviceData = rows => {
|
||||
@@ -924,6 +927,10 @@ onMounted(() => {
|
||||
initCharts()
|
||||
})
|
||||
searchDashboardData()
|
||||
setInterval(function(){
|
||||
searchDashboardData()
|
||||
},600000)
|
||||
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
@@ -115,28 +115,28 @@ const summaryCards = ref([
|
||||
className: 'urgent-card',
|
||||
valueClass: 'blue-text'
|
||||
},
|
||||
{
|
||||
title: '关键工序任务',
|
||||
value: 14,
|
||||
unit: '单',
|
||||
icon: icons.shield,
|
||||
className: 'key-card',
|
||||
valueClass: 'purple-text'
|
||||
},
|
||||
{
|
||||
title: '缺勤人员',
|
||||
value: 4,
|
||||
unit: '人',
|
||||
icon: icons.user,
|
||||
className: 'absence-card',
|
||||
valueClass: 'orange-text',
|
||||
people: [
|
||||
{ name: '张三', station: '铣床1#' },
|
||||
{ name: '王五', station: '车床2#' },
|
||||
{ name: '孙八', station: '磨床1#' },
|
||||
{ name: '郑十一', station: '检验台1#' }
|
||||
]
|
||||
}
|
||||
// {
|
||||
// title: '关键工序任务',
|
||||
// value: 14,
|
||||
// unit: '单',
|
||||
// icon: icons.shield,
|
||||
// className: 'key-card',
|
||||
// valueClass: 'purple-text'
|
||||
// },
|
||||
// {
|
||||
// title: '缺勤人员',
|
||||
// value: 4,
|
||||
// unit: '人',
|
||||
// icon: icons.user,
|
||||
// className: 'absence-card',
|
||||
// valueClass: 'orange-text',
|
||||
// people: [
|
||||
// { name: '张三', station: '铣床1#' },
|
||||
// { name: '王五', station: '车床2#' },
|
||||
// { name: '孙八', station: '磨床1#' },
|
||||
// { name: '郑十一', station: '检验台1#' }
|
||||
// ]
|
||||
// }
|
||||
])
|
||||
|
||||
const urgentRows = ref([])
|
||||
@@ -257,12 +257,121 @@ const boardBaseConfig = {
|
||||
// 格式化工序进度详情为HTML
|
||||
// 格式化工序进度详情为HTML
|
||||
// 格式化工序进度详情为HTML
|
||||
const blueProcessStyle = 'color: #09a8ff; font-weight: 700;'
|
||||
const greenProcessStyle = 'color: #28d961; font-weight: 700;'
|
||||
|
||||
const toNumber = value => {
|
||||
const num = Number(value)
|
||||
return Number.isFinite(num) ? num : 0
|
||||
}
|
||||
|
||||
const parseProcessStep = step => {
|
||||
const parts = step.split('|')
|
||||
const hasLineNo = parts[4] !== undefined && parts[4] !== '' && !Number.isNaN(Number(parts[4]))
|
||||
|
||||
return {
|
||||
name: parts[0] || '',
|
||||
status: parts[1] || '0',
|
||||
count: parts[2] || '0',
|
||||
planDate: parts[3] || '',
|
||||
assignee: hasLineNo ? '' : (parts[4] || ''),
|
||||
outsourceGroup: hasLineNo ? (parts[5] || '') : (parts[5] || ''),
|
||||
lineNo: hasLineNo ? toNumber(parts[4]) : null,
|
||||
issueQty: hasLineNo ? toNumber(parts[6]) : 0,
|
||||
badQty: hasLineNo ? toNumber(parts[7]) : 0
|
||||
}
|
||||
}
|
||||
|
||||
const isBlueProcess = (status, count) => {
|
||||
const statusNum = parseInt(status)
|
||||
const countNum = parseInt(count)
|
||||
return statusNum === 6 || statusNum === 3 || countNum > 0
|
||||
}
|
||||
|
||||
const isOutsourceProcess = process => (
|
||||
process.assignee === '外协' ||
|
||||
process.name.includes('(外协)') ||
|
||||
process.name.includes('(外协)')
|
||||
)
|
||||
|
||||
const isGreenProcess = process => (
|
||||
isOutsourceProcess(process) &&
|
||||
process.issueQty > toNumber(process.count) + toNumber(process.badQty)
|
||||
)
|
||||
|
||||
const getProcessBaseStyle = process => {
|
||||
if (isGreenProcess(process)) {
|
||||
return greenProcessStyle
|
||||
}
|
||||
|
||||
if (isBlueProcess(process.status, process.count)) {
|
||||
return blueProcessStyle
|
||||
}
|
||||
|
||||
const statusNum = parseInt(process.status)
|
||||
if (statusNum === 2 || statusNum === 4) {
|
||||
return greenProcessStyle
|
||||
}
|
||||
|
||||
return 'color: #ff3f37; font-weight: 700;'
|
||||
}
|
||||
|
||||
const getOutsourceGroupStyleState = steps => {
|
||||
const styles = new Map()
|
||||
const processes = steps.map(parseProcessStep)
|
||||
|
||||
let currentIndexes = []
|
||||
let lastProcess = null
|
||||
const flushOutsourceRun = () => {
|
||||
if (currentIndexes.length > 0 && lastProcess) {
|
||||
const style = getProcessBaseStyle(lastProcess)
|
||||
currentIndexes.forEach(index => styles.set(index, style))
|
||||
}
|
||||
currentIndexes = []
|
||||
lastProcess = null
|
||||
}
|
||||
|
||||
const isSameContinuousGroup = process => {
|
||||
if (!lastProcess) {
|
||||
return true
|
||||
}
|
||||
if (process.lineNo === null || lastProcess.lineNo === null) {
|
||||
return false
|
||||
}
|
||||
return process.lineNo === lastProcess.lineNo + 1 &&
|
||||
(process.outsourceGroup || '') === (lastProcess.outsourceGroup || '')
|
||||
}
|
||||
|
||||
processes.forEach((process, index) => {
|
||||
if (!isOutsourceProcess(process)) {
|
||||
flushOutsourceRun()
|
||||
return
|
||||
}
|
||||
if (!isSameContinuousGroup(process)) {
|
||||
flushOutsourceRun()
|
||||
}
|
||||
currentIndexes.push(index)
|
||||
lastProcess = process
|
||||
})
|
||||
flushOutsourceRun()
|
||||
|
||||
return { styles }
|
||||
}
|
||||
|
||||
const getOutsourceGroupStyle = (process, index, outsourceGroupStyleState) => {
|
||||
if (!isOutsourceProcess(process)) {
|
||||
return ''
|
||||
}
|
||||
return outsourceGroupStyleState.styles.get(index) || ''
|
||||
}
|
||||
|
||||
const formatProcessDetail = (processDetailStr) => {
|
||||
if (!processDetailStr || typeof processDetailStr !== 'string') {
|
||||
return ''
|
||||
}
|
||||
|
||||
const steps = processDetailStr.split(' -> ')
|
||||
const outsourceGroupStyleState = getOutsourceGroupStyleState(steps)
|
||||
const maxPerRow = 8
|
||||
const rows = Math.ceil(steps.length / maxPerRow)
|
||||
|
||||
@@ -281,15 +390,17 @@ const formatProcessDetail = (processDetailStr) => {
|
||||
// 计划开始时间行
|
||||
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 process = parseProcessStep(step)
|
||||
|
||||
// 获取颜色
|
||||
const colorStyle = getProcessColor(status, count)
|
||||
const colorStyle = getProcessColor(
|
||||
process.status,
|
||||
process.count,
|
||||
process,
|
||||
getOutsourceGroupStyle(process, startIndex + index, outsourceGroupStyleState)
|
||||
)
|
||||
|
||||
tableHtml += `<td class="plan-date-cell" style="${colorStyle}">${planDate}</td>`
|
||||
tableHtml += `<td class="plan-date-cell" style="${colorStyle}">${process.planDate}</td>`
|
||||
if (index < rowSteps.length - 1) {
|
||||
tableHtml += '<td class="arrow-cell-hidden"></td>'
|
||||
}
|
||||
@@ -299,14 +410,16 @@ const formatProcessDetail = (processDetailStr) => {
|
||||
// 工序名称行
|
||||
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 process = parseProcessStep(step)
|
||||
|
||||
const colorStyle = getProcessColor(status, count)
|
||||
const colorStyle = getProcessColor(
|
||||
process.status,
|
||||
process.count,
|
||||
process,
|
||||
getOutsourceGroupStyle(process, startIndex + index, outsourceGroupStyleState)
|
||||
)
|
||||
|
||||
tableHtml += `<td style="${colorStyle}">${name}</td>`
|
||||
tableHtml += `<td style="${colorStyle}">${process.name}</td>`
|
||||
if (index < rowSteps.length - 1) {
|
||||
tableHtml += '<td class="arrow-cell">→</td>'
|
||||
}
|
||||
@@ -316,13 +429,16 @@ const formatProcessDetail = (processDetailStr) => {
|
||||
// 完成数量行
|
||||
tableHtml += '<tr>'
|
||||
rowSteps.forEach((step, index) => {
|
||||
const parts = step.split('|')
|
||||
const count = parts[2] || '0'
|
||||
const status = parts[1] || '0'
|
||||
const process = parseProcessStep(step)
|
||||
|
||||
const colorStyle = getProcessColor(status, count)
|
||||
const colorStyle = getProcessColor(
|
||||
process.status,
|
||||
process.count,
|
||||
process,
|
||||
getOutsourceGroupStyle(process, startIndex + index, outsourceGroupStyleState)
|
||||
)
|
||||
|
||||
tableHtml += `<td class="count-cell" style="${colorStyle}">合格${count}</td>`
|
||||
tableHtml += `<td class="count-cell" style="${colorStyle}">合格${process.count}</td>`
|
||||
if (index < rowSteps.length - 1) {
|
||||
tableHtml += '<td class="arrow-cell-hidden"></td>'
|
||||
}
|
||||
@@ -336,21 +452,28 @@ const formatProcessDetail = (processDetailStr) => {
|
||||
}
|
||||
|
||||
// 获取颜色函数
|
||||
const getProcessColor = (status, count) => {
|
||||
const getProcessColor = (status, count, process = null, groupStyle = '') => {
|
||||
if (groupStyle) {
|
||||
return groupStyle
|
||||
}
|
||||
if (process) {
|
||||
return getProcessBaseStyle(process)
|
||||
}
|
||||
|
||||
const statusNum = parseInt(status)
|
||||
const countNum = parseInt(count)
|
||||
|
||||
// 加工状态=2或4 绿色
|
||||
if (statusNum === 2 || statusNum === 4) {
|
||||
return 'color: #28d961; font-weight: 700;'
|
||||
return greenProcessStyle
|
||||
}
|
||||
// 加工状态=6 蓝色
|
||||
if (statusNum === 6 || statusNum === 3) {
|
||||
return 'color: #09a8ff; font-weight: 700;'
|
||||
return blueProcessStyle
|
||||
}
|
||||
// 完成数量>0 蓝色
|
||||
if (countNum > 0) {
|
||||
return 'color: #09a8ff; font-weight: 700;'
|
||||
return blueProcessStyle
|
||||
}
|
||||
// 其余红色
|
||||
return 'color: #ff3f37; font-weight: 700;'
|
||||
@@ -358,7 +481,7 @@ const getProcessColor = (status, count) => {
|
||||
function makeTaskBoard(rows) {
|
||||
return {
|
||||
...boardBaseConfig,
|
||||
rowNum: 12,
|
||||
rowNum: 8,
|
||||
columnWidth: [150, 132, 200, 82, 750],
|
||||
header: ['生产订单', '物料名称', '物料编码', '加急数量', '进度'],
|
||||
data: extendRows(rows, 12).map(row => [
|
||||
|
||||
BIN
生产任务看板/dist.zip
BIN
生产任务看板/dist.zip
Binary file not shown.
@@ -8,7 +8,7 @@ const request_config = window.g.API_WEB_URL + `/submit/MESCommonBase.ashx`
|
||||
export const name = name_config
|
||||
const service = axios.create({
|
||||
baseURL: request_config,
|
||||
timeout: 1000 * 5 // 请求超时时间
|
||||
timeout: 300000 // 请求超时时间,300秒
|
||||
})
|
||||
|
||||
// && config.data.name !== '菜单系统模块_通知信息_查询数据' && config.data.name !== '菜单系统模块_首页信息_查询数据1' && config.data.name !== '菜单系统模块_首页信息_查询数据2'
|
||||
|
||||
@@ -102,17 +102,20 @@
|
||||
|
||||
<section class="table-area">
|
||||
<article class="panel table-panel">
|
||||
<div class="panel-caption">未按期齐套任务</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<div class="panel-caption">加急任务列表</div>
|
||||
<div class="table-wrap urgent-task-wrap">
|
||||
<table class="scroll-table urgent-scroll-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="column in leftColumns" :key="column.key">{{ column.label }}</th>
|
||||
<th v-for="column in urgentColumns" :key="column.key" :class="`col-${column.key}`">{{ column.label }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, index) in kitRows" :key="`${row.salesOrder}-${index}`">
|
||||
<td v-for="column in leftColumns" :key="column.key">{{ row[column.key] }}</td>
|
||||
<tbody class="scroll-body" :style="{ '--scroll-duration': urgentScrollDuration }">
|
||||
<tr v-for="(row, index) in displayUrgentRows" :key="`${row.productionOrder}-${index}`">
|
||||
<td v-for="column in urgentColumns" :key="column.key" :class="`col-${column.key}`">
|
||||
<div v-if="column.key === 'progress'" class="process-detail" v-html="formatProcessDetail(row.progress)" />
|
||||
<template v-else>{{ row[column.key] }}</template>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -120,17 +123,17 @@
|
||||
</article>
|
||||
|
||||
<article class="panel table-panel">
|
||||
<div class="panel-caption">加急件明细</div>
|
||||
<div class="panel-caption">未按期齐套任务</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<table class="scroll-table kit-scroll-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="column in urgentColumns" :key="column.key">{{ column.label }}</th>
|
||||
<th v-for="column in leftColumns" :key="column.key" :class="`col-${column.key}`">{{ column.label }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, index) in urgentRows" :key="`${row.salesOrder}-${index}`">
|
||||
<td v-for="column in urgentColumns" :key="column.key">{{ row[column.key] }}</td>
|
||||
<tbody class="scroll-body" :style="{ '--scroll-duration': kitScrollDuration }">
|
||||
<tr v-for="(row, index) in displayKitRows" :key="`${row.planNo}-${row.productionOrder}-${index}`">
|
||||
<td v-for="column in leftColumns" :key="column.key" :class="`col-${column.key}`">{{ row[column.key] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -150,8 +153,8 @@ const tableData = ref([])
|
||||
const now = ref(new Date('2026-05-06 10:10:10'))
|
||||
let timer = null
|
||||
let resizeHandler = null
|
||||
let Urgentnum = null
|
||||
let notintimenum = null
|
||||
const Urgentnum = ref(0)
|
||||
const notintimenum = ref(0)
|
||||
let complete = reactive({
|
||||
completeset:100,
|
||||
tasknum:0,
|
||||
@@ -200,8 +203,13 @@ onMounted(() => {
|
||||
resizeHandler = () => {
|
||||
setScale()
|
||||
}
|
||||
|
||||
window.addEventListener('resize', resizeHandler)
|
||||
searchDashboardData()
|
||||
// setInterval(function(){
|
||||
// searchDashboardData()
|
||||
// },600000)
|
||||
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
@@ -223,7 +231,7 @@ const searchNoProcessData = () => {
|
||||
queryDashboardData('生产任务综合看板_未排产无工艺查询', applyNoProcessData)
|
||||
}
|
||||
const searchUrgentData = () => {
|
||||
queryDashboardData('生产任务综合看板_加急任务查询', applyUrgentData)
|
||||
queryDashboardData('机加设备生产运营看板_加急任务查询', applyUrgentData)
|
||||
}
|
||||
const searchNotInTimeData = () => {
|
||||
queryDashboardData('生产任务综合看板_未及时开工查询', applyNotInTimeData)
|
||||
@@ -232,50 +240,43 @@ const searchUnCompleteData = () => {
|
||||
queryDashboardData('生产任务综合看板_未按期齐套任务', applyUnCompleteData)
|
||||
}
|
||||
const applyCompleteSetData = rows => {
|
||||
complete.completeset = rows[0].齐套率
|
||||
complete.untasknum = rows[0].未齐套任务数
|
||||
complete.tasknum = rows[0].总任务数
|
||||
const first = getFirstRow(rows)
|
||||
complete.completeset = first.齐套率 || 0
|
||||
complete.untasknum = first.未齐套任务数 || 0
|
||||
complete.tasknum = first.总任务数 || 0
|
||||
}
|
||||
const applyNoProcessData = rows => {
|
||||
const total = rows[0].未排产任务数
|
||||
const assembly = rows[0].装配未排产数
|
||||
const first = getFirstRow(rows)
|
||||
const total = first.未排产任务数 || 0
|
||||
const assembly = first.装配未排产数 || 0
|
||||
noProcessStats.total = total
|
||||
noProcessStats.machining = total - assembly
|
||||
noProcessStats.assembly = assembly
|
||||
topStats[1].value = rows[0].无生产工艺计划数
|
||||
topStats[1].value = first.无生产工艺计划数 || 0
|
||||
}
|
||||
const applyNotInTimeData = rows => {
|
||||
notintimenum = rows[0].未及时开工总数
|
||||
notintimenum.value = getFirstRow(rows).未及时开工总数 || 0
|
||||
}
|
||||
const applyUrgentData = rows => {
|
||||
urgentRows = rows.map( item => ({
|
||||
salesOrder: item.合同号,
|
||||
productionOrder: item.订单编号,
|
||||
productName: item.物料描述,
|
||||
productCode: item.物料编号,
|
||||
property: item.自制件属性,
|
||||
planQty: item.计划数量,
|
||||
releaseDate: item.下达日期,
|
||||
startDate: item.计划开始,
|
||||
finishDate: item.计划完成,
|
||||
const first = getFirstRow(rows)
|
||||
if (first.加急订单总数 !== undefined) {
|
||||
Urgentnum.value = first.加急订单总数
|
||||
}
|
||||
|
||||
urgentRows.value = rows.map(item => ({
|
||||
productionOrder: `${item.订单编号 || ''}-${item.合同号 || ''}`,
|
||||
materialName: item.物料描述,
|
||||
materialCode: item.物料编号,
|
||||
urgentQty: item.加急数量,
|
||||
progress: decodeHtml(item.工序进度详情 || ''),
|
||||
}))
|
||||
Urgentnum = rows[0].加急订单总数
|
||||
}
|
||||
const applyUnCompleteData = rows => {
|
||||
kitRows = rows.map( item => ({
|
||||
kitRows.value = rows.map( item => ({
|
||||
productionOrder: item.订单编号,
|
||||
productName: item.物料描述,
|
||||
productCode: item.物料编号,
|
||||
property: item.自制件属性,
|
||||
planQty: item.计划数量,
|
||||
releaseDate: item.下达日期,
|
||||
startDate: item.计划开始时间,
|
||||
finishDate: item.计划完成时间,
|
||||
planNo: item.合同号,
|
||||
stockQty: '0',
|
||||
kitDate: item.计划开始时间,
|
||||
pickStatus: item.拣配状态 == 1 ?'已拣配':'未拣配',
|
||||
issueStatus: item.发料状态 == 2 ?'发料缺件':'未发料',
|
||||
}))
|
||||
}
|
||||
const queryDashboardData = (procedureName, applyData) => {
|
||||
@@ -297,6 +298,240 @@ const getResponseRows = response => {
|
||||
return []
|
||||
}
|
||||
|
||||
const getFirstRow = rows => {
|
||||
if (Array.isArray(rows) && rows.length > 0) {
|
||||
return rows[0]
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
const escapeHtml = value => String(value == null ? '' : value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
|
||||
const decodeHtml = value => {
|
||||
if (!value || typeof value !== 'string') {
|
||||
return ''
|
||||
}
|
||||
if (typeof document === 'undefined') {
|
||||
return value
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
}
|
||||
const textarea = document.createElement('textarea')
|
||||
textarea.innerHTML = value
|
||||
return textarea.value
|
||||
}
|
||||
|
||||
const blueProcessStyle = 'color: #09a8ff; font-weight: 700;'
|
||||
const greenProcessStyle = 'color: #28d961; font-weight: 700;'
|
||||
const redProcessStyle = 'color: #ff3f37; font-weight: 700;'
|
||||
|
||||
const toNumber = value => {
|
||||
const num = Number(value)
|
||||
return Number.isFinite(num) ? num : 0
|
||||
}
|
||||
|
||||
const parseProcessStep = step => {
|
||||
const parts = step.split('|')
|
||||
const hasLineNo = parts[4] !== undefined && parts[4] !== '' && !Number.isNaN(Number(parts[4]))
|
||||
|
||||
return {
|
||||
name: parts[0] || '',
|
||||
status: parts[1] || '0',
|
||||
count: parts[2] || '0',
|
||||
planDate: parts[3] || '',
|
||||
assignee: hasLineNo ? '' : (parts[4] || ''),
|
||||
outsourceGroup: hasLineNo ? (parts[5] || '') : (parts[5] || ''),
|
||||
lineNo: hasLineNo ? toNumber(parts[4]) : null,
|
||||
issueQty: hasLineNo ? toNumber(parts[6]) : 0,
|
||||
badQty: hasLineNo ? toNumber(parts[7]) : 0
|
||||
}
|
||||
}
|
||||
|
||||
const isBlueProcess = (status, count) => {
|
||||
const statusNum = parseInt(status)
|
||||
const countNum = parseInt(count)
|
||||
return statusNum === 6 || statusNum === 3 || countNum > 0
|
||||
}
|
||||
|
||||
const isOutsourceProcess = process => (
|
||||
process.assignee === '外协' ||
|
||||
process.name.includes('(外协)') ||
|
||||
process.name.includes('(外协)')
|
||||
)
|
||||
|
||||
const isGreenProcess = process => (
|
||||
isOutsourceProcess(process) &&
|
||||
process.issueQty > toNumber(process.count) + toNumber(process.badQty)
|
||||
)
|
||||
|
||||
const getProcessBaseStyle = process => {
|
||||
if (isGreenProcess(process)) {
|
||||
return greenProcessStyle
|
||||
}
|
||||
if (isBlueProcess(process.status, process.count)) {
|
||||
return blueProcessStyle
|
||||
}
|
||||
|
||||
const statusNum = parseInt(process.status)
|
||||
if (statusNum === 2 || statusNum === 4) {
|
||||
return greenProcessStyle
|
||||
}
|
||||
|
||||
return redProcessStyle
|
||||
}
|
||||
|
||||
const getOutsourceGroupStyleState = steps => {
|
||||
const styles = new Map()
|
||||
const processes = steps.map(parseProcessStep)
|
||||
|
||||
let currentIndexes = []
|
||||
let lastProcess = null
|
||||
const flushOutsourceRun = () => {
|
||||
if (currentIndexes.length > 0 && lastProcess) {
|
||||
const style = getProcessBaseStyle(lastProcess)
|
||||
currentIndexes.forEach(index => styles.set(index, style))
|
||||
}
|
||||
currentIndexes = []
|
||||
lastProcess = null
|
||||
}
|
||||
|
||||
const isSameContinuousGroup = process => {
|
||||
if (!lastProcess) {
|
||||
return true
|
||||
}
|
||||
if (process.lineNo === null || lastProcess.lineNo === null) {
|
||||
return false
|
||||
}
|
||||
return process.lineNo === lastProcess.lineNo + 1 &&
|
||||
(process.outsourceGroup || '') === (lastProcess.outsourceGroup || '')
|
||||
}
|
||||
|
||||
processes.forEach((process, index) => {
|
||||
if (!isOutsourceProcess(process)) {
|
||||
flushOutsourceRun()
|
||||
return
|
||||
}
|
||||
if (!isSameContinuousGroup(process)) {
|
||||
flushOutsourceRun()
|
||||
}
|
||||
currentIndexes.push(index)
|
||||
lastProcess = process
|
||||
})
|
||||
flushOutsourceRun()
|
||||
|
||||
return { styles }
|
||||
}
|
||||
|
||||
const getOutsourceGroupStyle = (process, index, outsourceGroupStyleState) => {
|
||||
if (!isOutsourceProcess(process)) {
|
||||
return ''
|
||||
}
|
||||
return outsourceGroupStyleState.styles.get(index) || ''
|
||||
}
|
||||
|
||||
const getProcessColor = (status, count, process = null, groupStyle = '') => {
|
||||
if (groupStyle) {
|
||||
return groupStyle
|
||||
}
|
||||
if (process) {
|
||||
return getProcessBaseStyle(process)
|
||||
}
|
||||
|
||||
const statusNum = parseInt(status)
|
||||
const countNum = parseInt(count)
|
||||
if (statusNum === 2 || statusNum === 4) {
|
||||
return greenProcessStyle
|
||||
}
|
||||
if (statusNum === 6 || statusNum === 3) {
|
||||
return blueProcessStyle
|
||||
}
|
||||
if (countNum > 0) {
|
||||
return blueProcessStyle
|
||||
}
|
||||
return redProcessStyle
|
||||
}
|
||||
|
||||
const formatProcessDetail = processDetailStr => {
|
||||
if (!processDetailStr || typeof processDetailStr !== 'string') {
|
||||
return ''
|
||||
}
|
||||
|
||||
const steps = processDetailStr.split(' -> ')
|
||||
const outsourceGroupStyleState = getOutsourceGroupStyleState(steps)
|
||||
const maxPerRow = 8
|
||||
const rows = Math.ceil(steps.length / maxPerRow)
|
||||
let tableHtml = '<div style="text-align: left; width: 100%; overflow: hidden;"><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 process = parseProcessStep(step)
|
||||
const colorStyle = getProcessColor(
|
||||
process.status,
|
||||
process.count,
|
||||
process,
|
||||
getOutsourceGroupStyle(process, startIndex + index, outsourceGroupStyleState)
|
||||
)
|
||||
tableHtml += `<td class="plan-date-cell" style="${colorStyle}">${escapeHtml(process.planDate)}</td>`
|
||||
if (index < rowSteps.length - 1) {
|
||||
tableHtml += '<td class="arrow-cell-hidden"></td>'
|
||||
}
|
||||
})
|
||||
tableHtml += '</tr>'
|
||||
|
||||
tableHtml += '<tr>'
|
||||
rowSteps.forEach((step, index) => {
|
||||
const process = parseProcessStep(step)
|
||||
const colorStyle = getProcessColor(
|
||||
process.status,
|
||||
process.count,
|
||||
process,
|
||||
getOutsourceGroupStyle(process, startIndex + index, outsourceGroupStyleState)
|
||||
)
|
||||
tableHtml += `<td style="${colorStyle}">${escapeHtml(process.name)}</td>`
|
||||
if (index < rowSteps.length - 1) {
|
||||
tableHtml += '<td class="arrow-cell">→</td>'
|
||||
}
|
||||
})
|
||||
tableHtml += '</tr>'
|
||||
|
||||
tableHtml += '<tr>'
|
||||
rowSteps.forEach((step, index) => {
|
||||
const process = parseProcessStep(step)
|
||||
const colorStyle = getProcessColor(
|
||||
process.status,
|
||||
process.count,
|
||||
process,
|
||||
getOutsourceGroupStyle(process, startIndex + index, outsourceGroupStyleState)
|
||||
)
|
||||
tableHtml += `<td class="count-cell" style="${colorStyle}">合格${escapeHtml(process.count)}</td>`
|
||||
if (index < rowSteps.length - 1) {
|
||||
tableHtml += '<td class="arrow-cell-hidden"></td>'
|
||||
}
|
||||
})
|
||||
tableHtml += '</tr>'
|
||||
}
|
||||
|
||||
tableHtml += '</tbody></table></div>'
|
||||
return tableHtml
|
||||
}
|
||||
|
||||
const startClock = () => {
|
||||
timer = window.setInterval(() => {
|
||||
now.value = new Date(now.value.getTime() + 1000)
|
||||
@@ -314,60 +549,55 @@ const destroyDashboard = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const topStats = [
|
||||
const topStats = reactive([
|
||||
{ title: '待处理销售订单', value: 12, icon: svgIcons.salesOrder },
|
||||
{ title: '工艺BOM待完成', value: 9, icon: svgIcons.bom },
|
||||
]
|
||||
])
|
||||
|
||||
const leftColumns = [
|
||||
{ label: '销售订单', key: 'salesOrder' },
|
||||
{ label: '计划号', key: 'planNo' },
|
||||
{ label: '销售订单', key: 'planNo' },
|
||||
{ label: '生产订单', key: 'productionOrder' },
|
||||
{ label: '产品名称', key: 'productName' },
|
||||
{ label: '产品编码', key: 'productCode' },
|
||||
{ label: '计划数', key: 'planQty' },
|
||||
{ label: '入库数', key: 'stockQty' },
|
||||
{ label: '预计齐套', key: 'kitDate' },
|
||||
{ label: '拣配状态', key: 'pickStatus' },
|
||||
{ label: '发料状态', key: 'issueStatus' },
|
||||
{ label: '产品编码', key: 'productCode' }
|
||||
]
|
||||
|
||||
const urgentColumns = [
|
||||
{ label: '销售订单', key: 'salesOrder' },
|
||||
{ label: '生产订单', key: 'productionOrder' },
|
||||
{ label: '产品名称', key: 'productName' },
|
||||
{ label: '产品编码', key: 'productCode' },
|
||||
{ label: '属性', key: 'property' },
|
||||
{ label: '计划数', key: 'planQty' },
|
||||
{ label: '下达日期', key: 'releaseDate' },
|
||||
{ label: '计划开始', key: 'startDate' },
|
||||
{ label: '计划完成', key: 'finishDate' },
|
||||
{ label: '物料名称', key: 'materialName' },
|
||||
{ label: '物料编码', key: 'materialCode' },
|
||||
{ label: '加急数量', key: 'urgentQty' },
|
||||
{ label: '进度', key: 'progress' }
|
||||
]
|
||||
|
||||
let kitRows = Array.from({ length: 9 }, () => ({
|
||||
salesOrder: 'SH260014',
|
||||
const makeScrollRows = (rows, minRows = 10) => {
|
||||
if (!Array.isArray(rows) || rows.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const baseRows = [...rows]
|
||||
while (baseRows.length < minRows) {
|
||||
baseRows.push(...rows.slice(0, minRows - baseRows.length))
|
||||
}
|
||||
return [...baseRows, ...baseRows]
|
||||
}
|
||||
|
||||
const kitRows = ref(Array.from({ length: 9 }, () => ({
|
||||
planNo: '54023',
|
||||
productionOrder: '1075',
|
||||
productName: '垫铁+行程开关部装',
|
||||
productCode: '213-YGLQ-DF150V1.2.18',
|
||||
planQty: '1',
|
||||
stockQty: '0',
|
||||
kitDate: '2026-01-21',
|
||||
pickStatus: '已拣配',
|
||||
issueStatus: '未发料',
|
||||
}))
|
||||
productCode: '213-YGLQ-DF150V1.2.18'
|
||||
})))
|
||||
|
||||
let urgentRows = Array.from({ length: 10 }, () => ({
|
||||
salesOrder: 'SH260014',
|
||||
productionOrder: '1075',
|
||||
productName: '垫铁+行程开关部装',
|
||||
productCode: '213-YGLQ-DF150V1.2.18',
|
||||
property: '机加',
|
||||
planQty: '1',
|
||||
releaseDate: '2026-01-21',
|
||||
startDate: '2026-01-21',
|
||||
finishDate: '2026-01-21',
|
||||
}))
|
||||
const urgentRows = ref([])
|
||||
|
||||
const displayUrgentRows = computed(() => makeScrollRows(urgentRows.value, 8))
|
||||
const displayKitRows = computed(() => makeScrollRows(kitRows.value, 9))
|
||||
const getScrollDuration = (rows, minRows, secondsPerRow) => {
|
||||
const rowCount = Math.max(Array.isArray(rows) ? rows.length : 0, minRows)
|
||||
return `${Math.max(rowCount * secondsPerRow, 36)}s`
|
||||
}
|
||||
const urgentScrollDuration = computed(() => getScrollDuration(urgentRows.value, 8, 4.2))
|
||||
const kitScrollDuration = computed(() => getScrollDuration(kitRows.value, 9, 3.5))
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -630,7 +860,7 @@ let urgentRows = Array.from({ length: 10 }, () => ({
|
||||
|
||||
.table-area {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-columns: minmax(0, 7fr) minmax(0, 3fr);
|
||||
gap: 17px;
|
||||
min-height: 0;
|
||||
}
|
||||
@@ -661,6 +891,60 @@ table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.scroll-table {
|
||||
position: relative;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.scroll-table thead {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
display: table;
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
background: #061338;
|
||||
}
|
||||
|
||||
.scroll-table thead::after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: -2px;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background: #061338;
|
||||
}
|
||||
|
||||
.scroll-table tbody {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scroll-table tbody tr {
|
||||
display: table;
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.scroll-body {
|
||||
animation: table-scroll-up var(--scroll-duration, 36s) linear infinite;
|
||||
}
|
||||
|
||||
.table-wrap:hover .scroll-body {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
@keyframes table-scroll-up {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
border: 1px solid rgba(53, 149, 217, 0.48);
|
||||
@@ -675,12 +959,98 @@ td {
|
||||
th {
|
||||
height: 38px;
|
||||
font-size: 14px;
|
||||
font-weight: 200;
|
||||
background: rgba(12, 68, 119, 0.28);
|
||||
font-weight: 700;
|
||||
background: #082654;
|
||||
box-shadow: inset 0 0 12px rgba(27, 130, 214, 0.36);
|
||||
}
|
||||
|
||||
td {
|
||||
height: 50px;
|
||||
background: #07001f;
|
||||
}
|
||||
|
||||
.urgent-task-wrap {
|
||||
padding: 0 7px 10px;
|
||||
}
|
||||
|
||||
.urgent-task-wrap table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.urgent-task-wrap .col-productionOrder {
|
||||
width: 11.5%;
|
||||
}
|
||||
|
||||
.urgent-task-wrap .col-materialName {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.urgent-task-wrap .col-materialCode {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.urgent-task-wrap .col-urgentQty {
|
||||
width: 6.5%;
|
||||
}
|
||||
|
||||
.urgent-task-wrap .col-progress {
|
||||
width: 57%;
|
||||
}
|
||||
|
||||
.urgent-task-wrap td {
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.process-detail {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.process-table) {
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
table-layout: auto;
|
||||
}
|
||||
|
||||
:deep(.process-table td) {
|
||||
height: auto;
|
||||
max-width: 82px;
|
||||
padding: 2px 3px;
|
||||
border: 0;
|
||||
color: #c2cbe0;
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(.plan-date-cell) {
|
||||
color: #8899aa !important;
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
:deep(.count-cell) {
|
||||
font-size: 12px !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
:deep(.arrow-cell) {
|
||||
width: 10px;
|
||||
max-width: 10px;
|
||||
padding: 2px 0 !important;
|
||||
color: rgba(0, 164, 255, 0.6) !important;
|
||||
font-size: 10px !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
:deep(.arrow-cell-hidden) {
|
||||
width: 10px;
|
||||
max-width: 10px;
|
||||
padding: 2px 0 !important;
|
||||
}
|
||||
|
||||
.brand {
|
||||
|
||||
BIN
装配任务看板/dist.zip
BIN
装配任务看板/dist.zip
Binary file not shown.
@@ -142,6 +142,10 @@ onMounted(() => {
|
||||
window.addEventListener('resize', resizeHandler)
|
||||
searchDashboardData()
|
||||
startAllScroll()
|
||||
setInterval(function(){
|
||||
searchDashboardData
|
||||
},600000)
|
||||
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (timer) window.clearInterval(timer);
|
||||
@@ -380,7 +384,7 @@ const summaryCards = [
|
||||
{ title: '加急任务数', value: 125, unit: '条', icon: icons.urgent, valueClass: 'red-text' },
|
||||
{ title: '装配合格率', value: '100%', icon: icons.finalCheck, valueClass: 'green-text full-value' },
|
||||
{ title: '异常未关闭消息数', value: 5, unit: '条', icon: icons.urgent, valueClass: 'red-text' },
|
||||
{ title: '缺勤人员', value: 5, unit: '人', icon:icons.returnDown, valueClass: 'orange-text', className: 'absence-card', people: [{ name: '张三', group: '装配1班' }, { name: '李四', group: '装配2班' }, { name: '王五', group: '装配3班' }] },
|
||||
{ title: '缺勤人员', value: 0, unit: '人', icon:icons.returnDown, valueClass: 'orange-text', className: 'absence-card', people: [{ name: '全勤', group: '装配1班' }, { name: '全勤', group: '装配2班' }, { name: '全勤', group: '装配3班' }] },
|
||||
]
|
||||
const taskColumns = [
|
||||
{ label: '生产订单', key: 'productionOrder', width: '72px' },
|
||||
|
||||
Reference in New Issue
Block a user