feat:对接景区数据和监控视频

This commit is contained in:
zjc
2025-01-16 19:41:46 +08:00
parent 0c00523114
commit fd8ede8a32
20 changed files with 534 additions and 541 deletions

View File

@@ -90,7 +90,7 @@
<div class="mt-8">
<age :list="homeStore.userPortraitData?.ageRate" />
</div>
<div v-if="ageRateTotal > 0" class="count">总人数:<countup :end-val="ageRateTotal" /></div>
<div class="count">总人数<countup :end-val="ageRateTotal" /></div>
<div
class="cell pt-20"
v-for="(item, index) in homeStore?.userPortraitData.genderRate"

View File

@@ -58,38 +58,44 @@
<div class="flex justify-center pt-10">
<div class="item">
<p class="label">今日工单总条数</p>
<countup :endVal="1234" />
<countup :end-val="homeStore.wordkOrderData.toDayData.count" />
</div>
<div class="item">
<p class="label">工单完成数</p>
<countup :endVal="1234" />
<countup class="complete" :end-val="homeStore.wordkOrderData.toDayData.end" />
</div>
</div>
<div class="progress-box">
<span class="text">工单完成数</span>
<div class="progress-1">
<el-progress :percentage="50" :show-text="false" />
<el-progress
:percentage="homeStore.wordkOrderData.toDayData.rate"
:show-text="false"
/>
</div>
<span class="value">50%</span>
<span class="value">{{ homeStore.wordkOrderData.toDayData.rate }}%</span>
</div>
</div>
<div>
<div class="flex justify-center pt-10">
<div class="item">
<p class="label">紧急工单数</p>
<countup :endVal="1234" />
<countup class="error" :end-val="homeStore.wordkOrderData.warnData.count" />
</div>
<div class="item">
<p class="label">紧急工单完成数</p>
<countup :endVal="1234" />
<countup class="complete" :end-val="homeStore.wordkOrderData.warnData.end" />
</div>
</div>
<div class="progress-box">
<span class="text">工单完成数</span>
<div class="progress-2">
<el-progress :percentage="50" :show-text="false" />
<el-progress
:percentage="homeStore.wordkOrderData.warnData.rate"
:show-text="false"
/>
</div>
<span class="value">50%</span>
<span class="value">{{ homeStore.wordkOrderData.warnData.rate }}%</span>
</div>
</div>
</div>
@@ -162,9 +168,10 @@
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
})
console.log(res1, 'res1')
}
watch(
@@ -338,16 +345,22 @@
justify-content: center;
}
.label {
margin-bottom: vh(10);
font-weight: 400;
font-size: vw(14);
margin-bottom: vh(10);
color: rgba(255, 255, 255, 0.9);
color: #ffffff;
}
.countup-wrap {
color: #02f9fa;
font-size: vw(28);
font-weight: bold;
}
.error {
color: #e21b1b;
}
.complete {
color: #ffffff;
}
.progress-box {
margin-top: vh(10);
display: flex;
@@ -388,7 +401,7 @@
}
}
.right {
margin-top: vh(8);
margin-top: vh(4);
padding: vh(10) vw(10);
height: vh(106);
width: vw(1040);

View File

@@ -359,7 +359,7 @@
flex: 1;
& > div {
position: relative;
height: vh(110);
height: vh(114);
display: flex;
align-items: center;
background-image: url('@/assets/images/bg-4.png');

View File

@@ -127,7 +127,7 @@
fontSize: fitChartSize(12)
},
verticalAlign: 'bottom',
padding: [0, 0, 6, 0],
padding: [0, 0, 10, 0],
inside: true,
formatter: function (value) {
return `{value|${value}}{value|%}`
@@ -156,7 +156,7 @@
},
label: {
show: true,
offset: [10, -10],
offset: [10, -14],
color: '#fff',
fontWeight: 500,
position: 'left',

View File

@@ -66,7 +66,7 @@
legend: {
show: true,
x: 'center',
y: 'bottom',
y: '70%',
itemHeight: fitChartSize(12),
itemWidth: fitChartSize(12),
itemGap: fitChartSize(6),

View File

@@ -2,7 +2,6 @@
<box1 />
<box2 @switch-spot="switchSpot" />
<box3 />
</template>
<script setup>
@@ -63,6 +62,9 @@
case 'wordkOrderlist':
homeStore.setWordkOrderList(val.data)
break
case 'wordkOrderTotal':
homeStore.setWordkOrderData(val)
break
case 'trafficInformation':
homeStore.setTrafficInfoData(val)
break

View File

@@ -26,9 +26,9 @@
</script>
<style lang="scss" scoped>
.box-2 {
margin-top: vh(120);
width: vw(2356);
height: vh(965);
display: flex;
flex: 1;
margin-top: vh(120);
height: vh(965);
}
</style>

View File

@@ -67,29 +67,24 @@
</div>
<div class="flex align-center justify-between">
<div>
<div class="cell">
<span class="tag tag--success">普通</span>
<p class="content">
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
</p>
</div>
<div class="cell">
<span class="tag tag--error">普通</span>
<p class="content">
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
</p>
</div>
<div class="cell">
<span class="tag tag--primary">普通</span>
<p class="content">
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
</p>
</div>
<vue3-seamless-scroll
class="list"
:list="scenicStore.wordkOrderList"
:limitScrollNum="3"
:hover="true"
:step="0.2"
:wheel="true"
:isWatch="true"
>
<div class="cell" v-for="(item, index) in scenicStore.wordkOrderList" :key="index">
<span :class="`tag--${item.level}`">{{ item.level_text }}</span>
<p class="content">
{{ item.title }}
</p>
</div>
</vue3-seamless-scroll>
</div>
<img class="more" src="@/assets/images/more-col.png" alt="查看更多" />
<img class="more" src="@/assets/images/more-col.png" alt="查看更多" @click="handleMore" />
</div>
</div>
</div>
@@ -97,9 +92,16 @@
<script setup>
import countup from 'vue-countup-v3'
import { Vue3SeamlessScroll } from 'vue3-seamless-scroll'
import { useScenicStore } from '@/stores/scenic'
import { useRouter } from 'vue-router'
const router = useRouter()
const scenicStore = useScenicStore()
const handleMore = () => {
router.push('/workOrder')
}
</script>
<style scoped lang="scss">
@@ -205,6 +207,11 @@
}
}
}
.list {
margin-top: vh(4);
height: vh(116);
overflow: hidden;
}
.cell {
display: flex;
margin-left: vw(4);
@@ -216,30 +223,28 @@
padding: 0 vw(16);
font-weight: bold;
font-size: vw(14);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
border-radius: vw(2);
&--success {
color: #02f9fa;
border: 1px solid #02f9fa;
box-shadow: inset 0 0 vw(8) 0 #0be1ab;
}
&--error {
color: #ee2c2c;
border: 1px solid #ee2c2c;
box-shadow: inset 0 0 vw(8) 0 #ee2c2c;
}
&--primary {
color: #00aaff;
border: 1px solid #00aaff;
box-shadow: inset 0 0 vw(8) 0 #00aaff;
}
}
.tag--important {
@extend .tag;
background: #feae00;
}
.tag--warn {
@extend .tag;
background: #d9011b;
}
.tag--normal {
@extend .tag;
background: #2380fb;
}
.content {
margin-left: vw(4);
padding: 0 vw(10);
width: vw(660);
width: vw(760);
height: vh(24);
line-height: vh(24);
white-space: nowrap;

View File

@@ -115,7 +115,7 @@
label="安全告警占比"
:dataList="scenicStore.secureData.dataList"
:total="alarmTotal"
:width="350"
:width="460"
:height="300"
/>
</div>
@@ -141,26 +141,10 @@
<div class="border mr-8">
<Title3 title="拥堵次数占比" />
<jam :width="220" :height="160" />
<!-- <div class="legend">
<ul class="legend__wrapper">
<li class="legend-item" v-for="(item, index) in 6" :key="index">
<p class="legend-item-label">路段1</p>
<p class="legend-item-value">10%</p>
</li>
</ul>
</div> -->
</div>
<div class="border">
<Title3 title="拥堵时长占比" />
<jam :width="220" :height="160" />
<!-- <div class="legend">
<ul class="legend__wrapper">
<li class="legend-item" v-for="(item, index) in 6" :key="index">
<p class="legend-item-label">路段1</p>
<p class="legend-item-value">10%</p>
</li>
</ul>
</div> -->
</div>
</div>
</div>

View File

@@ -1,195 +1,106 @@
<!-- 购票来源 -->
<template>
<div class="ticket" :id="id" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import * as echarts from 'echarts'
import { guid } from '@/utils/util'
import { useEchart } from '@/hooks/echart'
let id = ref(guid())
let ticketChart = null
var qxzbData = [
{
name: '处理中',
value: 500,
full: 1000 // 最大值
import { useScenicStore } from '@/stores/scenic'
const scenicStore = useScenicStore()
const { id, setOption } = useEchart()
let x = 15
let y = 25
let params = null
watch(
() => scenicStore.userPortraitData.data.channel,
(val) => {
if (val.length > 0) {
setTimeout(() => {
init()
}, 1000)
}
},
{
name: '超期中',
value: 756,
full: 1000
},
{
name: '延期中',
value: 800,
full: 1000
immediate: true
}
]
var returnData = function (qxzbData, name) {
for (var a = 0; a < qxzbData.length; a++) {
if (qxzbData[a].name == name) {
return qxzbData[a].value
)
const setSeries = () => {
return scenicStore.userPortraitData.data.channel.map((item, index) => {
return {
name: item.name,
type: 'pie',
clockwise: false,
silent: true,
radius: [`${x * (index + 1)}%`, `${y + index * 15}%`],
center: ['50%', '40%'],
label: { show: false },
labelLine: { show: false },
emphasis: { show: false },
data: [
{
value: parseFloat(item.value),
name: item.name
},
{
value: 100,
itemStyle: {
color: '#07439C'
}
}
]
}
}
}
let option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'item',
formatter: function (rsp) {
if (rsp.name != '') {
return rsp.name + '<br/>' + rsp.marker + ' ' + rsp.value + ' ' + rsp.percent + ''
}
}
},
legend: {
x: 'center',
y: 'bottom',
data: qxzbData,
itemHeight: fitChartSize(8),
itemWidth: fitChartSize(8),
itemGap: fitChartSize(20),
formatter: function (name) {
return '{title|' + name + '} {value|' + returnData(qxzbData, name) + '}'
},
textStyle: {
rich: {
title: {
color: '#fff',
fontSize: fitChartSize(14)
},
value: {
color: '#00D5F6',
fontSize: fitChartSize(14)
}
}
}
},
color: ['#F15A25', '#01FEFE', '#12B5FD'],
series: [
{
name: qxzbData[0].name, //最里面的圈
type: 'pie',
clockWise: false, //顺时加载
radius: ['15%', '25%'],
center: ['50%', '40%'],
label: {
normal: {
show: false
}
},
labelLine: {
show: false
},
itemStyle: {
emphasis: {
show: false
}
},
data: [
{
value: qxzbData[0].value,
name: qxzbData[0].name
},
{
value: qxzbData[0].full,
itemStyle: {
normal: {
color: '#07439C'
}
}
}
]
},
{
name: qxzbData[1].name, //第二个圈
type: 'pie',
clockWise: false, //顺时加载
radius: ['35%', '45%'],
center: ['50%', '40%'],
label: {
normal: {
show: false
}
},
labelLine: {
show: false
},
itemStyle: {
emphasis: {
show: false
}
},
data: [
{
value: qxzbData[1].value,
name: qxzbData[1].name
},
{
value: qxzbData[1].full,
itemStyle: {
normal: {
color: '#07439C'
}
}
}
]
},
{
name: qxzbData[2].name, //最外层圈
type: 'pie',
clockWise: false, //顺时加载
radius: ['55%', '65%'],
center: ['50%', '40%'],
label: {
normal: {
show: false
}
},
labelLine: {
show: false
},
itemStyle: {
emphasis: {
show: false
}
},
data: [
{
value: qxzbData[2].value,
name: qxzbData[2].name
},
{
value: qxzbData[2].full,
itemStyle: {
normal: {
color: '#07439C'
}
}
}
]
}
]
})
}
const init = () => {
ticketChart = echarts.init(document.getElementById(id.value))
ticketChart.setOption(option)
}
const resize = () => {
if (ticketChart) {
ticketChart.resize()
if (!params) {
params = {
backgroundColor: 'transparent',
legend: {
show: true,
x: 'center',
y: '70%',
itemHeight: fitChartSize(12),
itemWidth: fitChartSize(12),
itemGap: fitChartSize(6),
formatter: function (name) {
let obj = scenicStore.userPortraitData.data.channel.find((item) => item.name == name)
return '{name|' + name + '} {value|' + obj?.value + '}{value|%}'
},
textStyle: {
rich: {
name: {
color: '#fff',
fontSize: fitChartSize(12)
},
value: {
color: '#00D5F6',
fontWeight: 600,
fontSize: fitChartSize(12)
}
}
}
},
color: ['#F15A25', '#01FEFE', '#12B5FD'],
series: setSeries()
}
} else {
params.series = setSeries()
}
setOption(params)
}
onMounted(() => {
init()
window.addEventListener('resize', resize)
})
</script>
<style scoped lang="scss">
.ticket {
width: vw(250);
height: vh(300);
width: vw(260);
height: vh(336);
}
</style>

View File

@@ -1,187 +1,134 @@
<!-- 客源地分析TOP5 -->
<template>
<div class="top" :id="id" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import * as echarts from 'echarts'
import { guid } from '@/utils/util'
import { useEchart } from '@/hooks/echart'
import { useScenicStore } from '@/stores/scenic'
const scenicStore = useScenicStore()
const { id, setOption } = useEchart()
let params = null
let id = ref(guid())
let topChart = null
let colorList = [
'rgba(255, 38, 38, 1)',
'rgba(255, 96, 0, 1)',
'rgba(255, 165, 7, 1)',
'rgba(0, 234, 255, 1)',
'rgba(0, 132, 255, 1)',
'#2379FF'
]
let colorListA = [
'rgba(255, 38, 38, 0.1)',
'rgba(255, 96, 0, 0.1)',
'rgba(255, 165, 7, 0.1)',
'rgba(0, 234, 255, 0.1)',
'rgba(0, 132, 255, 0.1)',
'#49B1FF'
]
let result = [
{ name: '河北', value: 86 },
{ name: '山西', value: 83 },
{ name: '河南', value: 73 },
{ name: '内蒙', value: 61 },
{ name: '辽宁', value: 61 }
]
let option = {
color: colorList,
backgroundColor: 'transparent',
tooltip: {
show: false
},
legend: {
show: false
},
grid: {
left: '4%',
right: '4%',
top: '6%',
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, 10, 0],
inside: true,
formatter: function (value) {
return `{value|${value}}`
},
rich: {
name: {
align: 'center',
color: '#D3E5FF',
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(12),
MaxSize: 0,
showBackground: true,
barBorderRadius: [30, 0, 0, 30],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
label: {
show: true,
offset: [10, -17],
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: colorListA[index]
},
{
offset: 1,
color: colorList[index]
}
]
}
}
}
})
}
]
}
const init = () => {
topChart = echarts.init(document.getElementById(id.value))
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: '10%',
bottom: '-4%',
containLabel: false
},
xAxis: [{ max: 100, show: false }],
yAxis: [
{
type: 'category',
splitLine: {
show: false
},
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
}
},
{
type: 'category',
axisTick: 'none',
axisLine: 'none',
show: true,
axisLabel: {
textStyle: {
color: '#fff',
fontSize: fitChartSize(18)
},
verticalAlign: 'bottom',
padding: [0, -fitChartSize(10), fitChartSize(10), 0],
inside: true,
formatter: function (value) {
return `{value|${value}}%`
},
rich: {
value: {
align: 'center',
color: '#fff',
fontSize: fitChartSize(14)
}
}
},
data: scenicStore.userPortraitData.data.provinceRate.map((item) => Number(item.value))
}
],
series: [
{
type: 'bar',
barWidth: fitChartSize(12),
showBackground: true,
barBorderRadius: [0, 0, 0, 0],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
label: {
show: true,
offset: [fitChartSize(12), -fitChartSize(20)],
color: '#D3E5FF',
fontWeight: 500,
position: 'left',
align: 'left',
fontSize: fitChartSize(14),
formatter: function (params) {
return params.data.name ?? '其他'
}
},
data: scenicStore.userPortraitData.data.provinceRate.map((item) => {
return {
name: item.name,
value: Number(item.value),
itemStyle: {
barBorderRadius: [0, 0, 0, 0],
color: parseFloat(item.value) > 50 ? '#FF7021' : '#00CCFF'
}
}
})
}
]
}
}
setOption(params)
}
onMounted(() => {
init()
window.addEventListener('resize', resize)
})
watch(
() => scenicStore.userPortraitData.data.provinceRate,
(val) => {
if (val.length > 0) {
setTimeout(() => {
init()
}, 1000)
}
},
{
immediate: true
}
)
</script>
<style scoped lang="scss">
.top {
width: vw(240);
height: vh(360);
width: vw(250);
height: vh(366);
}
</style>

View File

@@ -30,6 +30,9 @@
if (val) {
console.log(val, '接受消息')
switch (val.type) {
case 'wordkOrderlist':
scenicStore.setWordkOrderList(val.data)
break
case 'scenicSpotData':
scenicStore.setScenicSpotData(val.data)
break