feat:对接消息相关接口

This commit is contained in:
zjc
2025-01-08 18:05:12 +08:00
parent ab1ab210a9
commit 9ee304c8c2
39 changed files with 2081 additions and 1926 deletions

View File

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

33
src/api/news.js Normal file
View File

@@ -0,0 +1,33 @@
import request from './request'
// 最新消息
export function getNewsListApi() {
return request({
url: '/api/largeScreen/news/list',
method: 'get'
})
}
// 异常点位告警排行
export function getNewsPointRankApi() {
return request({
url: '/api/largeScreen/news/pointRank',
method: 'get'
})
}
// 消息分类统计
export function getNewsStateApi() {
return request({
url: '/api/largeScreen/news/state',
method: 'get'
})
}
// 消息统计
export function getNewsTotalApi() {
return request({
url: '/api/largeScreen/news/total',
method: 'get'
})
}

View File

@@ -4,95 +4,11 @@ import { ElMessage } from 'element-plus'
const router = useRouter()
const CODE_MESSAGE = {
200: '服务器成功返回请求数据',
201: '新建或修改数据成功',
202: '一个请求已经进入后台排队(异步任务)',
204: '删除数据成功',
400: '发出信息有误',
401: '用户没有权限(令牌失效、用户名、密码错误、登录过期)',
402: '令牌过期',
403: '用户得到授权,但是访问是被禁止的',
404: '访问资源不存在',
406: '请求格式不可得',
410: '请求资源被永久删除,且不会被看到',
500: '服务器发生错误',
502: '网关错误',
503: '服务不可用,服务器暂时过载或维护',
504: '网关超时'
}
/**
* axios响应拦截器
* @param config 请求配置
* @param data response数据
* @param status HTTP status
* @param statusText HTTP status text
* @returns {Promise<*|*>}
*/
const handleData = async ({ data, status, statusText }) => {
// 若data.code存在覆盖默认code
let code = data && data['code'] ? data['code'] : status
// 若code属于操作正常code则status修改为200
const codeVerificationArray = [200, 1, '200', '1']
if (codeVerificationArray.indexOf(data['code']) + 1) code = 1
switch (code) {
case 1:
// 业务层级错误处理以下是假定restful有一套统一输出格式(指不管成功与否都有相应的数据格式)情况下进行处理
// 例如响应内容:
// 错误内容:{ code: 0, msg: '非法参数' }
// 正确内容:{ code: 1, data: { }, msg: '操作正常' }
// return data
return data
case 401:
router
.push({
path: '/',
replace: true
})
.then(() => {})
break
case 402:
router
.push({
path: '/',
replace: true
})
.then(() => {})
break
case 403:
router
.push({
path: '/'
})
.then(() => {})
break
}
// 异常处理
// 若data.msg存在覆盖默认提醒消息
let errMsg = `${
data && data['msg'] ? data['msg'] : CODE_MESSAGE[code] ? CODE_MESSAGE[code] : statusText
}`
if (code === 401) {
errMsg = '您的登录信息已过期,请重新登录...'
} else if (code == 500 && errMsg == '房间不存在') {
console.log('') //大屏直播页面没有直播时做了轮询,如果没有房间,会一直报异常,这里把异常弹窗去掉
} else {
// 是否显示高亮错误(与errorHandler钩子触发逻辑一致)
// $baseMessage(errMsg, 'error', 'vab-hey-message-error', false)
ElMessage({
message: errMsg,
type: 'error',
duration: 5 * 1000
})
}
return Promise.reject(data)
}
/**
* @description axios初始化
*/
const instance = axios.create({
// baseURL: 'http://36.138.38.16:6180/fjtcc-api',
baseURL: 'http://36.138.38.16:8001/fjtcc-api',
timeout: 100000,
headers: {
@@ -102,13 +18,18 @@ const instance = axios.create({
}
})
const isHttpsOrHttp = (url) => {
return /^https?:\/\//i.test(url)
}
/**
* @description axios请求拦截器
*/
instance.interceptors.request.use(
(config) => {
if (config.data && config.headers['Content-Type'] === 'application/json";charset=UTF-8')
config.data = config.data
if (isHttpsOrHttp(config.url)) {
config.baseURL = ''
}
return config
},
(error) => {
@@ -116,21 +37,17 @@ instance.interceptors.request.use(
}
)
/**
* @description axios响应拦截器
*/
instance.interceptors.response.use(
(response) => handleData(response),
(error) => {
const { response } = error
if (response === undefined) {
// Message({
// message: '连接后台接口失败可能由以下原因造成后端不支持跨域CORS、接口地址不存在、请求超时等请联系管理员排查后端接口问题',
// type: 'error',
// duration: 5 * 1000,
// })
return {}
} else return handleData(response)
(res) => {
if (res.data.code == 200) {
return res.data
} else {
return Promise.reject(res.data)
}
},
(err) => {
// 对响应错误做些什么
return Promise.reject(err.response)
}
)

View File

@@ -0,0 +1,6 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 1364">
<circle id="Ellipse 141" cx="4" cy="4" r="4" fill="#D9011B" fill-opacity="0.4"/>
<circle id="Ellipse 142" cx="2" cy="4" r="2" fill="#D9011B"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 267 B

View File

Before

Width:  |  Height:  |  Size: 267 B

After

Width:  |  Height:  |  Size: 267 B

View File

@@ -0,0 +1,6 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 1366">
<circle id="Ellipse 141" cx="4" cy="4" r="4" fill="#2380FB" fill-opacity="0.4"/>
<circle id="Ellipse 142" cx="2" cy="4" r="2" fill="#2380FB"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 267 B

View File

@@ -0,0 +1,6 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 1365">
<circle id="Ellipse 141" cx="4" cy="4" r="4" fill="#FEAE00" fill-opacity="0.4"/>
<circle id="Ellipse 142" cx="2" cy="4" r="2" fill="#FEAE00"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -11,12 +11,11 @@
>
<div>
<p :class="[index % 2 === 0 ? 'item-title--primary' : 'item-title--error']">
三峡之巅-最新异常名称三峡之巅-最新异常名称三峡之巅-最新异常名称
{{ item.cameraName }}
</p>
<!-- <img class="item-img" src="@/assets/images/cover.png" /> -->
<video
class="item-img"
:ref="(el) => getRefs(el, index)"
:ref="(el) => getRefs(el, item, index)"
muted
autoplay
controls
@@ -38,10 +37,15 @@
let list = ref([])
const getRefs = (el) => {
const getRefs = async (el, item) => {
if (el) {
let res = await postRefreshApi({
businessVideoDisplayPosition: item.businessVideoDisplayPosition,
cameraIndexCode: item.cameraIndexCode
})
let hlsUrl = res.data.hlsUrl.replace('http://172.22.15.170:8050', 'http://36.138.38.16:6150')
const hls = new Hls()
hls.loadSource('http://36.138.38.16:6106/live/042f4987808b444aa96c1e8bc6e3ad2a/hls.m3u8')
hls.loadSource(hlsUrl)
hls.attachMedia(el)
hls.on(Hls.Events.MANIFEST_PARSED, () => {
el.play()
@@ -135,7 +139,7 @@
}
&-img {
width: 100%;
height: vw(164);
height: vh(164);
display: block;
object-fit: cover;
}

View File

@@ -1,7 +1,7 @@
<template>
<div class="count-item">
<div class="flex align-center">
<img src="@/assets/images/dian.svg" />
<img src="@/assets/images/dot-primary.svg" />
<span class="label">{{ label }}</span>
</div>
<div class="count">

View File

@@ -131,10 +131,8 @@
{ name: '交通', path: '/traffic' }
]
navRight.value = [
{ name: '停车' },
{ name: '工单', path: '/workOrder' },
{ name: '舆情', path: '/sentiment' },
{ name: '酒店' }
{ name: '舆情', path: '/sentiment' }
]
break
case '/scenic':

View File

@@ -3,28 +3,11 @@
</template>
<script setup>
onMounted(() => {
var map = new BMapGL.Map('container')
map.centerAndZoom(new BMapGL.Point(109.643452, 31.028006), 15)
map.enableScrollWheelZoom(true)
map.setMapType(BMAP_SATELLITE_MAP)
import { useMap } from '@/hooks/map'
// 绘制线
var polyline = new BMapGL.Polyline(
[
new BMapGL.Point(109.65053, 31.034752),
new BMapGL.Point(109.647332, 31.033104),
new BMapGL.Point(109.646883, 31.031797),
new BMapGL.Point(109.640254, 31.021136)
],
{
strokeStyle: 'solid',
strokeColor: 'red ',
strokeWeight: 4,
strokeOpacity: 0.8
}
)
map.addOverlay(polyline)
onMounted(() => {
const { initMap, addMarker } = useMap()
initMap('car-ship', 109.491961, 31.024285, 13)
})
</script>

View File

@@ -3,10 +3,11 @@ import { ref } from 'vue'
export function useMap() {
let map = ref(null)
// 初始化地图
const initMap = (id, lat, lng, scale = 15) => {
const initMap = (id, lat, lng, scale = 15, satellite) => {
map.value = new BMapGL.Map(id)
map.value.centerAndZoom(new BMapGL.Point(lat, lng), scale)
map.value.enableScrollWheelZoom(true)
if (satellite) map.value.setMapType(BMAP_SATELLITE_MAP)
}
// 添加图标

View File

@@ -48,6 +48,7 @@
series: [
{
type: 'pie',
clockwise: false,
silent: true,
center: ['30%', '50%'],
radius: ['60%', '70%'],
@@ -65,6 +66,7 @@
},
{
type: 'pie',
clockwise: false,
silent: true,
center: ['30%', '50%'],
radius: ['54%', '58%'],
@@ -82,6 +84,7 @@
},
{
type: 'pie',
clockwise: false,
silent: true,
center: ['30%', '50%'],
radius: ['0', '40%'],
@@ -95,6 +98,7 @@
},
{
type: 'pie',
clockwise: false,
silent: true,
center: ['30%', '50%'],
radius: ['0', '26%'],
@@ -116,7 +120,7 @@
<style lang="scss" scoped>
.age {
width: vw(290);
width: vw(300);
height: vh(120);
}
</style>

View File

@@ -5,14 +5,14 @@
<img class="more" src="@/assets/images/more.png" />
</template>
</Title1>
<div class="flex pt-20">
<div class="item" v-for="(item, index) in homeData?.scenicSpot" :key="index">
<div class="list flex pt-20">
<div class="item" v-for="(item, index) in 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" /><span class="unit"></span>
<countup :end-val="item.value || 0" /><span class="unit"></span>
</div>
</div>
</div>
@@ -145,7 +145,7 @@
<Title2 title="景区购票数" />
<div class="ticket-wrap">
<img src="@/assets/images/ticket.png" />
<div v-for="(item, index) in homeData?.admission" :key="index">
<div v-for="(item, index) in admission" :key="index">
<span class="label">{{ item.name }}</span><countup :end-val="item.value" />
</div>
</div>
@@ -155,7 +155,7 @@
<div class="flex">
<div class="age-box">
<Title3 title="年龄/性别占比" />
<age />
<div class="mt-8"> <age /></div>
<div class="count">总人数<countup :end-val="ageRateTotal" /></div>
<div
class="cell pt-20"
@@ -193,14 +193,32 @@
</template>
<script setup>
import countup from 'vue-countup-v3'
import top from './top.vue'
import age from './age.vue'
import gauge from './gauge.vue'
import ticket from './ticket.vue'
import countup from 'vue-countup-v3'
const homeData = inject('homeData')
// 景区信息
const scenicSpot = computed(() => {
if (homeData.value) return homeData.value?.scenicSpot
return [
{ value: 0, name: '全县景区数量' },
{ value: 0, name: '核心景区数' },
{ value: 0, name: '低感景区总数' }
]
})
const admission = computed(() => {
if (homeData.value) return homeData.value?.admission
return [
{ name: '当日购票量:', value: 0 },
{ name: '未来3天购票量', value: 0 },
{ name: '3天后购票量', value: 0 }
]
})
// 年龄占比
const ageRateTotal = computed(() => {
return homeData.value?.userPortrait.genderRate.reduce(
(total, current) => Number(current.count) + total,
@@ -208,9 +226,9 @@
)
})
const channelTotal = computed(() => {
return (
homeData.value?.userPortrait?.channel ||
[].reduce((total, current) => Number(current.count) + total, 0)
return homeData.value?.userPortrait?.channel.reduce(
(total, current) => Number(current.count) + total,
0
)
})
</script>
@@ -238,6 +256,9 @@
bottom: -30%;
z-index: 990;
}
.list {
height: vh(72);
}
.item {
position: relative;
height: vh(52);
@@ -258,8 +279,8 @@
left: vw(70);
top: vh(-10);
font-weight: 400;
font-size: vw(14);
color: rgba(255, 255, 255, 0.7);
font-size: vw(16);
color: #ffffff;
}
&-value {
position: absolute;
@@ -299,7 +320,7 @@
justify-content: center;
}
&-title {
font-size: vw(14);
font-size: vw(18);
color: rgba(255, 255, 255, 0.9);
}
&-value {
@@ -321,8 +342,8 @@
}
}
.age-box {
width: vw(290);
height: vh(290);
width: vw(320);
height: vh(296);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
&:nth-child(1) {
@@ -330,8 +351,8 @@
}
}
.box-1 {
width: vw(240);
height: vh(290);
width: vw(230);
height: vh(296);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
&:nth-child(2) {
@@ -374,7 +395,7 @@
color: rgba(255, 255, 255, 0.9);
}
.progress {
width: vw(170);
width: vw(150);
margin-left: vw(4);
:deep(.el-progress-bar__outer) {
background-color: #0858ae !important;

View File

@@ -49,7 +49,19 @@
</li>
</ul>
</div>
<Map />
<div class="map" id="map"></div>
<div class="spot-wrap">
<ul class="spot-list">
<li
class="spot-item"
v-for="(item, index) in spotList"
:key="index"
@click="handleMap(item.scenicSpotId)"
>
<img :src="`http://36.138.38.16:8001/fjtcc-api${item.img}`" />
</li>
</ul>
</div>
</div>
<div class="footer">
<div class="left">
@@ -122,12 +134,24 @@
<script setup>
import countup from 'vue-countup-v3'
import ScrollNumber from '@/components/ScrollNumber/index.vue'
import { getSpotListApi, getBaiduMapCrowdedApi } from '@/api/home'
import icon8 from '@/assets/images/icon-8.png'
import icon9 from '@/assets/images/icon-9.png'
import icon10 from '@/assets/images/icon-10.png'
import icon11 from '@/assets/images/icon-11.png'
let count = ref('6945959')
import { useMap } from '@/hooks/map'
let emit = defineEmits(['switch-spot'])
const { map, initMap, addMarker } = useMap()
const homeData = inject('homeData')
let count = ref(69459)
let spotList = ref([])
let list = ref([
{
label: '安全异常',
@@ -146,14 +170,65 @@
icon: icon11
}
])
const handleMap = (e) => {
emit('switch-spot', e)
// map.value.centerAndZoom(new BMapGL.Point('108.704166', '30.94776'), 16)
}
const getSpotList = async () => {
let res = await getSpotListApi()
spotList.value = res.data
let res1 = await getBaiduMapCrowdedApi({
nodeId: res.data[0].nodeid
})
console.log(res1, 'res1')
}
watch(
() => homeData.value?.baiduMap,
(val) => {
if (val) init(val)
},
{ immediate: true }
)
const init = (val) => {
initMap('map', val.index.lng, val.index.lat, 15, false)
val.list.map((item) => {
item.map((i) => {
// 创建折线
let arr = []
i.path.map((j) => {
arr.push(new BMapGL.Point(j[0], j[1]))
})
var polyline = new BMapGL.Polyline(arr, {
strokeColor: '#1EBA29',
strokeWeight: 4,
strokeOpacity: 0.8
})
map.value.addOverlay(polyline)
})
})
}
onMounted(() => {
getSpotList()
})
</script>
<style lang="scss" scoped>
:deep(.BMap_cpyCtrl) {
display: none;
}
:deep(.anchorBL) {
display: none;
}
.map {
width: 100%;
height: vh(700);
background-color: transparent;
}
.box-3 {
width: vw(1614);
height: vh(950);
margin-top: vh(120);
.header {
width: vw(1614);
height: vh(128);
@@ -163,7 +238,7 @@
background-size: 100% 100%;
.left {
display: flex;
width: vw(950);
width: vw(890);
margin-top: vh(20);
}
.right {
@@ -176,7 +251,7 @@
.label {
margin-bottom: vh(20);
font-weight: 400;
font-size: vw(16);
font-size: vw(18);
color: rgba(255, 255, 255, 0.9);
}
.value {
@@ -224,6 +299,26 @@
}
}
}
.spot-wrap {
position: absolute;
bottom: vw(20);
left: vw(20);
z-index: 99999;
}
.spot-list {
display: flex;
align-items: center;
}
.spot-item {
cursor: pointer;
width: vw(80);
height: vw(80);
margin-right: vw(10);
> img {
width: 100%;
height: 100%;
}
}
.footer {
display: flex;
width: 100%;

View File

@@ -204,7 +204,7 @@
v-for="(item, index) in homeData?.carShipData?.car.info"
:key="index"
>
<div>{{ item.scenic_area }}</div>
<div>{{ item.name }}</div>
<div>{{ item.started_count }}<span class="unit-1"></span></div>
<div>{{ item.not_started_count }}<span class="unit-1"></span></div>
</div>
@@ -213,7 +213,7 @@
</div>
<div>
<div class="ship">
<div class="label">总数</div>
<div class="label">总数</div>
<div class="flex align-center">
<countup class="value" :end-val="homeData?.carShipData?.ship?.count || 0" />
<span class="unit"></span>
@@ -231,7 +231,7 @@
v-for="(item, index) in homeData?.carShipData.ship.info"
:key="index"
>
<div>白帝城</div>
<div>{{ item.name }}</div>
<div>{{ item.started_count }}<span class="unit-1"></span></div>
<div>{{ item.not_started_count }}<span class="unit-1"></span></div>
</div>
@@ -366,7 +366,7 @@
}
.stop-box {
display: flex;
gap: vw(20);
gap: vw(8);
& > div {
flex: 1;

View File

@@ -90,21 +90,19 @@
},
detail: {
width: '100%',
lineHeight: 20,
height: 20,
offsetCenter: [0, '20%'],
valueAnimation: true,
formatter: function (value) {
return '{value|' + value.toFixed(0) + '}{unit|%}'
return '{value|' + value + '}{unit|%}'
},
rich: {
value: {
fontSize: fitChartSize(12),
fontSize: fitChartSize(18),
fontWeight: 'bolder',
color: '#02F9FA'
},
unit: {
fontSize: fitChartSize(12),
fontSize: fitChartSize(18),
color: '#02F9FA'
}
}

View File

@@ -1,3 +1,4 @@
<!-- 酒店入住人数及入住率 -->
<template>
<div class="lodging-ratio" :id="id" />
</template>
@@ -9,10 +10,61 @@
const { id, setOption } = useEchart()
const homeData = inject('homeData')
let params = null
watch(
() => homeData.value?.hotelData?.list,
() => {
setOption({
init()
}
)
const setSeriesData = () => {
return homeData.value?.hotelData?.list.map((item) => {
return {
name: item.hotel_name,
value: item.occupancy_rate,
itemStyle: {
barBorderRadius: [0, 0, 0, 0],
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(255, 112, 33, 0)'
},
{
offset: 1,
color: parseFloat(item.occupancy_rate) > 50 ? '#FF7021' : '#00CCFF'
}
]
}
}
}
})
}
const setCircleData = () => {
return homeData.value?.hotelData?.list.map((item) => {
return {
name: item.hotel_name,
value: item.occupancy_rate,
itemStyle: {
color: '#fff',
opacity: 1
}
}
})
}
const init = () => {
if (!params) {
params = {
backgroundColor: 'transparent',
tooltip: {
show: false
@@ -30,20 +82,20 @@
xAxis: [{ max: 100, show: false }],
yAxis: [
{
type: 'category',
splitLine: {
show: false
},
axisLine: {
show: false
},
type: 'category',
axisTick: {
show: false
},
data: homeData.value?.hotelData?.list.map((item) => item.hotel_name),
axisLabel: {
show: false
}
},
data: homeData.value?.hotelData?.list.map((item) => item.hotel_name)
},
{
type: 'category',
@@ -56,23 +108,21 @@
fontSize: fitChartSize(12)
},
verticalAlign: 'bottom',
padding: [0, 0, fitChartSize(6), 0],
padding: [0, 0, 6, 0],
inside: true,
formatter: function (value) {
return `{value|${value}}`
return `{value|${value}} {value|%}`
},
rich: {
name: {
align: 'center',
color: '#D3E5FF',
fontSize: fitChartSize(14),
fontFamily: 'Source Han Sans CN'
fontSize: fitChartSize(14)
},
value: {
align: 'center',
color: '#fff',
fontSize: fitChartSize(14),
fontFamily: 'Source Han Sans CN'
fontSize: fitChartSize(14)
}
}
},
@@ -81,54 +131,27 @@
],
series: [
{
name: '',
name: '内圆',
type: 'bar',
barWidth: fitChartSize(4),
MaxSize: 0,
barWidth: 4,
showBackground: true,
barBorderRadius: [30, 0, 0, 30],
barBorderRadius: [0, 0, 0, 0],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
label: {
show: true,
offset: [fitChartSize(10), -fitChartSize(20)],
offset: [6, -10],
color: '#D3E5FF',
fontWeight: 500,
position: 'left',
align: 'left',
fontSize: fitChartSize(14),
fontFamily: 'Source Han Sans CN',
formatter: function (params) {
return params.data.name
}
},
data: homeData.value?.hotelData?.list.map((item) => {
return {
name: item.hotel_name,
value: item.occupancy_rate,
itemStyle: {
barBorderRadius: [3, 0, 0, 3],
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(255, 112, 33, 0)'
},
{
offset: 1,
color: 'rgba(255, 112, 33, 1)'
}
]
}
}
}
})
data: setSeriesData()
},
{
name: '外圆',
@@ -140,22 +163,17 @@
symbol: 'circle',
symbolSize: fitChartSize(10),
z: 2,
data: homeData.value?.hotelData?.list.map((item) => {
return {
name: item.hotel_name,
value: item.occupancy_rate,
itemStyle: {
color: '#fff',
opacity: 1
}
}
}),
data: setCircleData(),
animationDelay: 500
}
]
})
}
)
} else {
params.series[0].data = setSeriesData()
params.series[1].data = setCircleData()
}
setOption(params)
}
</script>
<style scoped lang="scss">

View File

@@ -1,5 +1,19 @@
<template>
<div>
<div class="ticket" :id="id" />
<!-- <div class="legend">
<ul class="legend__wrapper">
<li
class="legend-item"
v-for="(item, index) in homeData?.userPortrait?.channel"
:key="index"
>
<p class="legend-item-label">{{ item.name }}</p>
<p class="legend-item-value">{{ item.value }}%</p>
</li>
</ul>
</div> -->
</div>
</template>
<script setup>
@@ -12,13 +26,51 @@
let x = 15
let y = 25
let params = null
watch(
() => homeData.value?.userPortrait?.channel,
() => {
setOption({
(val) => {
if (val) init()
},
{
immediate: true
}
)
const setSeries = () => {
return homeData.value?.userPortrait?.channel.map((item, index) => {
return {
name: item.name,
clockwise: false,
type: 'pie',
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'
}
}
]
}
})
}
const init = () => {
if (!params) {
params = {
backgroundColor: 'transparent',
legend: {
show: true,
x: 'center',
y: 'bottom',
itemHeight: fitChartSize(8),
@@ -41,35 +93,13 @@
}
},
color: ['#F15A25', '#01FEFE', '#12B5FD'],
series:
homeData.value?.userPortrait?.channel ||
[].map((item, index) => {
return {
name: item.name,
type: 'pie',
clockwise: false, //顺时加载
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'
series: setSeries()
}
} else {
params.series = setSeries()
}
]
setOption(params)
}
})
})
}
)
</script>
<style scoped lang="scss">
@@ -77,4 +107,69 @@
width: 100%;
height: vh(200);
}
.legend {
display: flex;
align-items: center;
justify-content: center;
@mixin icon($column) {
padding: 0 vw(10);
height: vh(70);
display: flex;
flex-direction: $column;
align-items: center;
justify-content: center;
}
&__wrapper {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: vw(8);
}
&-item {
color: #fff;
}
&-item:nth-child(1) {
@include icon(column);
background-image: url('@/assets/images/legend-item-1.png');
background-size: 100% 100%;
}
&-item:nth-child(2) {
@include icon(column);
background-image: url('@/assets/images/legend-item-1.png');
background-size: 100% 100%;
}
&-item:nth-child(3) {
@include icon(column);
background-image: url('@/assets/images/legend-item-1.png');
background-size: 100% 100%;
}
&-item:nth-child(4) {
@include icon(column-reverse);
background-image: url('@/assets/images/legend-item-2.png');
background-size: 100% 100%;
}
&-item:nth-child(5) {
@include icon(column-reverse);
background-image: url('@/assets/images/legend-item-2.png');
background-size: 100% 100%;
}
&-item:nth-child(6) {
@include icon(column-reverse);
background-image: url('@/assets/images/legend-item-2.png');
background-size: 100% 100%;
}
&-item-label {
font-weight: 400;
font-size: vw(12);
line-height: vh(14);
}
&-item-value {
font-weight: bold;
font-size: vw(16);
line-height: vh(18);
}
}
</style>

View File

@@ -51,26 +51,19 @@
axisLabel: {
textStyle: {
color: '#fff',
fontSize: fitChartSize(12)
fontSize: fitChartSize(18)
},
verticalAlign: 'bottom',
padding: [0, 0, fitChartSize(10), 0],
padding: [0, -fitChartSize(10), fitChartSize(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'
fontSize: fitChartSize(14)
}
}
},
@@ -79,52 +72,33 @@
],
series: [
{
name: '',
type: 'bar',
barWidth: fitChartSize(12),
MaxSize: 0,
showBackground: true,
barBorderRadius: [fitChartSize(30), 0, 0, fitChartSize(30)],
barBorderRadius: [0, 0, 0, 0],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
label: {
show: true,
offset: [fitChartSize(10), -fitChartSize(17)],
offset: [fitChartSize(12), -fitChartSize(20)],
color: '#D3E5FF',
fontWeight: 500,
position: 'left',
align: 'left',
fontSize: fitChartSize(14),
fontFamily: 'Source Han Sans CN',
formatter: function (params) {
return params.data.name
return params.data.name ?? '其他'
}
},
data: homeData.value?.userPortrait?.provinceRate.map((item, index) => {
data: homeData.value?.userPortrait?.provinceRate.map((item) => {
return {
name: item.name,
value: Number(item.value),
itemStyle: {
barBorderRadius: [3, 0, 0, 3],
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [
{
offset: 0,
color: 'transparent'
},
{
offset: 1,
barBorderRadius: [0, 0, 0, 0],
color: parseFloat(item.value) > 50 ? '#FF7021' : '#00CCFF'
}
]
}
}
}
})
}

View File

@@ -1,6 +1,6 @@
<template>
<box1 />
<box2 />
<box2 @switch-spot="switchSpot" />
<box3 />
</template>
@@ -22,10 +22,20 @@
JSON.stringify({
action: 'start',
type: 'index',
scenitspot: 'index'
scenicSpot: '4'
})
)
}
}
)
const switchSpot = (e) => {
sendMessage(
JSON.stringify({
action: 'start',
type: 'index',
scenicSpot: e
})
)
}
</script>

View File

@@ -5,15 +5,15 @@
</div>
<div class="flex pt-20 p-box">
<div class="item core">
<span class="title-1">全县景区数</span>
<span class="title-1">总核心监控点位</span>
<div class="flex align-end color1"> <countup endVal="895" /><span class="unit"></span></div>
</div>
<div class="item queue">
<span class="title-1">核心景区数</span>
<span class="title-1">核心分析点位</span>
<div class="flex align-end color2"> <countup endVal="895" /><span class="unit"></span></div>
</div>
<div class="item congestion">
<span class="title-1">低感景区总数</span>
<span class="title-1">总异常点位</span>
<div class="flex align-end color3"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
@@ -26,49 +26,65 @@
核心路段监控点位
<div class="item-num color1"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
<div class="item item2">
<div class="box1">
核心景区分析点位
<div class="item-num color1"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
<div class="item item3">
<div class="box1">
异常点位
<div class="item-num color2"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
<div class="item item4">
<div class="box1">
异常告警
<div class="item-num color3"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
<div class="item item5">
<div class="box1">
已处理
<div class="item-num color4"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
</div>
<div class="flex pt-20 p-box">
<div class="box">
<div class="title-3"><span>异常告警</span></div>
<div class="pt-20 chart-p">
<div class="check-box">
<el-select v-model="selectSpot" slot="prepend" placeholder="选择景区">
<el-option label="餐厅名" value="1"></el-option>
<el-option label="订单号" value="2"></el-option>
<el-option label="用户电话" value="3"></el-option>
</el-select>
</div>
<v-chart class="line-chart" :option="optionLine" autoresize />
<Line
:width="370"
:height="180"
: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'
]"
/>
</div>
</div>
<div class="box">
@@ -97,49 +113,65 @@
核心路段监控点位
<div class="item-num color1"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
<div class="item item2">
<div class="box1">
核心景区分析点位
<div class="item-num color1"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
<div class="item item3">
<div class="box1">
异常点位
<div class="item-num color2"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
<div class="item item4">
<div class="box1">
异常告警
<div class="item-num color3"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
<div class="item item5">
<div class="box1">
已处理
<div class="item-num color4"> <countup endVal="895" /><span class="unit"></span></div>
</div>
</div>
</div>
<div class="flex pt-20 p-box">
<div class="box">
<div class="title-3"><span>异常告警</span></div>
<div class="pt-20 chart-p">
<div class="check-box">
<el-select v-model="selectSpot" slot="prepend" placeholder="选择景区">
<el-option label="餐厅名" value="1"></el-option>
<el-option label="订单号" value="2"></el-option>
<el-option label="用户电话" value="3"></el-option>
</el-select>
</div>
<v-chart class="line-chart" :option="optionLine" autoresize />
<Line
:width="370"
:height="180"
: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'
]"
/>
</div>
</div>
<div class="box">
@@ -165,19 +197,15 @@
<script setup>
import * as echarts from 'echarts'
import countup from 'vue-countup-v3'
import { use } from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import { GaugeChart } from 'echarts/charts';
import { PieChart } from 'echarts/charts';
import { LineChart } from 'echarts/charts';
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { GaugeChart } from 'echarts/charts'
import { PieChart } from 'echarts/charts'
import { LineChart } from 'echarts/charts'
import { fitChartSize } from '@/utils/dataUtil'
import {
TitleComponent,
TooltipComponent,
LegendComponent,
} from 'echarts/components';
import VChart, { THEME_KEY } from 'vue-echarts';
import { ref, provide } from 'vue';
import { TitleComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import VChart, { THEME_KEY } from 'vue-echarts'
import { ref, provide } from 'vue'
use([
LineChart,
@@ -186,10 +214,10 @@
PieChart,
TitleComponent,
TooltipComponent,
LegendComponent,
]);
LegendComponent
])
provide(THEME_KEY, 'dark');
provide(THEME_KEY, 'dark')
let selectSpot = ref('')
const option1 = ref({
backgroundColor: 'transparent',
@@ -205,7 +233,7 @@
itemWidth: 5,
itemHeight: 5,
textStyle: {
fontSize:fitChartSize(12),
fontSize: fitChartSize(12)
}
},
series: [
@@ -244,19 +272,18 @@
{ value: 135, name: '异常状况4' },
{ value: 100, name: '异常状况5' },
{ value: 777, name: '异常状况6' },
{ value: 777, name: '其他' },
{ value: 777, name: '其他' }
]
},
],
});
}
]
})
const optionLine = ref({
backgroundColor: 'transparent',
grid: {
top: '50',
bottom: '20',
left: '4%',
containLabel: true,
containLabel: true
},
xAxis: {
nameLocation: 'middle',
@@ -265,7 +292,7 @@
axisLabel: {
fontSize: fitChartSize(10),
interval: 0, // 显示所有标签
rotate: 0, // 旋转标签45度
rotate: 0 // 旋转标签45度
},
data: ['10:00', '10:00', '10:00', '10:00', '10:00', '10:00', '10:00', '10:00'],
axisTisk: {
@@ -274,7 +301,7 @@
length: 1,
lineStyle: {
type: 'dashed', // 设置为虚线
width:0,
width: 0
}
},
axisLine: {
@@ -290,7 +317,7 @@
interval: 15,
axisLabel: {
fontSize: fitChartSize(12), // 设置Y轴刻度字体大小
color: 'rgba(255,255,255,0.9)',
color: 'rgba(255,255,255,0.9)'
},
splitLine: {
show: true,
@@ -307,7 +334,7 @@
type: 'line',
smooth: true,
label: {
show:false,//隐藏坐标点
show: false //隐藏坐标点
},
itemStyle: {
color: 'transparent'
@@ -330,27 +357,25 @@
{
offset: 1,
color: '#009DFF'
},
}
]),
width:3,
width: 3
}
}
]
})
onMounted(() => {})
</script>
<style scoped lang="scss">
:deep(.el-select__wrapper) {
background:linear-gradient( 270deg, rgba(8,41,86,0.16) 0%, #0B61B4 100%);
background: linear-gradient(270deg, rgba(8, 41, 86, 0.16) 0%, #0b61b4 100%);
border: none;
box-shadow: none;
border-top-left-radius: vh(30);
border-bottom-left-radius: vh(30);
font-size: vw(14);
color: #fff;
}
.line-chart {
width: 100%;
@@ -392,7 +417,7 @@
position: relative;
font-weight: 400;
font-size: vw(14);
color: #FFFFFF;
color: #ffffff;
line-height: 16px;
text-align: left;
font-style: normal;
@@ -401,7 +426,7 @@
padding-left: vw(30);
padding: 0 vw(10);
padding-left: vw(15);
background: linear-gradient( 90deg, #1B5EC7 0%, rgba(27,94,199,0) 100%);
background: linear-gradient(90deg, #1b5ec7 0%, rgba(27, 94, 199, 0) 100%);
border-radius: 0px 0px 0px 0px;
height: vh(24);
line-height: vh(24);
@@ -422,7 +447,7 @@
}
}
.li-1 {
background: linear-gradient( 90deg, #FFC10B 0%, rgba(255,209,44,0) 100%);
background: linear-gradient(90deg, #ffc10b 0%, rgba(255, 209, 44, 0) 100%);
.rk-img {
background-image: url('/src/assets/images/rk-1.png');
}
@@ -446,7 +471,6 @@
justify-content: center;
align-items: center;
.item {
margin: vw(10);
width: vw(142);
height: vh(106);
@@ -469,20 +493,20 @@
margin-top: vh(10);
}
.color1 {
color: #02F9FA;
color: #02f9fa;
}
.color2 {
color: #F15A25;
color: #f15a25;
}
.color3 {
color: #E21B1B;
color: #e21b1b;
}
.color4 {
color: #12B5FD;
color: #12b5fd;
}
}
.item1,.item2{
.item1,
.item2 {
background-image: url('/src/assets/images/four-t-1.png');
background-size: 100% 100%;
}
@@ -544,15 +568,14 @@
margin-top: vh(10);
}
.color1 {
color: #12B5FD;
color: #12b5fd;
}
.color2 {
color: #02F9FA;
color: #02f9fa;
}
.color3 {
color: #F15A25;
color: #f15a25;
}
}
.title-1 {
position: absolute;
@@ -562,7 +585,6 @@
color: rgba(255, 255, 255, 0.7);
}
.countup-wrap {
font-size: vw(28);
font-weight: bold;
}

View File

@@ -43,7 +43,7 @@
<div class="footer">
<div class="flex">
<div class="item">
<progress1 :width="140" :height="70" />
<CircleProgress :width="170" :height="90" />
<div>
<div class="bg">
<span class="label">今日工单总条数</span>
@@ -56,7 +56,7 @@
</div>
</div>
<div class="item">
<progress1 :width="140" :height="70" />
<CircleProgress :width="170" :height="90" />
<div>
<div class="bg">
<span class="label">今日工单总条数</span>
@@ -101,9 +101,6 @@
<script setup>
import countup from 'vue-countup-v3'
import progress1 from './progress-1.vue'
let count = ref('58459')
</script>
<style scoped lang="scss">
@@ -178,7 +175,7 @@
.footer {
.item {
flex: 1;
height: vh(120);
height: vh(140);
display: flex;
align-items: center;
background-image: url('@/assets/images/bg-3.png');
@@ -197,6 +194,7 @@
margin-bottom: vh(4);
}
.label {
width: vw(120);
font-weight: 400;
font-size: vw(14);
color: #fff;

View File

@@ -19,7 +19,7 @@
<div class="box mr-8">
<Title1 title="景区承载" />
<div class="flex">
<progress1 :width="200" :height="70" />
<circle-progress :width="200" :height="70" />
<div class="flex flex-1 justify-between">
<count-item label="景区当前人数" :count="35600" suffix="张" />
<count-item label="景区最大承载" :count="35600" suffix="人" />
@@ -36,9 +36,9 @@
<Title1 title="停车信息" />
<div class="flex">
<div class="flex">
<progress1 :width="140" :height="70" />
<progress1 :width="140" :height="70" />
<progress1 :width="140" :height="70" />
<circle-progress :width="140" :height="70" />
<circle-progress :width="140" :height="70" />
<circle-progress :width="140" :height="70" />
</div>
<div class="ml-20 flex flex-1 justify-between">
<count-item label="总停车场数" :count="561" suffix="个" />
@@ -90,7 +90,7 @@
<traffic-flow />
</div>
<div class="border mr-8">
<Title3 title="今日交通负载" />
<Title3 title="拥堵次数占比" />
<jam :width="220" :height="160" />
<div class="legend">
<ul class="legend__wrapper">
@@ -102,7 +102,7 @@
</div>
</div>
<div class="border">
<Title3 title="今日交通负载" />
<Title3 title="拥堵次数占比" />
<jam :width="220" :height="160" />
<div class="legend">
<ul class="legend__wrapper">
@@ -229,7 +229,6 @@
</template>
<script setup>
import progress1 from './progress-1.vue'
import { useMap } from '@/hooks/map'
import carIcon from '@/assets/images/car.png'
import shipIcon from '@/assets/images/ship.png'

View File

@@ -1,12 +1,12 @@
<template>
<div class="traffic-flow" id="traffic-flow" />
<div class="traffic-flow" :id="id" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import * as echarts from 'echarts'
import { useEchart } from '@/hooks/echart'
let trafficChart = null
const { id, setOption } = useEchart()
let option = {
grid: {
@@ -19,7 +19,7 @@
xAxis: {
boundaryGap: true,
type: 'category',
data: ['河北', '山西', '河南', '内蒙', '辽宁'],
data: ['10:00', '10:05', '10:10', '10:15', '10:20'],
axisTick: {
show: false
},
@@ -49,12 +49,11 @@
},
series: [
{
data: [820, 932, 901, 934, 1290],
data: [45, 53, 23, 45, 12],
type: 'bar',
showBackground: true,
barWidth: fitChartSize(8),
barWidth: fitChartSize(16),
itemStyle: {
barBorderRadius: [3, 3, 0, 0],
color: {
type: 'linear',
x: 0,
@@ -62,11 +61,11 @@
colorStops: [
{
offset: 0,
color: 'rgba(0, 208, 255, 0)'
color: 'rgba(0, 99, 255, 1)'
},
{
offset: 1,
color: 'rgba(0, 208, 255, 1)'
color: 'rgba(2, 249, 250, 1)'
}
]
}
@@ -78,17 +77,9 @@
]
}
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()
}
setOption(option)
}
onMounted(() => {
init()
})

View File

@@ -26,11 +26,18 @@
</div>
</div>
</div>
<div class="list-item">
<div class="item-li" v-for="item in 4">
<div class="title-3"><span>拥堵频次占比</span></div>
<div class="chart-box">
<v-chart class="chart" :option="option1" autoresize />
<div class="chart">
<div class="chart-item" v-for="item in 4" :key="item">
<Title3 title="拥堵频次占比" />
<div class="chart__inner">
<spotRate :dataList="spotRateData" :total="8888" />
<ul class="chart__legend">
<li class="chart__legend-item" v-for="(item, index) in spotRateData" :key="index">
<p class="dot" :style="{ background: colors[index] }" />
<p class="name">{{ item.name }}</p>
<p class="value">{{ item.value }}%</p>
</li>
</ul>
</div>
</div>
</div>
@@ -38,204 +45,87 @@
</template>
<script setup>
import spotRate from './spotRate.vue'
import countup from 'vue-countup-v3'
import * as echarts from 'echarts'
import { fitChartSize } from '@/utils/dataUtil'
import Map from '@/components/Map/marker.vue'
import VChart, { THEME_KEY } from 'vue-echarts'
import { ref, provide } from 'vue'
import { getSpotRateApi } from '@/api/workOrder.js'
provide(THEME_KEY, 'dark')
const option1 = ref({
backgroundColor: 'transparent',
tooltip: {
trigger: 'item'
},
legend: {
type: 'scroll',
orient: 'vertical',
right: fitChartSize(10),
top: fitChartSize(70),
bottom: fitChartSize(20),
itemWidth: 5,
itemHeight: 5,
padding: [20, 10, 20, 10],
// itemGap:15,
show: true,
lineStyle: {
borderColor: '#fff',
width: 5
},
textStyle: {
color: '#fff',
fontSize: fitChartSize(16),
lineHeight: 16,
rich: {
d: {
width: '100%',
borderWidth: 0.5,
height: 5,
backgroundColor: '1'
},
a: {
width: fitChartSize(75),
align: 'left',
fontSize: fitChartSize(16)
// backgroundColor:'1'
// color:'1',
// borderColor:'#eee',
// borderWidth:2
const colors = ['#FDC40A', '#FF5232', '#50F0A6', '#5FDFFA']
let spotRateData = ref([])
const getSpotRate = async () => {
let res = await getSpotRateApi()
spotRateData.value = res.data.data
}
}
},
formatter: (e) => {
let ratio = 0
let value = 0
if (option1.value.series[0].data.length > 0) {
option1.value.series[0].data.forEach((item) => {
if (e == item.name) {
ratio = ((item.value * 100) / 3799).toFixed(0)
value = item.value
}
})
}
// return `${e} ${ratio}%
let arr = ['{a|' + e + '}', '{b|' + ratio + '%}']
return arr.join('')
},
itemStyle: {
// color:'#fff'
},
backgroundColor: 'rgba(0,77,136,0.6)'
},
series: [
{
name: 'Access From',
type: 'pie',
left: fitChartSize(-50),
top: fitChartSize(40),
width: fitChartSize(350),
height: fitChartSize(350),
// width:200,
// height:200,
radius: ['40%', '50%'],
avoidLabelOverlap: false,
padAngle: 5,
itemStyle: {
borderRadius: 2
},
label: {
show: false,
position: 'center',
fontWeight: 'bold'
// formatter: function (o) {
// let data = o.data.value
// return `{value|${data}}` + '\n' + `{name|整改率}`
// },
// rich: {
// value: {
// color: '#fff',
// fontSize: fitChartSize(24),
// fontWeight: 'bold',
// padding: [0, 0, 5, 0]
// },
// name: {
// color: '#7894A8',
// fontSize: fitChartSize(12)
// }
// }
},
emphasis: {
label: {
show: true,
fontSize: fitChartSize(16),
fontWeight: 'bold',
color: '#fff',
fontWeight: 'bold',
formatter: function (o) {
let data = o.data.value
return `{value|${data}}` + '\n' + `{name|整改率}`
},
rich: {
value: {
color: '#fff',
fontSize: fitChartSize(24),
fontWeight: 'bold',
padding: [0, 0, 5, 0]
},
name: {
color: '#7894A8',
fontSize: fitChartSize(12)
}
}
}
},
labelLine: {
show: true
},
data: [
{ value: 200, name: '异常状况1' },
{ value: 500, name: '异常状况2' },
{ value: 234, name: '异常状况3' },
{ value: 135, name: '异常状况4' },
{ value: 100, name: '异常状况5' },
{ value: 777, name: '异常状况6' },
{ value: 888, name: '其他' }
]
}
]
onMounted(() => {
getSpotRate()
})
</script>
<style lang="scss" scoped>
.traffic-box-1 {
width: vw(815);
height: vh(975);
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
border-radius: 0px 0px 0px 0px;
margin-top: vh(100);
position: relative;
z-index: 9;
.list-item {
.chart {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
padding: 0 vw(10);
.item-li {
width: vw(388);
gap: vw(8);
padding: vw(8);
&-item {
width: vw(444);
height: vh(420);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
}
.title-3 {
margin-left: vw(10);
&__inner {
display: flex;
align-items: center;
}
&__legend {
flex: 1;
&-item {
position: relative;
width: vw(344);
height: vh(12);
margin-top: vh(20);
background-image: url('@/assets/images/title-6.png');
background-size: 100% 100%;
& > span {
position: absolute;
bottom: vh(4);
left: vw(20);
font-size: vw(15);
font-weight: bold;
background-image: linear-gradient(to bottom, #ffffff 0%, #0096ff 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 兼容WebKit内核浏览器 */
color: transparent; /* 兼容其他浏览器 */
}
}
.chart-box {
width: 100%;
height: vh(370);
height: vh(40);
display: flex;
align-items: center;
margin-bottom: vh(8);
background: linear-gradient(90deg, rgba(0, 150, 255, 0.34) 0%, rgba(0, 150, 255, 0) 100%);
&::before {
position: absolute;
content: '';
width: vw(4);
height: vh(40);
background-color: #0096ff;
}
.dot {
width: vw(4);
height: vw(4);
margin: 0 vw(16);
}
.name {
font-weight: 400;
font-size: vw(12);
color: #ffffff;
width: vw(130);
}
.value {
font-weight: bold;
font-size: vw(15);
color: #ffffff;
}
}
}
}
.traffic-box-1 {
position: relative;
margin-top: vh(120);
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
.title-num {
display: flex;
justify-content: flex-start;

View File

@@ -3,7 +3,6 @@
<!-- nav -->
<div class="left-nav">
<div class="top-box">
<div class="ul">
<div class="li active">路段1</div>
<div class="li">路段1</div>
@@ -15,7 +14,6 @@
</div>
<div class="traffic-box-2">
<div class="map-box">
<Map></Map>
<!-- 视频 -->
<div class="video-list">
@@ -30,21 +28,20 @@
</div>
</div>
</div>
</template>
<script setup>
import Map from '@/components/Map/marker.vue'
const options = reactive({
src: "http://192.168.1.60:8080/live/340200000013200000011_34020000001320000001/hls.m3u8", //视频源
src: 'http://192.168.1.60:8080/live/340200000013200000011_34020000001320000001/hls.m3u8', //视频源
type: 'm3u8', //视频类型
width:"100%",
height:"100%",
color: "#409eff", //主题色
title: "", //视频名称
width: '100%',
height: '100%',
color: '#409eff', //主题色
title: '', //视频名称
muted: false, //静音
webFullScreen: false,
speedRate: ["0.75", "1.0", "1.25", "1.5", "2.0"], //播放倍速
speedRate: ['0.75', '1.0', '1.25', '1.5', '2.0'], //播放倍速
autoPlay: true, //自动播放
loop: false, //循环播放
mirror: false, //镜像画面
@@ -56,18 +53,18 @@
// "quality",
// "speedRate",
// "volume",
"setting",
"pip",
"pageFullScreen",
"fullScreen",
], //显示所有按钮,
});
'setting',
'pip',
'pageFullScreen',
'fullScreen'
] //显示所有按钮,
})
</script>
<style lang="scss" scoped>
.n-box-traffic {
display: flex;
margin-top:vh(100);
margin-top: vh(120);
}
.left-nav {
margin-left: vw(10);
@@ -112,15 +109,15 @@
}
}
.traffic-box-2 {
width:vw(1390);
height:vh(975);
background-image: url('/src/assets/images/map-bg-2.png');
background-size: 100% 100%;
margin:0 vw(10);
padding:vh(35) vw(30);
margin-right:0;
position: relative;
z-index: 9;
width: vw(1290);
height: vh(955);
margin: 0 vw(8);
padding: vw(20);
box-sizing: border-box;
background-image: url('/src/assets/images/map-bg-2.png');
background-size: 100% 100%;
.map-box {
width: 100%;
height: 100%;
@@ -128,7 +125,7 @@
.video-list {
width: vw(320);
height: vh(120);
background: #0A254B;
background: #0a254b;
border-radius: 0px 0px 0px 0px;
position: absolute;
bottom: vw(10);
@@ -150,7 +147,7 @@
font-family: Inter, Inter;
font-weight: 400;
font-size: vw(12);
color: #FFFFFF;
color: #ffffff;
line-height: vh(14);
text-align: left;
font-style: normal;

View File

@@ -4,7 +4,7 @@
<div class="content-1">
<div class="tp-title">
<div class="box-1">
<img class="icon-xl" src="@/assets/images/work-icon-xl-1.png" alt="">
<img class="icon-xl" src="@/assets/images/work-icon-xl-1.png" alt="" />
<div class="xl-title">施家梁子大桥</div>
<div class="tp-item">3号摄像机</div>
<div class="tp-item item-bg-2">3号点位</div>
@@ -13,46 +13,40 @@
</div>
<div class="title-num">
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">路段长度
</div>
<div class="item-num"><span>
<div class="item-t flex"> <img src="@/assets/images/t-icon-1.png" alt="" />路段长度 </div>
<div class="item-num"
><span>
<countup endVal="88895" />
</span> </div>
</span>
</div>
</div>
<div class="item-box">
<div class="item-t flex"> <img src="@/assets/images/t-icon-1.png" alt="" />平均车速 </div>
<div class="item-num"
><span> <countup endVal="88895" /> </span
></div>
</div>
<div class="item-box">
<div class="item-t flex"> <img src="@/assets/images/t-icon-1.png" alt="" />拥堵距离 </div>
<div class="item-num"
><span> <countup endVal="88895" /> </span
></div>
</div>
<div class="item-box">
<div class="item-t flex"> <img src="@/assets/images/t-icon-1.png" alt="" />拥堵次数 </div>
<div class="item-num"
><span> <countup endVal="88895" /> </span
></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">平均车速
</div>
<div class="item-num"><span>
<countup endVal="88895" />
</span></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">拥堵距离
</div>
<div class="item-num"><span>
<countup endVal="88895" />
</span></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">拥堵次数
</div>
<div class="item-num"><span>
<countup endVal="88895" />
</span></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">拥堵开始时间
<img src="@/assets/images/t-icon-1.png" alt="" />拥堵开始时间
</div>
<div class="item-num"><span>12:00:00</span></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">拥堵持续时长
<img src="@/assets/images/t-icon-1.png" alt="" />拥堵持续时长
</div>
<div class="item-num"><span>1h30S</span></div>
</div>
@@ -69,7 +63,7 @@
<div class="content-1 content-2">
<div class="tp-title">
<div class="box-1">
<img class="icon-xl" src="@/assets/images/work-icon-xl-1.png" alt="">
<img class="icon-xl" src="@/assets/images/work-icon-xl-1.png" alt="" />
<div class="xl-title">施家梁子大桥</div>
<div class="tp-item">3号摄像机</div>
<div class="tp-item item-bg-2">3号点位</div>
@@ -78,46 +72,40 @@
</div>
<div class="title-num">
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">路段长度
</div>
<div class="item-num"><span>
<div class="item-t flex"> <img src="@/assets/images/t-icon-1.png" alt="" />路段长度 </div>
<div class="item-num"
><span>
<countup endVal="88895" />
</span> </div>
</span>
</div>
</div>
<div class="item-box">
<div class="item-t flex"> <img src="@/assets/images/t-icon-1.png" alt="" />平均车速 </div>
<div class="item-num"
><span> <countup endVal="88895" /> </span
></div>
</div>
<div class="item-box">
<div class="item-t flex"> <img src="@/assets/images/t-icon-1.png" alt="" />拥堵距离 </div>
<div class="item-num"
><span> <countup endVal="88895" /> </span
></div>
</div>
<div class="item-box">
<div class="item-t flex"> <img src="@/assets/images/t-icon-1.png" alt="" />拥堵次数 </div>
<div class="item-num"
><span> <countup endVal="88895" /> </span
></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">平均车速
</div>
<div class="item-num"><span>
<countup endVal="88895" />
</span></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">拥堵距离
</div>
<div class="item-num"><span>
<countup endVal="88895" />
</span></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">拥堵次数
</div>
<div class="item-num"><span>
<countup endVal="88895" />
</span></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">拥堵开始时间
<img src="@/assets/images/t-icon-1.png" alt="" />拥堵开始时间
</div>
<div class="item-num"><span>12:00:00</span></div>
</div>
<div class="item-box">
<div class="item-t flex">
<img src="@/assets/images/t-icon-1.png" alt="">拥堵持续时长
<img src="@/assets/images/t-icon-1.png" alt="" />拥堵持续时长
</div>
<div class="item-num"><span>1h30S</span></div>
</div>
@@ -135,37 +123,29 @@
<script setup>
import countup from 'vue-countup-v3'
import * as echarts from 'echarts'
import {
fitChartSize
} from '@/utils/dataUtil'
import Map from '@/components/Map/marker.vue'
import VChart, {
THEME_KEY
} from 'vue-echarts';
import {
ref,
provide
} from 'vue';
import { fitChartSize } from '@/utils/dataUtil'
import VChart, { THEME_KEY } from 'vue-echarts'
provide(THEME_KEY, 'dark')
provide(THEME_KEY, 'dark');
const option1 = ref({
backgroundColor: 'transparent',
width: '92%',
// height: fitChartSize(300),
grid: {
top: fitChartSize(90),
bottom: '0',
left: '4%',
containLabel: true,
containLabel: true
},
color: [{
color: [
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
colorStops: [
{
offset: 0,
color: '#0096FF' // 0% 处的颜色
},
@@ -182,13 +162,16 @@
y: 0,
x2: 0,
y2: 1,
colorStops: [{
colorStops: [
{
offset: 0,
color: '#FFCB39' // 0% 处的颜色
}, {
},
{
offset: 1,
color: '#0A4190' // 100% 处的颜色
}],
}
],
global: false // 缺省为 false
},
{
@@ -197,13 +180,16 @@
y: 0,
x2: 0,
y2: 1,
colorStops: [{
colorStops: [
{
offset: 0,
color: '#9846FE' // 0% 处的颜色
}, {
},
{
offset: 1,
color: '#0A4190' // 100% 处的颜色
}],
}
],
global: false // 缺省为 false
},
{
@@ -212,15 +198,18 @@
y: 0,
x2: 0,
y2: 1,
colorStops: [{
colorStops: [
{
offset: 0,
color: '#FF5619' // 0% 处的颜色
}, {
},
{
offset: 1,
color: '#0A4190' // 100% 处的颜色
}],
}
],
global: false // 缺省为 false
},
}
],
legend: {
type: 'plain',
@@ -229,7 +218,7 @@
top: fitChartSize(10),
bottom: fitChartSize(20),
itemWidth: 6,
itemHeight: 6,
itemHeight: 6
},
tooltip: {},
dataset: {
@@ -244,7 +233,7 @@
['13:00', 86.4, 465.2, 282.5, 123],
['13:30', 72.4, 453.9, 239.1, 123],
['14:00', 72.4, 453.9, 239.1, 123],
['14:30', 72.4, 453.9, 239.1],
['14:30', 72.4, 453.9, 239.1]
]
},
xAxis: {
@@ -255,9 +244,9 @@
length: 1,
lineStyle: {
type: 'dashed', // 设置为虚线
width: 0,
width: 0
}
}
},
},
yAxis: {
type: 'value',
@@ -266,7 +255,7 @@
interval: 100,
axisLabel: {
fontSize: fitChartSize(12), // 设置Y轴刻度字体大小
color: '#eee',
color: '#eee'
},
splitLine: {
show: true,
@@ -278,11 +267,14 @@
},
// Declare several bar series, each will be mapped
// to a column of dataset.source by default.
series: [{
series: [
{
type: 'bar'
}, {
},
{
type: 'bar'
}, {
},
{
type: 'bar'
},
{
@@ -294,15 +286,14 @@
<style lang="scss" scoped>
.traffic-box-3 {
margin-top: vh(120);
width: vw(818);
height: vh(480);
background: linear-gradient(330deg, #0B2F64 0%, #062B57 100%);
border-radius: 0px 0px 0px 0px;
padding: vw(10);
padding: vw(8);
position: relative;
z-index: 9;
margin: 0 vw(10);
margin-top: vh(100);
background: linear-gradient(330deg, #0b2f64 0%, #062b57 100%);
.content-2 {
margin-top: vh(20);
}
@@ -310,7 +301,7 @@
.chart-item {
margin-top: vh(10);
height: vh(293);
background: radial-gradient(70% at 99% 50%, #0A4190 0%, rgba(0, 77, 136, 0.6) 100%);
background: radial-gradient(70% at 99% 50%, #0a4190 0%, rgba(0, 77, 136, 0.6) 100%);
border-radius: 0px 0px 0px 0px;
border: 1px solid;
border-image: linear-gradient(180deg, rgba(0, 150, 255, 1), rgba(0, 90, 153, 0)) 1 1;
@@ -350,7 +341,7 @@
display: flex;
justify-content: flex-start;
padding: vw(20);
background: radial-gradient(70% at 99% 50%, #0A4190 0%, rgba(0, 77, 136, 0.6) 100%);
background: radial-gradient(70% at 99% 50%, #0a4190 0%, rgba(0, 77, 136, 0.6) 100%);
border-radius: 0px 0px 0px 0px;
border: 1px solid;
border-image: linear-gradient(180deg, rgba(0, 150, 255, 1), rgba(0, 90, 153, 0)) 1 1;
@@ -373,7 +364,6 @@
width: vw(8);
height: vh(8);
}
}
.item-num {
@@ -383,7 +373,7 @@
background-size: 100% 100%;
font-weight: bold;
font-size: vw(24);
color: #02F9FA;
color: #02f9fa;
text-align: left;
font-style: normal;
text-transform: none;
@@ -401,6 +391,7 @@
}
.tp-title {
height: vh(72);
display: flex;
align-items: center;
@@ -428,19 +419,19 @@
.tp-item {
width: vw(120);
height: vh(72);
height: vw(72);
background-image: url('/src/assets/images/t-a-1.png');
background-size: 100% 100%;
font-weight: 600;
font-size: vw(15);
color: #FFFFFF;
color: #ffffff;
padding-left: vw(42);
text-align: left;
font-style: normal;
text-transform: none;
display: flex;
align-items: center;
margin: 0 vw(20)
margin: 0 vw(20);
}
.item-bg-2 {
@@ -451,18 +442,15 @@
.rt-text {
width: vw(116);
height: vh(40);
height: vw(40);
font-weight: 400;
font-size: vw(14);
color: #FFFFFF;
padding-left: vw(15);
color: #ffffff;
text-align: left;
font-style: normal;
text-transform: none;
line-height: vw(40);
padding-left: vw(40);
background-image: url('/src/assets/images/t-b-1.png');
background-size: 100% 100%;
line-height: vh(40);
padding-left: vw(40);
}
.rt-text-2 {
background-image: url('/src/assets/images/t-b-2.png');

View File

@@ -0,0 +1,94 @@
<template>
<div class="spotRate" :id="id" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import { useEchart } from '@/hooks/echart'
const props = defineProps({
config: {
type: Object,
default: () => {
return {}
}
},
dataList: {
type: Array,
default: () => []
},
total: {
type: Number,
default: () => 0
},
colors: {
type: Array,
default: () => ['#FDC40A', '#FF5232', '#50F0A6', '#5FDFFA']
}
})
const { id, setOption } = useEchart()
var defaultCofig = {
color: [],
series: [
{
type: 'pie',
center: ['50%', '50%'],
radius: ['70%', '90%'],
itemStyle: {
borderWidth: fitChartSize(4),
borderColor: '#093672'
},
label: {
show: true,
position: 'center',
fontWeight: 'bold',
rich: {
value: {
color: '#fff',
fontSize: fitChartSize(24),
fontWeight: 'bold',
padding: [0, 0, 5, 0]
},
name: {
color: '#7894A8',
fontSize: fitChartSize(12)
}
}
},
labelLine: {
show: false
},
data: []
}
]
}
watch(
() => props.dataList,
(newVal) => {
if (newVal.length > 0) {
nextTick(() => {
defaultCofig.color = props.colors
defaultCofig.series[0].data = props.dataList
defaultCofig.series[0].label.formatter = () => {
return `{value|${props.total}}` + '\n' + `{name|工单总数}`
}
setOption({
...defaultCofig,
...props.config
})
})
}
},
{ immediate: true }
)
</script>
<style scoped lang="scss">
.spotRate {
width: vw(240);
height: vh(320);
}
</style>

View File

@@ -1,9 +1,7 @@
<template>
<div class="work-box-1">
<!-- 最新工单 -->
<div class="header">
<div class="title"> <span>最新工单</span> </div>
</div>
<Title1 title="最新工单" />
<!-- 列表 -->
<div class="list">
<div class="li" v-for="(item, index) in list" :key="index">
@@ -32,9 +30,9 @@
width: vw(815);
height: vh(950);
margin-top: vh(120);
box-sizing: border-box;
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
position: relative;
z-index: 99;
padding: 0 vw(20);
.list {
/* 滚动条整体样式 */
@@ -113,30 +111,5 @@
}
}
}
.header {
position: relative;
margin: vh(20);
.title {
margin: vh(5) auto;
width: vw(468);
height: vh(32);
font-weight: 800;
font-size: vw(16);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
background-image: url('@/assets/images/title-4.png');
background-size: 100% 100%;
span {
font-weight: 800;
color: transparent;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
background-image: linear-gradient(to bottom, #ffffff 0%, #87c9ff 100%);
}
}
}
}
</style>

View File

@@ -1,7 +1,9 @@
<template>
<div class="work-box-2">
<div class="box-1">
<div>
<Title1 title="今日工单" />
</div>
<div class="hd-list">
<img class="h-icon" src="@/assets/images/work-icon-1.png" />
<div class="item item1">
@@ -21,7 +23,7 @@
</div>
</div>
<div class="chart">
<div class="chart__wrapper">
<div class="chart__wrapper mr-8">
<Title3 title="工单总数" />
<!-- <div class="check-label">
<span class="active">全部</span>
@@ -55,7 +57,7 @@
</div>
</div>
<div class="progress-item">
<span class="progress-item__label">紧急</span>
<span class="progress-item__label">重要</span>
<div class="progress-item__inner">
<el-progress
:percentage="completeRateData.important * 100"
@@ -86,11 +88,11 @@
</div>
</div>
<div class="chart__wrapper">
<Title3 title="景区工单占比" />
<Title3 title="景区工单类型占比" />
<div class="chart__inner">
<spotRate :dataList="spotRateData" :total="8888" />
<spotRate :dataList="typeRateData" :total="8888" />
<ul class="chart__legend">
<li class="chart__legend-item" v-for="(item, index) in spotRateData" :key="index">
<li class="chart__legend-item" v-for="(item, index) in typeRateData" :key="index">
<p class="dot" :style="{ background: colors[index] }" />
<p class="name">{{ item.name }}</p>
<p class="value">{{ item.value }}%</p>
@@ -99,7 +101,7 @@
<div class="alarm">
<Title2 title="异常点位告警排名" />
<ul class="alarm__wrapper">
<li class="alarm-item" v-for="(item, index) in 8" :key="index">
<li class="alarm-item" v-for="(item, index) in pointRankData" :key="index">
<p
class="alarm-item__rank"
:class="{
@@ -108,9 +110,9 @@
'alarm-item__rank--primary': index == 2
}"
>
{{ item }}
{{ index + 1 }}
</p>
<p class="alarm-item__content">异常点位告警1号名称</p>
<p class="alarm-item__content">{{ item.link_title }}</p>
</li>
</ul>
</div>
@@ -131,7 +133,8 @@
getCompleteRateApi,
getSpotRateApi,
getTypeRateApi
} from '@/api/workOrder.js'
} from '@/api/workOrder'
import { getNewsPointRankApi } from '@/api/news'
const colors = ['#FDC40A', '#FF5232', '#50F0A6', '#5FDFFA']
let totalData = ref({
@@ -146,7 +149,13 @@
let xAxisData = ref([])
let typeRateData = ref([])
let spotRateData = ref([])
let pointRankData = ref([])
const getNewsPointRank = async () => {
let res = await getNewsPointRankApi()
pointRankData.value = res.data
console.log(res, '1111')
}
const getTypeRate = async () => {
let res = await getTypeRateApi()
typeRateData.value = res.data.data
@@ -174,6 +183,7 @@
getCompleteRate()
getTypeRate()
getSpotRate()
getNewsPointRank()
})
</script>
@@ -219,14 +229,13 @@
}
}
.work-box-2 {
width: vw(1522);
margin-top: vh(120);
margin-left: vw(10);
position: relative;
z-index: 99;
margin-left: vw(8);
.box-1 {
height: vh(500);
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
padding: vw(1) 0;
box-sizing: border-box;
background: linear-gradient(to bottom, #0b2f64 0%, #062b57 100%);
.chart {
display: flex;
justify-content: space-between;
@@ -313,7 +322,9 @@
}
}
.box-2 {
height: vh(434);
padding: vw(1);
height: vh(442);
box-sizing: border-box;
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
.chart {

View File

@@ -1,48 +1,69 @@
<template>
<div class="work-box-3">
<!-- 1 -->
<div class="work-2-flex">
<div class="header">
<div class="title"> <span>消息情况</span> </div>
<Title1 title="消息统计" />
<div class="statistics">
<div class="statistics-item">
<div class="flex align-center">
<img class="statistics-item__icon" src="@/assets/images/dot-primary.svg" alt="" />
<span class="statistics-item__label">今日消息总条数</span>
</div>
<div class="hd-list">
<div class="item item1">
工单完成数 <span class="color1"><countup endVal="45678" /></span>
</div>
<div class="item item3">
紧急工单数 <span class=""><countup endVal="45678" /></span>
</div>
<div class="item item2">
重要工单数 <span class=""><countup endVal="45678" /></span>
</div>
<div class="item item1">
普通工单数 <span class=""><countup endVal="45678" /></span>
<div class="statistics-item__value--primary">
<countup end-val="45678" />
<span class="statistics-item__value-suffix"></span>
</div>
</div>
<div class="chart-box flex">
<div class="box-item">
<v-chart class="line-chart" :option="option" autoresize />
<div class="statistics-item">
<div class="flex align-center">
<img class="statistics-item__icon" src="@/assets/images/dot-error.svg" alt="" />
<span class="statistics-item__label">紧急消息</span>
</div>
<div class="box-item">
<v-chart class="line-chart" :option="option" autoresize />
<div class="statistics-item__value--error">
<countup end-val="45678" />
<span class="statistics-item__value-suffix"></span>
</div>
<div class="box-item">
<v-chart class="line-chart" :option="option" autoresize />
</div>
<div class="statistics-item">
<div class="flex align-center">
<img class="statistics-item__icon" src="@/assets/images/dot-warning.svg" alt="" />
<span class="statistics-item__label">重要消息</span>
</div>
<div class="statistics-item__value--warning">
<countup end-val="45678" />
<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="" />
<span class="statistics-item__label">普通消息</span>
</div>
<div class="statistics-item__value--success">
<countup end-val="45678" />
<span class="statistics-item__value-suffix"></span>
</div>
</div>
</div>
<!-- 2 -->
<div class="chart-box">
<pie :width="150" :height="150" />
<pie :width="150" :height="150" />
<pie :width="150" :height="150" />
</div>
</div>
<div class="work-box-1">
<!-- 最新工单 -->
<div class="header">
<div class="title"> <span>消息情况</span> </div>
</div>
<!-- 列表 -->
<Title1 title="消息情况" />
<div class="list">
<div class="li" v-for="item in 40">
<span class="label label1">普通</span>
<p>这是一条工单信息这是一条工单信息这是一条工单信息这是一条工单信息这是一条</p>
<span class="time">2024-12.16 23:58</span>
<div class="item" v-for="(item, index) in list" :key="index">
<span
:class="{
'label--primary': item.level == 'normal',
'label--warning': item.level == 'warn',
'label--error': item.level == 'important'
}"
>{{ item.level_text }}</span
>
<p>{{ item.title }}</p>
<span class="time">{{ item.time }}</span>
</div>
</div>
</div>
@@ -51,162 +72,42 @@
<script setup>
import countup from 'vue-countup-v3'
import * as echarts from 'echarts'
import { fitChartSize } from '@/utils/dataUtil'
import Map from '@/components/Map/marker.vue'
import VChart, { THEME_KEY } from 'vue-echarts'
import { ref, provide } from 'vue'
provide(THEME_KEY, 'dark')
const option = ref({
backgroundColor: 'transparent',
title: [
{
text: '45.5%',
x: 'center',
top: '34%',
textStyle: {
color: '#00D0FF',
fontSize: fitChartSize(12)
import pie from './pie.vue'
import { getNewsListApi, getNewsStateApi, getNewsTotalApi } from '@/api/news'
const list = ref([])
const getNewsTotal = async () => {
let res = await getNewsTotalApi()
console.log(res, '============')
}
},
{
text: '完成率',
x: 'center',
top: '56%',
textStyle: {
color: '#fff',
fontSize: fitChartSize(12)
const getNewsState = async () => {
let res = await getNewsStateApi()
}
const getNewsList = async () => {
let res = await getNewsListApi()
list.value = res.data
}
],
series: [
// 内侧环
{
type: 'gauge',
radius: '100%',
center: ['50%', '50%'],
min: 0,
max: 100,
startAngle: 360,
endAngle: 0,
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: 360,
endAngle: 0,
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
}
]
}
]
onMounted(() => {
getNewsList()
getNewsTotal()
getNewsState()
})
</script>
<style lang="scss" scoped>
.work-box-3 {
width: vw(813);
height: vh(380);
width: vw(840);
margin-top: vh(120);
margin-left: vw(10);
position: relative;
z-index: 99;
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
margin-left: vw(8);
.work-box-1 {
width: vw(815);
height: vh(588);
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
position: relative;
z-index: 99;
padding: 0 vw(20);
height: vh(566);
display: flex;
flex-direction: column;
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
.list {
/* 滚动条整体样式 */
&::-webkit-scrollbar {
@@ -221,15 +122,15 @@
background: rgba(0, 150, 255, 0.63); /* 滑块的背景色 */
border-radius: 5px; /* 滑块的圆角 */
}
height: vh(510);
height: vh(500);
overflow: auto;
.li:nth-child(odd) {
.item:nth-child(odd) {
background: rgba(3, 78, 153, 0.3);
}
.li:nth-child(even) {
.item:nth-child(even) {
background-color: transparent;
}
.li {
.item {
display: flex;
align-items: center;
justify-content: flex-start;
@@ -245,13 +146,16 @@
display: inline-block;
margin-right: vw(10);
}
.label1 {
.label--primary {
@extend .label;
background: #2380fb;
}
.label2 {
.label--warning {
@extend .label;
background: #feae00;
}
.label3 {
.label--error {
@extend .label;
background: #d9011b;
}
.time {
@@ -281,129 +185,73 @@
}
}
}
.header {
position: relative;
margin: 0 vh(20);
.title {
margin: vh(5) auto;
width: vw(468);
height: vh(32);
font-weight: 800;
font-size: vw(16);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
background-image: url('@/assets/images/title-4.png');
background-size: 100% 100%;
span {
font-weight: 800;
color: transparent;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
background-image: linear-gradient(to bottom, #ffffff 0%, #87c9ff 100%);
}
}
}
}
.work-2-flex {
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
display: flex;
flex-direction: column;
height: vh(380);
overflow: hidden;
margin-bottom: vw(8);
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
.chart-box {
padding: vw(20) vw(15);
display: flex;
justify-content: space-between;
padding: vw(20) vw(15);
height: vh(360);
padding-top: vh(35);
box-sizing: border-box;
.box-item {
flex: 1;
width: vw(140);
height: vh(140);
padding: vw(10);
.line-chart {
// width:100%;
// height:100%;
}
}
}
.header {
position: relative;
margin: vh(20);
.title {
margin: vh(5) auto;
width: vw(468);
height: vh(32);
font-weight: 800;
font-size: vw(16);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
background-image: url('@/assets/images/title-4.png');
background-size: 100% 100%;
span {
font-weight: 800;
color: transparent;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
background-image: linear-gradient(to bottom, #ffffff 0%, #87c9ff 100%);
}
}
}
.hd-list {
display: flex;
justify-content: center;
position: relative;
.statistics {
padding: 0 vw(20);
.h-icon {
position: absolute;
left: vw(70);
top: 50%;
transform: translateY(-50%);
width: vw(74);
height: vh(74);
}
.item {
display: flex;
width: vw(210);
height: vh(58);
line-height: vh(58);
padding-left: vw(10);
text-align: center;
margin: 0 vw(15);
justify-content: space-around;
&-item {
font-weight: 400;
font-size: vw(14);
color: rgba(255, 255, 255, 0.9);
text-align: left;
font-style: normal;
text-transform: none;
span {
font-size: vw(24);
}
&-item__icon {
width: vw(24);
height: vw(24);
}
&-item__label {
color: #fff;
font-size: vw(14);
}
&-item__value {
position: relative;
top: vh(2);
margin-left: vw(5);
}
.color1 {
margin-top: vh(16);
padding-left: vw(12);
padding-bottom: vh(12);
display: flex;
align-items: center;
font-weight: bold;
font-size: vw(28);
color: #fff;
&--primary {
@extend .statistics-item__value;
color: #02f9fa;
}
}
.item1 {
background-image: url('@/assets/images/work-n-bg-1.png');
background-image: url('@/assets/images/mask-primary.png');
background-size: 100% 100%;
}
.item2 {
background-image: url('@/assets/images/work-n-bg-2.png');
&--error {
@extend .statistics-item__value;
background-image: url('@/assets/images/mask-error.png');
background-size: 100% 100%;
}
.item3 {
background-image: url('@/assets/images/work-n-bg-3.png');
&--warning {
@extend .statistics-item__value;
background-image: url('@/assets/images/mask-warning.png');
background-size: 100% 100%;
}
&--success {
@extend .statistics-item__value;
background-image: url('@/assets/images/mask-success.png');
background-size: 100% 100%;
}
}
&-item__value-suffix {
font-size: vw(12);
margin-top: vh(6);
}
}
}
}

View File

@@ -0,0 +1,157 @@
<template>
<div class="pie" :id="id" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import { useEchart } from '@/hooks/echart'
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()
})
</script>
<style lang="scss" scoped>
.pie {
width: vw(200);
height: vh(200);
}
</style>

View File

@@ -31,7 +31,8 @@ export default defineConfig({
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
extensions: ['.js', '.vue']
},
css: {
preprocessorOptions: {