|
|
|
|
@ -3,27 +3,28 @@
|
|
|
|
|
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
|
|
|
|
<div v-show="showSearch" class="mb-[10px]">
|
|
|
|
|
<el-card shadow="hover">
|
|
|
|
|
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
|
|
|
|
<!-- <el-form-item label="日期" style="width: 300px">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="dateRange"
|
|
|
|
|
value-format="YYYY-MM-DD"
|
|
|
|
|
type="daterange"
|
|
|
|
|
range-separator="-"
|
|
|
|
|
start-placeholder="开始日期"
|
|
|
|
|
end-placeholder="结束日期"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
|
|
|
|
<!-- <el-form-item label="日期范围" style="width: 300px">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="dateRange"
|
|
|
|
|
value-format="YYYY-MM-DD"
|
|
|
|
|
type="daterange"
|
|
|
|
|
range-separator="-"
|
|
|
|
|
start-placeholder="开始日期"
|
|
|
|
|
end-placeholder="结束日期"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="生产订单号" prop="orderCode">
|
|
|
|
|
<el-input
|
|
|
|
|
v-model="queryParams.orderCode"
|
|
|
|
|
placeholder="请输入生产订单号"
|
|
|
|
|
clearable
|
|
|
|
|
@keyup.enter="handleQuery"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="生产订单号" prop="orderCode">
|
|
|
|
|
<el-input
|
|
|
|
|
v-model="queryParams.orderCode"
|
|
|
|
|
placeholder="请输入生产订单号"
|
|
|
|
|
clearable
|
|
|
|
|
@keyup.enter="handleQuery"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>-->
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
|
<el-form-item label="物料编号" prop="materialCode">
|
|
|
|
|
<el-input
|
|
|
|
|
v-model="queryParams.materialCode"
|
|
|
|
|
@ -31,7 +32,8 @@
|
|
|
|
|
clearable
|
|
|
|
|
@keyup.enter="handleQuery"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>-->
|
|
|
|
|
</el-form-item>
|
|
|
|
|
-->
|
|
|
|
|
|
|
|
|
|
<el-form-item label="物料名称" prop="materialName">
|
|
|
|
|
<el-input
|
|
|
|
|
@ -41,8 +43,8 @@
|
|
|
|
|
@keyup.enter="handleQuery"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<!-- <el-form-item label="进度状态" prop="progressStatus">
|
|
|
|
|
<!--
|
|
|
|
|
<el-form-item label="进度状态" prop="progressStatus">
|
|
|
|
|
<el-select v-model="queryParams.progressStatus" placeholder="请选择进度状态" clearable @keyup.enter="handleQuery">
|
|
|
|
|
<el-option label="正常" value="正常" />
|
|
|
|
|
<el-option label="延期" value="延期" />
|
|
|
|
|
@ -59,6 +61,32 @@
|
|
|
|
|
</transition>
|
|
|
|
|
|
|
|
|
|
<!-- 统计图表卡片 -->
|
|
|
|
|
<!-- <el-row :gutter="10" class="mb-[10px]">
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
<template #header>
|
|
|
|
|
<span class="font-bold">进度状态分布</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div ref="statusChartRef" style="width: 100%; height: 300px"></div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
<template #header>
|
|
|
|
|
<span class="font-bold">整体进度分布</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div ref="progressChartRef" style="width: 100%; height: 300px"></div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
<template #header>
|
|
|
|
|
<span class="font-bold">工序完成率统计</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div ref="processStatsChartRef" style="width: 100%; height: 300px"></div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>-->
|
|
|
|
|
<el-row :gutter="10" class="mb-[10px]">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
@ -78,6 +106,7 @@
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 工序进度可视化 -->
|
|
|
|
|
<el-card shadow="never" class="mb-[10px]">
|
|
|
|
|
<template #header>
|
|
|
|
|
@ -89,9 +118,9 @@
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
<template #header>
|
|
|
|
|
<el-row :gutter="10" class="mb8">
|
|
|
|
|
<!-- <el-col :span="1.5">-->
|
|
|
|
|
<!-- <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['mes:prodReport:export']">导出</el-button>-->
|
|
|
|
|
<!-- </el-col>-->
|
|
|
|
|
<!-- <el-col :span="1.5">
|
|
|
|
|
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['mes:prodReport:export']">导出</el-button>
|
|
|
|
|
</el-col>-->
|
|
|
|
|
<right-toolbar v-model:showSearch="showSearch" :columns="columns" :search="true" @queryTable="getList" />
|
|
|
|
|
</el-row>
|
|
|
|
|
</template>
|
|
|
|
|
@ -107,7 +136,11 @@
|
|
|
|
|
<el-table-column label="计划开工时间" align="center" prop="planBeginTime" v-if="columns[7].visible" width="150" />
|
|
|
|
|
<el-table-column label="实际开工时间" align="center" prop="realBeginTime" v-if="columns[8].visible" width="150" />
|
|
|
|
|
<el-table-column label="计划完工时间" align="center" prop="planEndTime" v-if="columns[9].visible" width="150" />
|
|
|
|
|
<el-table-column label="整体进度" align="center" prop="overallProgress" v-if="columns[10].visible" width="100">
|
|
|
|
|
<el-table-column label="当前时间" align="center" prop="currentTime" v-if="columns[10].visible" width="150" />
|
|
|
|
|
<el-table-column label="总工序数" align="center" prop="totalProcessCount" v-if="columns[11].visible" width="100" />
|
|
|
|
|
<el-table-column label="在制工序" align="center" prop="wipProcesses" v-if="columns[12].visible" width="150" show-overflow-tooltip />
|
|
|
|
|
<el-table-column label="剩余工序" align="center" prop="remainingProcesses" v-if="columns[13].visible" width="150" show-overflow-tooltip />
|
|
|
|
|
<el-table-column label="整体进度" align="center" prop="overallProgress" v-if="columns[14].visible" width="120">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-progress
|
|
|
|
|
:percentage="parseFloat(scope.row.overallProgress)"
|
|
|
|
|
@ -116,23 +149,42 @@
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="进度状态" align="center" prop="progressStatus" v-if="columns[11].visible" width="100">
|
|
|
|
|
<el-table-column label="进度状态" align="center" prop="progressStatus" v-if="columns[15].visible" width="100">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-tag :type="scope.row.progressStatus === '正常' ? 'success' : 'danger'">
|
|
|
|
|
{{ scope.row.progressStatus }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="工序进度" align="center" v-if="columns[12].visible" width="300">
|
|
|
|
|
<el-table-column label="工序进度" align="center" v-if="columns[16].visible" width="350">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<div class="process-progress-container">
|
|
|
|
|
<!-- <div
|
|
|
|
|
v-for="process in scope.row.processProgressList"
|
|
|
|
|
:key="process.processId"
|
|
|
|
|
class="process-step"
|
|
|
|
|
:class="{
|
|
|
|
|
'completed': process.isCompleted === 1,
|
|
|
|
|
'in-progress': process.isInProgress === 1,
|
|
|
|
|
'pending': process.isCompleted !== 1 && process.isInProgress !== 1
|
|
|
|
|
}"
|
|
|
|
|
:title="`${process.processName} - ${process.statusDesc}${process.processProgress ? ' (' + process.processProgress + '%)' : ''}`"
|
|
|
|
|
>-->
|
|
|
|
|
<div
|
|
|
|
|
v-for="process in scope.row.processProgressList"
|
|
|
|
|
:key="process.processId"
|
|
|
|
|
class="process-step"
|
|
|
|
|
:class="{ 'completed': process.isCompleted, 'pending': !process.isCompleted }"
|
|
|
|
|
:class="{
|
|
|
|
|
'completed': process.isCompleted === 1,
|
|
|
|
|
'in-progress': process.isInProgress === 1,
|
|
|
|
|
'pending': process.isCompleted !== 1 && process.isInProgress !== 1
|
|
|
|
|
}"
|
|
|
|
|
:title="`${process.processName}`"
|
|
|
|
|
>
|
|
|
|
|
{{ process.processName }}
|
|
|
|
|
<span class="process-name">{{ process.processName }}</span>
|
|
|
|
|
<!-- <span v-if="process.processProgress && process.processProgress > 0" class="process-percentage">
|
|
|
|
|
{{ process.processProgress }}%
|
|
|
|
|
</span>-->
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
@ -165,9 +217,11 @@ const dateRange = ref<string[]>(['', '']);
|
|
|
|
|
const statusChartRef = ref<HTMLDivElement | null>(null);
|
|
|
|
|
const progressChartRef = ref<HTMLDivElement | null>(null);
|
|
|
|
|
const processChartRef = ref<HTMLDivElement | null>(null);
|
|
|
|
|
const processStatsChartRef = ref<HTMLDivElement | null>(null);
|
|
|
|
|
let statusChart: echarts.ECharts | null = null;
|
|
|
|
|
let progressChart: echarts.ECharts | null = null;
|
|
|
|
|
let processChart: echarts.ECharts | null = null;
|
|
|
|
|
let processStatsChart: echarts.ECharts | null = null;
|
|
|
|
|
|
|
|
|
|
// 列显隐信息
|
|
|
|
|
const columns = ref([
|
|
|
|
|
@ -181,9 +235,13 @@ const columns = ref([
|
|
|
|
|
{ key: 7, label: '计划开工时间', visible: true },
|
|
|
|
|
{ key: 8, label: '实际开工时间', visible: true },
|
|
|
|
|
{ key: 9, label: '计划完工时间', visible: true },
|
|
|
|
|
{ key: 10, label: '整体进度', visible: true },
|
|
|
|
|
{ key: 11, label: '进度状态', visible: true },
|
|
|
|
|
{ key: 12, label: '工序进度', visible: true }
|
|
|
|
|
{ key: 10, label: '当前时间', visible: false },
|
|
|
|
|
{ key: 11, label: '总工序数', visible: true },
|
|
|
|
|
{ key: 12, label: '在制工序', visible: true },
|
|
|
|
|
{ key: 13, label: '剩余工序', visible: true },
|
|
|
|
|
{ key: 14, label: '整体进度', visible: true },
|
|
|
|
|
{ key: 15, label: '进度状态', visible: true },
|
|
|
|
|
{ key: 16, label: '工序进度', visible: true }
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const queryParams = ref<WipTrackingReportQuery>({
|
|
|
|
|
@ -277,6 +335,9 @@ function initCharts() {
|
|
|
|
|
if (processChartRef.value) {
|
|
|
|
|
processChart = echarts.init(processChartRef.value);
|
|
|
|
|
}
|
|
|
|
|
if (processStatsChartRef.value) {
|
|
|
|
|
processStatsChart = echarts.init(processStatsChartRef.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 更新图表数据 */
|
|
|
|
|
@ -284,6 +345,7 @@ function updateCharts() {
|
|
|
|
|
updateStatusChart();
|
|
|
|
|
updateProgressChart();
|
|
|
|
|
updateProcessChart();
|
|
|
|
|
updateProcessStatsChart();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 更新进度状态分布图 */
|
|
|
|
|
@ -299,7 +361,8 @@ function updateStatusChart() {
|
|
|
|
|
const option = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '进度状态分布',
|
|
|
|
|
left: 'center'
|
|
|
|
|
left: 'center',
|
|
|
|
|
textStyle: { fontSize: 14 }
|
|
|
|
|
},
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'item',
|
|
|
|
|
@ -347,7 +410,8 @@ function updateProgressChart() {
|
|
|
|
|
const option = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '整体进度分布',
|
|
|
|
|
left: 'center'
|
|
|
|
|
left: 'center',
|
|
|
|
|
textStyle: { fontSize: 14 }
|
|
|
|
|
},
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
@ -381,6 +445,59 @@ function updateProgressChart() {
|
|
|
|
|
progressChart.setOption(option);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 更新工序完成率统计图 */
|
|
|
|
|
function updateProcessStatsChart() {
|
|
|
|
|
if (!processStatsChart || !reportList.value.length) return;
|
|
|
|
|
|
|
|
|
|
const processStats = {
|
|
|
|
|
'已完成': 0,
|
|
|
|
|
'进行中': 0,
|
|
|
|
|
'未开始': 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
reportList.value.forEach(item => {
|
|
|
|
|
if (item.processProgressList) {
|
|
|
|
|
item.processProgressList.forEach(process => {
|
|
|
|
|
if (process.isCompleted) {
|
|
|
|
|
processStats['已完成']++;
|
|
|
|
|
} else if (process.isInProgress) {
|
|
|
|
|
processStats['进行中']++;
|
|
|
|
|
} else {
|
|
|
|
|
processStats['未开始']++;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const option = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '工序完成率统计',
|
|
|
|
|
left: 'center',
|
|
|
|
|
textStyle: { fontSize: 14 }
|
|
|
|
|
},
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'item',
|
|
|
|
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
|
|
|
},
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
name: '工序状态',
|
|
|
|
|
type: 'pie',
|
|
|
|
|
radius: ['40%', '70%'],
|
|
|
|
|
data: Object.entries(processStats).map(([name, value]) => ({ name, value })),
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: function(params: any) {
|
|
|
|
|
const colors = ['#722ed1', '#52c41a', '#d9d9d9'];
|
|
|
|
|
return colors[params.dataIndex];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
processStatsChart.setOption(option);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 更新工序进度可视化图 */
|
|
|
|
|
function updateProcessChart() {
|
|
|
|
|
if (!processChart || !reportList.value.length) return;
|
|
|
|
|
@ -399,17 +516,13 @@ function updateProcessChart() {
|
|
|
|
|
const option = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '工序进度可视化(前10个订单)',
|
|
|
|
|
left: 'center'
|
|
|
|
|
left: 'center',
|
|
|
|
|
textStyle: { fontSize: 14 }
|
|
|
|
|
},
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
formatter: function(params: any) {
|
|
|
|
|
const data = params[0].data.value;
|
|
|
|
|
// return `订单号: ${data[0]}<br/>
|
|
|
|
|
// 进度: ${data[1]}%<br/>
|
|
|
|
|
// 状态: ${data[2]}<br/>
|
|
|
|
|
// 在制工序: ${data[3]}<br/>
|
|
|
|
|
// 剩余工序: ${data[4]}`;
|
|
|
|
|
return `
|
|
|
|
|
进度: ${data[1]}%<br/>
|
|
|
|
|
状态: ${data[2]}<br/>
|
|
|
|
|
@ -456,6 +569,7 @@ function handleResize() {
|
|
|
|
|
statusChart?.resize();
|
|
|
|
|
progressChart?.resize();
|
|
|
|
|
processChart?.resize();
|
|
|
|
|
processStatsChart?.resize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
@ -471,6 +585,7 @@ onBeforeUnmount(() => {
|
|
|
|
|
statusChart?.dispose();
|
|
|
|
|
progressChart?.dispose();
|
|
|
|
|
processChart?.dispose();
|
|
|
|
|
processStatsChart?.dispose();
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
@ -493,19 +608,34 @@ onBeforeUnmount(() => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-step {
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 6px 10px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
border: 1px solid;
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
min-width: 60px;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-step.completed {
|
|
|
|
|
background-color: #722ed1;
|
|
|
|
|
color: white;
|
|
|
|
|
border-color: #722ed1;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(114, 46, 209, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-step.in-progress {
|
|
|
|
|
background-color: #52c41a;
|
|
|
|
|
color: white;
|
|
|
|
|
border-color: #52c41a;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3);
|
|
|
|
|
animation: pulse 2s infinite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-step.pending {
|
|
|
|
|
@ -516,6 +646,30 @@ onBeforeUnmount(() => {
|
|
|
|
|
|
|
|
|
|
.process-step:hover {
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-name {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.process-percentage {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 进行中工序的脉动动画 */
|
|
|
|
|
@keyframes pulse {
|
|
|
|
|
0% {
|
|
|
|
|
box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3);
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
box-shadow: 0 2px 12px rgba(82, 196, 26, 0.6);
|
|
|
|
|
}
|
|
|
|
|
100% {
|
|
|
|
|
box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|