@@ -134,6 +134,12 @@ function handleResolutionChange(width, height) { | |||
</div> | |||
<!-- <button @click="handleIgnore" class="alert-btn">忽略</button> --> | |||
</Container> | |||
<ul class="main-screen-navigator alert-screen-navigator"> | |||
<li><a href="/main-screen">主屏页面</a></li> | |||
<li class="active">报警列表</li> | |||
<li><a href="/1-1">分屏页面</a></li> | |||
</ul> | |||
</div> | |||
</div> | |||
</template> | |||
@@ -75,10 +75,68 @@ function handleResolutionChange(width, height) { | |||
<TodayRate class=" " /> | |||
<SumRate class=" " /> | |||
</div> | |||
<ul class="main-screen-navigator alert-screen-navigator"> | |||
<li class="active">主屏页面</li> | |||
<li><a href="/alert-list">报警列表</a></li> | |||
<li><a href="/1-1">分屏页面</a></li> | |||
</ul> | |||
</div> | |||
</template> | |||
<style> | |||
ul, | |||
li { | |||
margin: 0; | |||
padding: 0; | |||
list-style: none; | |||
} | |||
.alert-screen-navigator a, | |||
.main-screen-navigator a { | |||
color: #fff; | |||
text-decoration: none; | |||
} | |||
.alert-screen-navigator, | |||
.main-screen-navigator { | |||
position: absolute; | |||
z-index: 1000; | |||
bottom: 32px; | |||
left: 0; | |||
right: 0; | |||
margin: 0 auto; | |||
width: 460px; | |||
display: flex; | |||
justify-content: space-around; | |||
align-items: center; | |||
background: rgba(46, 175, 214, 0.1); | |||
color: #fff; | |||
font-size: 16px; | |||
letter-spacing: 1px; | |||
user-select: none; | |||
padding: 12px; | |||
border-radius: 32px; | |||
} | |||
.alert-screen-navigator > li, | |||
.main-screen-navigator > li { | |||
cursor: pointer; | |||
transition: all 0.2s ease-out; | |||
border-radius: 20px; | |||
padding: 12px 24px; | |||
font-size: 20px; | |||
} | |||
.alert-screen-navigator > li:hover, | |||
.main-screen-navigator > li:hover { | |||
background: rgba(5, 106, 246, 0.168); | |||
} | |||
.alert-screen-navigator > li.active, | |||
.main-screen-navigator > li.active { | |||
background: rgba(5, 106, 246, 0.668); | |||
} | |||
.dark-table { | |||
height: 100%; | |||
background: transparent; | |||
@@ -0,0 +1,147 @@ | |||
<script setup> | |||
import { computed, onMounted, ref, watch } from "vue"; | |||
import * as echarts from "echarts"; | |||
import option from "./rateOption"; | |||
const props = defineProps({ | |||
isOnlyChild: { | |||
type: Boolean, | |||
default: false, | |||
}, | |||
rawData: { | |||
type: Object, | |||
default: () => { | |||
return { | |||
targetProduction: 0, | |||
nowProduction: 0, | |||
targetYield: 0, | |||
nowYield: 0, | |||
}; | |||
}, | |||
}, | |||
}); | |||
watch( | |||
() => props.isOnlyChild, | |||
(newVal) => { | |||
reInitChart(); | |||
} | |||
); | |||
const rate = computed(() => { | |||
const _rate = ((props.rawData.nowYield / props.rawData.targetYield) * 100) | |||
.toFixed(2) | |||
.toString(); | |||
return [parseInt(_rate), _rate.split(".")[1]]; | |||
}); | |||
const chart = ref(null); | |||
const rateChartRef = ref(null); | |||
function reInitChart() { | |||
if (chart.value) chart.value.dispose(); | |||
const _chart = echarts.init(rateChartRef.value); | |||
_chart.setOption(option); | |||
chart.value = _chart; | |||
} | |||
onMounted(() => { | |||
reInitChart(); | |||
}); | |||
</script> | |||
<template> | |||
<div class="chart rate-chart"> | |||
<div ref="rateChartRef" class="chart-container"></div> | |||
<div :class="['fake-chart-title', isOnlyChild ? 'is-only-child' : '']"> | |||
<span class="integer-part">{{ rate[0] }}.</span> | |||
<span class="decimal-part">{{ rate[1] }}%</span> | |||
</div> | |||
<div class="text-intro"> | |||
<div class="text-intro__item"> | |||
<span class="legend-box green"></span> | |||
<span>当前成品率: 32.73%</span> | |||
</div> | |||
<div class="text-intro__item"> | |||
<span class="legend-box blue"></span> | |||
<span>目标成品率: 90.72%</span> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.rate-chart { | |||
height: 240px; | |||
flex-grow: 1; | |||
position: relative; | |||
} | |||
.chart-container { | |||
margin: auto; | |||
width: 320px; | |||
height: 100%; | |||
background: "#0f01"; | |||
position: relative; | |||
} | |||
.fake-chart-title { | |||
user-select: none; | |||
position: absolute; | |||
top: 30%; | |||
left: 32%; | |||
} | |||
.fake-chart-title.is-only-child { | |||
left: 42%; | |||
} | |||
.fake-chart-title > .integer-part { | |||
font-size: 48px; | |||
color: #fff; | |||
} | |||
.fake-chart-title > .decimal-part { | |||
font-size: 32px; | |||
color: #fff; | |||
} | |||
.text-intro { | |||
position: absolute; | |||
height: auto; | |||
width: 240px; | |||
bottom: 18px; | |||
left: 0; | |||
right: 0; | |||
margin: 0 auto; | |||
padding: 12px; | |||
display: flex; | |||
flex-direction: column; | |||
gap: 12px; | |||
align-items: center; | |||
user-select: none; | |||
} | |||
.text-intro__item { | |||
font-size: 20px; | |||
display: flex; | |||
align-items: center; | |||
gap: 10px; | |||
} | |||
.legend-box { | |||
width: 16px; | |||
height: 16px; | |||
border-radius: 4px; | |||
} | |||
.green { | |||
background: #4cf0e8; | |||
} | |||
.blue { | |||
background: #1065ff; | |||
} | |||
</style> |
@@ -0,0 +1,93 @@ | |||
<script setup> | |||
import { onMounted, ref } from "vue"; | |||
import * as echarts from "echarts"; | |||
import getOptions from "./yieldOption"; | |||
const props = defineProps({ | |||
rawData: { | |||
type: Object, | |||
default: () => { | |||
return { | |||
targetProduction: 0, | |||
nowProduction: 0, | |||
targetYield: 0, | |||
nowYield: 0, | |||
}; | |||
}, | |||
}, | |||
}); | |||
const chart = ref(null); | |||
const yieldChartRef = ref(null); | |||
function reInitChart() { | |||
if (chart.value) chart.value.dispose(); | |||
const _chart = echarts.init(yieldChartRef.value); | |||
_chart.setOption(getOptions(props.rawData)); | |||
chart.value = _chart; | |||
} | |||
onMounted(() => { | |||
reInitChart(); | |||
}); | |||
</script> | |||
<template> | |||
<div class="chart yield-chart"> | |||
<div ref="yieldChartRef" class="chart-container"></div> | |||
<div class="text-intro"> | |||
<div class="text-intro__item"> | |||
<span class="legend-box green"></span> | |||
<span>当前产量: {{ rawData.nowProduction }}片</span> | |||
</div> | |||
<div class="text-intro__item"> | |||
<span>目标产量: {{ rawData.targetProduction }}片</span> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<style scoped> | |||
.yield-chart { | |||
height: 240px; | |||
flex-grow: 1; | |||
position: relative; | |||
} | |||
.chart-container { | |||
margin: auto; | |||
width: 320px; | |||
height: 100%; | |||
background: "#0f01"; | |||
} | |||
.text-intro { | |||
position: absolute; | |||
height: auto; | |||
width: 220px; | |||
bottom: 18px; | |||
left: 0; | |||
right: 0; | |||
margin: 0 auto; | |||
padding: 12px; | |||
display: flex; | |||
flex-direction: column; | |||
gap: 12px; | |||
align-items: center; | |||
user-select: none; | |||
} | |||
.text-intro__item { | |||
font-size: 20px; | |||
display: flex; | |||
align-items: center; | |||
gap: 10px; | |||
} | |||
.legend-box { | |||
width: 16px; | |||
height: 16px; | |||
border-radius: 4px; | |||
background: #4cf0e8; | |||
} | |||
</style> |
@@ -0,0 +1,146 @@ | |||
const radius = ["58%", "72%"]; | |||
const radius2 = ["45%", "58%"]; | |||
const grid = { | |||
top: 0, | |||
left: 24, | |||
right: 24, | |||
bottom: 32, | |||
}; | |||
const title = { | |||
// 由外部负责展示,此处占位 | |||
text: " ", | |||
left: "50%", | |||
top: "30%", | |||
textAlign: "center", | |||
textStyle: { | |||
fontWeight: 400, | |||
fontSize: 48, | |||
color: "#fffd", | |||
}, | |||
subtext: "当前成品率\u2002", | |||
subtextStyle: { | |||
fontSize: 20, | |||
fontWeight: 100, | |||
color: "#fffd", | |||
align: "right", | |||
}, | |||
}; | |||
const tooltip = { | |||
// trigger: "item", | |||
show: false, | |||
}; | |||
const legend = { | |||
top: "5%", | |||
left: "center", | |||
}; | |||
const bgSerie = { | |||
type: "pie", | |||
radius: radius, | |||
center: ["50%", "40%"], | |||
emptyCircleStyle: { | |||
color: "#042c5f33", | |||
}, | |||
}; | |||
const dataSerie = { | |||
type: "pie", | |||
radius: radius, | |||
center: ["50%", "40%"], | |||
avoidLabelOvervlap: false, | |||
label: { | |||
show: false, | |||
// position: "center", | |||
}, | |||
labelLine: { | |||
show: false, | |||
}, | |||
data: [ | |||
{ | |||
value: 90, | |||
name: "当前成品率", | |||
selected: false, | |||
itemStyle: { | |||
borderJoin: "round", | |||
borderCap: "round", | |||
borderWidth: 12, | |||
borderRadius: "50%", | |||
color: { | |||
type: "linear", | |||
x: 1, | |||
y: 0, | |||
x2: 0, | |||
y2: 1, | |||
colorStops: [ | |||
{ offset: 0, color: "#4CF0E811" }, | |||
{ offset: 1, color: "#4CF0E8" }, | |||
], | |||
}, | |||
}, | |||
}, | |||
{ | |||
value: 20, | |||
name: "-", | |||
itemStyle: { color: "transparent" }, | |||
label: { show: false }, | |||
}, | |||
], | |||
}; | |||
const targetSerie = { | |||
type: "pie", | |||
radius: radius2, | |||
center: ["50%", "40%"], | |||
avoidLabelOvervlap: false, | |||
label: { | |||
show: false, | |||
}, | |||
labelLine: { | |||
show: false, | |||
}, | |||
data: [ | |||
{ | |||
value: 90, | |||
name: "目标成品率", | |||
selected: false, | |||
itemStyle: { | |||
borderJoin: "round", | |||
borderCap: "round", | |||
borderWidth: 12, | |||
borderRadius: "50%", | |||
color: { | |||
type: "linear", | |||
x: 1, | |||
y: 0, | |||
x2: 0, | |||
y2: 1, | |||
colorStops: [ | |||
{ offset: 0, color: "#1065ff66" }, | |||
{ offset: 1, color: "#1065ff" }, | |||
], | |||
}, | |||
}, | |||
}, | |||
{ | |||
value: 20, | |||
name: "-", | |||
itemStyle: { color: "transparent" }, | |||
label: { show: false }, | |||
}, | |||
], | |||
}; | |||
export default (data) => { | |||
dataSerie.data[0].value = data.nowYield; | |||
dataSerie.data[1].value = data.targetYield - data.nowYield; | |||
return { | |||
tooltip, | |||
title, | |||
grid, | |||
series: [ | |||
// background | |||
bgSerie, | |||
// actual data | |||
dataSerie, | |||
// target data | |||
targetSerie, | |||
], | |||
}; | |||
}; |
@@ -0,0 +1,102 @@ | |||
const radius = ["55%", "70%"]; | |||
const grid = { | |||
top: 0, | |||
left: 24, | |||
right: 24, | |||
bottom: 32, | |||
}; | |||
const title = { | |||
text: "75%", | |||
left: "50%", | |||
top: "30%", | |||
textAlign: "center", | |||
textStyle: { | |||
fontWeight: 400, | |||
fontSize: 48, | |||
color: "#fffd", | |||
}, | |||
subtext: "当前产量\u2002", | |||
subtextStyle: { | |||
fontSize: 20, | |||
fontWeight: 100, | |||
color: "#fffd", | |||
align: "right", | |||
}, | |||
}; | |||
const tooltip = { | |||
// trigger: "item", | |||
show: false, | |||
}; | |||
const legend = { | |||
top: "5%", | |||
left: "center", | |||
}; | |||
const bgSerie = { | |||
type: "pie", | |||
radius: radius, | |||
center: ["50%", "40%"], | |||
emptyCircleStyle: { | |||
color: "#042c5f33", | |||
}, | |||
}; | |||
const dataSerie = { | |||
type: "pie", | |||
radius: radius, | |||
center: ["50%", "40%"], | |||
avoidLabelOvervlap: false, | |||
label: { | |||
show: false, | |||
// position: "center", | |||
}, | |||
labelLine: { | |||
show: false, | |||
}, | |||
data: [ | |||
{ | |||
value: 90, | |||
name: "当前产量", | |||
selected: false, | |||
itemStyle: { | |||
borderJoin: "round", | |||
borderCap: "round", | |||
borderWidth: 12, | |||
borderRadius: "50%", | |||
color: { | |||
type: "linear", | |||
x: 1, | |||
y: 0, | |||
x2: 0, | |||
y2: 1, | |||
colorStops: [ | |||
{ offset: 0, color: "#4CF0E811" }, | |||
{ offset: 1, color: "#4CF0E8" }, | |||
], | |||
}, | |||
}, | |||
}, | |||
{ | |||
value: 20, | |||
name: "-", | |||
itemStyle: { color: "transparent" }, | |||
label: { show: false }, | |||
}, | |||
], | |||
}; | |||
export default (data) => { | |||
title.text = | |||
(100 * (+data.nowProduction / +data.targetProduction)).toFixed(0) + "%"; | |||
dataSerie.data[0].value = data.nowProduction; | |||
dataSerie.data[1].value = data.targetProduction - data.nowProduction; | |||
return { | |||
tooltip, | |||
title, | |||
grid, | |||
series: [ | |||
// background | |||
bgSerie, | |||
// actual data | |||
dataSerie, | |||
], | |||
}; | |||
}; |
@@ -0,0 +1,102 @@ | |||
<script setup> | |||
import { ref, onMounted, nextTick } from "vue"; | |||
import * as echarts from "echarts"; | |||
import Container from "../Base/Container.vue"; | |||
import { useWsStore } from "../../store"; | |||
import setupFn from "./LineTodayOptions"; | |||
const show = ref(false); | |||
const chartContainer = ref(null); | |||
const chartInstance = ref(null); | |||
const store = useWsStore(); | |||
onMounted(() => { | |||
chartContainer.value.classList.add("h-full"); | |||
const d = loadData(store.data2.dailyTarget); | |||
// const d = loadData([ | |||
// { | |||
// targetProduction: 100, | |||
// nowProduction: 66, | |||
// targetYield: 13, | |||
// nowYield: 3, | |||
// }, | |||
// ]); | |||
if (!d) { | |||
show.value = false; | |||
if (chartInstance.value) { | |||
chartInstance.value.dispose(); | |||
chartInstance.value = null; | |||
} | |||
} else { | |||
if (!chartInstance.value) | |||
chartInstance.value = echarts.init(chartContainer.value); | |||
setupFn(chartInstance.value, d); | |||
show.value = true; | |||
} | |||
}); | |||
// 订阅 | |||
store.$subscribe((mutation, state) => { | |||
const d = loadData(state.data2.dailyTarget); | |||
// const d = loadData([ | |||
// { | |||
// targetProduction: 100, | |||
// nowProduction: 66, | |||
// targetYield: 13, | |||
// nowYield: 3, | |||
// }, | |||
// ]); | |||
if (!d) { | |||
show.value = false; | |||
if (chartInstance.value) { | |||
chartInstance.value.dispose(); | |||
chartInstance.value = null; | |||
} | |||
} else { | |||
if (!chartInstance.value) | |||
chartInstance.value = echarts.init(chartContainer.value); | |||
setupFn(chartInstance.value, d); | |||
show.value = true; | |||
} | |||
}); | |||
// utils | |||
function loadData(dailyTarget) { | |||
if ( | |||
dailyTarget == undefined || | |||
// dailyTarget?.length == 0 || | |||
!dailyTarget[0] | |||
) { | |||
return null; | |||
} | |||
return { | |||
targetProduction: dailyTarget[0].targetProduction, | |||
nowProduction: dailyTarget[0].nowProduction, | |||
targetYield: dailyTarget[0].targetYield, | |||
nowYield: dailyTarget[0].nowYield, | |||
}; | |||
} | |||
</script> | |||
<template> | |||
<Container class="chart" title="本日生产线情况" icon="cube"> | |||
<div | |||
ref="chartContainer" | |||
class="chart-chart" | |||
:style="{ opacity: show ? 1 : 0 }" | |||
></div> | |||
<p v-show="!show" class="empty-data-hint">暂无数据</p> | |||
</Container> | |||
</template> | |||
<style scoped> | |||
.chart { | |||
/* height: 300px; */ | |||
height: auto; | |||
} | |||
.chart-chart { | |||
height: 100%; | |||
} | |||
</style> |
@@ -1,79 +1,83 @@ | |||
<script setup> | |||
import { ref, onMounted, nextTick } from "vue"; | |||
import * as echarts from "echarts"; | |||
import Container from "../Base/Container.vue"; | |||
import { useWsStore } from "../../store"; | |||
import setupFn from "./LineTodayOptions"; | |||
import YieldChart from "../Chart/YieldChart.vue"; | |||
import RateChart from "../Chart/RateChart.vue"; | |||
const show = ref(false); | |||
const chartContainer = ref(null); | |||
const chartInstance = ref(null); | |||
const displayProductionChart = ref(false); | |||
const displayRateChart = ref(false); | |||
const websocketData = ref(null); | |||
const store = useWsStore(); | |||
onMounted(() => { | |||
chartContainer.value.classList.add("h-full"); | |||
const d = loadData(store.data2.dailyTarget); | |||
// const d = loadData([ | |||
// { | |||
// targetProduction: 100, | |||
// nowProduction: 66, | |||
// targetYield: 13, | |||
// nowYield: 3, | |||
// }, | |||
// ]); | |||
if (!d) { | |||
show.value = false; | |||
if (chartInstance.value) { | |||
chartInstance.value.dispose(); | |||
chartInstance.value = null; | |||
} | |||
websocketData.value = loadData([ | |||
{ | |||
targetProduction: 120, | |||
nowProduction: 10, | |||
targetYield: 13, | |||
nowYield: 3, | |||
}, | |||
]); | |||
// const d = loadData(state.data2.dailyTarget); | |||
console.log("websocketData==>", websocketData); | |||
if (!websocketData.value) { | |||
displayProductionChart.value = false; | |||
displayRateChart.value = false; | |||
} else { | |||
if (!chartInstance.value) | |||
chartInstance.value = echarts.init(chartContainer.value); | |||
setupFn(chartInstance.value, d); | |||
show.value = true; | |||
/** 阻止 targetProduction == 0 */ | |||
if (!websocketData.value.targetProduction) { | |||
displayProductionChart.value = false; | |||
} else { | |||
displayProductionChart.value = true; | |||
} | |||
/** 阻止 targetYield == 0 */ | |||
if (!websocketData.value.targetYield) { | |||
displayRateChart.value = false; | |||
} else { | |||
displayRateChart.value = true; | |||
} | |||
} | |||
}); | |||
// 订阅 | |||
store.$subscribe((mutation, state) => { | |||
const d = loadData(state.data2.dailyTarget); | |||
// const d = loadData([ | |||
// { | |||
// targetProduction: 100, | |||
// nowProduction: 66, | |||
// targetYield: 13, | |||
// nowYield: 3, | |||
// }, | |||
// ]); | |||
if (!d) { | |||
show.value = false; | |||
if (chartInstance.value) { | |||
chartInstance.value.dispose(); | |||
chartInstance.value = null; | |||
} | |||
} else { | |||
if (!chartInstance.value) | |||
chartInstance.value = echarts.init(chartContainer.value); | |||
setupFn(chartInstance.value, d); | |||
show.value = true; | |||
} | |||
}); | |||
// store.$subscribe((mutation, state) => { | |||
// // const d = loadData([ | |||
// // { | |||
// // targetProduction: 100, | |||
// // nowProduction: 66, | |||
// // targetYield: 13, | |||
// // nowYield: 3, | |||
// // }, | |||
// // ]); | |||
// const d = loadData(state.data2.dailyTarget); | |||
// if (!d) { | |||
// displayProductionChart.value = false; | |||
// displayRateChart.value = false; | |||
// } else { | |||
// displayRateChart.value = false; | |||
// if (!d.targetProduction) { | |||
// displayProductionChart.value = false; | |||
// } else { | |||
// displayProductionChart.value = true; | |||
// } | |||
// } | |||
// }); | |||
// utils | |||
function loadData(dailyTarget) { | |||
if ( | |||
dailyTarget == undefined || | |||
// dailyTarget?.length == 0 || | |||
!dailyTarget[0] | |||
) { | |||
if (dailyTarget == undefined || !dailyTarget[0]) { | |||
return null; | |||
} | |||
return { | |||
// 目标产量 | |||
targetProduction: dailyTarget[0].targetProduction, | |||
// 当前产量 | |||
nowProduction: dailyTarget[0].nowProduction, | |||
// 目标成品率 | |||
targetYield: dailyTarget[0].targetYield, | |||
// 当前成品率 | |||
nowYield: dailyTarget[0].nowYield, | |||
}; | |||
} | |||
@@ -81,12 +85,34 @@ function loadData(dailyTarget) { | |||
<template> | |||
<Container class="chart" title="本日生产线情况" icon="cube"> | |||
<div | |||
<!-- <div | |||
ref="chartContainer" | |||
class="chart-chart" | |||
:style="{ opacity: show ? 1 : 0 }" | |||
></div> | |||
<p v-show="!show" class="empty-data-hint">暂无数据</p> | |||
<p v-show="!show" class="empty-data-hint">暂无数据</p> --> | |||
<div class="container-body__h-full"> | |||
<yield-chart v-if="displayProductionChart" :raw-data="websocketData" /> | |||
<rate-chart | |||
v-if="displayRateChart" | |||
:raw-data="websocketData" | |||
:isOnlyChild="!displayProductionChart" | |||
/> | |||
<p | |||
v-if="!displayProductionChart && !displayRateChart" | |||
style=" | |||
height: 100%; | |||
line-height: 350px; | |||
user-select: none; | |||
flex: 1; | |||
color: #fffc; | |||
font-size: 24px; | |||
text-align: center; | |||
" | |||
> | |||
- 暂无数据 - | |||
</p> | |||
</div> | |||
</Container> | |||
</template> | |||
@@ -99,4 +125,10 @@ function loadData(dailyTarget) { | |||
.chart-chart { | |||
height: 100%; | |||
} | |||
.container-body__h-full { | |||
height: 100%; | |||
display: flex; | |||
gap: 12px; | |||
} | |||
</style> |
@@ -27,6 +27,8 @@ const setupFn = (chart, datalist = [0.0, 0.0, 0.0, 0.0]) => { | |||
axisLabel: { | |||
fontSize: 14, | |||
color: "#fff", | |||
rotate: 32, | |||
margin: 12 | |||
}, | |||
splitLine: { | |||
show: true, | |||
@@ -27,6 +27,8 @@ const setupFn = (chart, datalist = [0.0, 0.0, 0.0, 0.0]) => { | |||
axisLabel: { | |||
fontSize: 14, | |||
color: "#fff", | |||
rotate: 32, | |||
margin: 12 | |||
}, | |||
splitLine: { | |||
show: true, | |||