Merge branch 'master' of git.picaiba.com:g7hoo/chenzhou

This commit is contained in:
gtz 2024-03-19 15:58:00 +08:00
commit aeb43c8d41
11 changed files with 825 additions and 81 deletions

View File

@ -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>

View File

@ -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;

View File

@ -0,0 +1,147 @@
<script setup>
import { computed, onMounted, ref, watch } from "vue";
import * as echarts from "echarts";
import getOptions 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(getOptions(props.rawData));
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>当前成品率: {{ props.rawData.nowYield }}%</span>
</div>
<div class="text-intro__item">
<span class="legend-box blue"></span>
<span>目标成品率: {{ props.rawData.targetYield }}%</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>

View File

@ -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>

View File

@ -0,0 +1,148 @@
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 = 100 - data.nowYield;
targetSerie.data[0].value = data.targetYield;
targetSerie.data[1].value = 100 - data.targetYield;
return {
tooltip,
title,
grid,
series: [
// background
bgSerie,
// actual data
dataSerie,
// target data
targetSerie,
],
};
};

View File

@ -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,
],
};
};

View File

@ -1,43 +1,47 @@
<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 "./LineMonthOptions";
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 refreshToken = ref(1);
const store = useWsStore();
onMounted(() => {
chartContainer.value.classList.add("h-full");
const d = loadData(store.data2.monthlyTarget);
// const d = loadData([
// websocketData.value = loadData([
// {
// targetProduction: 100,
// nowProduction: 66,
// targetYield: 13,
// nowYield: 3,
// targetProduction: 0,
// nowProduction: 10,
// targetYield: 10.34,
// nowYield: 3.11,
// },
// ]);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
websocketData.value = loadData(store.data2.monthlyTarget);
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.monthlyTarget);
// const d = loadData([
// {
// targetProduction: 100,
@ -46,30 +50,35 @@ store.$subscribe((mutation, state) => {
// nowYield: 3,
// },
// ]);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
websocketData.value = loadData(state.data2.monthlyTarget);
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 {
if (refreshToken.value > 100000) refreshToken.value = 0;
refreshToken.value += 1;
displayProductionChart.value = true;
}
/** 阻止 targetYield == 0 */
if (!websocketData.value.targetYield) {
displayRateChart.value = false;
} else {
if (refreshToken.value > 100000) refreshToken.value = 0;
refreshToken.value += 1;
displayRateChart.value = true;
}
}
});
// utils
function loadData(monthlyTarget) {
if (
monthlyTarget == undefined ||
// monthlyTarget?.length == 0 ||
!monthlyTarget[0]
) {
if (monthlyTarget == undefined || !monthlyTarget[0]) {
return null;
}
return {
targetProduction: monthlyTarget[0].targetProduction,
nowProduction: monthlyTarget[0].nowProduction,
@ -81,12 +90,39 @@ function loadData(monthlyTarget) {
<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"
:key="refreshToken + '_yield_chart'"
:raw-data="websocketData"
/>
<rate-chart
v-if="displayRateChart"
:key="refreshToken + '_rate_chart'"
: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 +135,10 @@ function loadData(monthlyTarget) {
.chart-chart {
height: 100%;
}
.container-body__h-full {
height: 100%;
display: flex;
gap: 12px;
}
</style>

View File

@ -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>

View File

@ -1,43 +1,47 @@
<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 refreshToken = ref(1);
const store = useWsStore();
onMounted(() => {
chartContainer.value.classList.add("h-full");
const d = loadData(store.data2.dailyTarget);
// const d = loadData([
// websocketData.value = loadData([
// {
// targetProduction: 100,
// nowProduction: 66,
// targetYield: 13,
// nowYield: 3,
// targetProduction: 120,
// nowProduction: 10,
// targetYield: 10.34,
// nowYield: 3.11,
// },
// ]);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
websocketData.value = loadData(store.data2.dailyTarget);
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,
@ -46,34 +50,44 @@ store.$subscribe((mutation, state) => {
// nowYield: 3,
// },
// ]);
if (!d) {
show.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
chartInstance.value = null;
}
websocketData.value = loadData(state.data2.dailyTarget);
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 {
if (refreshToken.value > 100000) refreshToken.value = 0;
refreshToken.value += 1;
displayProductionChart.value = true;
}
/** 阻止 targetYield == 0 */
if (!websocketData.value.targetYield) {
displayRateChart.value = false;
} else {
if (refreshToken.value > 100000) refreshToken.value = 0;
refreshToken.value += 1;
displayRateChart.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 +95,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 +135,10 @@ function loadData(dailyTarget) {
.chart-chart {
height: 100%;
}
.container-body__h-full {
height: 100%;
display: flex;
gap: 12px;
}
</style>

View File

@ -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,

View File

@ -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,