Compare commits

..

3 Commits

Author SHA1 Message Date
‘937886381’
1d84c84d43 修改 2026-06-25 09:31:33 +08:00
‘937886381’
eb92a37c54 ziugai 2026-03-13 13:52:49 +08:00
‘937886381’
6f27310992 修改 2026-03-13 13:23:58 +08:00
22 changed files with 2546 additions and 1510 deletions

View File

@@ -14,7 +14,7 @@ VUE_APP_TITLE = 上上电缆
# 芋道管理系统/开发环境
# VUE_APP_BASE_API = 'http://192.168.0.31:48080'
VUE_APP_BASE_API = 'http://172.16.32.236:48080'
VUE_APP_BASE_API = 'http://172.16.15.221:48080'
# VUE_APP_BASE_API = 'http://line.kszny.picaiba.com'
# 路由懒加载

BIN
dist.zip Normal file

Binary file not shown.

View File

@@ -58,3 +58,11 @@ export function createPCTask(data) {
data: data
})
}
export function cancelUpdateTask(query) {
return request({
url: '/wms/job-main-task/update/task',
method: 'post',
params: query ,
});
}

View File

@@ -7,3 +7,35 @@ export function getLineEdgeLibraryList(data) {
params: data,
});
}
export function getAgvAlarmInfo(data) {
return request({
url: '/wms/large-screen/real-time/display',
method: 'post',
data: data,
});
}
export function getTaskTypeToday(data) {
return request({
url: '/wms/job-main-task/task/type/proportion/today',
method: 'get',
data,
});
}
export function getTaskStatusToday(data) {
return request({
url: '/wms/job-main-task/task/state/distribution/today',
method: 'get',
data,
});
}
export function getAgvExample(data) {
return request({
url: 'wms/large-screen/example',
method: 'post',
data,
});
}

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1779174119304" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5762" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 434.517333a159.402667 159.402667 0 0 1 159.402667 159.402667v159.402667h-318.805334V593.92A159.402667 159.402667 0 0 1 512 434.517333m0-34.133333a193.877333 193.877333 0 0 0-193.536 193.536v193.536h387.072V593.92A193.877333 193.877333 0 0 0 512 400.384zM772.437333 830.464H251.562667a18.773333 18.773333 0 0 0 0 37.205333h520.874666a18.773333 18.773333 0 0 0 0-37.205333zM170.666667 341.333333a17.066667 17.066667 0 0 0-14.677334 8.533334 17.408 17.408 0 0 0 6.144 23.552L273.066667 438.954667a16.042667 16.042667 0 0 0 8.533333 2.389333 17.408 17.408 0 0 0 15.018667-8.533333 17.066667 17.066667 0 0 0-5.12-23.210667l-112.64-65.194667A15.36 15.36 0 0 0 170.666667 341.333333zM318.805333 204.8a17.408 17.408 0 0 0-8.192 2.389333 17.066667 17.066667 0 0 0-6.485333 23.210667l65.194667 112.981333a17.066667 17.066667 0 0 0 14.677333 8.533334 18.773333 18.773333 0 0 0 8.533333-2.389334 17.066667 17.066667 0 0 0 6.144-23.210666l-64.853333-111.616A17.066667 17.066667 0 0 0 318.805333 204.8zM716.8 204.8a17.066667 17.066667 0 0 0-14.677333 8.533333L637.952 327.68a17.066667 17.066667 0 0 0 6.144 23.210667 20.138667 20.138667 0 0 0 8.533333 2.389333 17.066667 17.066667 0 0 0 15.018667-8.533333l64.853333-112.981334a17.066667 17.066667 0 0 0-6.144-23.210666A18.773333 18.773333 0 0 0 716.8 204.8zM518.485333 156.330667a17.066667 17.066667 0 0 0-17.066666 17.066666V303.786667a17.066667 17.066667 0 0 0 17.066666 17.066666 17.408 17.408 0 0 0 17.066667-17.066666V173.397333a17.408 17.408 0 0 0-17.066667-17.066666zM853.333333 341.333333a15.36 15.36 0 0 0-8.533333 2.389334L732.501333 409.6a17.066667 17.066667 0 0 0-6.485333 23.210667 17.408 17.408 0 0 0 15.018667 8.533333 16.042667 16.042667 0 0 0 8.533333-2.389333l112.64-64.853334a17.408 17.408 0 0 0 6.144-23.552A17.066667 17.066667 0 0 0 853.333333 341.333333z" fill="#1afa29" p-id="5763"></path></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1779174119304" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5762" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 434.517333a159.402667 159.402667 0 0 1 159.402667 159.402667v159.402667h-318.805334V593.92A159.402667 159.402667 0 0 1 512 434.517333m0-34.133333a193.877333 193.877333 0 0 0-193.536 193.536v193.536h387.072V593.92A193.877333 193.877333 0 0 0 512 400.384zM772.437333 830.464H251.562667a18.773333 18.773333 0 0 0 0 37.205333h520.874666a18.773333 18.773333 0 0 0 0-37.205333zM170.666667 341.333333a17.066667 17.066667 0 0 0-14.677334 8.533334 17.408 17.408 0 0 0 6.144 23.552L273.066667 438.954667a16.042667 16.042667 0 0 0 8.533333 2.389333 17.408 17.408 0 0 0 15.018667-8.533333 17.066667 17.066667 0 0 0-5.12-23.210667l-112.64-65.194667A15.36 15.36 0 0 0 170.666667 341.333333zM318.805333 204.8a17.408 17.408 0 0 0-8.192 2.389333 17.066667 17.066667 0 0 0-6.485333 23.210667l65.194667 112.981333a17.066667 17.066667 0 0 0 14.677333 8.533334 18.773333 18.773333 0 0 0 8.533333-2.389334 17.066667 17.066667 0 0 0 6.144-23.210666l-64.853333-111.616A17.066667 17.066667 0 0 0 318.805333 204.8zM716.8 204.8a17.066667 17.066667 0 0 0-14.677333 8.533333L637.952 327.68a17.066667 17.066667 0 0 0 6.144 23.210667 20.138667 20.138667 0 0 0 8.533333 2.389333 17.066667 17.066667 0 0 0 15.018667-8.533333l64.853333-112.981334a17.066667 17.066667 0 0 0-6.144-23.210666A18.773333 18.773333 0 0 0 716.8 204.8zM518.485333 156.330667a17.066667 17.066667 0 0 0-17.066666 17.066666V303.786667a17.066667 17.066667 0 0 0 17.066666 17.066666 17.408 17.408 0 0 0 17.066667-17.066666V173.397333a17.408 17.408 0 0 0-17.066667-17.066666zM853.333333 341.333333a15.36 15.36 0 0 0-8.533333 2.389334L732.501333 409.6a17.066667 17.066667 0 0 0-6.485333 23.210667 17.408 17.408 0 0 0 15.018667 8.533333 16.042667 16.042667 0 0 0 8.533333-2.389333l112.64-64.853334a17.408 17.408 0 0 0 6.144-23.552A17.066667 17.066667 0 0 0 853.333333 341.333333z" fill="#d81e06" p-id="5763"></path></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@@ -1,125 +1,138 @@
<template>
<div
:class="className"
:style="{ height: height, width: width, marginLeft: '10px' }" />
</template>
<script>
import * as echarts from 'echarts';
require('echarts/theme/macarons'); // echarts theme
import resize from '@/utils/chartMixins/resize';
const animationDuration = 1000;
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart',
},
title: {
type: String,
default: '',
},
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '300px',
},
ringData: {
type: Object,
default: () => {},
},
},
data() {
return {
chart: null,
targetId: '',
};
},
beforeDestroy() {
if (!this.chart) {
return;
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons');
const _this = this;
this.chart.setOption({
title: {
text: this.title
? '{space|}{tip|}{space|}{value|' + this.title + '}'
: '',
textStyle: {
rich: {
tip: {
width: 6,
height: 6,
borderRadius: 50,
backgroundColor: '#288AFF',
},
space: {
width: 8,
},
value: {
fontSize: 14,
color: 'black',
},
},
},
},
color: ['#33B36B', '#3A8DFF', '#F59A23'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999',
},
},
},
legend: {
data: ['当前满位', '当前空位', '在途'],
},
grid: {
containLabel: true,
},
series: [
{
// name: '投入',
type: 'pie',
radius: '50%',
data: [
{
name: '当前满位',
value: this.ringData.occupiedQuantity
},
{
name: '当前空位',
value: this.ringData.idleQuantity
},
{
name: '在途',
value: this.ringData.transitQuantity
},
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
],
});
},
},
};
</script>
<template>
<div
:class="className"
:style="{ height: height, width: width, marginLeft: '10px' }" />
</template>
<script>
import * as echarts from 'echarts';
require('echarts/theme/macarons'); // echarts theme
import resize from '@/utils/chartMixins/resize';
const animationDuration = 1000;
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart',
},
title: {
type: String,
default: '',
},
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '300px',
},
ringData: {
type: Object,
default: () => {},
},
},
data() {
return {
chart: null,
targetId: '',
};
},
watch: {
ringData: {
handler() {
this.$nextTick(() => {
this.initChart();
});
},
deep: true,
immediate: false,
},
},
beforeDestroy() {
if (!this.chart) {
return;
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
console.log(this.ringData,'ringData');
this.chart = echarts.init(this.$el, 'macarons');
const _this = this;
this.chart.setOption({
title: {
text: this.title
? '{space|}{tip|}{space|}{value|' + this.title + '}'
: '',
textStyle: {
rich: {
tip: {
width: 6,
height: 6,
borderRadius: 50,
backgroundColor: '#288AFF',
},
space: {
width: 8,
},
value: {
fontSize: 14,
color: 'black',
},
},
},
},
color: ['#33B36B', '#3A8DFF', '#F59A23'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999',
},
},
},
legend: {
data: ['当前满位', '当前空位', '在途'],
},
grid: {
containLabel: true,
},
series: [
{
// name: '投入',
type: 'pie',
radius: '50%',
data: [
{
name: '当前满位',
value: this.ringData.occupiedQuantity
},
{
name: '当前空位',
value: this.ringData.idleQuantity
},
{
name: '在途',
value: this.ringData.transitQuantity
},
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
],
});
},
},
};
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -2,36 +2,32 @@
<div style="flex: 1">
<div class="agvStatusContent">
<div class="title">AGV状态</div>
<div class="agvDetailData">
<div class="item" v-for="(agv, index) in agvList" :key="agv.id">
<img class="car" src="../../../../assets/img/agvCar.png" alt="AGV小车">
<div class="eqInfo">
<div class="eqCode">
<div class="num">{{ index + 1 }}</div>
<div class="code">{{ agv.code }}</div>
</div>
<div class="eqStatusData">
<!-- 状态图标 -->
<img style="width: 16px; height: 16px; margin-right: 4px;" :src="require(`@/assets/img/${agv.statusImg}`)"
:alt="agv.status">
<!-- 状态文字固定宽度 -->
<div class="runningState" :style="getStatusColor(agv.status)">
{{ agv.status }}
<!-- 添加滚动容器 -->
<div class="agvDetailDataWrapper">
<div class="agvDetailData">
<div class="item" v-for="(agv, index) in agvList" :key="agv.id">
<img class="car" src="../../../../assets/img/agvCar.png" alt="AGV小车">
<div class="eqInfo">
<div class="eqCode">
<div class="num">{{ index + 1 }}</div>
<div class="code">{{ agv.deviceName }}</div>
</div>
<!-- 动态栅格容器 -->
<div class="charge" :class="{
'charge--run': agv.status !== '充电中', // 运行中/待命用run背景
'charge--charge': agv.status === '充电中' // 充电中用charge背景
}">
<div class="grid-item" :style="{ backgroundColor: getGridColor(agv.status) }"
v-for="(grid, idx) in generateGridArray(getGridCount(agv.progress))" :key="idx"
></div>
<div class="eqStatusData">
<!-- 状态图标 -->
<img style="width: 14px; height: 14px; margin-right: 4px;" :src="getStatusImage(agv.alarmTrigger)"
:alt="agv.status">
<!-- 状态文字 -->
<div class="runningState" :style="getStatusColor(agv.status)">
{{ agv.alarmTrigger === true ? '报警' : '正常' }}
</div>
</div>
</div>
<div style="margin-left: 130px;">
<svg-icon style="font-size: 60px;"
:icon-class="agv.alarmTrigger ? 'redLight' : 'greenLight'" />
</div>
</div>
<div class="qty">
<div class="num">{{ agv.completed }}</div>
<div class="qtyTitle">今日完成</div>
</div>
</div>
</div>
@@ -44,58 +40,25 @@ export default {
name: 'AGVStatus',
components: {},
props: {
energyObj: {
type: Object,
default: () => ({
electricComu: 0,
steamComu: 0
})
}
},
data() {
return {
agvList: [
{
id: 1,
code: '设备编号1212121',
status: '运行中',
statusImg: 'runCircle.png',
completed: 47,
progress: 80 // 进度80% → 显示4个栅格
},
{
id: 2,
code: '设备编号3434343',
status: '待命',
statusImg: 'standbyCircle.png',
completed: 23,
progress: 40 // 进度40% → 显示2个栅格
},
{
id: 3,
code: '设备编号5656565',
status: '充电中',
statusImg: 'chargeCircle.png',
completed: 15,
progress: 100 // 进度100% → 显示5个栅格
},
{
id: 4,
code: '设备编号7878787',
status: '充电中',
statusImg: 'chargeCircle.png',
completed: 31,
progress: 60 // 进度60% → 显示3个栅格
}
]
agvList: {
type: Array,
default: () => []
}
},
methods: {
getStatusImage(alarmTrigger) {
// alarmTrigger: true → 报警(chargeCircle)false → 正常(runCircle)
if (alarmTrigger) {
return require('@/assets/img/chargeCircle.png')
} else {
return require('@/assets/img/runCircle.png')
}
},
// 获取状态文字颜色
getStatusColor(status) {
const baseStyle = {
color: '', // 默认颜色
letterSpacing: '0px' // 默认字间距
color: '',
letterSpacing: '0px'
};
switch (status) {
@@ -104,37 +67,16 @@ export default {
break;
case '待命':
baseStyle.color = 'rgba(245, 154, 36, 1)';
baseStyle.letterSpacing = '8px'; // 待命状态单独加字间距(可调整数值)
baseStyle.letterSpacing = '8px';
break;
case '充电中':
baseStyle.color = 'rgba(0, 225, 37, 1)';
break;
default:
baseStyle.color = '';
baseStyle.color = '#666';
}
return baseStyle;
},
// 根据状态获取背景图
getBgImage(status) {
return status === '充电中' ? 'chargeBg.png' : 'run.png';
},
// 根据状态获取栅格颜色
getGridColor(status) {
return status === '充电中'
? 'rgba(0, 225, 37, 1)'
: 'rgba(120, 138, 221, 1)';
},
getGridCount(progress) {
// 总栅格位置为5个进度0%→0个100%→5个按比例取整
const totalGrids = 5;
return Math.ceil((progress / 100) * totalGrids);
},
// 生成栅格数组用于v-for遍历
generateGridArray(count) {
// 生成 [1,2,...,count] 的数组长度为count
return Array.from({ length: count }, (_, i) => i + 1);
}
}
}
@@ -147,6 +89,8 @@ export default {
background: #FFFFFF;
border-radius: 8px;
padding: 16px 18px 26px;
display: flex;
flex-direction: column;
.title {
font-family: PingFangSC, PingFang SC;
@@ -159,6 +103,7 @@ export default {
font-style: normal;
position: relative;
padding-left: 12px;
flex-shrink: 0;
&::before {
content: '';
@@ -172,45 +117,65 @@ export default {
}
}
.agvDetailData {
// 滚动容器包装器
.agvDetailDataWrapper {
flex: 1;
overflow-y: auto;
margin-top: 15px;
// 隐藏滚动条
&::-webkit-scrollbar {
width: 0;
height: 0;
background: transparent;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
.agvDetailData {
display: flex;
flex-direction: column;
gap: 12px;
gap: 8px; // 减小间距
.item {
transform: scale(0.8); // 缩小到80%
width: 332px;
height: 101px;
padding: 22px 0 0 10px;
display: flex;
background: url('../../../../assets/img/agvItemBg.png') no-repeat;
background-size: cover;
flex-shrink: 0;
margin: -10px 0; // 负边距抵消缩放带来的空白
// 调整内部元素大小
.car {
width: 66px;
height: 57px;
width: 53px; // 从66px缩小
height: 46px; // 从57px缩小
}
.eqInfo {
margin-left: 6px;
display: flex;
flex-direction: column;
gap: 8px;
gap: 6px; // 减小间距
.eqCode {
display: flex;
align-items: center;
.num {
width: 21px;
height: 24px;
width: 17px; // 从21px缩小
height: 19px; // 从24px缩小
background: #0065FF;
backdrop-filter: blur(1.5px);
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
font-size: 13px; // 从16px缩小
color: #FFFFFF;
line-height: 18px;
line-height: 19px;
text-align: center;
}
@@ -218,7 +183,7 @@ export default {
margin-left: 4px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
font-size: 13px; // 从16px缩小
color: #020203;
line-height: 18px;
}
@@ -228,69 +193,14 @@ export default {
display: flex;
align-items: center;
// 状态文字固定宽度(确保三种状态宽度一致)
.runningState {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
font-size: 13px; // 从16px缩小
line-height: 20px;
width: 48px; // 固定宽度,适配三种状态文字
text-align: left; // 左对齐,避免空格填充
width: 48px;
text-align: left;
}
// 栅格容器样式
.charge {
width: 73px;
height: 27px;
margin-left: 5px;
background-size: 100% 100% !important;
background-repeat: no-repeat !important;
display: flex;
justify-content: flex-start;
gap: 1px;
padding: 5px 0px 4px 3px;
// 运行中/待命状态的背景图
&.charge--run {
background-image: url('../../../../assets/img/runBg.png');
}
// 充电中状态的背景图
&.charge--charge {
background-image: url('../../../../assets/img/chargeBg.png');
}
.grid-item {
width: 12px;
height: 18px;
border-radius: 2px;
}
}
}
}
.qty {
display: flex;
flex-direction: column;
margin-left: 24px;
gap: 12px;
.num {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 31px;
color: #000000;
line-height: 18px;
text-align: center;
}
.qtyTitle {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: rgba(2, 2, 3, 0.6);
line-height: 18px;
text-align: center;
}
}
}

View File

@@ -2,14 +2,14 @@
<div style="flex: 1">
<div class="agvStatusContent">
<div class="title">异常报警</div>
<div class="alarm-item" style="margin-top: 13px;">
<!-- <div class="alarm-item" style="margin-top: 13px;">
<div class="item">
9:05:21|FMS|AGV-03顶升失败任务TSK25-1004中断
</div>
<div class="item">
9:05:21|FMS|AGV-03顶升失败任务TSK25-1004中断
</div>
</div>
</div> -->
</div>
</div>
</template>

View File

@@ -0,0 +1,928 @@
<template>
<div style="flex: 1">
<div class="agvStatusContent">
<div class="title">报警详情</div>
<!-- deviceName 网格布局固定显示5个 -->
<div v-if="hasDeviceName" class="agvDetailDataGrid">
<div class="item" v-for="(agv, index) in agvList" :key="agv.id || index">
<div class="agvBasicInfo">
<div class="eqInfo">
<div class="eqCode">
<div class="num">{{ index + 1 }}</div>
<div class="code">{{ agv.deviceName }}</div>
</div>
</div>
</div>
<div class="alarmDetailWrapper" v-if="agv.data && agv.data.length > 0">
<div class="alarmDetailTitle">
<span class="titleText">告警详情</span>
<span class="alarmCount" v-if="agv.data.length > 1">({{ agv.data.length }})</span>
</div>
<div class="alarmDetailScroll" :ref="`scroll_${index}`" @mouseenter="pauseAutoScroll(index)"
@mouseleave="resumeAutoScroll(index)">
<div class="alarmDetailList">
<div class="alarmItem" v-for="(alarm, alarmIndex) in agv.data" :key="alarmIndex">
<div class="alarmHeader" v-if="agv.data.length > 1">
<span class="alarmIndex">告警 {{ alarmIndex + 1 }}</span>
</div>
<div class="alarmField" v-if="alarm.warnName">
<span class="fieldLabel">警告名称</span>
<span class="fieldValue">{{ alarm.warnName }}</span>
</div>
<div class="alarmField" v-if="alarm.faultComponent">
<span class="fieldLabel">故障部件</span>
<span class="fieldValue">{{ alarm.faultComponent }}</span>
</div>
<div class="alarmField" v-if="alarm.faultEventName">
<span class="fieldLabel">故障事件</span>
<span class="fieldValue">{{ alarm.faultEventName }}</span>
</div>
<div class="alarmField" v-if="alarm.eventCause">
<span class="fieldLabel">事件原因</span>
<span class="fieldValue">{{ alarm.eventCause }}</span>
</div>
<div class="alarmField" v-if="alarm.troubleshootStep">
<span class="fieldLabel">排查步骤</span>
<span class="fieldValue">{{ alarm.troubleshootStep }}</span>
</div>
<div class="alarmDivider" v-if="agv.data.length > 1 && alarmIndex < agv.data.length - 1"></div>
</div>
</div>
</div>
</div>
<div class="noAlarm" v-else>
<span class="noAlarmText">暂无告警信息</span>
</div>
</div>
</div>
<!-- 没有 deviceName 轮播滚动同时显示2个 -->
<div v-else class="agvDetailDataWrapper" @mouseenter="pauseCarousel" @mouseleave="resumeCarousel">
<div class="carouselContainer" :style="{ transform: `translateY(-${currentOffset}px)` }">
<div v-for="(item, index) in displayList" :key="item.id || index" class="carouselItemGroup">
<div class="carouselItem">
<div class="alarmBasicInfo">
<div class="eqCode">
<div class="num">{{ index + 1 }}</div>
<div class="code">{{ item.warnName || '告警信息' }}</div>
</div>
</div>
<div class="alarmDetailWrapper">
<div class="alarmDetailList">
<div class="alarmItem">
<div class="alarmField" v-if="item.warnName">
<span class="fieldLabel">警告名称</span>
<span class="fieldValue">{{ item.warnName }}</span>
</div>
<div class="alarmField" v-if="item.faultComponent">
<span class="fieldLabel">故障部件</span>
<span class="fieldValue">{{ item.faultComponent }}</span>
</div>
<div class="alarmField" v-if="item.faultEventName">
<span class="fieldLabel">故障事件</span>
<span class="fieldValue">{{ item.faultEventName }}</span>
</div>
<div class="alarmField" v-if="item.eventCause">
<span class="fieldLabel">事件原因</span>
<span class="fieldValue">{{ item.eventCause }}</span>
</div>
<div class="alarmField" v-if="item.troubleshootStep">
<span class="fieldLabel">排查步骤</span>
<span class="fieldValue">{{ item.troubleshootStep }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'AGVStatus',
components: {},
props: {
agvList: {
type: Array,
default: () => []
}
},
data() {
return {
currentIndex: 0,
currentOffset: 0,
carouselInterval: null,
isPaused: false,
itemHeight: 0,
itemsPerView: 2,
// 永久保存仅记录【无deviceName】时的轮播位置
savedCarouselIndex: 0,
lastHasDeviceName: false,
autoScrollTimers: {},
scrollPausedStatus: {},
currentAlarmIndex: {},
// 标记是否已经初始化过轮播
isCarouselInitialized: false,
// 优化:防抖和缓存
updateTimer: null,
pendingUpdate: false,
_retryCount: 0,
_cachedScrollElements: null,
_cachedAlarmItems: null,
resizeObserver: null
}
},
computed: {
hasDeviceName() {
if (!this.agvList || this.agvList.length === 0) return false
return this.agvList[0] && this.agvList[0].deviceName !== undefined
},
displayList() {
return this.agvList || []
}
},
watch: {
// 优化1使用防抖优化模式切换
hasDeviceName(newVal, oldVal) {
if (newVal === oldVal) return
console.log('模式切换:', '旧值=', oldVal, '新值=', newVal, '保存的位置=', this.savedCarouselIndex)
// 从有 deviceName 切换到无 deviceName
if (newVal === false && oldVal === true) {
console.log('切换到无deviceName模式使用保存的位置:', this.savedCarouselIndex)
this.isCarouselInitialized = false
this.stopCarousel()
this.$nextTick(() => {
setTimeout(() => {
this.initCarousel()
}, 100)
})
}
// 从无 deviceName 切换到有 deviceName
else if (newVal === true && oldVal === false) {
console.log('切换到有deviceName模式保存当前位置:', this.currentIndex)
this.saveCurrentCarouselPosition()
this.stopCarousel()
}
this.lastHasDeviceName = newVal
},
// 优化2使用防抖和深度比较优化数据监听
agvList: {
handler(newList, oldList) {
// 避免重复初始化
if (this.pendingUpdate) return
this.pendingUpdate = true
// 使用防抖
if (this.updateTimer) clearTimeout(this.updateTimer)
this.updateTimer = setTimeout(() => {
// 只在数据真正变化时才重新初始化
const hasChanged = JSON.stringify(newList) !== JSON.stringify(oldList)
if (hasChanged) {
if (!this.hasDeviceName) {
// 无 deviceName 模式:数据变化后重新初始化轮播
console.log('无deviceName模式数据更新保存的位置:', this.savedCarouselIndex)
this.stopCarousel()
this.isCarouselInitialized = false
this.$nextTick(() => {
setTimeout(() => {
this.initCarousel()
}, 50)
})
} else {
// 有 deviceName 模式:重新初始化自动滚动
this.stopAllAutoScroll()
// 清理缓存
this._cachedScrollElements = null
this._cachedAlarmItems = null
this.$nextTick(() => this.initAutoScroll())
}
}
this.pendingUpdate = false
}, 200) // 增加防抖延迟
},
deep: true
}
},
mounted() {
// 尝试从 localStorage 读取保存的位置
this.loadSavedPosition()
this.lastHasDeviceName = this.hasDeviceName
if (!this.hasDeviceName) {
this.initCarousel()
}
if (this.hasDeviceName) {
this.initAutoScroll()
}
// 使用 ResizeObserver 监听容器大小变化
this.initResizeObserver()
},
beforeDestroy() {
// 清理定时器
if (this.updateTimer) clearTimeout(this.updateTimer)
if (this.resizeObserver) {
this.resizeObserver.disconnect()
}
// 保存位置
if (!this.hasDeviceName) {
this.saveCurrentCarouselPosition()
}
this.stopCarousel()
this.stopAllAutoScroll()
// 清理缓存
this._cachedScrollElements = null
this._cachedAlarmItems = null
},
methods: {
/**
* 初始化 ResizeObserver
*/
initResizeObserver() {
if (window.ResizeObserver) {
this.resizeObserver = new ResizeObserver(() => {
if (!this.hasDeviceName && this.isCarouselInitialized) {
// 容器大小变化时重新计算高度
this.recalculateItemHeight()
}
})
const container = this.$el?.querySelector('.agvDetailDataWrapper')
if (container) {
this.resizeObserver.observe(container)
}
}
},
/**
* 重新计算项目高度
*/
recalculateItemHeight() {
const item = this.$el?.querySelector('.carouselItem')
if (item) {
const newHeight = item.offsetHeight + 12
if (newHeight !== this.itemHeight) {
this.itemHeight = newHeight
this.currentOffset = this.currentIndex * this.itemHeight
}
}
},
/**
* 从 localStorage 加载保存的位置
*/
loadSavedPosition() {
try {
const saved = localStorage.getItem('agv_carousel_position')
if (saved !== null) {
const position = parseInt(saved, 10)
if (!isNaN(position) && position >= 0) {
this.savedCarouselIndex = position
console.log('从 localStorage 加载轮播位置:', position)
}
}
} catch (e) {
console.error('读取 localStorage 失败:', e)
}
},
/**
* 保存当前轮播位置到 localStorage
*/
saveCurrentCarouselPosition() {
// 只有在无 deviceName 模式下才保存
if (!this.hasDeviceName && this.displayList.length > 0) {
const maxIndex = Math.max(0, this.displayList.length - this.itemsPerView)
// 确保保存的位置在有效范围内
let saveIndex = this.currentIndex
if (saveIndex > maxIndex) {
saveIndex = maxIndex
}
if (saveIndex >= 0) {
this.savedCarouselIndex = saveIndex
console.log('保存轮播位置到内存:', saveIndex)
// 同时保存到 localStorage
try {
localStorage.setItem('agv_carousel_position', saveIndex)
console.log('保存轮播位置到 localStorage:', saveIndex)
} catch (e) {
console.error('保存到 localStorage 失败:', e)
}
}
}
},
/**
* 优化后的轮播初始化
*/
initCarousel() {
// 防止重复初始化
if (this.isCarouselInitialized) {
console.log('轮播已初始化,跳过')
return
}
// 使用 requestAnimationFrame 代替 setTimeout
requestAnimationFrame(() => {
const container = this.$el?.querySelector('.carouselContainer')
const item = this.$el?.querySelector('.carouselItem')
if (!container || !item) {
// 最多重试3次
if (this._retryCount < 3) {
this._retryCount++
console.log(`DOM未就绪${this._retryCount}次重试`)
requestAnimationFrame(() => this.initCarousel())
}
return
}
this._retryCount = 0
this.itemHeight = item.offsetHeight + 12
const maxIndex = Math.max(0, this.displayList.length - this.itemsPerView)
// 读取保存的位置
let idx = this.savedCarouselIndex
// 如果内存中的位置是0尝试从localStorage读取
if (idx === 0 && this.displayList.length > this.itemsPerView) {
try {
const saved = localStorage.getItem('agv_carousel_position')
if (saved !== null) {
const savedIdx = parseInt(saved, 10)
if (!isNaN(savedIdx) && savedIdx > 0 && savedIdx <= maxIndex) {
idx = savedIdx
this.savedCarouselIndex = idx
console.log('从 localStorage 恢复到位置:', idx)
}
}
} catch (e) {
console.error('读取 localStorage 失败:', e)
}
}
// 边界检查
idx = Math.min(idx, maxIndex)
idx = Math.max(0, idx)
this.currentIndex = idx
this.currentOffset = idx * this.itemHeight
// 标记已初始化
this.isCarouselInitialized = true
console.log('轮播初始化完成,从位置继续:', this.currentIndex, '保存的位置:', this.savedCarouselIndex, '最大索引:', maxIndex)
// 启动轮播
this.startCarousel()
})
},
/**
* 启动轮播
*/
startCarousel() {
this.stopCarousel()
// 如果数据不足一页,不需要轮播
if (this.displayList.length <= this.itemsPerView) {
console.log('数据不足一页,不启动轮播')
return
}
this.carouselInterval = setInterval(() => {
// 暂停时 或 有 deviceName 模式时,不滚动也不保存
if (this.isPaused || this.hasDeviceName) return
const step = this.itemsPerView
let newIndex
if (this.currentIndex + step < this.displayList.length) {
newIndex = this.currentIndex + step
} else {
newIndex = 0
}
this.currentIndex = newIndex
this.currentOffset = this.currentIndex * this.itemHeight
// 保存当前位置
this.saveCurrentCarouselPosition()
}, 5000)
console.log('轮播已启动间隔5秒')
},
/**
* 优化后的自动滚动初始化
*/
initAutoScroll() {
// 使用 requestAnimationFrame 批量处理
requestAnimationFrame(() => {
this.agvList.forEach((agv, index) => {
if (agv.data && agv.data.length) {
// 延迟初始化,避免同时渲染
setTimeout(() => {
if (!this.currentAlarmIndex[index]) {
this.$set(this.currentAlarmIndex, index, 0)
}
this.scrollToAlarm(index, 0)
this.startAutoScroll(index)
}, index * 50) // 错开初始化时间
}
})
})
},
/**
* 优化:缓存 DOM 查询结果的滚动方法
*/
scrollToAlarm(index, alarmIndex) {
const ref = this.$refs[`scroll_${index}`]
if (!ref) return
// 缓存元素查询结果
if (!this._cachedScrollElements) {
this._cachedScrollElements = {}
}
const el = ref[0] || ref
if (this._cachedScrollElements[index] !== el) {
this._cachedScrollElements[index] = el
this._cachedAlarmItems = null // 清除缓存
}
// 获取或缓存告警项
if (!this._cachedAlarmItems || !this._cachedAlarmItems[index]) {
if (!this._cachedAlarmItems) this._cachedAlarmItems = {}
this._cachedAlarmItems[index] = el.querySelectorAll('.alarmItem')
}
const items = this._cachedAlarmItems[index]
if (items && items[alarmIndex]) {
// 使用 requestAnimationFrame 优化滚动性能
requestAnimationFrame(() => {
el.scrollTo({
top: items[alarmIndex].offsetTop - el.offsetTop,
behavior: 'smooth'
})
})
}
},
/**
* 启动单个AGV的告警自动滚动
*/
startAutoScroll(index) {
if (this.autoScrollTimers[index]) {
clearInterval(this.autoScrollTimers[index])
}
const len = this.agvList[index]?.data?.length
if (!len || len <= 1) return
this.autoScrollTimers[index] = setInterval(() => {
if (this.scrollPausedStatus[index]) return
const newIndex = (this.currentAlarmIndex[index] + 1) % len
this.currentAlarmIndex[index] = newIndex
this.scrollToAlarm(index, newIndex)
}, 10000)
},
/**
* 停止单个AGV的告警自动滚动
*/
stopAutoScroll(index) {
if (this.autoScrollTimers[index]) {
clearInterval(this.autoScrollTimers[index])
delete this.autoScrollTimers[index]
}
},
/**
* 暂停单个AGV的告警自动滚动
*/
pauseAutoScroll(index) {
this.$set(this.scrollPausedStatus, index, true)
},
/**
* 恢复单个AGV的告警自动滚动
*/
resumeAutoScroll(index) {
this.$set(this.scrollPausedStatus, index, false)
},
/**
* 停止所有告警自动滚动
*/
stopAllAutoScroll() {
Object.keys(this.autoScrollTimers).forEach(key => {
clearInterval(this.autoScrollTimers[key])
})
this.autoScrollTimers = {}
},
/**
* 停止轮播
*/
stopCarousel() {
if (this.carouselInterval) {
clearInterval(this.carouselInterval)
this.carouselInterval = null
}
this.isCarouselInitialized = false
},
/**
* 暂停轮播(鼠标悬停时)
*/
pauseCarousel() {
this.isPaused = true
},
/**
* 恢复轮播(鼠标离开时)
*/
resumeCarousel() {
this.isPaused = false
}
}
}
</script>
<style lang='scss' scoped>
.agvStatusContent {
width: 100%;
height: 442px;
background: #FFFFFF;
border-radius: 8px;
padding: 16px 24px;
display: flex;
flex-direction: column;
.title {
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 16px;
color: rgba(0, 0, 0, 0.85);
line-height: 16px;
letter-spacing: 1px;
text-align: left;
font-style: normal;
position: relative;
padding-left: 12px;
flex-shrink: 0;
margin-bottom: 16px;
&::before {
content: '';
width: 4px;
height: 16px;
background: #0A4BFF;
border-radius: 1px;
position: absolute;
left: 0;
top: 0;
}
}
// 有 deviceName 时:网格布局
.agvDetailDataGrid {
flex: 1;
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;
align-content: start;
max-height: 100%;
overflow-y: auto;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
&:hover {
background: rgba(0, 0, 0, 0.3);
}
}
.item {
background: #F8F9FC;
border-radius: 8px;
padding: 12px 16px;
height: 376px;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
// 无 deviceName 时:轮播容器
.agvDetailDataWrapper {
flex: 1;
overflow: hidden;
position: relative;
}
.carouselContainer {
transition: transform 0.5s ease-in-out;
will-change: transform; // 优化动画性能
}
.carouselItemGroup {
margin-bottom: 0;
}
.carouselItem {
background: #F8F9FC;
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 12px;
.alarmDetailWrapper {
margin-top: 8px;
.alarmDetailList {
gap: 6px;
.alarmItem {
padding: 6px;
.alarmField {
margin-bottom: 4px;
font-size: 11px;
.fieldLabel {
width: 65px;
font-size: 11px;
}
.fieldValue {
font-size: 11px;
}
}
}
}
}
.alarmBasicInfo {
padding-bottom: 8px;
.eqCode {
.num {
width: 20px;
height: 20px;
font-size: 11px;
line-height: 20px;
}
.code {
font-size: 12px;
}
}
}
}
.agvBasicInfo {
display: flex;
padding-bottom: 12px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
flex-shrink: 0;
.eqInfo {
.eqCode {
display: flex;
align-items: center;
.num {
width: 20px;
height: 20px;
background: #0065FF;
border-radius: 2px;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 12px;
color: #FFFFFF;
line-height: 20px;
text-align: center;
}
.code {
margin-left: 6px;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 14px;
color: #020203;
line-height: 18px;
}
}
}
}
.alarmBasicInfo {
display: flex;
padding-bottom: 12px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
flex-shrink: 0;
.eqCode {
display: flex;
align-items: center;
width: 100%;
.num {
width: 24px;
height: 24px;
background: #0065FF;
border-radius: 4px;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 12px;
color: #FFFFFF;
line-height: 24px;
text-align: center;
flex-shrink: 0;
}
.code {
margin-left: 8px;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 14px;
color: #020203;
line-height: 18px;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.alarmDetailWrapper {
margin-top: 12px;
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
.alarmDetailTitle {
display: flex;
align-items: center;
gap: 4px;
margin-bottom: 8px;
flex-shrink: 0;
.titleText {
font-size: 13px;
font-weight: 500;
color: rgba(0, 0, 0, 0.65);
line-height: 18px;
}
.alarmCount {
font-size: 12px;
color: rgba(0, 0, 0, 0.45);
}
}
.alarmDetailScroll {
flex: 1;
overflow-y: auto;
background: rgba(0, 0, 0, 0.02);
border-radius: 6px;
padding: 8px;
min-height: 0;
scroll-behavior: smooth;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
border-radius: 2px;
}
&::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 2px;
&:hover {
background: rgba(0, 0, 0, 0.3);
}
}
}
.alarmDetailList {
display: flex;
flex-direction: column;
gap: 8px;
.alarmItem {
padding: 8px;
background: #FFFFFF;
border-radius: 6px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
transition: all 0.3s ease;
.alarmHeader {
margin-bottom: 8px;
padding-bottom: 6px;
border-bottom: 1px dashed rgba(0, 0, 0, 0.06);
.alarmIndex {
font-size: 12px;
font-weight: 500;
color: #0A4BFF;
}
}
.alarmField {
display: flex;
margin-bottom: 6px;
font-size: 12px;
line-height: 1.5;
&:last-child {
margin-bottom: 0;
}
.fieldLabel {
flex-shrink: 0;
width: 70px;
color: rgba(0, 0, 0, 0.45);
}
.fieldValue {
flex: 1;
color: rgba(0, 0, 0, 0.85);
word-break: break-word;
}
}
.alarmDivider {
height: 1px;
background: rgba(0, 0, 0, 0.06);
margin: 8px 0 4px;
}
}
}
}
.noAlarm {
margin-top: 12px;
padding: 24px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.02);
border-radius: 6px;
.noAlarmText {
font-size: 12px;
color: rgba(0, 0, 0, 0.45);
}
}
.emptyState {
height: 200px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
background: #f5f5f5;
border-radius: 8px;
}
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<div style="flex: 1">
<div class="agvStatusContent">
<div class="title">异常原因Top5</div>
<!-- <div class="title">异常原因Top5</div>
<div class="alarm-item" style="margin-top: 14px;">
<div class="item top1">
<div class="item-data">
@@ -78,7 +78,7 @@
</div>
</div>
</div>
</div>
</div> -->
</div>
</div>
</template>
@@ -111,7 +111,7 @@ export default {
<style lang='scss' scoped>
.agvStatusContent {
width: 402px;
width: 1344px;
height: 442px;
background: #FFFFFF;
border-radius: 8px;

View File

@@ -1,5 +1,4 @@
<template>
<!-- 动态绑定 ref使用 props 中的 chartRef而非硬编码 -->
<div :ref="chartRef" id="coreLineChart" style="height: 170px; width: 100%;"></div>
</template>
@@ -9,21 +8,23 @@ import * as echarts from 'echarts';
export default {
name: 'Container',
props: {
// 1. 重命名 propsref → chartRef避免关键字冲突同时定义类型和默认值
chartRef: {
type: String,
required: true, // 强制父组件传值,避免获取不到 DOM
// validator: (value) => {
// // 验证ref 名不能为空,确保有效
// return value.trim() !== '';
// }
}
required: true,
},
data: {
type: Array,
default: () => [],
},
},
components: {},
data() {
return {};
watch: {
data: {
handler() {
this.initData();
},
deep: true
},
},
computed: {},
mounted() {
this.$nextTick(() => {
this.initData();
@@ -31,243 +32,122 @@ export default {
},
methods: {
initData() {
// 2. 动态获取 DOM通过 props 中的 chartRef 拿到对应的 ref 元素
const chartDom = this.$refs[this.chartRef];
if (!chartDom) {
console.error(`图表容器未找到!请确认父组件传递的 chartRef 为 "${this.chartRef}"`);
return;
}
if (!this.data || this.data.length === 0) {
console.warn('未传入数据或数据为空');
return;
}
const myChart = echarts.init(chartDom);
// 自定义颜色数组(与系列一一对应)
const customColors = [
'rgba(113, 100, 255, 1)',
'rgba(40, 138, 255, 1)',
'rgba(118, 218, 190, 1)',
'rgba(255, 206, 106, 1)',
];
// 主任务类型映射
const taskTypeMap = {
1: '满盘搬运',
2: '空盘搬运',
3: '备货搬运',
};
// 按你原有顺序定义颜色(顺序按映射顺序排)
const colorMap = {
1: 'rgba(40, 138, 255, 1)', // 满盘搬运
2: 'rgba(168, 233, 255, 1)', // 空盘搬运
3: 'rgba(143, 146, 255, 1)', // 备货搬运
};
// 转换数据格式
const seriesData = this.data.map(item => ({
name: taskTypeMap[item.mainTaskType] || '未知类型',
value: item.mainTaskCount,
percent: item.percentage,
originalType: item.mainTaskType,
}));
// 生成 series 配置
const option = {
// 标题配置(主标题+副标题)
// title: [
// {
// text: '月度',
// left: 'center',
// top: '35%',
// textStyle: {
// fontSize: 24,
// letterSpacing: 5,
// color: 'rgba(0, 0, 0, 0.85)',
// fontFamily: 'PingFangSC, PingFang SC'
// }
// },
// {
// text: '单位:万m²',
// left: 'center',
// top: '50%',
// textStyle: {
// fontSize: 16,
// color: 'rgba(0, 0, 0, 0.55)',
// fontFamily: 'PingFangSC, PingFang SC'
// }
// }
// ],
series: [
{
// name: '销量',
type: 'pie',
radius: ['60%', '90%'],
radius: ['50%', '80%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'outside',
distance: 10,
formatter: '{c}万m²\n{b}',
formatter: (params) => {
const percent = (params.percent).toFixed(1);
return `${params.value}万m²\n${params.name}`;
},
textStyle: {
fontSize: 14,
color: 'rgba(0, 0, 0, 0.7)',
fontFamily: 'PingFangSC, PingFang SC',
lineHeight: 1.5
}
lineHeight: 1.5,
},
},
labelLine: {
show: true,
length: 0,
length2: 30,
lineStyle: {
color: (params) => customColors[params.dataIndex]
}
color: (params) => colorMap[seriesData[params.dataIndex].originalType],
},
},
itemStyle: {
color: (params) => customColors[params.dataIndex]
color: (params) => colorMap[seriesData[params.dataIndex].originalType],
},
data: [
{
value: 348, name: '临时搬运',
label: {
normal: {
align: 'left',
distanceToLabelLine: 2,
formatter: (params) => [
`{b|${params.name.toLocaleString()}}`,
`{c|${params.percent.toFixed(1)}% ${params.value}}`
].join('\n'),
rich: {
b: {
color: 'rgba(140, 140, 140, 1)',
fontSize: 14,
padding: [0, 0, 0, 0]
},
c: {
color: 'rgba(0, 0, 0, 0.65)',
fontSize: 14,
padding: [30, 0, 0, 0]
}
}
}
data: seriesData.map(item => ({
...item,
label: {
show: true,
align: 'left',
distanceToLabelLine: 2,
formatter: (params) => {
const percent = (params.percent).toFixed(1);
return [
`{b|${params.name}}`,
`{c|${percent}% ${params.value}}`
].join('\n');
},
labelLine: {
lineStyle: { color: 'rgba(80, 181, 255, 1)' }
rich: {
b: {
color: 'rgba(140, 140, 140, 1)',
fontSize: 14,
padding: [0, 0, 0, 0],
},
c: {
color: 'rgba(0, 0, 0, 0.65)',
fontSize: 14,
padding: [30, 0, 0, 0],
},
},
itemStyle: { color: 'rgba(80, 181, 255, 1)' }
},
{
value: 435, name: '满盘搬运',
label: {
normal: {
align: 'left',
distanceToLabelLine: 2,
formatter: (params) => [
`{b|${params.name.toLocaleString()}}`,
`{c|${params.percent.toFixed(1)}% ${params.value}}`
].join('\n'),
rich: {
b: {
color: 'rgba(140, 140, 140, 1)',
fontSize: 14,
padding: [0, 0, 0, 0]
},
c: {
color: 'rgba(0, 0, 0, 0.65)',
fontSize: 14,
padding: [30, 0, 0, 0]
}
}
}
},
labelLine: {
length: 10,
length2: 20,
lineStyle: { color: 'rgba(40, 138, 255, 1)' }
},
itemStyle: { color: 'rgba(40, 138, 255, 1)' }
labelLine: {
lineStyle: { color: colorMap[item.originalType] },
},
{
value: 580, name: '备料搬运',
label: {
normal: {
align: 'left',
distanceToLabelLine: 2,
formatter: (params) => [
`{b|${params.name.toLocaleString()}}`,
`{c| ${params.value} ${params.percent.toFixed(1)}%}`
].join('\n'),
rich: {
b: {
color: 'rgba(140, 140, 140, 1)',
fontSize: 14,
padding: [0, 0, 0, 0]
},
c: {
color: 'rgba(0, 0, 0, 0.65)',
fontSize: 14,
padding: [30, 0, 0, 0]
}
}
}
},
labelLine: {
lineStyle: { color: 'rgba(143, 146, 255, 1)' }
},
itemStyle: { color: 'rgba(143, 146, 255, 1)' }
},
{
value: 484, name: '空盘搬运',
label: {
normal: {
align: 'left',
distanceToLabelLine: 2,
formatter: (params) => [
`{b|${params.name.toLocaleString()}}`,
`{c| ${params.value} ${params.percent.toFixed(1)}%}`
].join('\n'),
rich: {
b: {
color: 'rgba(140, 140, 140, 1)',
fontSize: 14,
padding: [0, 0, 0, 0]
},
c: {
color: 'rgba(0, 0, 0, 0.65)',
fontSize: 14,
padding: [30, 0, 0, 0]
}
}
}
},
// label: {
// normal: {
// align: 'left',
// distanceToLabelLine: 2,
// formatter: (params) => [
// `{b|${params.value.toLocaleString()}}`,
// `{c|${params.name}}`
// ].join('\n'),
// rich: {
// // hr: {
// // color: 'rgba(168, 233, 255, 1)',
// // fontSize: 20,
// // padding: [26, 8, 0, 0]
// // },
// b: {
// color: 'rgba(0, 0, 0, 0.65)',
// fontSize: 18,
// padding: [0, 0, 0, 0]
// },
// c: {
// color: 'rgba(0, 0, 0, 0.65)',
// fontSize: 14,
// padding: [30, 0, 0, -5]
// }
// }
// }
// },
labelLine: {
lineStyle: { color: 'rgba(168, 233, 255, 1)' }
},
itemStyle: { color: 'rgba(168, 233, 255, 1)' }
}
]
}
]
itemStyle: { color: colorMap[item.originalType] },
})),
},
],
};
option && myChart.setOption(option);
myChart.setOption(option);
// 窗口缩放监听
window.addEventListener('resize', () => {
myChart.resize();
});
const resizeHandler = () => myChart.resize();
window.addEventListener('resize', resizeHandler);
// 组件销毁清理
this.$once('hook:destroyed', () => {
window.removeEventListener('resize', () => {
myChart.resize();
});
window.removeEventListener('resize', resizeHandler);
myChart.dispose();
});
}
},
},
};
</script>

View File

@@ -5,11 +5,11 @@
<div :ref="chartRef" id="coreLineChart" class="chart-container"></div>
<!-- 自定义图例三排三列 + 带数值 -->
<div class="custom-legend">
<div class="legend-item" v-for="(item, index) in chartData" :key="index"
>
<div class="legend-item" v-for="(item, index) in chartData" :key="index" @click="handleLegendClick(index)"
:class="{ unselected: !selectedStatus[index] }">
<!-- 左侧颜色块 + 名称垂直居中对齐 -->
<div class="legend-left">
<div class="legend-color" :style="{ backgroundColor: customColors[index] }"></div>
<div class="legend-color" :style="{ backgroundColor: customColors[index % customColors.length] }"></div>
<span class="legend-name">{{ item.name }}</span>
</div>
<!-- 右侧数值与左侧垂直居中对齐 -->
@@ -28,7 +28,11 @@ export default {
chartRef: {
type: String,
required: true
}
},
data: {
type: Array,
default: () => []
},
},
data() {
return {
@@ -45,35 +49,97 @@ export default {
'rgba(80, 181, 255, 1)',
'rgba(168, 233, 255, 1)',
],
chartData: [
{ value: 348, name: '暂停中' },
{ value: 435, name: '已终止-回收' },
{ value: 580, name: '已终止-待回收' },
{ value: 484, name: '已终止' },
{ value: 484, name: '完成' },
{ value: 484, name: '执行中' },
{ value: 484, name: '异常' },
{ value: 484, name: '待执行' },
{ value: 484, name: '待下发' },
]
// 状态映射
stateMap: {
0: '待下发',
1: '待执行',
2: '执行中',
3: '取货完成',
4: '完成',
5: '暂停中',
6: '取消',
7: '异常',
}
};
},
computed: {
// 处理图表数据
chartData() {
if (!this.data || this.data.length === 0) {
return [];
}
return this.data
.map(item => ({
name: this.stateMap[item.mainTaskState] || `状态${item.mainTaskState}`,
value: item.mainTaskCount,
state: item.mainTaskState,
percentage: item.percentage
}));
},
// 计算总数
total() {
return this.chartData.reduce((sum, item) => sum + item.value, 0);
}
},
watch: {
// 监听数据变化,重新渲染图表
chartData: {
handler() {
this.initChart();
},
deep: true
},
// 监听选中状态变化,更新图表
selectedStatus: {
handler() {
if (this.myChart) {
this.updateChartSelection();
}
},
deep: true
}
},
mounted() {
this.$nextTick(() => {
this.initData();
this.initChart();
});
},
methods: {
initData() {
initChart() {
const chartDom = this.$refs[this.chartRef];
if (!chartDom) {
console.error(`图表容器未找到!请确认父组件传递的 chartRef 为 "${this.chartRef}"`);
return;
}
// 初始化或重置图表
if (this.myChart) {
this.myChart.dispose();
}
this.myChart = echarts.init(chartDom);
// 初始化选中状态(全部选中)
this.selectedStatus = this.chartData.map(() => true);
const total = this.chartData.reduce((sum, item) => sum + item.value, 0);
this.renderChart();
// 窗口缩放监听
const resizeHandler = () => this.myChart?.resize();
window.addEventListener('resize', resizeHandler);
this.$once('hook:destroyed', () => {
window.removeEventListener('resize', resizeHandler);
this.myChart?.dispose();
});
},
renderChart() {
if (!this.myChart || this.chartData.length === 0) {
return;
}
const option = {
legend: { show: false },
@@ -83,8 +149,6 @@ export default {
backgroundColor: '#FFFFFF',
boxShadow: '0px 4px 8px 0px rgba(0, 0, 0, 0.16)',
borderRadius: '4px',
width: 153,
height: 42,
borderWidth: 0,
textStyle: {
color: 'rgba(0, 0, 0, 0.85)',
@@ -92,8 +156,8 @@ export default {
lineHeight: 1.5
},
formatter: (params) => {
const color = this.customColors[params.dataIndex];
const percent = ((params.value / total) * 100).toFixed(1);
const color = this.customColors[params.dataIndex % this.customColors.length];
const percent = ((params.value / this.total) * 100).toFixed(1);
return `
<div style="display: flex; align-items: center; gap: 8px;">
<div style="width: 12px; height: 12px; background: ${color};"></div>
@@ -107,7 +171,7 @@ export default {
},
title: [
{
text: total.toString(),
text: this.total.toString(),
left: 'center',
top: '35%',
textStyle: {
@@ -137,39 +201,43 @@ export default {
label: { show: false },
labelLine: { show: false },
itemStyle: {
color: (params) => this.customColors[params.dataIndex],
color: (params) => this.customColors[params.dataIndex % this.customColors.length],
borderWidth: 2,
borderColor: '#fff'
},
data: this.chartData.map((item, index) => ({
...item,
itemStyle: { color: this.customColors[index] }
name: item.name,
value: item.value,
itemStyle: { color: this.customColors[index % this.customColors.length] }
})),
selected: this.selectedStatus.reduce((obj, selected, index) => {
obj[this.chartData[index].name] = selected;
return obj;
}, {})
selected: this.getSelectedMap()
}
]
};
option && this.myChart.setOption(option);
window.addEventListener('resize', () => this.myChart?.resize());
this.$once('hook:destroyed', () => {
window.removeEventListener('resize', () => this.myChart?.resize());
this.myChart?.dispose();
});
this.myChart.setOption(option, true);
},
handleLegendClick(index) {
this.selectedStatus[index] = !this.selectedStatus[index];
getSelectedMap() {
const selectedMap = {};
this.chartData.forEach((item, index) => {
selectedMap[item.name] = this.selectedStatus[index];
});
return selectedMap;
},
updateChartSelection() {
if (!this.myChart) return;
this.myChart.setOption({
series: [{
selected: this.selectedStatus.reduce((obj, selected, i) => {
obj[this.chartData[i].name] = selected;
return obj;
}, {})
selected: this.getSelectedMap()
}]
});
},
handleLegendClick(index) {
this.$set(this.selectedStatus, index, !this.selectedStatus[index]);
}
}
};
@@ -196,19 +264,24 @@ export default {
box-sizing: border-box;
display: grid;
grid-template-columns: repeat(3, 1fr); // 三列
grid-template-rows: repeat(3, 1fr); // 两行6个item刚好2行3列
// gap: 15px 10px; // 行列间距
align-items: center; // 网格内垂直居中
gap: 10px 15px;
align-items: center;
flex: 1;
}
// 单个图例项:横向排列,垂直居中
.legend-item {
display: flex;
align-items: left; // 内部元素垂直居中
flex-direction: column;
align-items: center; // 内部元素垂直居中
justify-content: space-between;
width: 100%;
cursor: pointer;
// gap: 10px; // 左右两部分间距
padding: 4px 0;
&:hover {
background-color: rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
}
// 左侧:颜色块 + 名称(垂直居中)
@@ -238,22 +311,21 @@ export default {
// 数值
.legend-value {
font-size: 14px;
margin-left: 16px;
color: rgba(0, 0, 0, 0.65);
font-weight: 500;
white-space: nowrap; // 防止换行
}
// 未选中状态
// .legend-item.unselected {
.legend-item.unselected {
// .legend-name,
// .legend-value {
// color: rgba(0, 0, 0, 0.3);
// }
.legend-name,
.legend-value {
color: rgba(0, 0, 0, 0.3);
}
// .legend-color {
// opacity: 0.5;
// }
// }
.legend-color {
opacity: 0.3;
}
}
</style>

View File

@@ -1,9 +1,9 @@
<template>
<div style="flex: 1">
<div class="agvStatusContent">
<div class="title">实时任务</div>
<!-- <div class="title">实时任务</div>
<base-table style="width: 100%;margin-top: 6px;height: 300px;" :page="1" :limit="10" :show-index="true" :beilv="1"
:tableConfig="tableProps" :table-data="maintenanceTasks" />
:tableConfig="tableProps" :table-data="maintenanceTasks" /> -->
</div>
</div>
</template>

View File

@@ -2,7 +2,7 @@
<div style="flex: 1">
<div class="agvStatusContent">
<div class="title">任务状态占比 ·今日</div>
<pieChart :chartRef=" 'taskStatusRef' " />
<pieChart :chartRef=" 'taskStatusRef' " :data="data" />
</div>
</div>
</template>
@@ -13,12 +13,9 @@ export default {
name: 'AGVStatus',
components: { pieChart },
props: {
energyObj: {
type: Object,
default: () => ({
electricComu: 0,
steamComu: 0
})
data: {
type: Array,
default: () => ([])
}
},
data() {

View File

@@ -2,7 +2,7 @@
<div style="flex: 1">
<div class="agvStatusContent">
<div class="title">任务类型占比 ·今日</div>
<pieChart :chartRef=" 'taskTypeRef' " />
<pieChart :chartRef=" 'taskTypeRef' " :data ='data' />
</div>
</div>
</template>
@@ -13,12 +13,9 @@ export default {
name: 'AGVStatus',
components: { pieChart },
props: {
energyObj: {
type: Object,
default: () => ({
electricComu: 0,
steamComu: 0
})
data: {
type: Array,
default: () => ([])
}
},
data() {

View File

@@ -1178,7 +1178,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道1 -->
<div v-for="i in 3" :key="`BM1010${i}`" :ref="`BM1010${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '706px'
}">
<div :ref="`BM1010${i}L`" style="width: 11px;
@@ -1195,7 +1195,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道2 -->
<div v-for="i in 3" :key="`BM1020${i}`" :ref="`BM1020${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '740px'
}">
<div :ref="`BM1020${i}L`" style="width: 11px;
@@ -1212,7 +1212,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道3 -->
<div v-for="i in 3" :key="`BM1030${i}`" :ref="`BM1030${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '774px'
}">
<div :ref="`BM1030${i}L`" style="width: 11px;
@@ -1229,7 +1229,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道4 -->
<div v-for="i in 3" :key="`BM1040${i}`" :ref="`BM1040${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '808px'
}">
<div :ref="`BM1040${i}L`" style="width: 11px;
@@ -1239,14 +1239,14 @@ padding: 1.5px;">
</div>
<div :ref="`BM1040${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px;">
padding: 1..5px;">
<div :class="getPositionClass(`BM1040${i}R`)"></div>
</div>
</div>
<!-- 满盘缓存区2巷道5 -->
<div v-for="i in 3" :key="`BM1050${i}`" :ref="`BM1050${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '842px'
}">
<div :ref="`BM1050${i}L`" style="width: 11px;
@@ -1263,7 +1263,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道6 -->
<div v-for="i in 3" :key="`BM1060${i}`" :ref="`BM1060${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '878px'
}">
<div :ref="`BM1060${i}L`" style="width: 11px;
@@ -1280,7 +1280,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道7 -->
<div v-for="i in 3" :key="`BM1070${i}`" :ref="`BM1070${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '910px'
}">
<div :ref="`BM1070${i}L`" style="width: 11px;
@@ -1297,7 +1297,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道8 -->
<div v-for="i in 3" :key="`BM1080${i}`" :ref="`BM1080${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '946px'
}">
<div :ref="`BM1080${i}L`" style="width: 11px;
@@ -1314,7 +1314,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道9 -->
<div v-for="i in 3" :key="`BM1090${i}`" :ref="`BM1090${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '980px'
}">
<div :ref="`BM1090${i}L`" style="width: 11px;
@@ -1331,7 +1331,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道10 -->
<div v-for="i in 3" :key="`BM1100${i}`" :ref="`BM1100${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '1012px'
}">
<div :ref="`BM1100${i}L`" style="width: 11px;
@@ -1348,7 +1348,7 @@ padding: 1.5px;">
<!-- 满盘缓存区2巷道11 -->
<div v-for="i in 3" :key="`BM1110${i}`" :ref="`BM1110${i}`" class="location" :style="{
top: `${201 + (i - 1) * 13}px`,
top: `${201 + (3 - i) * 13}px`,
left: '1048px'
}">
<div :ref="`BM1110${i}L`" style="width: 11px;
@@ -1362,7 +1362,6 @@ padding: 1.5px;">
<div :class="getPositionClass(`BM1110${i}R`)"></div>
</div>
</div>
<!-- 满盘缓存区3巷道1 -->
<div v-for="i in 4" :key="`BM2010${i}`" :ref="`BM2010${i}`" class="location" :style="{
top: `${250 + (i - 1) * 13}px`,
@@ -1694,12 +1693,12 @@ padding: 1.5px;">
<div :ref="`AM1010${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM1010${i}L`)" ></div>
<div :class="getPositionClass(`AM1010${i}L`)"></div>
</div>
<div :ref="`AM1010${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM1010${i}R`)" ></div>
<div :class="getPositionClass(`AM1010${i}R`)"></div>
</div>
</div>
<!-- 拉丝A线满盘缓存区第2巷道1排 -->
@@ -1710,12 +1709,12 @@ padding: 1.5px;">
<div :ref="`AM1020${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM1020${i}L`)" ></div>
<div :class="getPositionClass(`AM1020${i}L`)"></div>
</div>
<div :ref="`AM1020${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM1020${i}R`)" ></div>
<div :class="getPositionClass(`AM1020${i}R`)"></div>
</div>
</div>
<!-- 拉丝A线满盘缓存区第3巷道1排 -->
@@ -1726,12 +1725,12 @@ padding: 1.5px;">
<div :ref="`AM1030${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM1030${i}L`)" ></div>
<div :class="getPositionClass(`AM1030${i}L`)"></div>
</div>
<div :ref="`AM1030${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM1030${i}R`)" ></div>
<div :class="getPositionClass(`AM1030${i}R`)"></div>
</div>
</div>
<!-- 拉丝A线满盘缓存区第4巷道1排 -->
@@ -1742,12 +1741,12 @@ padding: 1.5px;">
<div :ref="`AM1040${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM1040${i}L`)" ></div>
<div :class="getPositionClass(`AM1040${i}L`)"></div>
</div>
<div :ref="`AM1040${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM1040${i}R`)" ></div>
<div :class="getPositionClass(`AM1040${i}R`)"></div>
</div>
</div>
<!-- 拉丝A线满盘缓存区第5巷道1排 -->
@@ -1758,12 +1757,12 @@ padding: 1.5px;">
<div :ref="`AM1050${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM1050${i}L`)" ></div>
<div :class="getPositionClass(`AM1050${i}L`)"></div>
</div>
<div :ref="`AM1050${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM1050${i}R`)" ></div>
<div :class="getPositionClass(`AM1050${i}R`)"></div>
</div>
</div>
<!-- 拉丝A线满盘缓存区第6巷道1排 -->
@@ -1774,12 +1773,12 @@ padding: 1.5px;">
<div :ref="`AM1060${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM1060${i}L`)" ></div>
<div :class="getPositionClass(`AM1060${i}L`)"></div>
</div>
<div :ref="`AM1060${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM1060${i}R`)" ></div>
<div :class="getPositionClass(`AM1060${i}R`)"></div>
</div>
</div>
<!-- 拉丝A线满盘缓存区第7巷道1排 -->
@@ -1790,12 +1789,12 @@ padding: 1.5px;">
<div :ref="`AM1070${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM1070${i}L`)" ></div>
<div :class="getPositionClass(`AM1070${i}L`)"></div>
</div>
<div :ref="`AM1070${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM1070${i}R`)" ></div>
<div :class="getPositionClass(`AM1070${i}R`)"></div>
</div>
</div>
<!-- 拉丝A线满盘缓存区第8巷道1排 -->
@@ -1806,12 +1805,12 @@ padding: 1.5px;">
<div :ref="`AM1080$${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM1080$${i}L`)" ></div>
<div :class="getPositionClass(`AM1080$${i}L`)"></div>
</div>
<div :ref="`AM1080$${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM1080$${i}R`)" ></div>
<div :class="getPositionClass(`AM1080$${i}R`)"></div>
</div>
</div>
@@ -1824,12 +1823,12 @@ padding: 1.5px;">
<div :ref="`AM2010${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM2010${i}L`)" ></div>
<div :class="getPositionClass(`AM2010${i}L`)"></div>
</div>
<div :ref="`AM2010${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM2010${i}R`)" ></div>
<div :class="getPositionClass(`AM2010${i}R`)"></div>
</div>
</div>
<!-- 拉丝B线满盘缓存区第2巷道1排 -->
@@ -1840,12 +1839,12 @@ padding: 1.5px;">
<div :ref="`AM2020${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM2020${i}L`)" ></div>
<div :class="getPositionClass(`AM2020${i}L`)"></div>
</div>
<div :ref="`AM2020${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM2020${i}R`)" ></div>
<div :class="getPositionClass(`AM2020${i}R`)"></div>
</div>
</div>
<!-- 拉丝B线满盘缓存区第3巷道1排 -->
@@ -1856,12 +1855,12 @@ padding: 1.5px;">
<div :ref="`AM2030${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM2030${i}L`)" ></div>
<div :class="getPositionClass(`AM2030${i}L`)"></div>
</div>
<div :ref="`AM2030${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM2030${i}R`)" ></div>
<div :class="getPositionClass(`AM2030${i}R`)"></div>
</div>
</div>
<!-- 拉丝B线满盘缓存区第4巷道1排 -->
@@ -1872,12 +1871,12 @@ padding: 1.5px;">
<div :ref="`AM2040${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM2040${i}L`)" ></div>
<div :class="getPositionClass(`AM2040${i}L`)"></div>
</div>
<div :ref="`AM2040${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM2040${i}R`)" ></div>
<div :class="getPositionClass(`AM2040${i}R`)"></div>
</div>
</div>
<!-- 拉丝B线满盘缓存区第5巷道1排 -->
@@ -1888,12 +1887,12 @@ padding: 1.5px;">
<div :ref="`AM2050${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM2050${i}L`)" ></div>
<div :class="getPositionClass(`AM2050${i}L`)"></div>
</div>
<div :ref="`AM2050${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM2050${i}R`)" ></div>
<div :class="getPositionClass(`AM2050${i}R`)"></div>
</div>
</div>
<!-- 拉丝B线满盘缓存区第6巷道1排 -->
@@ -1904,12 +1903,12 @@ padding: 1.5px;">
<div :ref="`AM2060${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM2060${i}L`)" ></div>
<div :class="getPositionClass(`AM2060${i}L`)"></div>
</div>
<div :ref="`AM2060${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM2060${i}R`)" ></div>
<div :class="getPositionClass(`AM2060${i}R`)"></div>
</div>
</div>
<!-- 拉丝B线满盘缓存区第7巷道1排 -->
@@ -1920,12 +1919,12 @@ padding: 1.5px;">
<div :ref="`AM2070${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM2070${i}L`)" ></div>
<div :class="getPositionClass(`AM2070${i}L`)"></div>
</div>
<div :ref="`AM2070${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM2070${i}R`)" ></div>
<div :class="getPositionClass(`AM2070${i}R`)"></div>
</div>
</div>
<!-- 拉丝B线满盘缓存区第8巷道1排 -->
@@ -1936,12 +1935,12 @@ padding: 1.5px;">
<div :ref="`AM2080${i}L`" style="width: 11px;
height: 11px;
padding: 1.5px;">
<div :class="getPositionClass(`AM2080${i}L`)" ></div>
<div :class="getPositionClass(`AM2080${i}L`)"></div>
</div>
<div :ref="`AM2080${i}R`" style="width: 11px;
height: 11px;
padding: 1.5px; ">
<div :class="getPositionClass(`AM2080${i}R`)" ></div>
<div :class="getPositionClass(`AM2080${i}R`)"></div>
</div>
</div>
<!-- 循环渲染状态项 -->
@@ -2035,8 +2034,6 @@ export default {
}
}
</script>
"lineEdgeLibraryState": 1,
"usableState": 3,
<style lang='scss' scoped>
.threeDimensionalChart {
width: 1493px;

View File

@@ -1,49 +1,39 @@
<template>
<div id="dayReport" class="dayReport" :style="styles">
<ReportHeader top-title="车间生产看板" />
<div
class="main-body"
style="
<div id="dayReport" class="dayReport" :style="styles">
<ReportHeader top-title="车间生产看板" />
<div class="main-body" style="
flex: 1;
display: flex;
padding: 16px 20px 22px 24px;
flex-direction: column;
">
<div class="top" style="display: flex; gap: 16px">
<div
class="top-three"
style="display: grid; gap: 12px; grid-template-columns: 367px 1493px">
<agvStatus />
<threeDimensionalChart :dataList="dataList" />
</div>
</div>
<div class="top" style="display: flex; gap: 16px; margin-top: 6px">
<div
class="bottom-three"
style="
<div class="top" style="display: flex; gap: 16px">
<div class="top-three" style="display: grid; gap: 12px; grid-template-columns: 367px 1493px">
<agvStatus :agvList='agvAlarmListData' />
<threeDimensionalChart :dataList="dataList" />
</div>
</div>
<div class="top" style="display: flex; gap: 16px; margin-top: 6px">
<div class="bottom-three" style="
display: grid;
gap: 12px;
grid-template-columns: 512px 930px 402px;
grid-template-columns: 1344px 512px;
">
<div
class="left-three"
style="display: grid; gap: 16px; grid-template-rows: 213px 213px">
<taskType />
<taskStatus />
</div>
<div
class="center-three"
style="display: grid; gap: 16px; grid-template-rows: 330px 92px">
<realTask />
<alarm />
<!-- <taskStatus /> -->
</div>
<alarmTop />
</div>
</div>
</div>
</div>
<div class="center-three" style="">
<!-- <realTask /> -->
<alarmInfo :agvList='agvAlarmInfo' />
<!-- <taskStatus /> -->
</div>
<div class="left-three" style="display: grid; gap: 16px; grid-template-rows: 213px 213px">
<taskType :data="taskTypeToday" />
<taskStatus :data="taskStatusToday" />
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ReportHeader from './components/Header.vue';
@@ -52,12 +42,12 @@ import agvStatus from './components/agvStatus.vue';
import taskType from './components/taskType.vue';
import realTask from './components/realTask.vue';
import alarm from './components/alarm.vue';
import alarmTop from './components/alarmTop.vue';
import alarmInfo from './components/alarmInfo.vue';
import taskStatus from './components/taskStatus.vue';
import threeDimensionalChart from './components/threeDimensionalChart.vue';
import { getLineEdgeLibraryList } from '../../../api/visualization/visualization';
import { getLineEdgeLibraryList, getAgvAlarmInfo, getTaskTypeToday, getTaskStatusToday, getAgvExample } from '../../../api/visualization/visualization';
// import financeCosts from './components/financeCosts.vue'
// import keyProductionIndicators from './components/keyProductionIndicators.vue'
@@ -75,21 +65,28 @@ export default {
taskStatus,
realTask,
alarm,
alarmTop,
alarmInfo,
},
data() {
return {
// isFullScreen: false,
timer: null,
beilv: 1,
value: 100,
value: 100,
taskTypeToday: [],
taskStatusToday: [],
agvAlarmInfo: [],
agvAlarmListData: [],
agvAlarmInfoData:[],
timer: null, // 存储定时器
dataList: [],
};
},
created() {
// this.init()
this.windowWidth(document.documentElement.clientWidth);
// this.init()
this.windowWidth(document.documentElement.clientWidth);
localStorage.removeItem('agv_carousel_position');
},
computed: {
// ...mapGetters(['sidebar']),
@@ -109,7 +106,7 @@ export default {
this.beilv2 = this.clientWidth / 1920;
this.timer = true;
let _this = this;
setTimeout(function () {
setTimeout(function () {·
_this.timer = false;
}, 500);
}
@@ -133,89 +130,224 @@ export default {
_this.clientWidth = `${document.documentElement.clientWidth}`;
this.beilv = _this.clientWidth / baseWidth;
})();
};
this.getData();
},
methods: {
getData(data) {
console.log(data);
getLineEdgeLibraryList().then((res) => {
console.log('res', res);
};
this.getData();
this.loadAgvExampleData()
this.timer = setInterval(() => {
this.getData();
}, 30000);
this.dataList = res.data;
});
},
},
beforeDestroy() {
// 组件销毁前清除定时器
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
methods: {
async getData(data) {
console.log(data);
const [lineEdgeRes, taskTypeRes, taskStatusRes, agvAlarmRes] = await Promise.all([
getLineEdgeLibraryList(),
getTaskTypeToday(),
getTaskStatusToday(),
getAgvAlarmInfo()
]);
this.dataList = lineEdgeRes.data;
this.taskTypeToday = taskTypeRes.data;
this.taskStatusToday = taskStatusRes.data;
this.agvAlarmListData = agvAlarmRes;
// this.agvAlarmListData = [
// {
// "deviceName": "AGV-01",
// "alarmTrigger": false,
// "data": [
// {
// "createTime": "2024-01-15 08:30:25",
// "updateTime": "2024-01-15 09:15:30",
// "creator": "admin",
// "updater": "operator1",
// "deleted": false,
// "id": 1001,
// "type": 1,
// "code": "ERR-001",
// "name": "电机过载保护",
// "solutionContent": "检查电机负载,清理轨道障碍物",
// "warnName": "电机异常",
// "faultComponent": "驱动电机",
// "faultEventName": "电机电流过高",
// "eventCause": "轨道异物卡滞",
// "troubleshootStep": "1.停止AGV运行 2.检查轨道 3.清理异物 4.重启设备",
// "classification": 2,
// "remark": "需要及时处理,避免电机损坏",
// "status": 1,
// "sort": 1,
// "version": 1
// },
// {
// "createTime": "2024-01-15 08:30:25",
// "updateTime": "2024-01-15 09:15:30",
// "creator": "admin",
// "updater": "operator1",
// "deleted": false,
// "id": 1001,
// "type": 1,
// "code": "ERR-001",
// "name": "电机过载保护",
// "solutionContent": "检查电机负载,清理轨道障碍物",
// "warnName": "电机异常",
// "faultComponent": "驱动电机",
// "faultEventName": "电机电流过高",
// "eventCause": "轨道异物卡滞",
// "troubleshootStep": "1.停止AGV运行 2.检查轨道 3.清理异物 4.重启设备",
// "classification": 2,
// "remark": "需要及时处理,避免电机损坏",
// "status": 1,
// "sort": 1,
// "version": 1
// }
// ]
// },
// {
// "deviceName": "AGV-02",
// "alarmTrigger": false,
// "data": [
// {
// "createTime": "2024-01-15 10:20:15",
// "updateTime": "2024-01-15 10:45:22",
// "creator": "admin",
// "updater": "operator2",
// "deleted": false,
// "id": 1002,
// "type": 2,
// "code": "ERR-002",
// "name": "电池电量过低",
// "solutionContent": "AGV已自动返回充电桩充电",
// "warnName": "电量告警",
// "faultComponent": "锂电池组",
// "faultEventName": "电压低于阈值",
// "eventCause": "连续运行时间过长,未及时充电",
// "troubleshootStep": "1.等待AGV自动充电 2.检查充电桩连接 3.手动更换电池",
// "classification": 1,
// "remark": "电量剩余15%,需关注充电状态",
// "status": 2,
// "sort": 2,
// "version": 1
// }
// ]
// },
// {
// "deviceName": "AGV-03",
// "alarmTrigger": false,
// "data": [
// {
// "createTime": "2024-01-15 14:35:42",
// "updateTime": "2024-01-15 15:00:18",
// "creator": "admin",
// "updater": "operator3",
// "deleted": false,
// "id": 1003,
// "type": 3,
// "code": "ERR-003",
// "name": "激光雷达通讯故障",
// "solutionContent": "检查雷达连接线,重启雷达驱动",
// "warnName": "传感器异常",
// "faultComponent": "激光雷达",
// "faultEventName": "通讯超时",
// "eventCause": "线束松动或电磁干扰",
// "troubleshootStep": "1.检查雷达指示灯 2.重新插拔连接线 3.重启系统 4.更换雷达",
// "classification": 3,
// "remark": "影响导航定位功能",
// "status": 1,
// "sort": 3,
// "version": 2
// }
// ]
// },
// {
// "deviceName": "AGV-04",
// "alarmTrigger": false,
// "data": [
// {
// "createTime": "2024-01-15 09:45:33",
// "updateTime": "2024-01-15 10:30:45",
// "creator": "admin",
// "updater": "operator1",
// "deleted": false,
// "id": 1004,
// "type": 1,
// "code": "ERR-004",
// "name": "举升机构异常",
// "solutionContent": "检查液压系统,补充液压油",
// "warnName": "机械故障",
// "faultComponent": "举升液压缸",
// "faultEventName": "压力不足",
// "eventCause": "液压油泄漏或油泵故障",
// "troubleshootStep": "1.检查液压油位 2.排查泄漏点 3.更换密封圈 4.测试压力",
// "classification": 2,
// "remark": "举升高度不达标,需紧急处理",
// "status": 1,
// "sort": 4,
// "version": 1
// }
// ]
// },
// {
// "deviceName": "AGV-05",
// "alarmTrigger": false,
// "data": [
// {
// "createTime": "2024-01-15 16:20:55",
// "updateTime": "2024-01-15 17:05:33",
// "creator": "admin",
// "updater": "operator2",
// "deleted": false,
// "id": 1005,
// "type": 2,
// "code": "ERR-005",
// "name": "导航定位偏移",
// "solutionContent": "重新标定导航路径,更新地图",
// "warnName": "定位异常",
// "faultComponent": "导航系统",
// "faultEventName": "定位精度超差",
// "eventCause": "地面二维码磨损或反光柱被遮挡",
// "troubleshootStep": "1.清洁二维码/反光柱 2.重新标定 3.更新导航地图 4.校准传感器",
// "classification": 3,
// "remark": "偏差超过5cm需重新校准",
// "status": 2,
// "sort": 5,
// "version": 1
// }
// ]
// }
// ];
// 根据告警状态决定显示内容
if (this.isAllAlarmTriggered()) {
console.log('无告警,加载示例数据');
// await this.loadAgvExampleData();
this.agvAlarmInfo = this.agvAlarmInfoData
} else {
console.log('有告警,使用真实数据');
this.agvAlarmInfo = agvAlarmRes;
// this.agvAlarmInfo = this.agvAlarmListData;
}
},
async loadAgvExampleData() {
const res = await getAgvExample()
console.log('getAgvExample 接口返回:', res);
this.agvAlarmInfoData = res
},
isAllAlarmTriggered() {
if (!this.agvAlarmListData || this.agvAlarmListData.length === 0) return false;
return this.agvAlarmListData.every(agv => agv.alarmTrigger === false);
},
windowWidth(value) {
this.clientWidth = value;
this.beilv2 = this.clientWidth / 1920;
},
// change() {
// this.isFullScreen = screenfull.isFullscreen
// },
// init() {
// if (!screenfull.isEnabled) {
// this.$message({
// message: 'you browser can not work',
// type: 'warning'
// })
// return false
// }
// screenfull.on('change', this.change)
// },
// destroy() {
// if (!screenfull.isEnabled) {
// this.$message({
// message: 'you browser can not work',
// type: 'warning'
// })
// return false
// }
// screenfull.off('change', this.change)
// },
// // 全屏
// screenfullChange() {
// console.log('screenfull.enabled', screenfull.isEnabled);
// if (!screenfull.isEnabled) {
// this.$message({
// message: 'you browser can not work',
// type: 'warning'
// })
// return false
// }
// screenfull.toggle(this.$refs.dayReportB)
// },
// changeDate(val) {
// this.date = val
// // this.weekDay = this.weekArr[moment(this.date).format('e')]
// // this.getData()
// if (this.date === moment().format('yyyy-MM-DD')) {
// this.loopTime()
// } else {
// clearInterval(this.timer)
// }
// },
// 导出
// exportPDF() {
// this.$message.success('正在导出,请稍等!')
// const element = document.getElementById('dayRepDom')
// element.style.display = 'block'
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
// html2canvas(element, {
// dpi: 300, // Set to 300 DPI
// scale: 3 // Adjusts your resolution
// }).then(function(canvas) {
// const imgWidth = 595.28
// const imgHeight = 841.89
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
// setTimeout(() => {
// PDF.save(fileName) // 导出文件名
// }, 1000)
// })
// element.style.display = 'none'
// }
},
};
</script>

View File

@@ -6,9 +6,9 @@
* @Description:
-->
<template>
<div>
<div style="display: flex; align-items: center">
<div
<div>
<div style="display: flex; align-items: center">
<!-- <div
style="
width: 16px;
height: 16px;
@@ -20,13 +20,15 @@
margin-right:5px
">
L
</div>
<span v-if="injectData.prop==='val4'">{{injectData.leftMaterialName+'-'+injectData.leftMaterialCode}}</span>
<span v-else-if="injectData.prop==='val5'">{{injectData.leftLibraryStartName}}</span>
<span v-else-if="injectData.prop==='val6'">{{injectData.leftLibraryStartName}}</span>
</div>
<div style="display: flex; align-items: center">
<div
</div> -->
<!-- <span v-if="injectData.prop === 'val4'">{{ injectData.leftMaterialCode }}</span> -->
<span v-if="injectData.prop==='val4'">{{ injectData.leftMaterialCode + '-' + injectData.leftMaterial }}</span>
<span v-else-if="injectData.prop==='val5'">{{ removeSuffix(injectData.leftLibraryStartName, '左侧') }}</span>
<span v-else-if="injectData.prop==='val6'">{{ removeSuffix(injectData.leftLibraryEndName, '左侧') }}</span>
</div>
<div style="display: flex; align-items: center">
<!-- <div
style="
width: 16px;
height: 16px;
@@ -38,12 +40,12 @@
margin-right:5px
">
R
</div>
<span v-if="injectData.prop==='val4'">{{injectData.rightMaterialName+'-'+injectData.rightMaterialCode}}</span>
<span v-else-if="injectData.prop==='val5'">{{injectData.rightLibraryStartName}}</span>
<span v-else-if="injectData.prop==='val6'">{{injectData.rightLibraryEndName}}</span>
</div>
</div>
</div> -->
<!-- <span v-if="injectData.prop==='val4'">{{injectData.rightMaterialName+'-'+injectData.rightMaterialCode}}</span> -->
<span v-if="injectData.prop==='val5'"> {{ removeSuffix(injectData.leftLibraryStartCode, 'L') }}</span>
<span v-else-if="injectData.prop==='val6'">{{ removeSuffix(injectData.leftLibraryEndCode, 'L') }}</span>
</div>
</div>
</template>
<script>
@@ -59,6 +61,10 @@ export default {
},
created() {
},
methods: {},
methods: {
removeSuffix(str, suffix) {
return str?.endsWith(suffix) ? str.slice(0, -suffix.length) : str;
}
},
};
</script>

View File

@@ -1,94 +1,48 @@
<template>
<div class="app-container">
<div>
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick" />
<div style="font-size: 14px">
自动刷新(10s)
<el-switch v-model="autoRefresh"></el-switch>
</div>
</div>
<base-table
v-loading="dataListLoading"
:table-props="tableProps"
:page="listQuery.pageNo"
:limit="listQuery.pageSize"
:max-height="tableH"
:table-data="tableData"
@emitFun="getDataList">
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="100"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination
:limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo"
:total="listQuery.total"
@pagination="getDataList" />
<!-- //新增 -->
<base-dialog
:dialogTitle="'新建人工任务'"
:dialogVisible="addOrUpdateVisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
:destroy-on-close="true"
width="70%">
<add-or-update
ref="addOrUpdate"
@setSN="setStepNum"
@refreshDataList="successSubmit"></add-or-update>
<template #footer>
<slot name="footer">
<el-row slot="footer" type="flex" justify="end">
<el-col :span="24">
<el-button
v-if="stepNum > 1"
size="small"
class="btnTextStyle"
@click="handleConfirm('up')">
上一步
</el-button>
<el-button
size="small"
class="btnTextStyle"
@click="handleCancel">
取消
</el-button>
<el-button
type="primary"
class="btnTextStyle"
size="small"
@click="handleConfirm">
{{ stepNum < 3 ? '下一步' : '创建任务' }}
</el-button>
</el-col>
</el-row>
</slot>
</template>
</base-dialog>
<!-- //详情 -->
<detail-drawer
ref="detailDrawer"
@closeDrawer="closeDrawer"></detail-drawer>
<!-- //有货终止 -->
<base-dialog
:dialogTitle="'终止任务'"
:dialogVisible="stopStockVisible"
@cancel="handleCancelStopStock"
@confirm="handleConfirmStopStock"
:before-close="handleCancelStopStock"
:destroy-on-close="true"
width="50%">
<stop-in-stock ref="StopStockRef"></stop-in-stock>
</base-dialog>
</div>
<div class="app-container">
<div>
<search-bar :formConfigs="formConfig" ref="searchBarForm" isFold @headBtnClick="buttonClick" />
<div style="font-size: 14px">
自动刷新(10s)
<el-switch v-model="autoRefresh"></el-switch>
</div>
</div>
<base-table v-loading="dataListLoading" :table-props="tableProps" :page="listQuery.pageNo"
:limit="listQuery.pageSize" :max-height="tableH" :table-data="tableData" @emitFun="getDataList">
<method-btn v-if="tableBtn.length" slot="handleBtn" :width="100" label="操作" :method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination :limit.sync="listQuery.pageSize" :page.sync="listQuery.pageNo" :total="listQuery.total"
@pagination="getDataList" />
<!-- //新增 -->
<base-dialog :dialogTitle="'新建人工任务'" :dialogVisible="addOrUpdateVisible" @cancel="handleCancel"
@confirm="handleConfirm" :before-close="handleCancel" :destroy-on-close="true" width="70%">
<add-or-update ref="addOrUpdate" @setSN="setStepNum" @refreshDataList="successSubmit"></add-or-update>
<template #footer>
<slot name="footer">
<el-row slot="footer" type="flex" justify="end">
<el-col :span="24">
<el-button v-if="stepNum > 1" size="small" class="btnTextStyle" @click="handleConfirm('up')">
上一步
</el-button>
<el-button size="small" class="btnTextStyle" @click="handleCancel">
取消
</el-button>
<el-button type="primary" class="btnTextStyle" size="small" @click="handleConfirm">
{{ stepNum < 3 ? '下一步' : '创建任务' }} </el-button>
</el-col>
</el-row>
</slot>
</template>
</base-dialog>
<!-- //详情 -->
<detail-drawer ref="detailDrawer" @closeDrawer="closeDrawer"></detail-drawer>
<!-- //有货终止 -->
<base-dialog :dialogTitle="'终止任务'" :dialogVisible="stopStockVisible" @cancel="handleCancelStopStock"
@confirm="handleConfirmStopStock" :before-close="handleCancelStopStock" :destroy-on-close="true" width="50%">
<stop-in-stock ref="StopStockRef"></stop-in-stock>
</base-dialog>
</div>
</template>
<script>
@@ -102,9 +56,14 @@ import detailDrawer from './components/detailDrawer.vue';
import stopInStock from './components/stopInStock.vue';
import tableHeightMixin from '@/mixins/lb/tableHeightMixin';
import { parseTime } from '@/filter/code-filter';
import { getTaskPage } from '@/api/ssdl/taskList';
import { getTaskPage, cancelUpdateTask } from '@/api/ssdl/taskList';
const tableProps = [
{
prop: 'id',
label: '任务号',
width: 140,
},
{
prop: 'mainTaskCode',
label: '任务编号',
@@ -127,19 +86,19 @@ const tableProps = [
},
{
prop: 'val4',
label: '搬运对象(L/R)',
label: '搬运对象',
subcomponent: subSpan3,
width: 220,
},
{
prop: 'val5',
label: '起点(L/R)',
label: '起点',
subcomponent: subSpan3,
width: 220,
},
{
prop: 'val6',
label: '终点(L/R)',
label: '终点',
subcomponent: subSpan3,
width: 220,
},
@@ -171,28 +130,34 @@ export default {
type: 'detail',
btnName: '详情',
},
// {
// type: 'stop',
// btnName: '终止',
// showParam: {
// type: '&',
// data: [
// {
// type: 'equal',
// name: 'mainTaskState',
// value: 2,
// },
// ],
// },
// },
{
type: 'cancel',
btnName: '取消',
showParam: {
type: '&',
data: [
{
type: 'equal',
name: 'mainTaskState',
value: 0,
},
],
},
},
].filter((v) => v),
tableData: [],
listQuery: {
//分页
pageSize: 10,
pageNo: 1,
total: 1,
},
total: 1,
startTime: undefined, // ✅ 必须加
endTime: undefined, // ✅ 必须加
},
urlOptions: {
// getDataListURL: getProductPage,
cancelURL: cancelUpdateTask,
},
formConfig: [
{
type: 'select',
@@ -272,10 +237,45 @@ export default {
},
{
type: 'input',
label: '任务号',
placeholder: '任务号',
param: 'code',
},
label: '任务号',
placeholder: '任务号',
param: 'id',
},
{
type: 'datePicker',
label: '时间',
dateType: 'datetimerange',
format: 'yyyy-MM-dd HH:mm:ss',
valueFormat: 'yyyy-MM-dd HH:mm:ss',
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'timeSlot',
},
{
type: 'input',
label: '物料编码',
placeholder: '物料编码',
param: 'materialCode',
},
{
type: 'input',
label: '起点编号',
placeholder: '起点编号',
param: 'startCode',
},
{
type: 'input',
label: '终点编号',
placeholder: '终点编号',
param: 'endCode',
},
{
type: 'input',
label: 'agv',
placeholder: 'agv',
param: 'agv',
},
{
type: 'button',
btnName: '搜索',
@@ -293,13 +293,13 @@ export default {
{
type: 'separate',
},
{
type: 'button',
btnName: '新增',
name: 'add',
color: 'success',
plain: true,
},
// {
// type: 'button',
// btnName: '新增',
// name: 'add',
// color: 'success',
// plain: true,
// },
],
addOrUpdateVisible: false, //dialog状态
dataListLoading: false,
@@ -344,7 +344,9 @@ export default {
this.autoRefresh = true;
},
methods: {
buttonClick(val) {
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.listQuery.pageNo = 1;
@@ -352,14 +354,21 @@ export default {
this.listQuery.mainTaskType = val.val1 || null;
this.listQuery.taskAttribute = val.val2 || null;
this.listQuery.mainTaskState = val.val3 || null;
this.listQuery.mainTaskCode = val.code || null;
this.listQuery.materialCode = val.materialCode || null;
this.listQuery.startCode = val.startCode || null;
this.listQuery.endCode = val.endCode || null;
this.listQuery.agv = val.agv || null;
this.listQuery.startTime = val.timeSlot ? val.timeSlot[0] : undefined
this.listQuery.endTime = val.timeSlot ? val.timeSlot[1] : undefined
console.log('val', this.listQuery);
this.getDataList();
break;
case 'add':
this.addOrUpdateHandle();
break;
case 'reset':
this.listQuery.code = null;
this.listQuery.id = null;
this.listQuery = {
pageSize: 10,
pageNo: 1,
@@ -373,7 +382,9 @@ export default {
},
// 获取数据列表
getDataList() {
this.dataListLoading = true;
this.dataListLoading = true;
console.log(this.listQuery,'listQuery');
getTaskPage(this.listQuery).then((response) => {
this.tableData = response.data.list;
this.listQuery.total = response.data.total;
@@ -405,8 +416,8 @@ export default {
},
//tableBtn点击
handleClick(val) {
if (val.type === 'stop') {
this.stopFun(val.data);
if (val.type === 'cancel') {
this.cancelFun(val.data.id);
// if (val.data === '无货') {
// this.stopFun(val.data);
// } else {
@@ -420,7 +431,30 @@ export default {
this.$refs.detailDrawer.init(val.data.id, 'detail');
});
}
},
},
cancelFun(id) {
this.$confirm(`确认取消任务号为${id}的任务, 是否继续?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.urlOptions.cancelURL({ id }).then(({ data }) => {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.getDataList();
},
});
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
stopFun(val) {
this.$prompt(
'确认终止该任务?此操作任务立即终止,车辆保持暂停,请人工回收货物,释放车辆!',