feat:重新对接首页接口

This commit is contained in:
zjc
2025-01-14 19:01:09 +08:00
parent 880db48579
commit ebac43f818
28 changed files with 2250 additions and 2133 deletions

View File

@@ -11,8 +11,8 @@ export function getWeatherApi() {
// 核心景区视频
export function getVideoListApi(data) {
return request({
url: 'http://36.138.38.16:6180/fjtcc-api/api/video/list',
method: 'get',
url: '/api/largeScreen/video/list',
method: 'post',
params: data
})
}
@@ -20,8 +20,8 @@ export function getVideoListApi(data) {
// 刷新播放地址
export function postRefreshApi(data) {
return request({
url: 'http://36.138.38.16:6180/fjtcc-api/api/video/refresh',
method: 'POST',
url: '/api/largeScreen/video/refresh',
method: 'post',
data
})
}

View File

@@ -2,6 +2,11 @@ import axios from 'axios'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
//
let devToken =
'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImE1OWFmNWYwLTU3OWItNDJkNy1hZDJhLTY0Y2JlODA5ZWI1NiJ9.BTxvu6jUWbN0qONWf5K6VzXopE8T8qXzKuX-mij21VJT4U0LdgnqToyqeNDQ2OyJ6cvpdJBzQ9mEEb-dnwrTpQ'
let proToken =
'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImE1OWFmNWYwLTU3OWItNDJkNy1hZDJhLTY0Y2JlODA5ZWI1NiJ9.dSLZekRsYf5ZZDCYqFEOgHTi4GeHD0m10gGHXrbgpc-hD52Zt7Vw05cxhQ-lzY29yf2IxH0oYi28DBfHdtf9SA'
const router = useRouter()
/**
@@ -9,11 +14,11 @@ const router = useRouter()
*/
const instance = axios.create({
// baseURL: 'http://36.138.38.16:6180/fjtcc-api',
baseURL: 'http://36.138.38.16:8001/fjtcc-api',
// baseURL: 'http://36.138.38.16:8001/fjtcc-api',
baseURL: ' http://172.22.15.170/fjtcc-api',
timeout: 100000,
headers: {
Authorization:
'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImE1OWFmNWYwLTU3OWItNDJkNy1hZDJhLTY0Y2JlODA5ZWI1NiJ9.BTxvu6jUWbN0qONWf5K6VzXopE8T8qXzKuX-mij21VJT4U0LdgnqToyqeNDQ2OyJ6cvpdJBzQ9mEEb-dnwrTpQ',
Authorization: proToken,
'Content-Type': 'application/json;charset=UTF-8'
}
})

View File

@@ -55,16 +55,13 @@
list.value.forEach(async (item, index) => {
var video = document.getElementById(`video${index}`)
let res1 = await postRefreshApi({
type: 'hls',
businessVideoDisplayPosition: item.businessVideoDisplayPosition,
cameraIndexCode: item.cameraIndexCode
})
let hlsUrl = res1.data.hlsUrl.replace(
'http://172.22.15.170:8050',
'http://36.138.38.16:6150'
)
item.hlsUrl = hlsUrl
item.hlsUrl = res1.data.hlsUrl
const hls = new Hls()
hls.loadSource(hlsUrl)
hls.loadSource(res1.data.hlsUrl)
hls.attachMedia(video)
hls.on(Hls.Events.MANIFEST_PARSED, () => {
video.play()

View File

@@ -62,7 +62,7 @@
height: vh(28);
font-weight: 400;
font-size: vw(12);
color: #0096ff;
color: #fff;
display: flex;
background: linear-gradient(180deg, rgba(0, 99, 255, 0) 0%, #0063ff 100%);
& > div {

View File

@@ -31,9 +31,9 @@
const toOrderNum = (num) => {
num = num.toString()
orderNum.value = num.split('') // 将其便变成数据,渲染至滚动数组
nextTick(() => {
setTimeout(() => {
setNumberTransform()
})
}, 500)
}
watch(
() => props.count,

View File

@@ -1,8 +1,10 @@
import { ref } from 'vue'
import { useHomeStore } from '@/stores/home'
const homeStore = useHomeStore()
export function useWebSocket(url) {
let socket = ref(null) // socket对象
let data = ref(null) // 存储接收到的数据
let isConnected = ref(false) // 是否连接成功
const connectWebSocket = () => {
@@ -21,9 +23,47 @@ export function useWebSocket(url) {
}
socket.value.onmessage = (message) => {
// 处理接收到的消息
console.log('Received message:', JSON.parse(message.data))
if (JSON.parse(message.data)) {
data.value = JSON.parse(message.data)
let data = JSON.parse(message.data)
console.log(data, '接收到的消息')
switch (data.type) {
case 'userPortrait':
homeStore.setUserPortraitData(data.data)
break
case 'admission':
homeStore.setScenicData(data)
break
case 'queuingInScenicSpots':
homeStore.setScenicQueueData(data)
break
case 'queuingScenicSpots':
homeStore.setScenicBearData(data)
break
case 'visitorInfo':
homeStore.setVisitorInfoData(data.data)
break
case 'visitorDataInfo':
homeStore.setVisitorInfoList(data.data)
break
case 'baiduMap':
homeStore.setBaiduMapData(data.data)
break
case 'wordkOrderlist':
homeStore.setWordkOrderList(data.data)
break
case 'trafficInformation':
homeStore.setTrafficInfoData(data)
break
case 'carStopInfo':
homeStore.setCarStopInfoData(data)
break
case 'carShipData':
homeStore.setCarShipData(data.data)
break
case 'hotelData':
homeStore.setHotelData(data.data)
break
}
}
}
socket.value.onclose = () => {
@@ -47,5 +87,5 @@ export function useWebSocket(url) {
}
})
return { socket, data, isConnected, connectWebSocket, sendMessage }
return { socket, isConnected, connectWebSocket, sendMessage }
}

View File

@@ -1,12 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

143
src/stores/home.js Normal file
View File

@@ -0,0 +1,143 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
export const useHomeStore = defineStore('home', () => {
// 景区信息数据
let scenicData = ref({
scenicSpot: [
{ value: 0, name: '全县景区数量' },
{ value: 0, name: '核心景区数' },
{ value: 0, name: '低感景区总数' }
],
data: [
{ name: '当日购票量:', value: 0 },
{ name: '未来3天购票量', value: 0 },
{ name: '3天后购票量', value: 0 }
]
})
// 景区排队数据
let scenicQueueData = ref({
dataList: [],
info: []
})
// 景区承载量
let scenicBearData = ref({
dataList: [],
info: []
})
// 用户画像数据
let userPortraitData = ref({
genderRate: [],
provinceRate: [],
channel: [],
ageRate: []
})
// 景区游客统计
let visitorInfoData = ref({
total_count_this_year: 0,
total_count_today: 0,
total_count_today_within_three_hours: 0
})
// 景区拥堵情况统计
let visitorInfoList = ref([])
// 百度地图数据
let baiduMapData = ref(null)
// 工单列表
let wordkOrderList = ref([])
// 交通信息
let trafficInfoData = ref({
data: {
congestion: [],
congestionList: [],
countItem: {
max_congestion_duration: 0,
now_yongdu_sum: 0,
yongdu_luduan_count: 0,
yongdu_sum: 0
},
countRate: [],
timeRate: []
},
info: {
dqydld: 0,
ldzs: 0,
zdydsc: 0,
zydcs: 0
}
})
// 停车信息
let carStopInfoData = ref({
countInfo: { ckzs: 0, ysycws: 0, zcws: 0 },
dataList: [],
dataList1: [],
dataList2: [],
spotInfo: []
})
// 车船信息
let carShipData = ref(null)
// 酒店数据
let hotelData = ref(null)
const setVisitorInfoList = (val) => {
visitorInfoList.value = val
}
const setHotelData = (val) => {
hotelData.value = val
}
const setCarShipData = (val) => {
carShipData.value = val
}
const setCarStopInfoData = (val) => {
carStopInfoData.value = val
}
const setTrafficInfoData = (val) => {
trafficInfoData.value = val
}
const setVisitorInfoData = (val) => {
visitorInfoData.value = val
}
const setScenicBearData = (val) => {
scenicBearData.value = val
}
const setScenicQueueData = (val) => {
scenicQueueData.value = val
}
const setWordkOrderList = (val) => {
wordkOrderList.value = val
}
const setBaiduMapData = (val) => {
baiduMapData.value = val
}
const setScenicData = (val) => {
scenicData.value = val
}
const setUserPortraitData = (val) => {
userPortraitData.value = val
}
return {
scenicData,
userPortraitData,
baiduMapData,
wordkOrderList,
scenicQueueData,
scenicBearData,
visitorInfoData,
visitorInfoList,
trafficInfoData,
carStopInfoData,
carShipData,
hotelData,
setScenicData,
setUserPortraitData,
setBaiduMapData,
setWordkOrderList,
setScenicQueueData,
setScenicBearData,
setVisitorInfoData,
setVisitorInfoList,
setTrafficInfoData,
setCarStopInfoData,
setCarShipData,
setHotelData
}
})

View File

@@ -5,18 +5,19 @@
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import { useEchart } from '@/hooks/echart'
import { useHomeStore } from '@/stores/home'
const homeStore = useHomeStore()
const { id, setOption } = useEchart()
const homeData = inject('homeData')
let params = null
watch(
() => homeData.value?.userPortrait?.ageRate,
() => {
init()
}
() => homeStore.userPortraitData?.ageRate,
(val) => {
if (val.length > 0) init()
},
{ immediate: true }
)
const init = () => {
if (!params) {
@@ -30,7 +31,7 @@
itemWidth: fitChartSize(8),
itemGap: fitChartSize(10),
formatter: function (name) {
let obj = homeData.value?.userPortrait?.ageRate.find((item) => item.name == name)
let obj = homeStore.userPortraitData?.ageRate.find((item) => item.name == name)
return '{name|' + name + '} {value|' + obj.value + '}{value|%}'
},
textStyle: {
@@ -63,7 +64,7 @@
color: '#D3F0FE',
fontSize: fitChartSize(12)
},
data: homeData.value?.userPortrait?.ageRate || []
data: homeStore.userPortraitData?.ageRate || []
},
{
type: 'pie',
@@ -81,7 +82,7 @@
color: '#D3F0FE',
fontSize: fitChartSize(12)
},
data: homeData.value?.userPortrait?.ageRate || []
data: homeStore.userPortraitData?.ageRate || []
},
{
type: 'pie',

View File

@@ -6,13 +6,13 @@
</template>
</Title1>
<div class="list flex pt-20">
<div class="item" v-for="(item, index) in scenicSpot" :key="index">
<div class="item" v-for="(item, index) in homeStore.scenicData.scenicSpot" :key="index">
<img v-if="index == 0" class="item-icon" src="@/assets/images/core.png" />
<img v-if="index == 1" class="item-icon" src="@/assets/images/queue.png" />
<img v-if="index == 2" class="item-icon" src="@/assets/images/congestion.png" />
<span class="item-label">{{ item.name }}</span>
<div class="item-value flex align-end rela">
<countup :end-val="item.value || 0" /><span class="unit"></span>
<countup :end-val="item.value" /><span class="unit"></span>
</div>
</div>
</div>
@@ -20,90 +20,44 @@
<div class="box">
<Title2 title="景区排队人数" />
<div class="statistic">
<div class="statistic-item">
<span class="statistic-title">三峡之巅</span>
<span class="statistic-value">
<div
class="statistic-item"
v-for="(item, index) in homeStore.scenicQueueData.info"
:key="index"
>
<span class="statistic-title">{{ item.name }}</span>
<span v-if="item.value > 0" class="statistic-value">
<span class="prefix">排队</span>
<span class="value">100</span>
<!-- <span class="value">{{ item.value }}</span> -->
<countup class="value" :end-val="item.value" />
<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>
<span v-else class="statistic-value">{{ item.value }}</span>
</div>
</div>
<div class="flex rela">
<Title3 title="景区排队人数" />
<div class="dropdown">
<Dropdown
label="选择排队景区"
:options="[
{ label: '山峡之巅', value: 100 },
{ label: '白帝城', value: 100 },
{ label: '龙桥河', value: 100 }
]"
/>
</div>
</div>
<div class="pt-20">
<Line
:width="370"
:height="140"
:config="{ legend: false }"
:data="[
{
name: '企业数',
data: [64, 159, 112, 86, 151, 131, 118, 232, 23, 64, 159, 112, 86, 151, 131, 118]
}
]"
:xAxisData="[
'12-16 10:00',
'12-16 14:00',
'12-16 16:00',
'12-16 22:00',
'12-17 02:00',
'12-17 06:00',
'12-17 10:00',
'12-17 14:00',
'12-17 16:00',
'12-16 22:00',
'12-18 02:00',
'12-18 06:00',
'12-8 10:00',
'12-18 14:00',
'12-18 16:00',
'12-18 20:00'
]"
:data="scenicQueueList"
:xAxisData="scenicQueueXAxisData"
/>
</div>
</div>
<div class="box">
<Title2 title="景区承载量" />
<div class="statistic">
<div class="statistic-item">
<gauge :value="80" />
<span class="statistic-title">三峡之巅</span>
</div>
<div class="statistic-item">
<gauge :value="50" />
<span class="statistic-title">三峡之巅</span>
</div>
<div class="statistic-item">
<gauge :value="50" />
<span class="statistic-title">三峡之巅</span>
</div>
<div class="statistic-item">
<gauge :value="50" />
<span class="statistic-title">三峡之巅</span>
<div
class="statistic-item"
v-for="(item, index) in homeStore.scenicBearData.info"
:key="index"
>
<gauge :value="item.value" />
<span class="statistic-title">{{ item.name }}</span>
</div>
</div>
<Title3 title="今日景区承载量" />
@@ -112,30 +66,8 @@
:width="370"
:height="140"
:config="{ legend: false }"
:data="[
{
name: '企业数',
data: [64, 159, 112, 86, 151, 131, 118, 232, 23, 64, 159, 112, 86, 151, 131, 118]
}
]"
:xAxisData="[
'12-16 10:00',
'12-16 14:00',
'12-16 16:00',
'12-16 22:00',
'12-17 02:00',
'12-17 06:00',
'12-17 10:00',
'12-17 14:00',
'12-17 16:00',
'12-16 22:00',
'12-18 02:00',
'12-18 06:00',
'12-8 10:00',
'12-18 14:00',
'12-18 16:00',
'12-18 20:00'
]"
:data="scenicBearList"
:xAxisData="scenicBearXAxisData"
/>
</div>
</div>
@@ -145,7 +77,7 @@
<Title2 title="景区购票数" />
<div class="ticket-wrap">
<img src="@/assets/images/ticket.png" />
<div v-for="(item, index) in admission" :key="index">
<div v-for="(item, index) in homeStore.scenicData.data" :key="index">
<span class="label">{{ item.name }}</span><countup :end-val="item.value" />
</div>
</div>
@@ -156,10 +88,10 @@
<div class="age-box">
<Title3 title="年龄/性别占比" />
<div class="mt-8"> <age /></div>
<div class="count">总人数<countup :end-val="ageRateTotal" /></div>
<div v-if="ageRateTotal > 0" class="count">总人数:<countup :end-val="ageRateTotal" /></div>
<div
class="cell pt-20"
v-for="(item, index) in homeData?.userPortrait.genderRate"
v-for="(item, index) in homeStore?.userPortraitData.genderRate"
:key="index"
>
<img v-if="item.name == '男'" class="icon" src="@/assets/images/man.png" />
@@ -179,14 +111,18 @@
</div>
</div>
</div>
<div class="box-1">
<Title3 title="客源地分析TOP5" />
<top />
</div>
<div class="box-1">
<Title3 title="购票来源" />
<div class="count">游客总数<countup :end-val="channelTotal" /></div>
<ticket :list="channelData" />
<div v-if="channelTotal > 0" class="count">
游客总数<countup :end-val="channelTotal" />
</div>
<ticket />
</div>
</div>
</div>
@@ -198,41 +134,39 @@
import gauge from './gauge.vue'
import ticket from './ticket.vue'
import countup from 'vue-countup-v3'
import { useHomeStore } from '@/stores/home'
const homeData = inject('homeData')
const homeStore = useHomeStore()
// 景区信息
const scenicSpot = computed(() => {
if (homeData.value) return homeData.value?.scenicSpot
return [
{ value: 0, name: '全县景区数量' },
{ value: 0, name: '核心景区数' },
{ value: 0, name: '低感景区总数' }
]
// 今日景区承载量
const scenicBearList = computed(() => {
return [{ data: homeStore.scenicBearData.dataList.map((item) => item.value) }]
})
// 景区购票数
const admission = computed(() => {
if (homeData.value) return homeData.value?.admission
return [
{ name: '当日购票量:', value: 0 },
{ name: '未来3天购票量', value: 0 },
{ name: '3天后购票量', value: 0 }
]
const scenicBearXAxisData = computed(() => {
return homeStore.scenicBearData.dataList.map((item) => item.name)
})
// 年龄占比
// 今日景区排队量
const scenicQueueList = computed(() => {
return [{ data: homeStore.scenicQueueData.dataList.map((item) => item.value) }]
})
const scenicQueueXAxisData = computed(() => {
return homeStore.scenicQueueData.dataList.map((item) => item.name)
})
// 年龄占比 - 游客总数
const ageRateTotal = computed(() => {
return homeData.value?.userPortrait.genderRate.reduce(
return homeStore?.userPortraitData?.genderRate.reduce(
(total, current) => Number(current.count) + total,
0
)
})
// 年龄占比
const channelData = computed(() => {
if (homeData.value) return homeData.value?.userPortrait.channel
return []
})
// 购票来源 - 游客总数
const channelTotal = computed(() => {
return homeData.value?.userPortrait?.channel.reduce(
return homeStore.userPortraitData?.channel.reduce(
(total, current) => Number(current.count) + total,
0
)
@@ -326,14 +260,16 @@
justify-content: center;
}
&-title {
font-size: vw(18);
color: rgba(255, 255, 255, 0.9);
font-size: vw(16);
color: #fff;
}
&-value {
margin-top: vh(10);
font-weight: bold;
font-size: vw(24);
font-size: vw(20);
color: #02f9fa;
display: flex;
align-items: flex-end;
}
.value {
font-weight: bold;
@@ -344,6 +280,7 @@
.suffix {
color: #ff4400;
font-size: vw(12);
margin-bottom: vh(4);
}
}
}
@@ -366,7 +303,7 @@
}
}
.count {
margin: vw(20) vw(20) 0 vw(20);
margin: vh(10) vw(20) 0 vw(20);
height: vh(24);
font-weight: bold;
font-size: vw(14);

View File

@@ -5,37 +5,26 @@
<div class="left">
<div class="item">
<div class="label">今年总游客数</div>
<scroll-number :count="visitorInfo.total_count_this_year" prefix="1" />
<scroll-number :count="homeStore.visitorInfoData.total_count_this_year" prefix="1" />
</div>
<div class="item">
<div class="label">全县景区总游客人数</div>
<scroll-number :count="visitorInfo.total_count_today" prefix="2" />
<scroll-number :count="homeStore.visitorInfoData.total_count_today" prefix="2" />
</div>
<div class="item">
<div class="label">总在园人数</div>
<scroll-number :count="visitorInfo.total_count_today_within_three_hours" prefix="3" />
<scroll-number
:count="homeStore.visitorInfoData.total_count_today_within_three_hours"
prefix="3"
/>
</div>
</div>
<div class="right">
<div class="item">
<div class="label">安全度</div>
<div class="value">安全</div>
</div>
<div class="item">
<div class="label">舒适度</div>
<div class="value">舒适</div>
</div>
<div class="item">
<div class="label">接待情况</div>
<div class="value">排队</div>
</div>
<div class="item">
<div class="label">交通拥挤度</div>
<div class="value">舒适</div>
</div>
<div class="item">
<div class="label">停车场负荷度</div>
<div class="value">超负荷</div>
<div class="item" v-for="(item, index) in homeStore.visitorInfoList" :key="index">
<div class="label">{{ item.name }}</div>
<div :class="[item.type == 1 ? 'value--error' : 'value--primary']">{{
item.value
}}</div>
</div>
</div>
</div>
@@ -104,28 +93,23 @@
</div>
</div>
</div>
<div class="right">
<div class="item">
<span class="item-tag--warning">重要</span>
<p class="content">
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
</p>
</div>
<div class="item">
<span class="item-tag--error">紧急</span>
<p class="content">
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
</p>
</div>
<div class="item">
<span class="item-tag--primary">普通</span>
<p class="content">
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
</p>
</div>
<div>
<vue3-seamless-scroll
class="right"
:list="homeStore.wordkOrderList"
:limitScrollNum="3"
:hover="true"
:step="0.2"
:wheel="true"
:isWatch="true"
>
<div class="item" v-for="(item, index) in homeStore.wordkOrderList" :key="index">
<span :class="`item-tag--${item.level}`">{{ item.level_text }}</span>
<p class="content">
{{ item.title }}
</p>
</div>
</vue3-seamless-scroll>
</div>
</div>
</div>
@@ -133,6 +117,7 @@
<script setup>
import countup from 'vue-countup-v3'
import { Vue3SeamlessScroll } from 'vue3-seamless-scroll'
import ScrollNumber from '@/components/ScrollNumber/index.vue'
import { getSpotListApi, getBaiduMapCrowdedApi } from '@/api/home'
@@ -142,22 +127,14 @@
import icon11 from '@/assets/images/icon-11.png'
import { useMap } from '@/hooks/map'
import { useHomeStore } from '@/stores/home'
const homeStore = useHomeStore()
let emit = defineEmits(['switch-spot'])
const { map, initMap, addMarker } = useMap()
const homeData = inject('homeData')
const visitorInfo = computed(() => {
if (homeData.value) return homeData.value?.visitorInfo.data
return {
total_count_this_year: 0,
total_count_today: 0,
total_count_today_within_three_hours: 0
}
})
let spotList = ref([])
let list = ref([
@@ -185,13 +162,13 @@
const getSpotList = async () => {
let res = await getSpotListApi()
spotList.value = res.data
let res1 = await getBaiduMapCrowdedApi({
nodeId: res.data[0].nodeid
})
// let res1 = await getBaiduMapCrowdedApi({
// nodeId: res.data[0].nodeid
// })
}
watch(
() => homeData.value?.baiduMap,
() => homeStore?.baiduMapData,
(val) => {
if (val) init(val)
},
@@ -245,7 +222,7 @@
background-size: 100% 100%;
.left {
display: flex;
width: vw(890);
width: vw(740);
margin-top: vh(20);
}
.right {
@@ -264,8 +241,15 @@
.value {
font-weight: bold;
font-size: vw(28);
color: #02f9fa;
line-height: vh(33);
&--error {
@extend .value;
color: #ff4400;
}
&--primary {
@extend .value;
color: #02f9fa;
}
}
}
.countup-wrap {
@@ -331,7 +315,7 @@
width: 100%;
height: vh(120);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
background-size: 100% 400%;
.left {
flex: 1;
display: flex;
@@ -400,13 +384,14 @@
}
}
.right {
padding: vh(10) vw(24);
margin-top: vh(8);
padding: vh(10) vw(10);
height: vh(106);
width: vw(1040);
overflow: hidden;
.item {
display: flex;
margin-bottom: vh(12);
&:nth-last-child(1) {
margin-bottom: 0;
}
margin-bottom: vh(10);
&-tag {
padding: 0 vw(16);
font-weight: bold;
@@ -417,22 +402,22 @@
justify-content: center;
border-radius: vw(2);
}
&-tag--warning {
&-tag--important {
@extend .item-tag;
background: #feae00;
}
&-tag--error {
&-tag--warn {
@extend .item-tag;
background: #d9011b;
}
&-tag--primary {
&-tag--normal {
@extend .item-tag;
background: #2380fb;
}
.content {
margin-left: vw(4);
padding: 0 vw(10);
width: vw(900);
width: vw(950);
height: vh(24);
line-height: vh(24);
white-space: nowrap;

View File

@@ -3,30 +3,30 @@
<Title1 title="交通信息" />
<div class="traffic-info flex justify-evenly pt-10 pb-20">
<div class="cell">
<img class="icon" :src="icon1" alt="" />
<img class="icon" src="@/assets/images/icon-1.png" alt="" />
<div>
<countup :end-val="countItems.now_yongdu_sum" />
<countup :end-val="homeStore.trafficInfoData.info.ldzs" />
<div class="label">路段总数</div>
</div>
</div>
<div class="cell">
<img class="icon" :src="icon2" alt="" />
<img class="icon" src="@/assets/images/icon-2.png" alt="" />
<div>
<countup :end-val="countItems.yongdu_luduan_count" />
<countup :end-val="homeStore.trafficInfoData.info.dqydld" />
<div class="label">当前拥堵路段</div>
</div>
</div>
<div class="cell">
<img class="icon" :src="icon3" alt="" />
<img class="icon" src="@/assets/images/icon-3.png" alt="" />
<div>
<countup :end-val="countItems.yongdu_sum" />
<countup :end-val="homeStore.trafficInfoData.info.zydcs" />
<div class="label">总拥堵次数</div>
</div>
</div>
<div class="cell">
<img class="icon" :src="icon4" alt="" />
<img class="icon" src="@/assets/images/icon-4.png" alt="" />
<div>
<countup :end-val="countItems.max_congestion_duration" />
<countup :end-val="homeStore.trafficInfoData.info.zdydsc" />
<div class="label">最大拥堵时长 </div>
</div>
</div>
@@ -47,13 +47,13 @@
<div class="box">
<div class="pt-10">
<Title3 title="拥堵次数占比" />
<jam-count :list="countRate" />
<jam-count :list="homeStore.trafficInfoData.data.countRate" />
</div>
</div>
<div class="box">
<div class="pt-10">
<Title3 title="拥堵时长" />
<jam-duration :list="timeRate" />
<jam-duration :list="homeStore.trafficInfoData.data.timeRate" />
</div>
</div>
</div>
@@ -63,36 +63,27 @@
<img class="icon" src="@/assets/images/icon-5.png" alt="" />
<div>
<div class="label">车库总数</div>
<countup class="value" :end-val="500" />
<countup class="value" :end-val="homeStore.carStopInfoData.countInfo.ckzs" />
</div>
<div>
<div class="label">总车位数</div>
<countup class="value" :end-val="500" />
<countup class="value" :end-val="homeStore.carStopInfoData.countInfo.zcws" />
</div>
<div>
<div class="label">已使用车位数</div>
<countup class="value" :end-val="500" />
<countup class="value" :end-val="homeStore.carStopInfoData.countInfo.ysycws" />
</div>
</div>
<div>
<div>
<div class="label">三峡之巅</div>
<div class="value error">已满</div>
</div>
<div>
<div class="label">白帝城</div>
<div class="value error">已满</div>
</div>
<div>
<div class="label">天坑地缝</div>
<div class="value success">空余</div>
</div>
<div>
<div class="label">永安宫</div>
<div class="value success">空余</div>
<div v-for="item in homeStore.carStopInfoData.spotInfo">
<div class="label">{{ item.name }}</div>
<div class="value" :class="{ error: item.type == 1, success: item.type == 0 }">
{{ item.value }}
</div>
</div>
</div>
</div>
<div class="flex pt-10">
<div class="box-1">
<div class="pt-10">
@@ -102,30 +93,8 @@
:width="250"
:height="150"
:config="{ legend: false }"
:data="[
{
name: '企业数',
data: [64, 159, 112, 86, 151, 131, 118, 232, 23, 64, 159, 112, 86, 151, 131, 118]
}
]"
:xAxisData="[
'12-16 10:00',
'12-16 14:00',
'12-16 16:00',
'12-16 22:00',
'12-17 02:00',
'12-17 06:00',
'12-17 10:00',
'12-17 14:00',
'12-17 16:00',
'12-16 22:00',
'12-18 02:00',
'12-18 06:00',
'12-8 10:00',
'12-18 14:00',
'12-18 16:00',
'12-18 20:00'
]"
:data="parkData"
:xAxisData="parkXAxisData"
/>
</div>
</div>
@@ -133,16 +102,17 @@
<div class="box-1">
<div class="pt-10">
<Title3 title="车源地" />
<traffic />
<vehicle-source :list="homeStore.carStopInfoData.dataList1" />
</div>
</div>
<div class="box-1">
<div class="pt-10">
<Title3 title="景区停车场空位" />
<vacancy />
<vacancy :list="homeStore.carStopInfoData.dataList2" />
</div>
</div>
</div>
<div class="flex">
<div class="flex-1">
<Title1 title="车船信息" class="title1" />
@@ -151,13 +121,14 @@
<Title1 title="酒店信息" class="title1" />
</div>
</div>
<div class="flex">
<div class="car-ship">
<div class="mb-6">
<div class="car">
<div class="label">车总数</div>
<div class="flex align-center">
<countup class="value" :end-val="homeData?.carShipData?.car?.count || 0" />
<countup class="value" :end-val="homeStore.carShipData?.car?.count || 0" />
<span class="unit"></span>
</div>
</div>
@@ -169,16 +140,18 @@
</div>
<div class="content">
<vue3-seamless-scroll
:list="homeData?.carShipData?.car.info"
v-model="carMove"
:list="homeStore.carShipData?.car?.info"
:limitScrollNum="3"
:hover="true"
:step="0.2"
:copy-num="0"
:wheel="true"
:isWatch="true"
>
<div
class="cell"
v-for="(item, index) in homeData?.carShipData?.car.info"
v-for="(item, index) in homeStore.carShipData?.car?.info"
:key="index"
>
<div>{{ item.name }}</div>
@@ -193,7 +166,7 @@
<div class="ship">
<div class="label">船总数</div>
<div class="flex align-center">
<countup class="value" :end-val="homeData?.carShipData?.ship?.count || 0" />
<countup class="value" :end-val="homeStore.carShipData?.ship?.count || 0" />
<span class="unit"></span>
</div>
</div>
@@ -205,16 +178,18 @@
</div>
<div class="content">
<vue3-seamless-scroll
:list="homeData?.carShipData?.car.info"
v-model="shipMove"
:list="homeStore.carShipData?.ship?.info"
:limitScrollNum="3"
:hover="true"
:step="0.2"
:copy-num="0"
:wheel="true"
:isWatch="true"
>
<div
class="cell"
v-for="(item, index) in homeData?.carShipData?.ship.info"
v-for="(item, index) in homeStore.carShipData?.ship?.info"
:key="index"
>
<div>{{ item.name }}</div>
@@ -230,17 +205,17 @@
<div>
<div class="item">
<div class="label">酒店总数</div>
<countup class="value" :end-val="homeData?.hotelData.info.hotel_count || 0" />
<countup class="value" :end-val="homeStore.hotelData?.info?.hotel_count || 0" />
</div>
<div class="item">
<div class="label">房间总数</div>
<countup class="value" :end-val="homeData?.hotelData.info.total_room_count || 0" />
<countup class="value" :end-val="homeStore.hotelData?.info?.total_room_count || 0" />
</div>
<div class="item">
<div class="label">总入住</div>
<countup
class="value success"
:end-val="homeData?.hotelData.info.total_guest_count || 0"
:end-val="homeStore.hotelData?.info?.total_guest_count || 0"
/>
</div>
<div class="item">
@@ -248,7 +223,7 @@
<div class="flex align-center">
<countup
class="value success"
:end-val="homeData?.hotelData.info.occupancy_rate || 0"
:end-val="homeStore.hotelData?.info?.occupancy_rate || 0"
/>
<span class="suffix">%</span>
</div>
@@ -258,7 +233,7 @@
<div class="occupancy">
<Title3 title="酒店入住人数及入住率" />
</div>
<occupancy />
<occupancy :list="homeStore.hotelData?.list" />
</div>
</div>
</div>
@@ -270,51 +245,32 @@
import jamCount from './jam-count.vue'
import vacancy from './vacancy.vue'
import occupancy from './occupancy.vue'
import traffic from './traffic.vue'
import vehicleSource from './vehicle-source.vue'
import countup from 'vue-countup-v3'
import icon1 from '@/assets/images/icon-1.png'
import icon2 from '@/assets/images/icon-2.png'
import icon3 from '@/assets/images/icon-3.png'
import icon4 from '@/assets/images/icon-4.png'
import { Vue3SeamlessScroll } from 'vue3-seamless-scroll'
import { useHomeStore } from '@/stores/home'
const homeData = inject('homeData')
const homeStore = useHomeStore()
const carMove = computed(() => {
return homeStore.carShipData?.car?.info.length > 3
})
const shipMove = computed(() => {
return homeStore.carShipData?.ship?.info.length > 3
})
const parkData = computed(() => {
return [{ data: homeStore.carStopInfoData.dataList.map((item) => item.value) }]
})
const parkXAxisData = computed(() => {
return homeStore.carStopInfoData.dataList.map((item) => item.name)
})
const congestionData = computed(() => {
if (homeData.value) {
return [{ data: homeData.value?.trafficInformation?.congestion.map((item) => item.value) }]
}
return []
return [{ data: homeStore.trafficInfoData.data.congestion.map((item) => item.value) }]
})
const congestionXAxisData = computed(() => {
if (homeData.value) {
return homeData.value?.trafficInformation?.congestion.map((item) => item.name)
}
return []
})
const countItems = computed(() => {
if (homeData.value) {
return homeData.value?.trafficInformation?.countItem
}
return {
max_congestion_duration: 0, // 最大拥堵时长
now_yongdu_sum: 0,
yongdu_luduan_count: 0,
yongdu_sum: 0
}
})
const countRate = computed(() => {
if (homeData.value) {
return homeData.value?.trafficInformation?.countRate
}
return []
})
const timeRate = computed(() => {
if (homeData.value) {
return homeData.value?.trafficInformation?.timeRate
}
return []
return homeStore.trafficInfoData.data.congestion.map((item) => item.name)
})
</script>
@@ -466,7 +422,7 @@
flex: 1;
font-weight: 400;
font-size: vw(12);
color: #52b8ff;
color: #fff;
}
}
.content {
@@ -492,9 +448,12 @@
line-height: vh(27);
text-align: center;
background: #074686;
&:nth-child(2n + 1) {
&:nth-child(odd) {
background: rgba(0, 150, 255, 0.1);
}
&:nth-child(even) {
background: #074686;
}
& > div {
flex: 1;
}

View File

@@ -1,3 +1,4 @@
<!-- 景区承载量 -->
<template>
<div class="gauge" :id="id" />
</template>
@@ -23,9 +24,14 @@
watch(
() => props.value,
() => {
init()
}
(val) => {
if (val) {
nextTick(() => {
init()
})
}
},
{ immediate: true }
)
const init = () => {
@@ -97,12 +103,12 @@
},
rich: {
value: {
fontSize: fitChartSize(18),
fontSize: fitChartSize(14),
fontWeight: 'bolder',
color: '#02F9FA'
},
unit: {
fontSize: fitChartSize(18),
fontSize: fitChartSize(14),
color: '#02F9FA'
}
}
@@ -120,10 +126,6 @@
}
setOption(params)
}
onMounted(() => {
init()
})
</script>
<style lang="scss" scoped>

View File

@@ -1,3 +1,4 @@
<!-- 拥堵次数占比 -->
<template>
<div class="jam-count" :id="id" />
</template>
@@ -15,7 +16,8 @@
const { id, setOption } = useEchart()
var colorList = ['#FDC40A', '#FF5232', '#50F0A6']
let params = null
const colorList = ['#FDC40A', '#FF5232', '#50F0A6']
watch(
() => props.list,
@@ -27,7 +29,7 @@
}
)
const setSeriesData = () => {
const getSeriesData = () => {
return props.list.map((item) => {
return {
name: item.name,
@@ -41,75 +43,80 @@
}, 0)
}
const init = () => {
setOption({
color: colorList,
grid: {
left: '4%',
right: '4%',
top: '4%',
bottom: '4%',
containLabel: true
},
legend: {
orient: 'horizontal',
x: 'center',
bottom: '-2%',
itemHeight: fitChartSize(16),
itemWidth: fitChartSize(16),
itemGap: fitChartSize(10),
formatter: (name) => {
let obj = props.list.find((item) => item.name == name)
return '{name|' + name + '} {value|' + obj?.count + '}{value|%}'
if (!params) {
params = {
color: colorList,
grid: {
left: '4%',
right: '4%',
top: '4%',
bottom: '4%',
containLabel: true
},
textStyle: {
rich: {
name: {
color: '#fff',
fontSize: fitChartSize(12)
},
value: {
color: '#00D5F6',
fontSize: fitChartSize(12)
}
}
}
},
series: [
{
type: 'pie',
center: ['50%', '40%'],
radius: ['45%', '60%'],
itemStyle: {
borderWidth: fitChartSize(4),
borderColor: '#093672'
legend: {
orient: 'horizontal',
x: 'center',
bottom: '-3%',
itemHeight: fitChartSize(12),
itemWidth: fitChartSize(12),
itemGap: fitChartSize(6),
formatter: (name) => {
let obj = props.list.find((item) => item.name == name)
return '{name|' + name + '} {value|' + obj?.count + '}{value|%}'
},
label: {
show: true,
position: 'center',
fontWeight: 'bold',
formatter: function (o) {
return `{label|拥堵次数}` + '\n' + `{value|${calcCount()}}`
},
textStyle: {
rich: {
label: {
color: '#7894A8',
padding: [0, 0, 5, 0],
name: {
color: '#fff',
fontSize: fitChartSize(12)
},
value: {
color: '#fff',
fontSize: fitChartSize(18),
fontWeight: 'bold'
color: '#00D5F6',
fontSize: fitChartSize(12)
}
}
},
labelLine: {
show: false
},
data: setSeriesData()
}
]
})
}
},
series: [
{
type: 'pie',
center: ['50%', '40%'],
radius: ['45%', '60%'],
itemStyle: {
borderWidth: fitChartSize(4),
borderColor: '#093672'
},
label: {
show: true,
position: 'center',
fontWeight: 'bold',
formatter: function (o) {
return `{label|拥堵次数}` + '\n' + `{value|${calcCount()}}`
},
rich: {
label: {
color: '#7894A8',
padding: [0, 0, 5, 0],
fontSize: fitChartSize(12)
},
value: {
color: '#fff',
fontSize: fitChartSize(16),
fontWeight: 'bold'
}
}
},
labelLine: {
show: false
},
data: getSeriesData()
}
]
}
} else {
params.series[0].data = getSeriesData()
}
setOption(params)
}
onMounted(() => {

View File

@@ -144,7 +144,7 @@
{
name: '',
type: 'bar',
barWidth: fitChartSize(4),
barWidth: fitChartSize(8),
showBackground: true,
barBorderRadius: [0, 0, 0, 0],
backgroundStyle: {

View File

@@ -1,6 +1,6 @@
<!-- 酒店入住人数及入住率 -->
<template>
<div class="lodging-ratio" :id="id" />
<!-- 酒店入住人数及入住率 -->
<div class="occupancy" :id="id" />
</template>
<script setup>
@@ -9,24 +9,29 @@
const { id, setOption } = useEchart()
const homeData = inject('homeData')
let props = defineProps({
list: {
type: Array,
default: () => []
}
})
watch(
() => props.list,
(val) => {
if (val.length > 0) init()
},
{ immediate: true }
)
let params = null
watch(
() => homeData.value?.hotelData?.list,
() => {
init()
}
)
const setSeriesData = () => {
return homeData.value?.hotelData?.list.map((item) => {
return props.list.map((item) => {
return {
name: item.hotel_name,
value: item.occupancy_rate,
itemStyle: {
barBorderRadius: [0, 0, 0, 0],
color: {
type: 'linear',
x: 0,
@@ -40,7 +45,7 @@
},
{
offset: 1,
color: parseFloat(item.occupancy_rate) > 50 ? '#FF7021' : '#00CCFF'
color: '#FF7021'
}
]
}
@@ -48,9 +53,8 @@
}
})
}
const setCircleData = () => {
return homeData.value?.hotelData?.list.map((item) => {
const setScatterData = () => {
return props.list.map((item) => {
return {
name: item.hotel_name,
value: item.occupancy_rate,
@@ -61,7 +65,9 @@
}
})
}
const setYAxisData = () => {
return props.list.map((item) => item.occupancy_rate)
}
const init = () => {
if (!params) {
params = {
@@ -75,30 +81,40 @@
grid: {
left: '4%',
right: '4%',
top: '20%',
top: '16%',
bottom: '-10%',
containLabel: true
},
xAxis: [{ max: 100, show: false }],
xAxis: [
{
splitLine: {
show: false
},
max: 100,
type: 'value',
show: false
}
],
yAxis: [
{
type: 'category',
splitLine: {
show: false
},
axisLine: {
show: false
},
type: 'category',
axisTick: {
show: false
},
data: [],
axisLabel: {
show: false
},
data: homeData.value?.hotelData?.list.map((item) => item.hotel_name)
}
},
{
type: 'category',
inverse: true,
axisTick: 'none',
axisLine: 'none',
show: true,
@@ -111,29 +127,25 @@
padding: [0, 0, 6, 0],
inside: true,
formatter: function (value) {
return `{value|${value}} {value|%}`
return `{value|${value}}{value|%}`
},
rich: {
name: {
align: 'center',
color: '#D3E5FF',
fontSize: fitChartSize(14)
},
value: {
align: 'center',
color: '#fff',
fontWeight: 600,
fontSize: fitChartSize(14)
}
}
},
data: homeData.value?.hotelData?.list.map((item) => item.occupancy_rate)
data: setYAxisData()
}
],
series: [
{
name: '内圆',
name: '',
type: 'bar',
barWidth: 4,
barWidth: fitChartSize(8),
showBackground: true,
barBorderRadius: [0, 0, 0, 0],
backgroundStyle: {
@@ -141,8 +153,8 @@
},
label: {
show: true,
offset: [6, -10],
color: '#D3E5FF',
offset: [10, -10],
color: '#fff',
fontWeight: 500,
position: 'left',
align: 'left',
@@ -163,22 +175,22 @@
symbol: 'circle',
symbolSize: fitChartSize(10),
z: 2,
data: setCircleData(),
data: setScatterData(),
animationDelay: 500
}
]
}
} else {
params.series[0].data = setSeriesData()
params.series[1].data = setCircleData()
params.series[1].data = setScatterData()
}
setOption(params)
}
</script>
<style scoped lang="scss">
.lodging-ratio {
width: 100%;
height: vh(130);
.occupancy {
width: vw(360);
height: vh(120);
}
</style>

View File

@@ -1,30 +1,24 @@
<!-- 购票来源 -->
<template>
<div>
<div class="ticket" :id="id" />
</div>
<div class="ticket" :id="id" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import { useEchart } from '@/hooks/echart'
let props = defineProps({
list: {
type: Array,
default: () => []
}
})
import { useHomeStore } from '@/stores/home'
const homeStore = useHomeStore()
const { id, setOption } = useEchart()
const homeData = inject('homeData')
let x = 15
let y = 25
let params = null
watch(
() => props.list,
() => homeStore.userPortraitData.channel,
(val) => {
if (val.length > 0) init()
},
@@ -34,7 +28,7 @@
)
const setSeries = () => {
return props.list.map((item, index) => {
return homeStore.userPortraitData.channel.map((item, index) => {
return {
name: item.name,
type: 'pie',
@@ -71,21 +65,21 @@
y: 'bottom',
itemHeight: fitChartSize(12),
itemWidth: fitChartSize(12),
itemGap: fitChartSize(10),
itemGap: fitChartSize(6),
formatter: function (name) {
let obj = props.list.find((item) => item.name == name)
let obj = homeStore.userPortraitData.channel.find((item) => item.name == name)
return '{name|' + name + '} {value|' + obj?.value + '}{value|%}'
},
textStyle: {
rich: {
name: {
color: '#fff',
fontSize: fitChartSize(14)
fontSize: fitChartSize(12)
},
value: {
color: '#00D5F6',
fontWeight: 600,
fontSize: fitChartSize(14)
fontSize: fitChartSize(12)
}
}
}
@@ -103,7 +97,7 @@
<style scoped lang="scss">
.ticket {
width: 100%;
height: vh(200);
height: vh(230);
}
.legend {
display: flex;

View File

@@ -1,3 +1,4 @@
<!-- 客源地分析TOP5 -->
<template>
<div class="top" :id="id" />
</template>
@@ -5,11 +6,11 @@
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import { useEchart } from '@/hooks/echart'
import { useHomeStore } from '@/stores/home'
const homeStore = useHomeStore()
const { id, setOption } = useEchart()
const homeData = inject('homeData')
const init = () => {
setOption({
backgroundColor: 'transparent',
@@ -67,7 +68,7 @@
}
}
},
data: homeData.value?.userPortrait?.provinceRate.map((item) => Number(item.value))
data: homeStore?.userPortraitData?.provinceRate.map((item) => Number(item.value))
}
],
series: [
@@ -91,7 +92,7 @@
return params.data.name ?? '其他'
}
},
data: homeData.value?.userPortrait?.provinceRate.map((item) => {
data: homeStore?.userPortraitData?.provinceRate.map((item) => {
return {
name: item.name,
value: Number(item.value),
@@ -107,9 +108,12 @@
}
watch(
() => homeData.value?.userPortrait?.provinceRate,
() => {
init()
() => homeStore?.userPortraitData?.provinceRate,
(val) => {
if (val.length > 0) init()
},
{
immediate: true
}
)
</script>

View File

@@ -1,102 +0,0 @@
<template>
<div class="traffic-flow" id="traffic-flow" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import * as echarts from 'echarts'
let trafficChart = null
let option = {
grid: {
left: '4%',
right: '4%',
top: '10%',
bottom: '10%',
containLabel: true
},
xAxis: {
boundaryGap: true,
type: 'category',
data: ['河北', '山西', '河南', '内蒙', '辽宁'],
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: 'rgba(5, 72, 134, 1)'
}
},
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: {
show: false,
lineStyle: {
color: 'rgba(0, 150, 255,0.4)',
type: 'dashed'
}
}
},
series: [
{
data: [820, 932, 901, 934, 1290],
type: 'bar',
showBackground: true,
barWidth: fitChartSize(8),
itemStyle: {
barBorderRadius: [3, 3, 0, 0],
color: {
type: 'linear',
x: 0,
y: 1,
colorStops: [
{
offset: 0,
color: 'rgba(0, 208, 255, 0)'
},
{
offset: 1,
color: 'rgba(0, 208, 255, 1)'
}
]
}
},
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
}
}
]
}
const init = () => {
trafficChart = echarts.init(document.getElementById('traffic-flow'))
trafficChart.setOption(option)
window.addEventListener('resize', resize)
}
const resize = () => {
if (trafficChart) {
trafficChart.dispose()
trafficChart = null
init()
}
}
onMounted(() => {
init()
})
</script>
<style scoped lang="scss">
.traffic-flow {
width: 100%;
height: vh(160);
}
</style>

View File

@@ -1,189 +1,194 @@
<template>
<div class="vacancy" id="vacancy" />
<!-- 景区停车场空位 -->
<div class="vacancy" :id="id" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import * as echarts from 'echarts'
import { useEchart } from '@/hooks/echart'
let topChart = null
let result = [
{ name: '三峡之颠', value: 86 },
{ name: '白帝城', value: 83 },
{ name: '瞿塘峡', value: 73 },
{ name: '天坑地缝', value: 61 }
]
let option = {
backgroundColor: 'transparent',
tooltip: {
show: false
const { id, setOption } = useEchart()
let props = defineProps({
list: {
type: Array,
default: () => []
}
})
watch(
() => props.list,
(val) => {
if (val.length > 0) init()
},
legend: {
show: false
},
grid: {
left: '4%',
right: '4%',
top: '20%',
bottom: '-4%',
containLabel: true
},
xAxis: [
{
splitLine: {
show: false
},
type: 'value',
show: false
}
],
yAxis: [
{
splitLine: {
show: false
},
axisLine: {
show: false
},
type: 'category',
axisTick: {
show: false
},
data: result.map((item) => item.name),
axisLabel: {
show: false
}
},
{
type: 'category',
inverse: true,
axisTick: 'none',
axisLine: 'none',
show: true,
axisLabel: {
textStyle: {
color: '#fff',
fontSize: fitChartSize(12)
},
verticalAlign: 'bottom',
padding: [0, 0, fitChartSize(6), 0],
inside: true,
formatter: function (value) {
return `{label|余} {value|${value}}`
},
rich: {
label: {
align: 'center',
color: '#fff',
fontSize: fitChartSize(14),
fontFamily: 'Source Han Sans CN'
},
value: {
align: 'center',
color: '#fff',
fontSize: fitChartSize(14),
fontFamily: 'Source Han Sans CN'
}
}
},
data: result.map((item) => item.value)
}
],
series: [
{
name: '',
type: 'bar',
barWidth: fitChartSize(4),
MaxSize: 0,
showBackground: true,
barBorderRadius: [30, 0, 0, 30],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
label: {
show: true,
offset: [10, -13],
color: '#D3E5FF',
fontWeight: 500,
position: 'left',
align: 'left',
fontSize: fitChartSize(14),
fontFamily: 'Source Han Sans CN',
formatter: function (params) {
return params.data.name
}
},
data: result.map((item, index) => {
return {
name: item.name,
value: item.value,
itemStyle: {
barBorderRadius: [3, 0, 0, 3],
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(0, 204, 255, 0)'
},
{
offset: 1,
color: 'rgba(0, 204, 255, 1)'
}
]
{ immediate: true }
)
let params = null
const getSeriesData = () => {
return props.list.map((item) => {
return {
...item,
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(255, 112, 33, 0)'
},
{
offset: 1,
color: '#00CCFF'
}
}
]
}
})
},
{
name: '外圆',
type: 'scatter',
emphasis: {
scale: false
},
showSymbol: true,
symbol: 'circle',
symbolSize: fitChartSize(10),
z: 2,
data: result.map((item, index) => {
return {
name: item.name,
value: item.value,
itemStyle: {
color: '#fff',
opacity: 1
}
}
}),
animationDelay: 500
}
}
]
})
}
const getScatterData = () => {
return props.list.map((item) => {
return {
...item,
itemStyle: {
color: '#fff',
opacity: 1
}
}
})
}
const getYAxisData = () => {
return props.list.map((item) => item.value)
}
const init = () => {
topChart = echarts.init(document.getElementById('vacancy'))
topChart.setOption(option)
}
const resize = () => {
if (topChart) {
topChart.dispose()
topChart = null
init()
if (!params) {
params = {
backgroundColor: 'transparent',
tooltip: {
show: false
},
legend: {
show: false
},
grid: {
left: '4%',
right: '4%',
top: '16%',
bottom: '-10%',
containLabel: true
},
xAxis: [
{
splitLine: {
show: false
},
type: 'value',
show: false
}
],
yAxis: [
{
splitLine: {
show: false
},
axisLine: {
show: false
},
type: 'category',
axisTick: {
show: false
},
data: [],
axisLabel: {
show: false
}
},
{
type: 'category',
inverse: true,
axisTick: 'none',
axisLine: 'none',
show: true,
axisLabel: {
textStyle: {
color: '#fff',
fontSize: fitChartSize(12)
},
verticalAlign: 'bottom',
padding: [0, 0, 6, 0],
inside: true,
formatter: function (value) {
return `{value|余}{value|${value}}`
},
rich: {
value: {
align: 'center',
color: '#fff',
fontWeight: 600,
fontSize: fitChartSize(14)
}
}
},
data: getYAxisData()
}
],
series: [
{
name: '',
type: 'bar',
barWidth: fitChartSize(8),
showBackground: true,
barBorderRadius: [0, 0, 0, 0],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
label: {
show: true,
offset: [10, -10],
color: '#fff',
fontWeight: 500,
position: 'left',
align: 'left',
fontSize: fitChartSize(14),
formatter: function (params) {
return params.data.name
}
},
data: getSeriesData()
},
{
name: '外圆',
type: 'scatter',
emphasis: {
scale: false
},
showSymbol: true,
symbol: 'circle',
symbolSize: fitChartSize(10),
z: 2,
animationDelay: 500,
data: getScatterData()
}
]
}
} else {
params.yAxis[1].data = getYAxisData()
params.series[0].data = getSeriesData()
params.series[1].data = getScatterData()
}
setOption(params)
}
onMounted(() => {
init()
window.addEventListener('resize', resize)
})
</script>
<style scoped lang="scss">
.vacancy {
width: 100%;
height: vh(170);
width: vw(250);
height: vh(160);
}
</style>

View File

@@ -0,0 +1,119 @@
<!-- 车源地 -->
<template>
<div class="vehicle-source" :id="id" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import { useEchart } from '@/hooks/echart'
const { id, setOption } = useEchart()
let props = defineProps({
list: {
type: Array,
default: () => []
}
})
watch(
() => props.list,
(val) => {
if (val.length > 0) init()
},
{ immediate: true }
)
let params = null
const getSeriesData = () => {
return props.list.map((item) => item.value)
}
const getXAxisData = () => {
return props.list.map((item) => item.name)
}
const init = () => {
if (!params) {
params = {
grid: {
left: '4%',
right: '4%',
top: '10%',
bottom: '10%',
containLabel: true
},
xAxis: {
boundaryGap: true,
type: 'category',
data: getXAxisData(),
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: 'rgba(5, 72, 134, 1)'
}
},
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: {
show: false,
lineStyle: {
color: 'rgba(0, 150, 255,0.4)',
type: 'dashed'
}
}
},
series: [
{
data: getSeriesData(),
type: 'bar',
showBackground: true,
barWidth: fitChartSize(8),
itemStyle: {
barBorderRadius: [0, 0, 0, 0],
color: {
type: 'linear',
x: 0,
y: 1,
colorStops: [
{
offset: 0,
color: 'rgba(0, 208, 255, 0)'
},
{
offset: 1,
color: 'rgba(0, 208, 255, 1)'
}
]
}
},
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
}
}
]
}
} else {
params.xAxis.data = getXAxisData()
params.series[0].data = getSeriesData()
}
setOption(params)
}
</script>
<style scoped lang="scss">
.vehicle-source {
width: 100%;
height: vh(160);
}
</style>

View File

@@ -10,14 +10,13 @@
import box3 from './components/box-3.vue'
import { useWebSocket } from '@/hooks/socket'
const { data, isConnected, sendMessage } = useWebSocket('ws://36.138.38.16:81/ws/third-party')
provide('homeData', data)
const { isConnected, sendMessage } = useWebSocket('ws://36.138.38.16:81/ws/third-party')
watch(
() => isConnected.value,
(val) => {
if (val) {
console.log('--------------------------------------------')
sendMessage(
JSON.stringify({
action: 'start',

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
<template>
<main class="wrapper">
<CoreVideo />
<box5/>
<box3 />
<box5 />
<box3 />
<Header title="三峡之巅-安全检测" is-skip :nav-left="navLeft" :nav-right="navRight" />
<Correspondence />
</main>
@@ -11,39 +11,42 @@
<script setup>
import box3 from './components/box-3.vue'
import box5 from './components/box-5.vue'
const navLeft = [{
name: '奉节县',
path: '/sceneTesting'
},
{
name: '三峡之巅',
path: '/sceneTesting'
},
{
name: '白帝城',
path: '/sceneTesting'
}, {
name: '龙河桥',
path: '/sceneTesting'
}
]
const navRight = [{
name: '路段',
path: '/roadTesting'
},
{
name: '路段',
path: '/roadTesting'
},
{
name: '路段',
path: '/roadTesting'
},
{
name: '路段',
path: '/roadTesting'
}
]
const navLeft = [
{
name: '奉节县',
path: '/sceneTesting'
},
{
name: '三峡之巅',
path: '/sceneTesting'
},
{
name: '白帝城',
path: '/sceneTesting'
},
{
name: '龙河桥',
path: '/sceneTesting'
}
]
const navRight = [
{
name: '路段',
path: '/roadTesting'
},
{
name: '路段',
path: '/roadTesting'
},
{
name: '路段',
path: '/roadTesting'
},
{
name: '路段',
path: '/roadTesting'
}
]
onMounted(() => {})
</script>
@@ -54,6 +57,5 @@ const navLeft = [{
height: 100vh;
overflow: hidden;
background-color: #0a254b;
}
</style>

View File

@@ -98,7 +98,7 @@
<p class="value">{{ item.value }}%</p>
</li>
</ul>
<div class="alarm">
<div v-if="pointRankData.length > 0" class="alarm">
<Title2 title="异常点位告警排名" />
<ul class="alarm__wrapper">
<li class="alarm-item" v-for="(item, index) in pointRankData" :key="index">
@@ -154,7 +154,6 @@
const getNewsPointRank = async () => {
let res = await getNewsPointRankApi()
pointRankData.value = res.data
console.log(res, '1111')
}
const getTypeRate = async () => {
let res = await getTypeRateApi()

View File

@@ -5,49 +5,54 @@
<div class="statistics">
<div class="statistics-item">
<div class="flex align-center">
<img class="statistics-item__icon" src="@/assets/images/dot-primary.svg" alt="" />
<img class="statistics-item__icon" src="@/assets/images/dot-primary.svg" />
<span class="statistics-item__label">今日消息总条数</span>
</div>
<div class="statistics-item__value--primary">
<countup end-val="45678" />
<countup :end-val="countInfo.total" />
<span class="statistics-item__value-suffix"></span>
</div>
</div>
<div class="statistics-item">
<div class="flex align-center">
<img class="statistics-item__icon" src="@/assets/images/dot-error.svg" alt="" />
<img class="statistics-item__icon" src="@/assets/images/dot-error.svg" />
<span class="statistics-item__label">紧急消息</span>
</div>
<div class="statistics-item__value--error">
<countup end-val="45678" />
<countup :end-val="countInfo.warn" />
<span class="statistics-item__value-suffix"></span>
</div>
</div>
<div class="statistics-item">
<div class="flex align-center">
<img class="statistics-item__icon" src="@/assets/images/dot-warning.svg" alt="" />
<img class="statistics-item__icon" src="@/assets/images/dot-warning.svg" />
<span class="statistics-item__label">重要消息</span>
</div>
<div class="statistics-item__value--warning">
<countup end-val="45678" />
<countup :end-val="countInfo.important" />
<span class="statistics-item__value-suffix"></span>
</div>
</div>
<div class="statistics-item">
<div class="flex align-center">
<img class="statistics-item__icon" src="@/assets/images/dot-success.svg" alt="" />
<img class="statistics-item__icon" src="@/assets/images/dot-success.svg" />
<span class="statistics-item__label">普通消息</span>
</div>
<div class="statistics-item__value--success">
<countup end-val="45678" />
<countup :end-val="countInfo.normal" />
<span class="statistics-item__value-suffix"></span>
</div>
</div>
</div>
<div class="chart-box">
<pie :width="150" :height="150" />
<pie :width="150" :height="150" />
<pie :width="150" :height="150" />
<pie
v-for="(item, index) in newsStateList"
:key="index"
:value="item.value"
:label="item.name"
:width="150"
:height="150"
/>
</div>
</div>
<div class="work-box-1">
@@ -75,14 +80,22 @@
import pie from './pie.vue'
import { getNewsListApi, getNewsStateApi, getNewsTotalApi } from '@/api/news'
const list = ref([])
let list = ref([])
let countInfo = ref({
important: 0,
normal: 0,
total: 0,
warn: 0
})
let newsStateList = ref([])
const getNewsTotal = async () => {
let res = await getNewsTotalApi()
console.log(res, '============')
countInfo.value = res.data
}
const getNewsState = async () => {
let res = await getNewsStateApi()
newsStateList.value = res.data
console.log(newsStateList.value, '=================')
}
const getNewsList = async () => {
let res = await getNewsListApi()

View File

@@ -8,145 +8,169 @@
const { id, setOption } = useEchart()
let option = {
backgroundColor: 'transparent',
title: [
{
text: '45.5%',
x: 'center',
top: '42%',
textStyle: {
color: '#02F9FA',
fontSize: fitChartSize(24)
}
},
{
text: '完成率',
x: 'center',
top: '54%',
textStyle: {
color: '#fff',
fontSize: fitChartSize(12)
}
}
],
series: [
{
type: 'gauge',
radius: '100%',
center: ['50%', '50%'],
min: 0,
max: 100,
startAngle: 90,
endAngle: 450,
clockwise: false,
itemStyle: {
color: '#00D0FF'
},
axisLine: {
show: true,
roundCap: false,
lineStyle: {
color: [
[0, '#075199'],
[1, '#075199']
],
width: fitChartSize(6)
}
},
progress: {
show: true,
roundCap: false,
width: fitChartSize(6)
},
pointer: {
// 指针
show: false
},
axisTick: {
// 刻度
show: false
},
splitLine: {
// 分割线
show: false
},
axisLabel: {
// 刻度标签
show: false
},
detail: {
// 仪表盘详情
show: false
},
data: [
{
value: 50
}
]
},
{
type: 'gauge',
radius: '95%',
center: ['50%', '50%'],
min: 0,
max: 100,
startAngle: 90,
endAngle: 450,
clockwise: false,
itemStyle: {
color: '#057EB9'
},
axisLine: {
roundCap: false,
lineStyle: {
color: [
[0, '#075199'],
[1, '#075199']
],
width: fitChartSize(10)
}
},
progress: {
show: true,
roundCap: false,
width: fitChartSize(10)
},
pointer: {
// 指针
show: false
},
axisTick: {
// 刻度
show: false
},
splitLine: {
// 分割线
show: false
},
axisLabel: {
// 刻度标签
show: false
},
detail: {
// 仪表盘详情
show: false
},
data: [
{
value: 50
}
]
}
]
}
const init = () => {
setOption(option)
}
onMounted(() => {
init()
let props = defineProps({
value: {
type: Number,
default: () => 0
},
label: {
type: String,
default: () => ''
}
})
watch(
() => props.value,
(val) => {
if (val) {
nextTick(() => {
init()
})
}
},
{ immediate: true }
)
let params = null
const init = () => {
if (!params) {
params = {
backgroundColor: 'transparent',
title: [
{
text: `${(props.value * 100).toFixed(2)}%`,
x: 'center',
top: '40%',
textStyle: {
color: '#02F9FA',
fontSize: fitChartSize(24)
}
},
{
text: props.label,
x: 'center',
top: '56%',
textStyle: {
color: '#fff',
fontSize: fitChartSize(12)
}
}
],
series: [
{
type: 'gauge',
radius: '100%',
center: ['50%', '50%'],
min: 0,
max: 1,
startAngle: 90,
endAngle: 450,
clockwise: false,
itemStyle: {
color: '#00D0FF'
},
axisLine: {
show: true,
roundCap: false,
lineStyle: {
color: [
[0, '#075199'],
[1, '#075199']
],
width: fitChartSize(6)
}
},
progress: {
show: true,
roundCap: false,
width: fitChartSize(6)
},
pointer: {
// 指针
show: false
},
axisTick: {
// 刻度
show: false
},
splitLine: {
// 分割线
show: false
},
axisLabel: {
// 刻度标签
show: false
},
detail: {
// 仪表盘详情
show: false
},
data: [
{
value: props.value
}
]
},
{
type: 'gauge',
radius: '95%',
center: ['50%', '50%'],
min: 0,
max: 1,
startAngle: 90,
endAngle: 450,
clockwise: false,
itemStyle: {
color: '#057EB9'
},
axisLine: {
roundCap: false,
lineStyle: {
color: [
[0, '#075199'],
[1, '#075199']
],
width: fitChartSize(10)
}
},
progress: {
show: true,
roundCap: false,
width: fitChartSize(10)
},
pointer: {
// 指针
show: false
},
axisTick: {
// 刻度
show: false
},
splitLine: {
// 分割线
show: false
},
axisLabel: {
// 刻度标签
show: false
},
detail: {
// 仪表盘详情
show: false
},
data: [
{
value: props.value
}
]
}
]
}
} else {
params.series[0].data[0].value = props.value
}
setOption(params)
}
</script>
<style lang="scss" scoped>