This commit is contained in:
duanliang
2024-12-13 10:49:43 +08:00
24 changed files with 662 additions and 23 deletions

21
package-lock.json generated
View File

@@ -12,7 +12,11 @@
"echarts": "^5.5.1", "echarts": "^5.5.1",
"pinia": "^2.2.6", "pinia": "^2.2.6",
"vue": "^3.5.13", "vue": "^3.5.13",
<<<<<<< HEAD
"vue-echarts": "^7.0.3", "vue-echarts": "^7.0.3",
=======
"vue-countup-v3": "^1.4.2",
>>>>>>> c263e68dd9f06fa54de5add6bfa1b7bdb129383d
"vue-router": "^4.4.5" "vue-router": "^4.4.5"
}, },
"devDependencies": { "devDependencies": {
@@ -2221,6 +2225,11 @@
"url": "https://github.com/sponsors/mesqueeb" "url": "https://github.com/sponsors/mesqueeb"
} }
}, },
"node_modules/countup.js": {
"version": "2.8.0",
"resolved": "https://registry.npmmirror.com/countup.js/-/countup.js-2.8.0.tgz",
"integrity": "sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ=="
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -4270,6 +4279,7 @@
} }
} }
}, },
<<<<<<< HEAD
"node_modules/vue-demi": { "node_modules/vue-demi": {
"version": "0.13.11", "version": "0.13.11",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz", "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
@@ -4313,6 +4323,17 @@
"@vue/runtime-core": { "@vue/runtime-core": {
"optional": true "optional": true
} }
=======
"node_modules/vue-countup-v3": {
"version": "1.4.2",
"resolved": "https://registry.npmmirror.com/vue-countup-v3/-/vue-countup-v3-1.4.2.tgz",
"integrity": "sha512-nRC65jBcdgwybxqztgd/WaK8ZN5T9ECPyiCFGYFMewCsvqdRVo1CtpT7JREcPNF837Fgu/izTSFiuzrIGD6w0A==",
"dependencies": {
"countup.js": "^2.6.2"
},
"peerDependencies": {
"vue": "^3.0.0"
>>>>>>> c263e68dd9f06fa54de5add6bfa1b7bdb129383d
} }
}, },
"node_modules/vue-router": { "node_modules/vue-router": {

View File

@@ -14,6 +14,7 @@
"pinia": "^2.2.6", "pinia": "^2.2.6",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-echarts": "^7.0.3", "vue-echarts": "^7.0.3",
"vue-countup-v3": "^1.4.2",
"vue-router": "^4.4.5" "vue-router": "^4.4.5"
}, },
"devDependencies": { "devDependencies": {

BIN
src/assets/images/bg-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

BIN
src/assets/images/bg-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 KiB

BIN
src/assets/images/bg-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

BIN
src/assets/images/bg-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
src/assets/images/core.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/assets/images/more.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
src/assets/images/queue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,189 @@
<template>
<div
:id="id"
:style="{
width: styleUtil.px2vw(width),
height: styleUtil.px2vh(height)
}"
/>
</template>
<script setup>
import * as echarts from 'echarts'
import styleUtil from '@/utils/styleUtil'
import { fitChartSize } from '@/utils/dataUtil'
const props = defineProps({
width: {
type: Number,
default: () => 0
},
height: {
type: Number,
default: () => 0
},
id: {
type: String,
default: () => ''
}
})
let lineChart = null
let defaultCofig = {
grid: {
left: '2%',
right: '2%',
bottom: '5%',
top: '5%',
containLabel: true
},
title: {
show: false
},
xAxis: {
boundaryGap: false,
type: 'category',
data: [],
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: '#0096FF'
}
},
axisLabel: {
fontSize: fitChartSize(12),
color: 'rgba(255,255,255,0.9)'
}
},
yAxis: {
type: 'value',
axisLabel: {
fontSize: fitChartSize(12),
color: 'rgba(255,255,255,0.9)'
},
splitLine: {
lineStyle: {
color: 'rgba(0, 150, 255,0.4)',
type: 'dashed'
}
}
},
visualMap: {
show: false,
dimension: 0,
pieces: [
{
lte: 6,
color: 'green'
},
{
gt: 6,
lte: 8,
color: 'red'
},
{
gt: 8,
lte: 14,
color: 'green'
},
{
gt: 14,
lte: 17,
color: 'red'
},
{
gt: 17,
color: 'green'
}
]
},
series: [
{
data: [],
type: 'line',
smooth: true,
symbol: 'none',
markArea: {
itemStyle: {
color: 'rgba(255, 173, 177, 0)'
},
data: [
[
{
xAxis: '10:00'
},
{
xAxis: '10:05'
},
{
xAxis: '10:10'
},
{
xAxis: '10:15'
},
{
xAxis: '10:20'
},
{
xAxis: '10:25'
},
{
xAxis: '10:30'
},
{
xAxis: '10:35'
}
]
]
},
lineStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: '#7DE7FF'
},
{
offset: 0.5,
color: '#02F9FA'
},
{
offset: 1,
color: '#009DFF'
}
])
}
}
]
}
onMounted(() => {
init()
})
const init = () => {
lineChart = echarts.init(document.getElementById(props.id))
defaultCofig.xAxis.data = [
'10:00',
'10:05',
'10:10',
'10:15',
'10:20',
'10:25',
'10:30',
'10:35'
]
defaultCofig.series[0].data = [820, 932, 901, 934, 1290, 1330, 1320, 1290]
lineChart.setOption({
...defaultCofig
})
window.addEventListener('resize', resize)
}
const resize = () => {
if (lineChart) {
lineChart.dispose()
lineChart = null
init()
}
}
</script>
<style lang="scss"></style>

View File

@@ -3,6 +3,7 @@ import { createPinia } from 'pinia'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import '@/styles/reset.css' import '@/styles/reset.css'
import '@/styles/common.scss'
const app = createApp(App) const app = createApp(App)

11
src/styles/common.scss Normal file
View File

@@ -0,0 +1,11 @@
.flex {
display: flex;
}
.align-end {
align-items: flex-end;
}
.pt-20 {
padding-top: vh(20);
}

View File

@@ -2,7 +2,12 @@
<div class="box-1"> <div class="box-1">
<div class="title">核心景区视频</div> <div class="title">核心景区视频</div>
<ul class="list"> <ul class="list">
<li class="item" v-for="(item, index) in 10" :key="index"> <li
class="item"
:style="{ backgroundImage: `url(${index == 0 ? item1 : item2})` }"
v-for="(item, index) in 10"
:key="index"
>
<p class="item-title">三峡之巅-最新异常名称</p> <p class="item-title">三峡之巅-最新异常名称</p>
<img class="item-img" src="@/assets/images/img-1.png" alt="" /> <img class="item-img" src="@/assets/images/img-1.png" alt="" />
</li> </li>
@@ -10,14 +15,20 @@
</div> </div>
</template> </template>
<script setup></script> <script setup>
import item1 from '@/assets/images/item-1.png'
import item2 from '@/assets/images/item-2.png'
console.log(item1, 'item1')
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
.box-1 { .box-1 {
margin: vh(10); margin: vh(10);
width: vw(326); width: vw(326);
border-radius: vw(2); border-radius: vw(2);
background: linear-gradient(270deg, #08366a 0%, #004192 100%); background-image: url('@/assets/images/bg-1.png');
background-size: 100% 100%;
.title { .title {
width: vw(260); width: vw(260);
@@ -61,21 +72,22 @@
.item { .item {
margin-bottom: vh(13); margin-bottom: vh(13);
background-size: 100% 100%;
&-title { &-title {
margin-bottom: vh(6);
margin-left: vw(45);
color: #fff; color: #fff;
font-size: vw(16); font-size: vw(16);
margin-bottom: vh(6);
height: vh(24); height: vh(24);
line-height: vh(24); line-height: vh(24);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
border-radius: vh(2);
background: linear-gradient(270deg, #0063ff 0%, rgba(0, 5, 25, 0) 100%);
} }
&-img { &-img {
width: 100%; width: 100%;
height: vh(164); height: vh(164);
display: block;
} }
} }
} }

View File

@@ -0,0 +1,244 @@
<template>
<div class="box-2">
<div class="header">
<div class="title"> 景区信息 </div>
<span class="more" />
</div>
<div class="flex pt-20">
<div class="item core">
<span class="title-1">全县景区数</span>
<div class="flex align-end"> <countup endVal="895" /><span class="unit"></span></div>
</div>
<div class="item queue">
<span class="title-1">核心景区数</span>
<div class="flex align-end"> <countup endVal="895" /><span class="unit"></span></div>
</div>
<div class="item congestion">
<span class="title-1">低感景区总数</span>
<div class="flex align-end"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
<div class="flex pt-20">
<div class="box">
<div class="title-2"><span>景区排队人数</span></div>
<div class="statistic">
<div class="statistic-item">
<span class="statistic-title">三峡之巅</span>
<span class="statistic-value">
<span class="prefix">排队</span>
<span class="count">100</span>
<span class="suffix"></span>
</span>
</div>
<div class="statistic-item">
<span class="statistic-title">白帝城</span>
<span class="statistic-value">通畅</span>
</div>
<div class="statistic-item">
<span class="statistic-title">天坑地缝</span>
<span class="statistic-value">通畅</span>
</div>
<div class="statistic-item">
<span class="statistic-title">永安宫</span>
<span class="statistic-value">通畅</span>
</div>
</div>
<div class="title-3"><span>景区排队人数</span></div>
<div class="pt-20">
<Line :width="370" :height="180" id="line" />
</div>
</div>
<div class="box">
<div class="title-2"><span>景区承载量</span></div>
<div class="statistic">
<div class="statistic-item">
<gauge id="gauge1" />
<span class="statistic-title">三峡之巅</span>
</div>
<div class="statistic-item">
<gauge id="gauge2" />
<span class="statistic-title">三峡之巅</span>
</div>
<div class="statistic-item">
<gauge id="gauge3" />
<span class="statistic-title">三峡之巅</span>
</div>
<div class="statistic-item">
<gauge id="gauge4" />
<span class="statistic-title">三峡之巅</span>
</div>
</div>
<div class="title-3"><span>今日景区承载量</span></div>
<div class="pt-20">
<Line :width="370" :height="180" id="line1" />
</div>
</div>
</div>
</div>
</template>
<script setup>
import countup from 'vue-countup-v3'
import gauge from './gauge.vue'
import Line from '@/components/Line/index.vue'
onMounted(() => {})
</script>
<style scoped lang="scss">
.box-2 {
margin-top: vh(120);
width: vw(800);
height: vh(950);
padding: vw(8);
box-sizing: border-box;
background-image: url('@/assets/images/bg-2.png');
background-size: 100% 100%;
.header {
position: relative;
.title {
margin: vh(10) auto;
width: vw(468);
height: vh(32);
font-weight: 800;
font-size: vw(16);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
background-image: url('@/assets/images/title-4.png');
background-size: 100% 100%;
}
.more {
position: absolute;
top: vh(0);
right: vw(20);
cursor: pointer;
width: vw(60);
height: vh(24);
background-image: url('@/assets/images/more.png');
background-size: 100% 100%;
}
}
.item {
position: relative;
flex: 1;
padding-left: vw(70);
height: vh(52);
margin: 0 vh(10);
display: flex;
align-items: center;
background-size: 100% 100%;
}
.title-1 {
position: absolute;
top: vh(-10);
font-weight: 400;
font-size: vw(14);
color: rgba(255, 255, 255, 0.7);
}
.countup-wrap {
color: #02f9fa;
font-size: vw(28);
font-weight: bold;
}
.unit {
color: #02f9fa;
font-size: vw(14);
margin-bottom: vh(4);
}
.core {
background-image: url('@/assets/images/core.png');
}
.queue {
background-image: url('@/assets/images/queue.png');
}
.congestion {
background-image: url('@/assets/images/congestion.png');
}
.box {
width: vw(384);
height: vh(360);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
&:nth-child(1) {
margin-right: vw(10);
}
.title-2 {
width: vw(253);
height: vh(28);
display: flex;
align-items: center;
background-image: url('@/assets/images/title-5.png');
background-size: 100% 100%;
& > span {
padding-left: vw(22);
font-weight: bold;
font-size: vw(15);
background-image: linear-gradient(to bottom, #ffffff 0%, #75c1ff 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 兼容WebKit内核浏览器 */
color: transparent; /* 兼容其他浏览器 */
}
}
.statistic {
display: flex;
margin-top: vh(12);
width: 100%;
height: vh(88);
background-image: url('@/assets/images/bg-4.png');
background-size: 100% 100%;
&-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
&-title {
font-size: vw(14);
color: rgba(255, 255, 255, 0.9);
}
&-value {
margin-top: vh(10);
font-weight: bold;
font-size: vw(24);
color: #02f9fa;
}
.count {
font-weight: bold;
font-size: vw(28);
color: #ff4400 !important;
}
.prefix,
.suffix {
color: #ff4400;
font-size: vw(12);
}
}
.title-3 {
position: relative;
width: vw(344);
height: vh(12);
margin-top: vh(20);
background-image: url('@/assets/images/title-6.png');
background-size: 100% 100%;
& > span {
position: absolute;
bottom: vh(4);
left: vw(20);
font-size: vw(15);
font-weight: bold;
background-image: linear-gradient(to bottom, #ffffff 0%, #0096ff 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 兼容WebKit内核浏览器 */
color: transparent; /* 兼容其他浏览器 */
}
}
}
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<div class="gauge" :id="id" />
</template>
<script setup>
import * as echarts from 'echarts'
import { fitChartSize } from '@/utils/dataUtil'
const props = defineProps({
id: {
type: String,
default: () => ''
}
})
let gaugeChart = null
onMounted(() => {
init()
})
const init = () => {
// 基于准备好的dom初始化echarts实例
gaugeChart = echarts.init(document.getElementById(props.id))
gaugeChart.setOption({
series: [
{
type: 'gauge',
startAngle: 180,
endAngle: 0,
min: 0,
max: 100,
radius: '100%',
splitNumber: 10,
center: ['50%', '60%'],
itemStyle: {
color: '#58D9F9',
shadowColor: 'rgba(0,138,255,0.45)',
shadowBlur: 10,
shadowOffsetX: 2,
shadowOffsetY: 2
},
progress: {
show: true,
roundCap: true,
width: fitChartSize(6)
},
pointer: {
show: false
},
axisLine: {
roundCap: true,
lineStyle: {
width: fitChartSize(6)
}
},
axisTick: {
show: false
},
splitLine: {
show: false
},
axisLabel: {
show: false
},
detail: {
width: '100%',
lineHeight: 20,
height: 20,
offsetCenter: [0, '20%'],
valueAnimation: true,
formatter: function (value) {
return '{value|' + value.toFixed(0) + '}{unit|%}'
},
rich: {
value: {
fontSize: fitChartSize(12),
fontWeight: 'bolder',
color: '#02F9FA'
},
unit: {
fontSize: fitChartSize(12),
color: '#02F9FA'
}
}
},
data: [
{
value: 80
}
]
}
]
})
window.addEventListener('resize', resize)
}
const resize = () => {
if (gaugeChart) {
gaugeChart.dispose()
init()
}
}
</script>
<style lang="scss" scoped>
.gauge {
width: vw(70);
height: vh(50);
}
</style>

View File

@@ -2,20 +2,29 @@
<main class="wrapper"> <main class="wrapper">
<box1 /> <box1 />
<div class="header"> <div class="header">
<ul class="nav-left">
<li class="nav-left-item">安全</li>
<li class="nav-left-item">景区</li>
<li class="nav-left-item">交通</li>
</ul>
<div class="title">奉节县旅游指挥调度中心</div> <div class="title">奉节县旅游指挥调度中心</div>
<ul> <ul class="nav-right">
<li></li> <li class="nav-right-item">停车</li>
<li></li> <li class="nav-right-item">工单</li>
<li></li> <li class="nav-right-item">舆情</li>
<li class="nav-right-item">酒店</li>
</ul> </ul>
<box4 /> <box4 />
</div> </div>
<box2 />
</main> </main>
</template> </template>
<script setup> <script setup>
import box1 from './components/box-1.vue' import box1 from './components/box-1.vue'
import box4 from './components/box-4.vue' import box4 from './components/box-4.vue'
import box2 from './components/box-2.vue'
onMounted(() => {}) onMounted(() => {})
</script> </script>
@@ -26,6 +35,9 @@
height: 100vh; height: 100vh;
overflow: hidden; overflow: hidden;
background-color: #0a254b; background-color: #0a254b;
.header {
position: absolute;
left: vw(326);
.title { .title {
width: vw(3133); width: vw(3133);
height: vh(120); height: vh(120);
@@ -40,5 +52,44 @@
background-image: url('@/assets/images/title.png'); background-image: url('@/assets/images/title.png');
background-size: 100% 100%; background-size: 100% 100%;
} }
.nav-left {
position: absolute;
left: vw(630);
top: vh(34);
display: flex;
&-item {
cursor: pointer;
margin-left: vh(-10);
width: vw(210);
height: vh(56);
padding-top: vh(10);
font-weight: 600;
font-size: vw(28);
text-align: center;
color: rgba(208, 236, 255, 0.9);
background-image: url('@/assets/images/title-2.png');
background-size: 100% 100%;
}
}
.nav-right {
position: absolute;
right: vw(424);
top: vh(34);
display: flex;
&-item {
cursor: pointer;
margin-right: vh(-10);
width: vw(210);
height: vh(56);
padding-top: vh(10);
font-weight: 600;
font-size: vw(28);
text-align: center;
color: rgba(208, 236, 255, 0.9);
background-image: url('@/assets/images/title-3.png');
background-size: 100% 100%;
}
}
}
} }
</style> </style>