新增数据可视化大屏案例

This commit is contained in:
ktianc 2022-11-28 16:58:16 +08:00
parent 7bd8f8c907
commit 5b8473d1ca
10 changed files with 522 additions and 0 deletions

View File

@ -26,6 +26,7 @@
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@iconify/iconify": "^3.0.0",
"@kjgl77/datav-vue3": "^1.3.3",
"@vueuse/core": "^9.5.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.10",
@ -40,6 +41,7 @@
"lodash-es": "^4.17.21",
"mitt": "^3.0.0",
"mockjs": "^1.1.0",
"moment": "^2.29.4",
"nprogress": "^0.2.0",
"pinia": "^2.0.23",
"pinia-plugin-persist": "^1.0.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

View File

@ -28,6 +28,8 @@ import { setupRouter } from './router'
// 权限
import { setupPermission } from './directives'
import DataVVue3 from '@kjgl77/datav-vue3'
import { createApp } from 'vue'
import App from './App.vue'
@ -49,6 +51,8 @@ const setupAll = async () => {
setupPermission(app)
app.use(DataVVue3)
app.mount('#app')
}

View File

@ -0,0 +1,63 @@
<script lang="ts" setup>
import { reactive, PropType, ref, watch } from 'vue'
const props = defineProps({
centerBottomData: {
type: Array as PropType<string[][]>,
required: true
}
})
const scrollBoardRef = ref<any>(null)
const centerBottomData = ref(props.centerBottomData)
const config = reactive({
header: ['部门名称', '甲醛', 'PM2.5', 'PM10', '温度', '湿度', '更新时间'],
data: centerBottomData.value,
index: true,
columnWidth: [50],
align: ['center'],
rowNum: 6,
waitTime: 2000,
headerHeight: 40
})
watch(
() => props.centerBottomData,
(val: string[][]) => {
scrollBoardRef.value.updateRows(val)
},
{
deep: true
}
)
</script>
<template>
<div class="center-bottom-view">
<dv-scroll-board ref="scrollBoardRef" :config="config" />
</div>
</template>
<style lang="less">
.center-bottom-view {
width: 100%;
height: 100%;
margin-top: 20px;
.dv-scroll-board {
width: 100%;
height: 100%;
color: #fff;
.header {
font-size: 18px;
}
.rows .row-item {
font-size: 18px;
}
}
}
</style>

View File

@ -0,0 +1,151 @@
<script lang="ts" setup>
import { Echart } from '@/components/Echart'
import { propTypes } from '@/utils/propTypes'
import { EChartsOption } from 'echarts'
import { PropType, ref, watch, reactive } from 'vue'
import { CenterTopPropsType } from '../typers'
const props = defineProps({
centerTopData: {
type: Object as PropType<CenterTopPropsType>,
required: true
},
activeMenuName: propTypes.string
})
const lineOptions = ref({})
watch(
() => props.centerTopData,
(val: CenterTopPropsType) => {
lineOptions.value = {
xAxis: {
data: [
'6H',
'7H',
'8H',
'9H',
'10H',
'11H',
'12H',
'13H',
'14H',
'15H',
'16H',
'17H',
'18H',
'19H'
],
type: 'category'
},
textStyle: {
fontFamily: 'Microsoft YaHei',
fontSize: 20,
fontStyle: 'normal',
fontWeight: 'normal',
color: '#ecc460'
},
grid: {
left: 20,
right: 20,
bottom: 20,
top: 80,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
yAxis: [
{
type: 'value',
name: '',
min: 0,
axisLabel: {
formatter: '{value}'
}
}
],
legend: {
data: ['PM2.5', '甲醛', '温度', '湿度'],
// top: 50,
textStyle: {
color: '#c3f19d'
}
},
series: [
{
name: 'PM2.5',
type: 'bar',
color: '#bbff67',
emphasis: {
focus: 'series'
},
data: val.pm25,
showBackground: false,
barGap: 0
},
{
name: '甲醛',
type: 'bar',
color: '#6deedf',
emphasis: {
focus: 'series'
},
data: val.hcho,
showBackground: false,
barGap: 0
},
{
name: '温度',
type: 'line',
emphasis: {
focus: 'series'
},
data: val.temp
},
{
name: '湿度',
type: 'line',
emphasis: {
focus: 'series'
},
data: val.hum
}
]
}
},
{
immediate: true,
deep: true
}
)
// const lineOptions: EChartsOption = reactive()
</script>
<template>
<div class="center-top-view">
<span class="text-3xl font-bold">{{ props.activeMenuName }}</span>
<div>
<Echart :options="lineOptions" :height="400" />
</div>
</div>
</template>
<style lang="less">
.center-top-view {
width: 100%;
height: 100%;
box-shadow: 0 0 3px blue;
display: flex;
flex-direction: column;
background-color: rgba(6, 30, 93, 0.5);
border-top: 2px solid rgba(1, 153, 209, 0.5);
box-sizing: border-box;
padding: 10px 20px;
}
</style>

View File

@ -0,0 +1,95 @@
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { PropType } from 'vue'
import { LeftPropsType } from '../typers/index'
const props = defineProps({
leftData: {
type: Object as PropType<LeftPropsType>,
required: true
},
activeMenuName: propTypes.string
})
</script>
<template>
<div class="left-view">
<span class="text-3xl font-bold">{{ props.activeMenuName }}</span>
<div class="main-content">
<dv-border-box11 title="室内甲醛">
<div class="data-view">
<span class="data-title">{{ props.leftData.hcho }}ug/</span>
<span class="data-desc">提示低于80ug/m³适合长期居住</span>
</div>
</dv-border-box11>
<dv-border-box11 title="室内PM2.5">
<div class="data-view">
<span class="data-title">{{ props.leftData.pm25 }}ug/</span>
<span class="data-desc">提示低于75ug/m³适合长期居住</span>
</div>
</dv-border-box11>
<dv-border-box11 title="室内温度">
<div class="data-view">
<span class="data-title">{{ props.leftData.temp }}°C</span>
<span class="data-desc">提示当前室外温度为25°C</span>
</div>
</dv-border-box11>
<dv-border-box11 title="室内湿度">
<div class="data-view">
<span class="data-title">{{ props.leftData.hum }}%RH</span>
<span class="data-desc">提示当前室外湿度为38%RH</span>
</div>
</dv-border-box11>
</div>
</div>
</template>
<style lang="less">
.left-view {
width: 100%;
height: 100%;
box-shadow: 0 0 3px blue;
display: flex;
flex-direction: column;
background-color: rgba(6, 30, 93, 0.5);
border-top: 2px solid rgba(1, 153, 209, 0.5);
padding: 10px 20px;
.main-content {
flex: 1;
display: flex;
flex-direction: column;
margin-top: 10px;
.dv-border-box-11 {
.border-box-content {
justify-content: center;
align-items: center;
display: -webkit-flex;
}
.data-view {
position: relative;
height: 100%;
width: 100%;
justify-content: center;
display: -webkit-flex;
.data-title {
font-size: 35px;
display: block;
position: absolute;
top: 40%;
}
.data-desc {
font-size: 14px;
display: block;
position: absolute;
bottom: 10%;
}
}
}
}
}
</style>

View File

@ -0,0 +1,55 @@
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { PropType } from 'vue'
const props = defineProps({
menus: {
type: Array as PropType<string[]>,
required: true
},
activeIndex: propTypes.number
})
</script>
<template>
<div class="top-menu-view">
<dv-border-box10>
<div class="menu-item-view" v-for="(item, index) in props.menus" :key="index">
<dv-decoration7
class="animate__animated animate__fadeInDown"
v-if="index === props.activeIndex"
>
{{ item }}
</dv-decoration7>
<span v-else>{{ item }}</span>
</div>
</dv-border-box10>
</div>
</template>
<style lang="less">
.top-menu-view {
.dv-border-box-10 {
height: 80px;
overflow: hidden;
.menu-item-view {
float: left;
line-height: 80px;
margin: 0 30px;
font-size: 22px;
color: #909399;
font-weight: bold;
&:first-of-type {
margin-left: 50px;
}
.dv-decoration-7 {
height: 100%;
color: #fff;
}
}
}
}
</style>

View File

@ -0,0 +1,139 @@
<script lang="ts" setup>
import Left from './components/Left.vue'
import CenterTop from './components/CenterTop.vue'
import CenterBottom from './components/CenterBottom.vue'
import TopMenu from './components/TopMenu.vue'
import { LeftPropsType, CenterTopPropsType } from './typers/index'
import { ref, onBeforeUnmount } from 'vue'
import moment from 'moment'
const randomNumber = (min: number, max: number) => {
return Math.floor(Math.random() * (max - min)) + min
}
const randomNumberStr = (min: number, max: number) => {
return randomNumber(min, max).toString()
}
const randArray = (len: number, min: number, max: number) => {
return Array(len)
.fill(1)
.map(() => randomNumber(min, max))
}
const leftData = ref({} as LeftPropsType)
const centerBottomData = ref([] as string[][])
const centerTopData = ref({} as CenterTopPropsType)
const menus = ref([] as string[])
const activeIndex = ref(-1)
const activeMenuName = ref('')
const generateData = () => {
leftData.value = {
pm25: randomNumberStr(5, 50),
temp: randomNumberStr(5, 50),
hum: randomNumberStr(5, 50),
hcho: randomNumberStr(5, 50)
}
menus.value = [
'技术1部',
'技术2部',
'运营部',
'销售部',
'人力资源部',
'技术支持部',
'客服部',
'老板办公室'
]
centerBottomData.value = []
for (let item of menus.value) {
centerBottomData.value.push([
item,
randomNumberStr(5, 50),
randomNumberStr(5, 50),
randomNumberStr(5, 50),
randomNumberStr(5, 50),
randomNumberStr(5, 50),
moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
])
}
centerTopData.value = {
pm25: randArray(14, 10, 50),
temp: randArray(14, 10, 50),
hum: randArray(14, 10, 50),
hcho: randArray(14, 10, 50)
}
activeIndex.value++
if (activeIndex.value === menus.value.length) {
activeIndex.value = 0
}
activeMenuName.value = menus.value[activeIndex.value]
}
generateData()
const timer = setInterval(() => {
setTimeout(() => {
generateData()
}, 0)
}, 6000)
onBeforeUnmount(() => {
console.log('已清除定时器')
clearInterval(timer)
})
</script>
<template>
<div id="data-view">
<dv-full-screen-container>
<div class="flex justify-between">
<div small-bg>
<dv-decoration8 style="width: 500px; height: 60px" />
</div>
<div class="text-4xl leading-80px font-bold">
<span>办公室空气质量实时检测</span>
</div>
<div small-bg>
<dv-decoration8 :reverse="true" style="width: 500px; height: 60px" />
</div>
</div>
<div class="mx-10px my-15px">
<TopMenu :menus="menus" :activeIndex="activeIndex" />
</div>
<div class="h-1/1 overflow-hidden mb-10px ml-10px">
<div class="float-left w-20/100 h-1/1 mr-20px">
<Left :leftData="leftData" :activeMenuName="activeMenuName" />
</div>
<div class="float-left w-78/100 h-5/10">
<CenterTop :centerTopData="centerTopData" :activeMenuName="activeMenuName" />
</div>
<div class="float-left w-78/100 h-48/100">
<CenterBottom :centerBottomData="centerBottomData" />
</div>
</div>
</dv-full-screen-container>
</div>
</template>
<style lang="less">
#data-view {
width: 100%;
height: 100%;
background-color: #030409;
color: #fff;
#dv-full-screen-container {
background-image: url('@/assets/imgs/bg.png');
background-size: 100% 100%;
box-shadow: 0 0 3px blue;
display: flex;
flex-direction: column;
}
}
</style>

View File

@ -0,0 +1,13 @@
export type LeftPropsType = {
pm25: string
temp: string
hum: string
hcho: string
}
export type CenterTopPropsType = {
pm25: number[]
temp: number[]
hum: number[]
hcho: number[]
}