You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
7.9 KiB
Vue
350 lines
7.9 KiB
Vue
<template>
|
|
<div class="dashboard">
|
|
<!-- Top Cards -->
|
|
<div class="card-row">
|
|
<StatCard title="订单" :items="[
|
|
{ label: '销售总量', value: 1000 },
|
|
{ label: '未出库量', value: 100, color: 'red' },
|
|
{ label: '已出库量', value: 100, color: 'green' }
|
|
]" />
|
|
<StatCard title="工单" :items="[
|
|
{ label: '工单总数', value: 10 },
|
|
{ label: '未投产', value: 10, color: 'red' },
|
|
{ label: '已投产', value: 10, color: 'blue' },
|
|
{ label: '已完工', value: 10, color: 'green' }
|
|
]" />
|
|
<StatCard title="任务" :items="[
|
|
{ label: '任务总数', value: 10 },
|
|
{ label: '未开工', value: 10, color: 'red' },
|
|
{ label: '执行中', value: 10, color: 'blue' },
|
|
{ label: '已完工', value: 10, color: 'green' }
|
|
]" />
|
|
<StatCard title="产量" :items="[
|
|
{ label: '产量总量', value: 10 },
|
|
{ label: '完成率', value: '10%' },
|
|
{ label: '良品率', value: '90%', color: 'green' }
|
|
]" />
|
|
</div>
|
|
|
|
<!-- Middle -->
|
|
<div class="middle-row">
|
|
<div class="panel">
|
|
<div class="head">
|
|
<div>实时报工</div>
|
|
</div>
|
|
<el-timeline style="max-width: 600px">
|
|
<el-timeline-item :timestamp="item.time" placement="top" v-for="item in timeline ">
|
|
<div class="content">
|
|
<div style="color: #9EBDFE;font-size: 0.8vw;margin-bottom: 0.4vw;">{{ item.user }} {{ item.action }}</div>
|
|
<div>工单号:{{ item.order }}</div>
|
|
<div>物料:{{ item.material }}</div>
|
|
<div>良品 {{ item.good }} / 不良 {{ item.bad }}</div>
|
|
</div>
|
|
</el-timeline-item>
|
|
</el-timeline>
|
|
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<div class="head">
|
|
<div>生产预警</div>
|
|
</div>
|
|
<div class="th">
|
|
<div class="td">工单计划截止日</div>
|
|
<div class="td" style="text-align: right">计划数</div>
|
|
</div>
|
|
<div v-for="(d,i) in warning" :key="d.day" class="progress-item">
|
|
<div class="td" style="width: 50px">{{ d.day }}</div>
|
|
<div class="td" style="width: calc(100% - 130px); ">
|
|
<el-progress
|
|
:text-inside="true"
|
|
:stroke-width="6"
|
|
:percentage="(d.value / d.max)*100"
|
|
:status="i > 0 ? 'exception':''"
|
|
>
|
|
<div></div>
|
|
</el-progress>
|
|
<div :style="{left:((d.value / d.max)*100)+'%'}" class="progressNum" :class="i > 0 ? 'exception':''">
|
|
{{ d.value }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="td" style="width: 80px;padding-right: 12px;text-align: right">{{ d.max }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="margin-right: 0" class="panel">
|
|
<div class="head">
|
|
<div>物料库存</div>
|
|
</div>
|
|
<div class="stock-summary">
|
|
<div class="stockCard">
|
|
<div class="num">100</div>
|
|
<div class="text">物料数</div>
|
|
</div>
|
|
<div class="stockCard">
|
|
<div class="num">1000.1234</div>
|
|
<div class="text">库存总量</div>
|
|
</div>
|
|
<div class="stockCard">
|
|
<div class="num">20</div>
|
|
<div class="text">超库存上限</div>
|
|
</div>
|
|
<div class="stockCard">
|
|
<div class="num">10</div>
|
|
<div class="text">超库存下限</div>
|
|
</div>
|
|
</div>
|
|
<div ref="pieRef" class="chart"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bottom Chart -->
|
|
<div style="margin-right: 0" class="panel">
|
|
<div class="head">
|
|
<div>物料库存趋势</div>
|
|
</div>
|
|
<div ref="lineRef" class="chart"></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted, ref } from 'vue';
|
|
import * as echarts from 'echarts';
|
|
import StatCard from '@/components/StatCard.vue';
|
|
|
|
const timeline = [
|
|
{ time: '10:00', user: '张三', action: '印刷', order: 'GD2033322222', material: '电子标签001', good: 100, bad: 10 },
|
|
{ time: '09:00', user: '张三', action: '印刷', order: 'GD2033322222', material: '电子标签001', good: 100, bad: 10 }
|
|
];
|
|
|
|
const warning = [
|
|
{ day: '1天内', value: 1000, max: 2000 },
|
|
{ day: '2天内', value: 1000, max: 3000 },
|
|
{ day: '3天内', value: 1000, max: 3000 },
|
|
{ day: '4天内', value: 1000, max: 3000 },
|
|
{ day: '5天内', value: 1000, max: 3000 }
|
|
];
|
|
|
|
const pieRef = ref(null);
|
|
const lineRef = ref(null);
|
|
|
|
onMounted(() => {
|
|
const pie = echarts.init(pieRef.value);
|
|
pie.setOption({
|
|
tooltip: {},
|
|
series: [{
|
|
type: 'pie',
|
|
radius: '70%',
|
|
label: {
|
|
formatter: '{b}\n{c}'
|
|
},
|
|
itemStyle: {
|
|
borderRadius: 5 // 扇区圆角半径
|
|
},
|
|
data: [
|
|
{
|
|
name: '原料',
|
|
value: 100,
|
|
itemStyle: {
|
|
color: '#4A63EA'
|
|
}
|
|
},
|
|
{
|
|
name: '成品',
|
|
value: 150,
|
|
itemStyle: {
|
|
color: '#86AAF4'
|
|
}
|
|
},
|
|
{
|
|
name: '半成品',
|
|
value: 208,
|
|
itemStyle: {
|
|
color: '#698CF1'
|
|
}
|
|
},
|
|
{
|
|
name: '原辅料',
|
|
value: 149,
|
|
itemStyle: {
|
|
color: '#CDDFF9'
|
|
}
|
|
}
|
|
]
|
|
}]
|
|
});
|
|
|
|
const line = echarts.init(lineRef.value);
|
|
line.setOption({
|
|
tooltip: {},
|
|
grid:{
|
|
top:40,
|
|
left:80,
|
|
right:20,
|
|
bottom:30,
|
|
},
|
|
legend: { data: ['入库', '出库'] },
|
|
xAxis: { type: 'category', data: ['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04', '2022-01-05'] },
|
|
yAxis: { type: 'value' },
|
|
series: [
|
|
{
|
|
name: '入库',
|
|
type: 'bar',
|
|
data: [100, 140, 230, 100, 130],
|
|
itemStyle: {
|
|
color: '#4A63EA'
|
|
}
|
|
},
|
|
{
|
|
name: '出库',
|
|
type: 'bar',
|
|
data: [150, 100, 200, 150, 100],
|
|
itemStyle: {
|
|
color: '#85ABF4'
|
|
}
|
|
},
|
|
{
|
|
name: '库存',
|
|
type: 'line',
|
|
data: [100, 140, 230, 100, 130],
|
|
itemStyle: {
|
|
color: '#4B64EA'
|
|
},
|
|
label: {
|
|
show: true,
|
|
color: '#3D59E9'
|
|
}
|
|
}
|
|
]
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.dashboard {
|
|
padding: 16px;
|
|
background: #f5f7fa;
|
|
}
|
|
|
|
.card-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
}
|
|
|
|
.middle-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr 2fr;
|
|
margin-top: 16px;
|
|
}
|
|
|
|
.panel {
|
|
background: #fff;
|
|
padding: 16px;
|
|
border-radius: 8px;
|
|
margin-right: 16px;
|
|
|
|
.head {
|
|
color: #5291FD;
|
|
font-weight: bold;
|
|
border-bottom: 1px solid #ddd;
|
|
margin-bottom: 0.5vw;
|
|
padding-bottom: 0.5vw;
|
|
}
|
|
}
|
|
|
|
.timeline {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
.timeline li {
|
|
display: flex;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.time {
|
|
width: 60px;
|
|
color: #409eff;
|
|
}
|
|
|
|
.content {
|
|
background: #f0f4ff;
|
|
padding: 8px;
|
|
border-radius: 6px;
|
|
flex: 1;
|
|
}
|
|
|
|
.progress-item {
|
|
width: 100%;
|
|
margin-bottom: 1.6vw;
|
|
|
|
.td {
|
|
display: inline-block;
|
|
font-size: 14px;
|
|
color: #666;
|
|
position: relative;
|
|
}
|
|
|
|
.progressNum {
|
|
position: absolute;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
text-align: center;
|
|
width: 2vw;
|
|
height: 1vw;
|
|
color: #fff;
|
|
line-height: 1vw;
|
|
background-color: #419EFF;
|
|
border-radius: 1vw;
|
|
}
|
|
|
|
.exception {
|
|
background-color: #F56C6C;
|
|
}
|
|
}
|
|
|
|
.chart {
|
|
width: 100%;
|
|
height: 260px;
|
|
}
|
|
|
|
.stock-summary {
|
|
width: 100%;
|
|
height: 5vw;
|
|
|
|
.stockCard {
|
|
display: inline-block;
|
|
width: 25%;
|
|
height: 100%;
|
|
text-align: center;
|
|
|
|
.num {
|
|
height: 60%;
|
|
line-height: 3vw;
|
|
font-size: 1.4vw;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.text {
|
|
height: 40%;
|
|
color: #666;
|
|
}
|
|
}
|
|
}
|
|
|
|
.th {
|
|
width: 100%;
|
|
margin-bottom: 1vw;
|
|
|
|
.td {
|
|
width: 50%;
|
|
display: inline-block;
|
|
padding-right: 12px;
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
}
|
|
</style>
|