feat:对接景区接口

This commit is contained in:
zjc
2025-01-17 19:15:14 +08:00
parent fd8ede8a32
commit 8eb966fa14
29 changed files with 510 additions and 315 deletions

View File

@@ -65,3 +65,11 @@ export function getSpotApi() {
method: 'post'
})
}
// 景区列表
export function getSpotListApi() {
return request({
url: '/fjtcc-api/api/largeScreen/spot/list',
method: 'get'
})
}

View File

@@ -1,6 +1,5 @@
<template>
<div
class="progress"
:id="id"
:style="{
width: styleUtil.px2vw(width),
@@ -10,10 +9,11 @@
</template>
<script setup>
import * as echarts from 'echarts'
import { guid } from '@/utils/util'
import styleUtil from '@/utils/styleUtil'
import { fitChartSize } from '@/utils/dataUtil'
import { useEchart } from '@/hooks/echart'
const { id, setOption } = useEchart()
const props = defineProps({
width: {
@@ -23,14 +23,36 @@
height: {
type: Number,
default: () => 0
},
value: {
type: Number,
default: () => 0
},
title: {
type: [String, Number],
default: () => ''
},
subTitle: {
type: [String, Number],
default: () => ''
}
})
let progressChart = null
let id = ref(guid())
watch(
() => props.value,
(val) => {
setTimeout(() => {
init()
}, 1000)
},
{ immediate: true }
)
let params = null
let defaultCofig = {
title: [
{
text: '45.5%',
text: props.title,
x: 'center',
top: '34%',
textStyle: {
@@ -39,7 +61,7 @@
}
},
{
text: '完成率',
text: props.subTitle,
x: 'center',
top: '56%',
textStyle: {
@@ -99,7 +121,7 @@
},
data: [
{
value: 50
value: props.value
}
]
},
@@ -151,27 +173,24 @@
},
data: [
{
value: 50
value: props.value
}
]
}
]
}
onMounted(() => {
init()
})
const init = () => {
// 基于准备好的dom初始化echarts实例
progressChart = echarts.init(document.getElementById(id.value))
progressChart.setOption({ ...defaultCofig })
window.addEventListener('resize', resize)
}
const resize = () => {
if (progressChart) {
progressChart.dispose()
init()
if (!params) {
params = defaultCofig
} else {
params.title[0].text = props.title
params.title[1].text = props.subTitle
params.series.map((item) => {
item.data[0].value = props.value
})
}
setOption(params)
}
</script>

View File

@@ -1,11 +1,13 @@
<template>
<div class="count-item">
<div class="flex align-center">
<img src="@/assets/images/dot-primary.svg" />
<img v-if="type == 0" src="@/assets/images/dot-primary.svg" />
<img v-if="type == 1" src="@/assets/images/dot-error.svg" />
<span class="label">{{ label }}</span>
</div>
<div class="count">
<img class="bg" src="@/assets/images/mask-success.png" />
<img v-if="type == 0" class="bg" src="@/assets/images/mask-success.png" />
<img v-if="type == 1" class="bg" src="@/assets/images/mask-error.png" />
<div class="flex align-center">
<countup class="value" :end-val="count" :style="{ color: color }" />
<span class="suffix" :style="{ color: color }">{{ suffix }}</span>
@@ -26,6 +28,10 @@
type: Number,
default: 0
},
type: {
type: Number,
default: 0
},
color: {
type: String,
default: '#02f9fa'

View File

@@ -12,7 +12,12 @@
>
{{ item.name }}
</li>
<!-- <el-dropdown v-if="navLeft.length > 3" trigger="click" @command="handleCommand">
<!-- v-if="navLeft.length > 3" -->
<el-dropdown
v-if="router.currentRoute.value.path == '/scenic'"
trigger="click"
@command="handleCommand"
>
<li
class="nav-left-item"
:style="{
@@ -24,12 +29,12 @@
</li>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="(item, index) in navLeft" :key="index" :command="item">
<el-dropdown-item v-for="(item, index) in otherNav" :key="index" :command="item">
<span class="label"> {{ item.name }}</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown> -->
</el-dropdown>
</ul>
<div class="title">
<span>{{ title }}</span>
@@ -63,9 +68,9 @@
import title2 from '@/assets/images/title-2.png'
import title2Select from '@/assets/images/title-2-select.png'
import { getWeatherApi } from '@/api/home'
import { getSpotApi } from '@/api/sentiment'
import { getSpotListApi } from '@/api/sentiment'
const emit = defineEmits(['change'])
import pubSub from 'pubsub-js'
const router = useRouter()
@@ -78,6 +83,18 @@
let isBack = ref(false)
let current = ref(0)
let otherScenic = ref('')
let otherNav = ref([
{
name: '大窝景区'
},
{
name: '天坑景区'
},
{
name: '旅游环线'
}
])
let spotList = ref([])
// 补零
const fillZero = (value) => {
@@ -102,6 +119,7 @@
const handleBack = () => {
router.go(-1)
}
const handleCommand = () => {}
// 点击导航
const handleNav = (item, index) => {
if (isSkip.value) {
@@ -111,15 +129,24 @@
otherScenic.value = ''
current.value = index
title.value = item.name
pubSub.publish('scenicChange', item)
}
}
// 获取天气数据
const getWeather = async () => {
let res = await getWeatherApi()
weatherData.value = res.data
}
// 获取景区列表
const getSpotList = async () => {
let res = await getSpotListApi()
spotList.value = res.data
}
// 设置当前路由导航栏
const setNav = async () => {
navLeft.value = []
navRight.value = []
switch (router.currentRoute.value.path) {
case '/home':
title.value = '奉节县旅游指挥调度中心'
@@ -138,55 +165,29 @@
case '/scenic':
isSkip.value = false
isBack.value = true
let res = await getSpotApi()
navLeft.value = res.data.map((item) => {
return {
name: item.ssname,
id: item.id
}
})
let res = await getSpotListApi()
navLeft.value = res.data
title.value = navLeft.value[current.value].name
navRight.value = []
pubSub.publish('scenicChange', navLeft.value[current.value])
break
case '/sentiment':
title.value = '舆情检测'
navLeft.value = []
navRight.value = []
isBack.value = true
break
case '/workOrder':
title.value = '工单消息'
navLeft.value = []
navRight.value = []
isBack.value = true
break
case '/traffic':
title.value = '交通大屏'
navLeft.value = []
navRight.value = []
isBack.value = true
break
case '/monitor':
title.value = '监控大屏'
navLeft.value = [
{
name: '奉节县',
path: '/sceneTesting'
},
{
name: '三峡之巅',
path: '/sceneTesting'
},
{
name: '白帝城',
path: '/sceneTesting'
},
{
name: '龙河桥',
path: '/sceneTesting'
}
]
navRight.value = []
let res1 = await getSpotListApi()
navLeft.value = res1.data
isBack.value = true
break
case '/sceneTesting':
@@ -240,6 +241,7 @@
)
onMounted(() => {
getWeather()
getSpotList()
getCurrentDate()
setInterval(() => {
getCurrentDate()
@@ -253,7 +255,7 @@
left: vw(326);
.weather {
position: absolute;
left: 0;
right: 0;
top: vh(10);
font-weight: 400;
font-size: vw(18);
@@ -264,7 +266,7 @@
}
.date {
position: absolute;
right: 0;
left: 0;
top: vh(10);
font-weight: 400;
font-size: vw(18);
@@ -293,7 +295,7 @@
}
}
.title {
width: vw(3133);
width: vw(3170);
height: vh(120);
line-height: vh(90);
text-align: center;
@@ -316,7 +318,7 @@
}
.nav-left {
position: absolute;
right: vw(2100);
right: vw(2120);
top: vh(34);
display: flex;
&-item {
@@ -338,7 +340,7 @@
}
.nav-right {
position: absolute;
left: vw(2110);
left: vw(2130);
top: vh(34);
display: flex;
&-item {

View File

@@ -31,7 +31,7 @@ export function useWebSocket(url) {
}
const sendMessage = (message) => {
if (socket.value) {
if (socket.value && isConnected.value) {
socket.value.send(message)
}
}

View File

@@ -46,10 +46,9 @@ export const useHomeStore = defineStore('home', () => {
let wordkOrderList = ref([])
// 工单统计
let wordkOrderData = ref({
toDayData: { count: 15, end: 0, rate: '0.0' },
warnData: { count: 15, end: 0, rate: '0.0' }
toDayData: { count: 0, end: 0, rate: '0.0' },
warnData: { count: 0, end: 0, rate: '0.0' }
})
// 交通信息
let trafficInfoData = ref({
data: {

View File

@@ -13,21 +13,30 @@ export const useScenicStore = defineStore('scenic', () => {
// 景区排队信息
let scenicQueueData = ref({ dataList: [], header: { jrcp: 0, jrjdrs: 0, pdcxsj: 0, pdrs: 0 } })
// 景区负载信息
let scenicBearData = ref({ dataList: [], header: { jqzdcz: 0, jrjdrs: 0 } })
let scenicBearData = ref({ dataList: [], header: { jqRate: 0, jqzdcz: 0, jrjdrs: 0 } })
// 景区停车信息
let stopCarData = ref({
dataList: [],
dataLists: [],
headList: []
headList: [],
info: {
count: 0,
remain: 0
}
})
// 安全信息
let secureData = ref({
dataList: []
dataList: [],
headList: [
{ name: '当前告警总数', count: 0, type: 0 },
{ name: '安全告警总数', count: 0, type: 0 },
{ name: '已解除告警数', count: 0, type: 0 }
]
})
// 交通信息
let trafficData = ref({
infoList: [
{ name: '总通景路段', value: 100 },
{ name: '总通景路段', value: 0 },
{ name: '通景路段拥堵', value: 0 },
{ name: '通景拥堵开始时间', value: 0 },
{ name: '拥堵持续时间', value: 0 }
@@ -49,7 +58,29 @@ export const useScenicStore = defineStore('scenic', () => {
})
// 工单列表
let wordkOrderList = ref([])
// 工单统计
let wordkOrderData = ref({
toDayData: { count: 0, end: 0, rate: '0.0' },
warnData: { count: 0, end: 0, rate: '0.0' }
})
// 车船信息
let carShipData = ref({
car: {
count: { nonDrivingCount: 0, drivingCount: 0 },
list: []
},
ship: {
count: { nonDrivingCount: 0, drivingCount: 0 },
list: []
}
})
const setCarShipData = (val) => {
carShipData.value = val
}
const setWordkOrderData = (val) => {
wordkOrderData.value = val
}
const setWordkOrderList = (val) => {
wordkOrderList.value = val
}
@@ -75,6 +106,7 @@ export const useScenicStore = defineStore('scenic', () => {
scenicSpotData.value = val
}
return {
wordkOrderData,
wordkOrderList,
scenicSpotData,
scenicQueueData,
@@ -83,6 +115,8 @@ export const useScenicStore = defineStore('scenic', () => {
secureData,
trafficData,
userPortraitData,
carShipData,
setWordkOrderData,
setWordkOrderList,
setScenicSpotData,
setScenicQueueData,
@@ -90,6 +124,7 @@ export const useScenicStore = defineStore('scenic', () => {
setStopCarData,
setSecureData,
setTrafficData,
setUserPortraitData
setUserPortraitData,
setCarShipData
}
})

View File

@@ -4,7 +4,7 @@ export const proBaseUrl = 'http://192.168.77.200'
export const socketBaseUrl = 'ws://36.138.38.16:81'
export const proSocketBaseUrl = 'ws://192.168.77.200:8060'
export const mode = 'pro'
export const mode = 'pro' // 测试 dev 正式 pro
export const devToken =
'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImE1OWFmNWYwLTU3OWItNDJkNy1hZDJhLTY0Y2JlODA5ZWI1NiJ9.BTxvu6jUWbN0qONWf5K6VzXopE8T8qXzKuX-mij21VJT4U0LdgnqToyqeNDQ2OyJ6cvpdJBzQ9mEEb-dnwrTpQ'

View File

@@ -55,7 +55,7 @@
v-for="(item, index) in homeStore.scenicBearData.info"
:key="index"
>
<gauge :value="item.value" />
<gauge :value="parseFloat(item.value)" />
<span class="statistic-title">{{ item.name }}</span>
</div>
</div>
@@ -131,10 +131,10 @@
</template>
<script setup>
import top from './top.vue'
import age from './age.vue'
import gauge from './gauge.vue'
import ticket from './ticket.vue'
import top from './top'
import age from './age'
import gauge from './gauge'
import ticket from './ticket'
import countup from 'vue-countup-v3'
import { useHomeStore } from '@/stores/home'

View File

@@ -69,7 +69,7 @@
<span class="text">工单完成数</span>
<div class="progress-1">
<el-progress
:percentage="homeStore.wordkOrderData.toDayData.rate"
:percentage="parseFloat(homeStore.wordkOrderData.toDayData.rate)"
:show-text="false"
/>
</div>
@@ -91,7 +91,7 @@
<span class="text">工单完成数</span>
<div class="progress-2">
<el-progress
:percentage="homeStore.wordkOrderData.warnData.rate"
:percentage="parseFloat(homeStore.wordkOrderData.warnData.rate)"
:show-text="false"
/>
</div>

View File

@@ -231,7 +231,7 @@
</div>
<div>
<div class="occupancy">
<Title3 title="酒店入住人数及入住率" />
<Title3 title="酒店入住率" />
</div>
<occupancy :list="homeStore.hotelData?.list" />
</div>

View File

@@ -25,11 +25,9 @@
watch(
() => props.value,
(val) => {
if (val) {
nextTick(() => {
init()
})
}
setTimeout(() => {
init()
}, 1000)
},
{ immediate: true }
)
@@ -44,7 +42,7 @@
endAngle: 0,
min: 0,
max: 100,
radius: '100%',
radius: '120%',
splitNumber: 10,
center: ['50%', '60%'],
itemStyle: {
@@ -65,11 +63,11 @@
}
],
global: false
},
shadowColor: 'rgba(0,138,255,0.45)',
shadowBlur: 10,
shadowOffsetX: 2,
shadowOffsetY: 2
}
// shadowColor: 'red',
// shadowBlur: 10,
// shadowOffsetX: 2,
// shadowOffsetY: 2
},
progress: {
show: true,
@@ -130,7 +128,7 @@
<style lang="scss" scoped>
.gauge {
width: vw(70);
width: vw(120);
height: vh(50);
}
</style>

View File

@@ -34,7 +34,7 @@
return props.list.map((item) => {
return {
name: item.name,
value: item.count,
value: item.duration,
itemStyle: {
color: {
type: 'linear',
@@ -61,7 +61,7 @@
return props.list.map((item) => {
return {
name: item.name,
value: item.count,
value: item.duration,
itemStyle: {
color: '#fff',
opacity: 1
@@ -70,7 +70,7 @@
})
}
const setYAxisData = () => {
return props.list.map((item) => item.count)
return props.list.map((item) => item.duration)
}
const init = () => {
if (!params) {
@@ -110,10 +110,10 @@
axisTick: {
show: false
},
data: [],
axisLabel: {
show: false
}
},
data: []
},
{
type: 'category',
@@ -122,10 +122,8 @@
axisLine: 'none',
show: true,
axisLabel: {
textStyle: {
color: '#fff',
fontSize: fitChartSize(12)
},
color: '#fff',
fontSize: fitChartSize(12),
verticalAlign: 'bottom',
padding: [0, 0, 6, 0],
inside: true,
@@ -150,7 +148,7 @@
type: 'bar',
barWidth: fitChartSize(8),
showBackground: true,
barBorderRadius: [0, 0, 0, 0],
borderRadius: [0, 0, 0, 0],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},

View File

@@ -85,7 +85,7 @@
grid: {
left: '4%',
right: '4%',
top: '14%',
top: '16%',
bottom: '4%',
containLabel: false
},
@@ -122,10 +122,8 @@
axisLine: 'none',
show: true,
axisLabel: {
textStyle: {
color: '#fff',
fontSize: fitChartSize(12)
},
color: '#fff',
fontSize: fitChartSize(12),
verticalAlign: 'bottom',
padding: [0, 0, 10, 0],
inside: true,
@@ -150,7 +148,7 @@
type: 'bar',
barWidth: fitChartSize(8),
showBackground: true,
barBorderRadius: [0, 0, 0, 0],
borderRadius: [0, 0, 0, 0],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},

View File

@@ -13,8 +13,8 @@
const { id, setOption } = useEchart()
let x = 15
let y = 25
let x = 25
let y = 35
let params = null
watch(
@@ -38,7 +38,7 @@
type: 'pie',
clockwise: false,
silent: true,
radius: [`${x * (index + 1)}%`, `${y + index * 15}%`],
radius: [`${x * (index + 1)}%`, `${y + index * 25}%`],
center: ['50%', '40%'],
label: { show: false },
labelLine: { show: false },
@@ -66,7 +66,7 @@
legend: {
show: true,
x: 'center',
y: '70%',
y: '74%',
itemHeight: fitChartSize(12),
itemWidth: fitChartSize(12),
itemGap: fitChartSize(6),

View File

@@ -52,10 +52,8 @@
axisLine: 'none',
show: true,
axisLabel: {
textStyle: {
color: '#fff',
fontSize: fitChartSize(18)
},
color: '#fff',
fontSize: fitChartSize(18),
verticalAlign: 'bottom',
padding: [0, -fitChartSize(10), fitChartSize(10), 0],
inside: true,
@@ -78,7 +76,7 @@
type: 'bar',
barWidth: fitChartSize(12),
showBackground: true,
barBorderRadius: [0, 0, 0, 0],
borderRadius: [0, 0, 0, 0],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
@@ -99,7 +97,7 @@
name: item.name,
value: Number(item.value),
itemStyle: {
barBorderRadius: [0, 0, 0, 0],
borderRadius: [0, 0, 0, 0],
color: parseFloat(item.value) > 50 ? '#FF7021' : '#00CCFF'
}
}

View File

@@ -34,6 +34,7 @@
return props.list.map((item) => {
return {
...item,
value: item.occupiedPercentage,
itemStyle: {
color: {
type: 'linear',
@@ -60,6 +61,7 @@
return props.list.map((item) => {
return {
...item,
value: item.occupiedPercentage,
itemStyle: {
color: '#fff',
opacity: 1
@@ -68,7 +70,7 @@
})
}
const getYAxisData = () => {
return props.list.map((item) => item.value)
return props.list.map((item) => item.occupied)
}
const init = () => {
if (!params) {
@@ -148,7 +150,7 @@
type: 'bar',
barWidth: fitChartSize(8),
showBackground: true,
barBorderRadius: [0, 0, 0, 0],
borderRadius: [0, 0, 0, 0],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},

View File

@@ -83,8 +83,16 @@
type: 'bar',
showBackground: true,
barWidth: fitChartSize(8),
label: {
show: true,
position: 'top',
textStyle: {
color: '#fff',
fontSize: fitChartSize(10)
}
},
itemStyle: {
barBorderRadius: [0, 0, 0, 0],
borderRadius: [0, 0, 0, 0],
color: {
type: 'linear',
x: 0,

View File

@@ -126,7 +126,6 @@
params.series[0].data = getSeriesData()
params.series[1].data = getSeriesData()
}
console.log(params, 'params')
setOption(params)
}
</script>

View File

@@ -20,15 +20,15 @@
(val) => {
if (val && !map.value) {
setTimeout(() => {
initMap('big-car-ship', 109.491961, 31.024285, 16)
addMarker(carIcon, [109.491045, 31.028725], [36, 50])
addMarker(carIcon, [109.483266, 31.024718], [36, 50])
addMarker(shipIcon, [109.479062, 31.021499], [36, 50])
addMarker(shipIcon, [109.488907, 31.017476], [36, 50])
}, 500)
initMap('big-car-ship', 109.552461, 31.049607, 15)
addMarker(carIcon, [109.551419, 31.050001], [36, 50])
addMarker(shipIcon, [109.551671, 31.04847], [36, 50])
}, 1000)
}
}
)
onMounted(() => {})
</script>
<style scoped lang="scss">
@@ -42,15 +42,15 @@
:deep(.el-dialog) {
width: vw(2540);
height: vh(900);
padding: 0;
padding: vh(40) vw(40);
}
:deep(.el-dialog__header) {
padding-bottom: 0 !important;
}
}
.big-car-ship {
width: vw(2540);
height: vh(900);
width: 100%;
height: vh(820);
}
.close {
cursor: pointer;

View File

@@ -39,35 +39,55 @@
<div class="footer">
<div class="flex">
<div class="item">
<CircleProgress :width="170" :height="90" />
<circle-progress
:width="170"
:height="90"
:value="scenicStore.wordkOrderData.toDayData.rate"
:title="`${scenicStore.wordkOrderData.toDayData.rate}%`"
subTitle="完成率"
/>
<div>
<div class="bg">
<span class="label">今日工单总条数</span>
<span class="value"> <countup :endVal="1234" /></span>
<span class="value success">
<countup :end-val="scenicStore.wordkOrderData.toDayData.count" />
</span>
</div>
<div class="bg">
<span class="label">工单完成数</span>
<span class="value"> <countup :endVal="1234" /></span>
<span class="value">
<countup :end-val="scenicStore.wordkOrderData.toDayData.end" />
</span>
</div>
</div>
</div>
<div class="item">
<CircleProgress :width="170" :height="90" />
<circle-progress
:width="170"
:height="90"
:value="scenicStore.wordkOrderData.warnData.rate"
:title="`${scenicStore.wordkOrderData.warnData.rate}%`"
subTitle="完成率"
/>
<div>
<div class="bg">
<span class="label">今日工单总条</span>
<span class="value"> <countup :endVal="1234" /></span>
<span class="label">今日紧急工单数</span>
<span class="value error">
<countup :end-val="scenicStore.wordkOrderData.warnData.count" />
</span>
</div>
<div class="bg">
<span class="label">工单完成数</span>
<span class="value"> <countup :endVal="1234" /></span>
<span class="label">紧急工单完成数</span>
<span class="value">
<countup :end-val="scenicStore.wordkOrderData.warnData.end" />
</span>
</div>
</div>
</div>
</div>
<div class="flex align-center justify-between">
<div>
<vue3-seamless-scroll
<Vue3SeamlessScroll
class="list"
:list="scenicStore.wordkOrderList"
:limitScrollNum="3"
@@ -82,7 +102,7 @@
{{ item.title }}
</p>
</div>
</vue3-seamless-scroll>
</Vue3SeamlessScroll>
</div>
<img class="more" src="@/assets/images/more-col.png" alt="查看更多" @click="handleMore" />
</div>
@@ -203,8 +223,14 @@
.value {
font-weight: bold;
font-size: vw(28);
color: #fff;
}
.success {
color: #02f9fa;
}
.error {
color: #e21b1b;
}
}
}
.list {

View File

@@ -40,7 +40,13 @@
<div class="box mr-8">
<Title1 title="景区承载" />
<div class="flex">
<circle-progress :width="200" :height="70" />
<circle-progress
:width="200"
:height="70"
:value="scenicStore.scenicBearData.header.jqRate"
:title="`${scenicStore.scenicBearData.header.jqRate}%`"
subTitle="景区承载率"
/>
<div class="flex flex-1 justify-between">
<count-item
label="景区当前人数"
@@ -69,14 +75,24 @@
<div class="box-1 mr-8">
<Title1 title="停车信息" />
<div class="flex">
<div class="flex">
<circle-progress :width="140" :height="70" />
<circle-progress :width="140" :height="70" />
<circle-progress :width="140" :height="70" />
<div class="height70 flex flex-1">
<circle-progress
v-for="(item, index) in garageList"
:key="index"
:width="140"
:height="70"
:value="item.ratio"
:title="item.over"
:subTitle="`${item.name}剩余`"
/>
</div>
<div class="ml-20 flex flex-1 justify-between">
<count-item label="总车位数" :count="561" suffix="个" />
<count-item label="剩余车位数" :count="15" suffix="个" />
<count-item label="总车位数" :count="scenicStore.stopCarData.info.count" suffix="个" />
<count-item
label="剩余车位数"
:count="scenicStore.stopCarData.info.remain"
suffix="个"
/>
</div>
</div>
<div class="flex">
@@ -103,9 +119,14 @@
<div class="box-2">
<Title1 title="安全信息 " />
<div class="count-box flex">
<count-item label="当前告警总数" :count="561" suffix="次" />
<count-item label="安全告警总数" :count="561" suffix="次" />
<count-item label="已解除告警数" :count="561" />
<count-item
v-for="item in scenicStore.secureData.headList"
:label="item.name"
:count="item.count"
:type="item.type"
:color="item.type == 0 ? '#0096FF' : '#E21B1B'"
suffix="次"
/>
</div>
<div class="border flex-1">
<div class="pt-10">
@@ -115,7 +136,7 @@
label="安全告警占比"
:dataList="scenicStore.secureData.dataList"
:total="alarmTotal"
:width="460"
:width="440"
:height="300"
/>
</div>
@@ -140,11 +161,21 @@
</div>
<div class="border mr-8">
<Title3 title="拥堵次数占比" />
<jam :width="220" :height="160" />
<jam
:width="220"
:height="300"
sub-title="拥堵频次总数"
:list="scenicStore.trafficData.data.countRate"
/>
</div>
<div class="border">
<Title3 title="拥堵时长占比" />
<jam :width="220" :height="160" />
<jam
:width="220"
:height="300"
sub-title="拥堵总时长"
:list="scenicStore.trafficData.data.countRate"
/>
</div>
</div>
</div>
@@ -167,7 +198,7 @@
<span class="text">{{ item.name }}</span>
<div class="progress">
<el-progress
:percentage="item.value"
:percentage="parseFloat(item.value)"
:show-text="false"
:color="
item.name == '女'
@@ -186,7 +217,7 @@
</div>
<div class="border flex-1">
<Title3 title="购票来源" />
<div class="count">总人数<countup endVal="124563" /></div>
<div class="count">总人数<countup :end-val="channelTotal" /></div>
<ticket />
</div>
</div>
@@ -196,54 +227,66 @@
<Title1 title="车船信息" />
<div class="flex mb-6">
<div class="border mr-8 pt-10 pb-10">
<Title2 title="景区车辆" />
<div class="flex justify-between">
<Title2 title="景区车辆" />
<div class="sum">
<span>总数</span>
<countup
:end-val="
scenicStore.carShipData.car.count?.drivingCount +
scenicStore.carShipData.car.count?.nonDrivingCount
"
/>
</div>
</div>
<div class="car-box mt-10">
<img class="icon" src="@/assets/images/icon-6.png" />
<div class="car-item pr-20">
<div class="label">车总数</div>
<div class="value">130</div>
<div class="label">运营车辆</div>
<div class="value flex">
<countup :end-val="scenicStore.carShipData.car.count?.drivingCount" />
<span class="suffix"> </span>
</div>
</div>
<div class="car-item">
<div class="label">今日累计运营(班次)</div>
<div class="value">130</div>
<div class="label">空闲车辆</div>
<div class="value flex">
<countup :end-val="scenicStore.carShipData.car.count?.nonDrivingCount" />
<span class="suffix"> </span>
</div>
</div>
</div>
<div class="progress-box">
<span class="text">运营130辆</span>
<div class="progress">
<el-progress
:percentage="50"
:show-text="false"
color="linear-gradient(to right, rgba(0,150,255,0) 0%, #F15A25 100%)"
/>
</div>
<span class="value">空余100辆</span>
</div>
</div>
<div class="border pt-10 pb-10">
<Title2 title="景区船只" />
<div class="flex justify-between">
<Title2 title="景区船只" />
<div class="sum">
<span>总数</span>
<countup
:end-val="
scenicStore.carShipData.ship.count?.drivingCount +
scenicStore.carShipData.ship.count?.nonDrivingCount
"
/>
</div>
</div>
<div class="car-box mt-10">
<img class="icon" src="@/assets/images/icon-6.png" />
<div class="car-item pr-20">
<div class="label">船总数</div>
<div class="value">130</div>
<div class="label">运营船只</div>
<div class="value flex">
<countup :end-val="scenicStore.carShipData.ship.count?.drivingCount" />
<span class="suffix"></span>
</div>
</div>
<div class="car-item">
<div class="label">今日累计运营(班次)</div>
<div class="value">130</div>
<div class="label">空闲船只</div>
<div class="value flex">
<countup :end-val="scenicStore.carShipData.ship.count?.nonDrivingCount" />
<span class="suffix"></span>
</div>
</div>
</div>
<div class="progress-box">
<span class="text">运营130辆</span>
<div class="progress">
<el-progress
:percentage="50"
:show-text="false"
color="linear-gradient(to right, rgba(0,150,255,0) 0%, #F15A25 100%)"
/>
</div>
<span class="value">空余100辆</span>
</div>
</div>
</div>
<div class="border pt-10 pb-10">
@@ -273,6 +316,21 @@
const scenicStore = useScenicStore()
const { initMap, addMarker } = useMap()
const garageList = computed(() => {
return scenicStore.stopCarData.headList.map((item) => {
return {
...item,
ratio: ((item.count - item.over) / item.count) * 100
}
})
})
const channelTotal = computed(() => {
return scenicStore.userPortraitData.data.channel.reduce((pre, cur) => {
return pre + parseInt(cur.count)
}, 0)
})
const ageTotal = computed(() => {
return scenicStore.userPortraitData.data.genderRate.reduce((pre, cur) => {
return pre + parseInt(cur.count)
@@ -313,12 +371,26 @@
})
let show = ref(false)
watch(
() => scenicStore.carShipData,
(val) => {
if (val.car.list.length > 0) {
setTimeout(() => {
val.car.list.map((item) => {
addMarker(carIcon, [109.551419, 31.050001], [36, 50])
})
val.ship.list.map((item) => {
addMarker(shipIcon, [109.551671, 31.04847], [36, 50])
})
}, 1000)
}
},
{ immediate: true }
)
onMounted(() => {
initMap('car-ship', 109.491961, 31.024285, 13)
addMarker(carIcon, [109.491045, 31.028725], [36, 50])
addMarker(carIcon, [109.483266, 31.024718], [36, 50])
addMarker(shipIcon, [109.479062, 31.021499], [36, 50])
addMarker(shipIcon, [109.488907, 31.017476], [36, 50])
initMap('car-ship', 109.552461, 31.049607, 15)
})
</script>
@@ -329,6 +401,9 @@
:deep(.anchorBL) {
display: none;
}
.height70 {
height: vh(70);
}
.legend {
display: flex;
align-items: center;
@@ -399,29 +474,7 @@
height: vh(904);
}
}
.progress-box {
margin-top: vh(10);
height: vh(20);
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(to right, rgba(0, 150, 255, 0) 0%, rgba(0, 150, 255, 0.17) 100%);
.text {
margin-right: vw(10);
font-weight: 400;
font-size: vw(14);
color: #ffffff;
}
.progress {
width: vw(70);
}
.value {
margin-left: vw(10);
font-weight: 400;
font-size: vw(14);
color: #f15a25;
}
}
.container {
margin: vh(120) 0 0 vw(10);
.count-box {
@@ -491,11 +544,23 @@
font-size: vw(24);
color: #02f9fa;
margin-top: vh(6);
align-items: flex-end;
}
.suffix {
font-size: vw(12);
color: #02f9fa;
}
}
.sum {
font-weight: bold;
font-size: vw(18);
color: #02f9fa;
display: flex;
align-items: center;
}
.car-ship {
width: vw(660);
height: vh(240);
height: vh(250);
}
.full {
cursor: pointer;

View File

@@ -22,77 +22,78 @@
type: Number,
default: () => 0
},
config: {
type: Object,
default: () => {
return {}
}
list: {
type: Array,
default: () => []
},
title: {
type: [String, Number],
default: () => ''
},
subTitle: {
type: [String, Number],
default: () => ''
}
})
const { id, setOption } = useEchart()
var colorList = ['#FDC40A', '#FF5232', '#50F0A6', '#5FDFFA', '']
var title = ['企业', '农业', '工业', '纺织']
var dataValue = ['15', '30', '35', '20']
var dataList = title.map((item, index) => {
return {
name: item,
value: dataValue[index]
}
})
watch(
() => props.list,
(val) => {
setTimeout(() => {
init()
}, 1000)
},
{ immediate: true }
)
var defaultCofig = {
color: colorList,
series: [
{
type: 'pie',
startAngle: 360,
center: ['50%', '40%'],
radius: ['65%', '80%'],
label: {
show: false
},
labelLine: {
show: false
},
data: [
{
value: 50,
itemStyle: {
color: {
type: 'linear',
x: 1,
y: 1,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(0, 150, 255, 0)'
},
{
offset: 0.5,
color: 'rgba(0, 150, 255, 0.47)'
},
{
offset: 1,
color: 'rgba(0, 150, 255, 0)'
}
]
}
}
},
let params = null
{
name: 'bottom',
value: 50,
itemStyle: {
color: 'transparent'
}
}
]
const getSeriesData = () => {
return props.list.map((item) => {
return {
...item,
value: parseFloat(item.value)
}
})
}
const getTotal = () => {
return props.list.reduce((per, cur) => {
return per + cur.count
}, 0)
}
const formatLabel = () => {
return () => `{value|${getTotal()}}` + '\n' + `{name|${props.subTitle}}`
}
let defaultCofig = {
color: ['#A665F1', '#0063FF', '#00D0FF', '#01FEFE', '#FC2638', '#FED958'],
legend: {
x: 'left',
y: '74%',
itemHeight: fitChartSize(12),
itemWidth: fitChartSize(12),
itemGap: fitChartSize(10),
formatter: function (name) {
return '{name|' + name + '}'
},
textStyle: {
rich: {
name: {
color: '#fff',
fontSize: fitChartSize(14)
},
value: {
color: '#00D5F6',
fontSize: fitChartSize(14)
}
}
}
},
series: [
{
type: 'pie',
center: ['50%', '40%'],
@@ -105,9 +106,7 @@
show: true,
position: 'center',
fontWeight: 'bold',
formatter: function (o) {
return `{value|123456}` + '\n' + `{name|整改率}`
},
formatter: formatLabel(),
rich: {
value: {
color: '#fff',
@@ -116,7 +115,7 @@
padding: [0, 0, 5, 0]
},
name: {
color: '#7894A8',
color: '#fff',
fontSize: fitChartSize(12)
}
}
@@ -124,21 +123,20 @@
labelLine: {
show: false
},
data: dataList
data: getSeriesData()
}
]
}
const init = () => {
setOption({
...defaultCofig,
...props.config
})
if (!params) {
params = defaultCofig
} else {
params.series[0].label.formatter = formatLabel()
params.series[0].data = getSeriesData()
}
setOption(params)
}
onMounted(() => {
init()
})
</script>
<style scoped lang="scss"></style>

View File

@@ -13,8 +13,8 @@
const { id, setOption } = useEchart()
let x = 15
let y = 25
let x = 30
let y = 40
let params = null
watch(
@@ -38,7 +38,7 @@
type: 'pie',
clockwise: false,
silent: true,
radius: [`${x * (index + 1)}%`, `${y + index * 15}%`],
radius: [`${x * (index + 1)}%`, `${y + index * 30}%`],
center: ['50%', '40%'],
label: { show: false },
labelLine: { show: false },
@@ -100,7 +100,7 @@
<style scoped lang="scss">
.ticket {
width: vw(260);
width: vw(240);
height: vh(336);
}
</style>

View File

@@ -25,7 +25,7 @@
grid: {
left: '4%',
right: '4%',
top: '10%',
top: '4%',
bottom: '-4%',
containLabel: false
},
@@ -52,10 +52,8 @@
axisLine: 'none',
show: true,
axisLabel: {
textStyle: {
color: '#fff',
fontSize: fitChartSize(18)
},
color: '#fff',
fontSize: fitChartSize(18),
verticalAlign: 'bottom',
padding: [0, -fitChartSize(10), fitChartSize(10), 0],
inside: true,
@@ -78,7 +76,7 @@
type: 'bar',
barWidth: fitChartSize(12),
showBackground: true,
barBorderRadius: [0, 0, 0, 0],
borderRadius: [0, 0, 0, 0],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
@@ -99,7 +97,7 @@
name: item.name,
value: Number(item.value),
itemStyle: {
barBorderRadius: [0, 0, 0, 0],
borderRadius: [0, 0, 0, 0],
color: parseFloat(item.value) > 50 ? '#FF7021' : '#00CCFF'
}
}

View File

@@ -1,3 +1,4 @@
<!-- 交通负载 -->
<template>
<div class="traffic-flow" :id="id" />
</template>
@@ -43,7 +44,7 @@
left: '4%',
right: '4%',
top: '10%',
bottom: '10%',
bottom: '4%',
containLabel: true
},
xAxis: {
@@ -82,6 +83,14 @@
type: 'bar',
showBackground: true,
barWidth: fitChartSize(16),
label: {
show: true,
position: 'top',
textStyle: {
color: '#fff',
fontSize: fitChartSize(10)
}
},
itemStyle: {
color: {
type: 'linear',

View File

@@ -10,20 +10,26 @@
import { useScenicStore } from '@/stores/scenic'
import { mode, socketBaseUrl, proSocketBaseUrl } from '@/utils/config'
import PubSub from 'pubsub-js'
const scenicStore = useScenicStore()
const { isConnected, sendMessage, dataRes } = useWebSocket(
`${mode == 'dev' ? socketBaseUrl : proSocketBaseUrl}/ws/scenic-spot`
)
let id = ref('')
watch(
() => isConnected.value,
() => [isConnected.value, id.value],
(val) => {
if (val) {
sendMessage(JSON.stringify({ action: 'start', type: '', scenicSpotId: '1' }))
if (val[0] && val[1]) {
sendMessage(JSON.stringify({ action: 'start', type: '', scenicSpotId: id.value }))
}
}
},
{ immediate: true }
)
watch(
() => dataRes.value,
(val) => {
@@ -33,6 +39,9 @@
case 'wordkOrderlist':
scenicStore.setWordkOrderList(val.data)
break
case 'wordkOrderTotal':
scenicStore.setWordkOrderData(val)
break
case 'scenicSpotData':
scenicStore.setScenicSpotData(val.data)
break
@@ -54,10 +63,22 @@
case 'userPortrait':
scenicStore.setUserPortraitData(val)
break
case 'carShipData':
scenicStore.setCarShipData(val.data)
break
}
}
}
)
let scenicChange = null
onMounted(() => {
scenicChange = PubSub.subscribe('scenicChange', (msg, data) => {
id.value = data.scenicSpotId
})
})
onUnmounted(() => {
PubSub.unsubscribe(scenicChange)
})
</script>
<style scoped lang="scss"></style>