style:首页

This commit is contained in:
zjc
2024-12-16 17:38:50 +08:00
parent cc9782e007
commit 0c148685d2
39 changed files with 2838 additions and 1282 deletions

1235
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,11 +10,13 @@
}, },
"dependencies": { "dependencies": {
"@newpanjing/datav-vue3": "^0.0.0-alpha.0", "@newpanjing/datav-vue3": "^0.0.0-alpha.0",
"axios": "^1.7.9",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"element-plus": "^2.9.0",
"pinia": "^2.2.6", "pinia": "^2.2.6",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-echarts": "^7.0.3",
"vue-countup-v3": "^1.4.2", "vue-countup-v3": "^1.4.2",
"vue-echarts": "^7.0.3",
"vue-router": "^4.4.5" "vue-router": "^4.4.5"
}, },
"devDependencies": { "devDependencies": {
@@ -23,6 +25,7 @@
"sass": "^1.82.0", "sass": "^1.82.0",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",
"unplugin-auto-import": "^0.18.6", "unplugin-auto-import": "^0.18.6",
"unplugin-vue-components": "^0.27.5",
"vite": "^6.0.1", "vite": "^6.0.1",
"vite-plugin-vue-devtools": "^7.6.5" "vite-plugin-vue-devtools": "^7.6.5"
} }

146
src/api/request.js Normal file
View File

@@ -0,0 +1,146 @@
import axios from 'axios'
import qs from 'qs'
import router from '@/router'
import { Message } from 'element-ui'
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)
Message({
message: errMsg,
type: 'error',
duration: 5 * 1000
})
}
return Promise.reject(data)
}
/**
* @description axios初始化
*/
const instance = axios.create({
baseURL: '/dq_api',
timeout: 100000,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
/**
* @description axios请求拦截器
*/
instance.interceptors.request.use(
(config) => {
// 规范写法 不可随意自定义
let urlParams = {}
if (config.params) {
urlParams = {
...config.params
}
}
config.url = config.url + '?' + qs.stringify(urlParams)
if (
config.data &&
config.headers['Content-Type'] === 'application/x-www-form-urlencoded;charset=UTF-8'
)
config.data = qs.stringify(config.data)
return config
},
(error) => {
return Promise.reject(error)
}
)
/**
* @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)
}
)
export default instance

21
src/api/sys.js Normal file
View File

@@ -0,0 +1,21 @@
// qrcode/service/qrcode/getQrcodeContent
import request from './request'
// 根据code获取二维码信息
export function reqQrCodeInfo(data) {
return request({
url: `/qrcode/service/qrcode/getQrcodeContent/${data.code}`,
method: 'get',
data,
})
}
// 获取人员信息
export function reqUserInfo(data) {
return request({
url: `/dq/goods/screen/getUserDetail`,
method: 'post',
data,
})
}

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -0,0 +1,212 @@
<template>
<div
ref="scrollBox"
:class="`infinite-scroll-component-box-${direction}`"
:style="{
'--speed-': `${speed}s`,
mask: `linear-gradient(${degList[direction]}, #000 70%, transparent)`
}"
>
<div class="scroll-content" ref="scrollContent">
<template v-if="Array.isArray(content)">
<div v-for="(item, index) in content" :key="'arrayFirst' + index" class="loop-item">
<slot :item="item" :index="index"></slot>
</div>
<template v-if="loop">
<div v-for="(item, index) in content" class="loop-item" :key="'arrayCopy' + index">
<slot :item="item" :index="index"></slot>
</div>
</template>
</template>
<template v-else>
<slot></slot>
<slot v-if="loop"></slot>
</template>
</div>
</div>
</template>
<script setup>
const props = defineProps({
direction: {
type: String,
default: 'top',
validate: (value) => ['left', 'top', 'right', 'bottom'].findIndex(value) !== -1
},
content: {
type: [String, Array],
default: ''
},
mask: {
type: Boolean,
default: true
},
// 速率倍速越大越快默认值20
speedRate: {
type: Number,
default: 20
}
})
onMounted(() => {
init()
})
let degList = {
left: '90deg',
top: '180deg',
right: '270deg',
bottom: '0deg'
}
// 滚动速度
let speed = ref(0)
// 是否需要无限滚动
let loop = ref(false)
// 容器ref
let scrollBox = ref()
watch(
() => props.content,
() => {
loop.value = false
init()
},
{ deep: true }
)
// 初始化速度,以及是否需要无限滚动
const init = async () => {
await nextTick()
if (props.direction === 'left' || props.direction === 'right') {
// 可视区域的宽度
const boxWidth = scrollBox.value.offsetWidth
// 滚动内容的宽度
const itemWidth = scrollBox.value.getElementsByClassName('scroll-content')[0].offsetWidth
if (itemWidth >= boxWidth) {
loop.value = true
speed.value = itemWidth / props.speedRate
} else {
speed.value = 0
scrollBox.value.getElementsByClassName('scroll-content')[0].style.transform =
'translateX(0)'
loop.value = false
}
} else {
const boxHeight = scrollBox.value.offsetHeight
const itemHeight = scrollBox.value.getElementsByClassName('scroll-content')[0].offsetHeight
if (itemHeight >= boxHeight) {
loop.value = true
speed.value = itemHeight / props.speedRate
} else {
speed.value = 0
scrollBox.value.getElementsByClassName('scroll-content')[0].style.transform =
'translateY(0)'
loop.value = false
}
}
}
</script>
<style lang="scss" scoped>
.infinite-scroll-component-box-left,
.infinite-scroll-component-box-right {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
.scroll-content {
position: absolute;
left: 0;
display: flex;
align-items: center;
justify-content: center;
&:hover {
animation-play-state: paused;
}
}
}
.infinite-scroll-component-box-top,
.infinite-scroll-component-box-bottom {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
.scroll-content {
width: 100%;
position: absolute;
left: 0;
display: flex;
align-items: center;
justify-content: center;
&:hover {
animation-play-state: paused;
}
.loop-item {
width: 100%;
}
}
}
.infinite-scroll-component-box-left .scroll-content {
flex-direction: row;
animation: var(--speed-) move-left linear infinite;
@keyframes move-left {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
}
.infinite-scroll-component-box-right .scroll-content {
flex-direction: row-reverse;
animation: var(--speed-) move-right linear infinite;
@keyframes move-right {
0% {
transform: translateX(-50%);
}
100% {
transform: translateX(0);
}
}
}
.infinite-scroll-component-box-top .scroll-content {
flex-direction: column;
animation: var(--speed-) move-top linear infinite;
@keyframes move-top {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-50%);
}
}
}
.infinite-scroll-component-box-bottom .scroll-content {
flex-direction: column-reverse;
animation: var(--speed-) move-bottom linear infinite;
@keyframes move-bottom {
0% {
transform: translateY(-50%);
}
100% {
transform: translateY(0);
}
}
}
</style>

View File

@@ -11,7 +11,7 @@
import * as echarts from 'echarts' import * as echarts from 'echarts'
import styleUtil from '@/utils/styleUtil' import styleUtil from '@/utils/styleUtil'
import { fitChartSize } from '@/utils/dataUtil' import { fitChartSize } from '@/utils/dataUtil'
import { guid } from '@/utils/util'
const props = defineProps({ const props = defineProps({
width: { width: {
type: Number, type: Number,
@@ -20,19 +20,29 @@
height: { height: {
type: Number, type: Number,
default: () => 0 default: () => 0
},
id: {
type: String,
default: () => ''
} }
}) })
let id = ref(guid())
let lineChart = null let lineChart = null
let timer = null
let currentIndex = -1
let defaultCofig = { let defaultCofig = {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { backgroundColor: 'transparent',
type: 'cross' borderWidth: 0,
formatter: (params) => {
let str = `<div style="
background: #07356B;
border: 1px solid #0096FF;
color: #fff;
font-weight: 600;
font-size: ${fitChartSize(16)}px;
border-radius: ${fitChartSize(4)}px;
padding: ${fitChartSize(4)}px ${fitChartSize(12)}px;">
${params[0].value}</div>`
return str
} }
}, },
grid: { grid: {
@@ -54,7 +64,7 @@
}, },
axisLine: { axisLine: {
lineStyle: { lineStyle: {
color: '#0096FF' color: 'rgba(5, 72, 134, 1)'
} }
}, },
axisLabel: { axisLabel: {
@@ -101,11 +111,9 @@
] ]
} }
onMounted(() => {
init()
})
const init = () => { const init = () => {
lineChart = echarts.init(document.getElementById(props.id)) const dom = document.getElementById(id.value)
lineChart = echarts.init(dom)
defaultCofig.xAxis.data = [ defaultCofig.xAxis.data = [
'10:00', '10:00',
'10:05', '10:05',
@@ -120,8 +128,25 @@
lineChart.setOption({ lineChart.setOption({
...defaultCofig ...defaultCofig
}) })
// // 开启轮播
// startTooltipLoop()
// // 鼠标悬浮,停止轮播
// dom.addEventListener('mousemove', () => {
// console.log('mouse move')
// closeSwitchTooltip()
// })
// // 鼠标离开,继续轮播
// dom.addEventListener('mousedown', () => {
// console.log('mouse down')
// startTooltipLoop()
// })
// 监听窗口大小变化
window.addEventListener('resize', resize) window.addEventListener('resize', resize)
} }
const resize = () => { const resize = () => {
if (lineChart) { if (lineChart) {
lineChart.dispose() lineChart.dispose()
@@ -129,5 +154,39 @@
init() init()
} }
} }
// 切换tooltip
const switchTooltip = () => {
// 取消之前高亮的图形
lineChart.dispatchAction({
type: 'downplay',
seriesIndex: 0,
dataIndex: currentIndex
})
currentIndex = (currentIndex + 1) % 8
// 高亮当前图形
lineChart.dispatchAction({
type: 'highlight',
seriesIndex: 0,
dataIndex: currentIndex
})
// 显示tooltip
lineChart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: currentIndex
})
}
const startTooltipLoop = () => {
timer = setInterval(() => switchTooltip(), 3000)
}
const closeSwitchTooltip = () => {
clearInterval(timer)
timer = undefined
}
onMounted(() => {
init()
})
</script> </script>
<style lang="scss"></style> <style lang="scss"></style>

View File

@@ -3,17 +3,65 @@
</template> </template>
<script setup> <script setup>
import { onMounted } from 'vue' import axios from 'axios'
const fetchData = async () => {
try {
const res = await axios.get(
'https://jiaotong.baidu.com/openapi/v2/event/alarmlist?nodeId=7162&roadType=1,2,3,4,5&eventSource=1,2,3&ak=hPEEq9eAtL3t1SXL8hqNYQkGmWT1oOWh&returnType=1'
)
console.log(res, '数据')
} catch (error) {
console.error(error)
}
}
onMounted(() => { onMounted(() => {
// fetchData()
var map = new BMapGL.Map('container') var map = new BMapGL.Map('container')
map.centerAndZoom(new BMapGL.Point(106.506299, 29.613929), 18) map.centerAndZoom(new BMapGL.Point(109.643452, 31.028006), 15)
map.enableScrollWheelZoom(true) map.enableScrollWheelZoom(true)
map.setMapType(BMAP_SATELLITE_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 ',
// strokeTexture: {
// url: 'https://mapopen-pub-jsapigl.bj.bcebos.com/svgmodel/Icon_road_blue_arrow.png',
// width: 16,
// height: 64
// },
strokeWeight: 4,
strokeOpacity: 0.8
}
)
map.addOverlay(polyline)
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
:deep(.BMap_cpyCtrl) {
display: none;
}
:deep(.anchorBL) {
display: none;
}
// ::v-deep {
// .BMap_cpyCtrl {
// display: none;
// }
// .anchorBL {
// display: none;
// }
// }
.map { .map {
width: vw(3040); width: 100%;
height: vh(1080); height: vh(700);
} }
</style> </style>

View File

@@ -0,0 +1,42 @@
<template>
<div class="title-1">
<div class="title">
<span> {{ title }} </span>
</div>
<slot name="right" />
</div>
</template>
<script setup>
let props = defineProps({
title: {
type: String,
default: ''
}
})
</script>
<style scoped lang="scss">
.title-1 {
position: relative;
.title {
margin: vh(10) auto;
width: vw(468);
height: vh(32);
display: flex;
align-items: center;
justify-content: center;
background-image: url('@/assets/images/title-4.png');
background-size: 100% 100%;
& > span {
font-weight: 800;
font-size: vw(16);
background-image: linear-gradient(to bottom, #ffffff 0%, #75c1ff 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 兼容WebKit内核浏览器 */
color: transparent; /* 兼容其他浏览器 */
}
}
}
</style>

View File

@@ -0,0 +1,35 @@
<template>
<div class="title-2">
<span>{{ title }}</span>
</div>
</template>
<script setup>
let props = defineProps({
title: {
type: String,
default: ''
}
})
</script>
<style scoped lang="scss">
.title-2 {
width: vw(250);
height: vh(28);
display: flex;
align-items: center;
background-image: url('@/assets/images/title-5.png');
background-size: 100% 100%;
& > span {
padding-left: vw(22);
font-weight: bold;
font-size: vw(15);
background-image: linear-gradient(to bottom, #ffffff 0%, #75c1ff 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 兼容WebKit内核浏览器 */
color: transparent; /* 兼容其他浏览器 */
}
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<div class="title-3">
<span>{{ title }}</span>
</div>
</template>
<script setup>
let props = defineProps({
title: {
type: String,
default: ''
}
})
</script>
<style scoped lang="scss">
.title-3 {
position: relative;
width: 100%;
height: vh(12);
margin-top: vh(20);
background-image: url('@/assets/images/title-6.png');
background-size: 100% 100%;
& > span {
position: absolute;
bottom: vh(4);
left: vw(20);
font-size: vw(15);
font-weight: bold;
background-image: linear-gradient(to bottom, #ffffff 0%, #0096ff 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 兼容WebKit内核浏览器 */
color: transparent; /* 兼容其他浏览器 */
}
}
</style>

View File

@@ -1,11 +1,33 @@
.flex { .flex {
display: flex; display: flex;
} }
.flex-1 {
flex: 1;
}
.align-center {
align-items: center;
}
.align-end { .align-end {
align-items: flex-end; align-items: flex-end;
} }
.justify-between {
justify-content: space-between;
}
.justify-evenly {
justify-content: space-evenly;
}
.mb-6 {
margin-bottom: vh(6);
}
.pt-10 {
padding-top: vh(10);
}
.pb-10 {
padding-bottom: vh(10);
}
.mb-10 {
margin-bottom: vh(10);
}
.pt-20 { .pt-20 {
padding-top: vh(20); padding-top: vh(20);
} }

View File

@@ -55,3 +55,11 @@ export function throttle(func, wait = 500, immediate = true) {
}, wait) }, wait)
} }
} }
export function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
})
}

View File

@@ -9,7 +9,7 @@
:key="index" :key="index"
> >
<p class="item-title">三峡之巅-最新异常名称</p> <p class="item-title">三峡之巅-最新异常名称</p>
<img class="item-img" src="@/assets/images/img-1.png" alt="" /> <img class="item-img" src="@/assets/images/cover.png" />
</li> </li>
</ul> </ul>
</div> </div>
@@ -18,8 +18,6 @@
<script setup> <script setup>
import item1 from '@/assets/images/item-1.png' import item1 from '@/assets/images/item-1.png'
import item2 from '@/assets/images/item-2.png' import item2 from '@/assets/images/item-2.png'
console.log(item1, 'item1')
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -88,6 +86,7 @@
width: 100%; width: 100%;
height: vh(164); height: vh(164);
display: block; display: block;
object-fit: cover;
} }
} }
} }

View File

@@ -1,32 +1,33 @@
<template> <template>
<div class="box-2"> <div class="box-2">
<div class="header"> <Title1 title="景区信息">
<div class="title"> 景区信息 </div> <template #right>
<span class="more" /> <span class="more" />
</div> </template>
</Title1>
<div class="flex pt-20"> <div class="flex pt-20">
<div class="item core"> <div class="item core">
<span class="title-1">全县景区数</span> <span class="label">全县景区数</span>
<div class="flex align-end"> <countup endVal="895" /><span class="unit"></span></div> <div class="flex align-end"> <countup endVal="895" /><span class="unit"></span></div>
</div> </div>
<div class="item queue"> <div class="item queue">
<span class="title-1">核心景区数</span> <span class="label">核心景区数</span>
<div class="flex align-end"> <countup endVal="895" /><span class="unit"></span></div> <div class="flex align-end"> <countup endVal="895" /><span class="unit"></span></div>
</div> </div>
<div class="item congestion"> <div class="item congestion">
<span class="title-1">低感景区总数</span> <span class="label">低感景区总数</span>
<div class="flex align-end"> <countup endVal="895" /><span class="unit"></span></div> <div class="flex align-end"> <countup endVal="895" /><span class="unit"></span></div>
</div> </div>
</div> </div>
<div class="flex pt-20"> <div class="flex pt-20">
<div class="box"> <div class="box">
<div class="title-2"><span>景区排队人数</span></div> <Title2 title="景区排队人数" />
<div class="statistic"> <div class="statistic">
<div class="statistic-item"> <div class="statistic-item">
<span class="statistic-title">三峡之巅</span> <span class="statistic-title">三峡之巅</span>
<span class="statistic-value"> <span class="statistic-value">
<span class="prefix">排队</span> <span class="prefix">排队</span>
<span class="count">100</span> <span class="value">100</span>
<span class="suffix"></span> <span class="suffix"></span>
</span> </span>
</div> </div>
@@ -43,13 +44,13 @@
<span class="statistic-value">通畅</span> <span class="statistic-value">通畅</span>
</div> </div>
</div> </div>
<div class="title-3"><span>景区排队人数</span></div> <Title3 title="景区排队人数" />
<div class="pt-20"> <div class="pt-20">
<Line :width="370" :height="180" id="line" /> <Line :width="370" :height="140" />
</div> </div>
</div> </div>
<div class="box"> <div class="box">
<div class="title-2"><span>景区承载量</span></div> <Title2 title="景区承载量" />
<div class="statistic"> <div class="statistic">
<div class="statistic-item"> <div class="statistic-item">
<gauge id="gauge1" /> <gauge id="gauge1" />
@@ -68,24 +69,82 @@
<span class="statistic-title">三峡之巅</span> <span class="statistic-title">三峡之巅</span>
</div> </div>
</div> </div>
<div class="title-3"><span>今日景区承载量</span></div> <Title3 title="今日景区承载量" />
<div class="pt-20"> <div class="pt-20">
<Line :width="370" :height="180" id="line1" /> <Line :width="370" :height="140" />
</div> </div>
</div> </div>
</div> </div>
<div class="ticket-box">
<Title2 title="景区购票数" />
<div class="ticket-wrap">
<img src="@/assets/images/ticket.png" alt="" />
<div> <span class="label">当日购票量</span><countup endVal="768578" /> </div>
<div> <span class="label">未来3天购票量</span><countup endVal="768578" /> </div>
<div> <span class="label">3天后购票量</span><countup endVal="768578" /> </div>
</div>
</div>
<Title1 title="游客画像" />
<div class="flex">
<div class="box-1">
<Title3 title="年龄/性别占比" />
<pie id="pie" />
<div class="count">总人数<countup endVal="124563" /></div>
<div class="cell pt-20">
<img class="icon" src="@/assets/images/man.png" />
<div class="bg">
<span class="text">男性</span>
<div class="progress">
<el-progress
:percentage="50"
:show-text="false"
color="linear-gradient( to right, #074D90 0%, #55E0FF 100%)"
/>
</div>
<span class="man">50%</span>
</div>
</div>
<div class="cell pt-20">
<img class="icon" src="@/assets/images/woman.png" />
<div class="bg">
<span class="text">女性</span>
<div class="progress">
<el-progress
:percentage="50"
:show-text="false"
color="linear-gradient( to right,
#0A4482 0%, #FF7021 100%)"
/>
</div>
<span class="woman">50%</span>
</div>
</div>
</div>
<div class="box-1">
<Title3 title="客源地分析TOP5" />
<top />
</div>
<div class="box-1">
<Title3 title="购票来源" />
<div class="count">总人数<countup endVal="124563" /></div>
<ticket />
</div>
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import countup from 'vue-countup-v3' import countup from 'vue-countup-v3'
import pie from './pie.vue'
import top from './top.vue'
import gauge from './gauge.vue' import gauge from './gauge.vue'
import Line from '@/components/Line/index.vue' import ticket from './ticket.vue'
onMounted(() => {})
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.box-2 { .box-2 {
margin-top: vh(120); margin-top: vh(120);
width: vw(800); width: vw(800);
@@ -94,33 +153,16 @@
box-sizing: border-box; box-sizing: border-box;
background-image: url('@/assets/images/bg-2.png'); background-image: url('@/assets/images/bg-2.png');
background-size: 100% 100%; background-size: 100% 100%;
.header { .more {
position: relative; position: absolute;
.title { top: vh(0);
margin: vh(10) auto; right: vw(20);
width: vw(468); cursor: pointer;
height: vh(32); width: vw(60);
font-weight: 800; height: vh(24);
font-size: vw(16); background-image: url('@/assets/images/more.png');
color: #fff; background-size: 100% 100%;
display: flex;
align-items: center;
justify-content: center;
background-image: url('@/assets/images/title-4.png');
background-size: 100% 100%;
}
.more {
position: absolute;
top: vh(0);
right: vw(20);
cursor: pointer;
width: vw(60);
height: vh(24);
background-image: url('@/assets/images/more.png');
background-size: 100% 100%;
}
} }
.item { .item {
position: relative; position: relative;
flex: 1; flex: 1;
@@ -130,18 +172,18 @@
display: flex; display: flex;
align-items: center; align-items: center;
background-size: 100% 100%; background-size: 100% 100%;
} .label {
.title-1 { position: absolute;
position: absolute; top: vh(-10);
top: vh(-10); font-weight: 400;
font-weight: 400; font-size: vw(14);
font-size: vw(14); color: rgba(255, 255, 255, 0.7);
color: rgba(255, 255, 255, 0.7); }
} .countup-wrap {
.countup-wrap { color: #02f9fa;
color: #02f9fa; font-size: vw(28);
font-size: vw(28); font-weight: bold;
font-weight: bold; }
} }
.unit { .unit {
color: #02f9fa; color: #02f9fa;
@@ -159,30 +201,12 @@
} }
.box { .box {
width: vw(384); width: vw(384);
height: vh(360); height: vh(320);
background-image: url('@/assets/images/bg-3.png'); background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%; background-size: 100% 100%;
&:nth-child(1) { &:nth-child(1) {
margin-right: vw(10); margin-right: vw(10);
} }
.title-2 {
width: vw(253);
height: vh(28);
display: flex;
align-items: center;
background-image: url('@/assets/images/title-5.png');
background-size: 100% 100%;
& > span {
padding-left: vw(22);
font-weight: bold;
font-size: vw(15);
background-image: linear-gradient(to bottom, #ffffff 0%, #75c1ff 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent; /* 兼容WebKit内核浏览器 */
color: transparent; /* 兼容其他浏览器 */
}
}
.statistic { .statistic {
display: flex; display: flex;
margin-top: vh(12); margin-top: vh(12);
@@ -207,10 +231,10 @@
font-size: vw(24); font-size: vw(24);
color: #02f9fa; color: #02f9fa;
} }
.count { .value {
font-weight: bold; font-weight: bold;
font-size: vw(28); font-size: vw(28);
color: #ff4400 !important; color: #ff4400;
} }
.prefix, .prefix,
.suffix { .suffix {
@@ -218,21 +242,88 @@
font-size: vw(12); font-size: vw(12);
} }
} }
.title-3 { }
position: relative; .box-1 {
width: vw(344); width: vw(253);
height: vh(12); height: vh(270);
margin-top: vh(20); background-image: url('@/assets/images/bg-3.png');
background-image: url('@/assets/images/title-6.png'); background-size: 100% 100%;
&:nth-child(1) {
margin-right: vw(10);
}
&:nth-child(2) {
margin-right: vw(10);
}
}
.count {
margin: vw(20) vw(20) 0 vw(20);
height: vh(24);
font-weight: bold;
font-size: vw(14);
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
border-left: vw(4) solid #37d8fc;
border-right: vw(4) solid #37d8fc;
background: rgba(0, 150, 255, 0.19);
}
.cell {
margin-left: vw(10);
display: flex;
align-items: center;
.bg {
display: flex;
align-items: center;
height: vh(20);
padding-right: vw(10);
background: linear-gradient(to right, rgba(0, 150, 255, 0), rgba(0, 150, 255, 0.17) 100%);
}
.icon {
width: vw(26);
height: vh(28);
margin-right: vw(4);
}
.text {
font-weight: 400;
font-size: vw(14);
color: rgba(255, 255, 255, 0.9);
}
.progress {
width: vw(120);
margin-left: vw(4);
}
.man {
font-weight: bold;
font-size: vw(14);
color: #02f9fa;
margin-left: vw(10);
}
.woman {
font-weight: bold;
font-size: vw(14);
color: #f15a25;
margin-left: vw(10);
}
}
.ticket-box {
margin-top: vh(20);
width: 100%;
height: vh(106);
background-color: radial-gradient(to right, #0a4190 0%, rgba(0, 77, 136, 0.6) 100%);
.title {
width: vw(253);
height: vh(28);
display: flex;
align-items: center;
background-image: url('@/assets/images/title-5.png');
background-size: 100% 100%; background-size: 100% 100%;
& > span { & > span {
position: absolute; padding-left: vw(22);
bottom: vh(4);
left: vw(20);
font-size: vw(15);
font-weight: bold; font-weight: bold;
background-image: linear-gradient(to bottom, #ffffff 0%, #0096ff 100%); font-size: vw(15);
background-image: linear-gradient(to bottom, #ffffff 0%, #75c1ff 100%);
-webkit-background-clip: text; -webkit-background-clip: text;
background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent; /* 兼容WebKit内核浏览器 */ -webkit-text-fill-color: transparent; /* 兼容WebKit内核浏览器 */
@@ -240,5 +331,33 @@
} }
} }
} }
.ticket-wrap {
display: flex;
align-items: center;
justify-content: space-between;
& > img {
width: vw(74);
height: vh(74);
}
& > div {
flex: 1;
height: vh(58);
display: flex;
align-items: center;
background-image: url('@/assets/images/ticket-item-bg.png');
background-size: 100% 100%;
}
.label {
padding-left: vw(10);
font-weight: 400;
font-size: vw(14);
color: rgba(255, 255, 255, 0.9);
}
.countup-wrap {
color: #02f9fa;
font-size: vw(28);
font-weight: bold;
}
}
} }
</style> </style>

View File

@@ -0,0 +1,245 @@
<template>
<div class="box-3">
<div class="header">
<div class="flex">
<div class="left">
<div class="item">
<div class="label">今年总游客数</div>
<countup v-for="item in count" :endVal="item" />
</div>
<div class="item">
<div class="label">全县景区总游客人数</div>
<countup v-for="item in count" :endVal="item" />
</div>
<div class="item">
<div class="label">总在园人数</div>
<countup v-for="item in count" :endVal="item" />
</div>
</div>
<div class="right">
<div class="item">
<div class="label">安全度</div>
<div class="value">安全</div>
</div>
<div class="item">
<div class="label">舒适度</div>
<div class="value">舒适</div>
</div>
<div class="item">
<div class="label">接待情况</div>
<div class="value">排队</div>
</div>
<div class="item">
<div class="label">交通拥挤度</div>
<div class="value">舒适</div>
</div>
<div class="item">
<div class="label">停车场负荷度</div>
<div class="value">超负荷</div>
</div>
</div>
</div>
</div>
<Map />
<div class="footer">
<div class="left">
<div>
<div class="flex">
<div class="item">
<p class="label">今日工单总条数</p>
<countup :endVal="1234" />
</div>
<div class="item">
<p class="label">工单完成数</p>
<countup :endVal="1234" />
</div>
</div>
<p>工单完成数</p>
</div>
<div>
<div class="flex">
<div class="item">
<p class="label">紧急工单数</p>
<countup :endVal="1234" />
</div>
<div class="item">
<p class="label">紧急工单完成数</p>
<countup :endVal="1234" />
</div>
</div>
<p>工单完成数</p>
</div>
</div>
<div class="right">
<div class="item">
<span class="tag tag--success">普通</span>
<p class="content">
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
</p>
</div>
<div class="item">
<span class="tag tag--error">普通</span>
<p class="content">
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
</p>
</div>
<div class="item">
<span class="tag tag--primary">普通</span>
<p class="content">
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工单信息工工单信息工单信息工单信息工单信息工单信息
</p>
</div>
</div>
</div>
</div>
</template>
<script setup>
import countup from 'vue-countup-v3'
import Map from '@/components/Map/index.vue'
let count = ref('6945959')
</script>
<style lang="scss" scoped>
.box-3 {
width: vw(1614);
height: vh(950);
margin-top: vh(120);
.header {
width: vw(1614);
height: vh(128);
padding: 0 vw(90);
box-sizing: border-box;
background-image: url('@/assets/images/group.png');
background-size: 100% 100%;
.left {
display: flex;
width: vw(950);
margin-top: vh(20);
}
.right {
flex: 1;
display: flex;
margin-top: vh(20);
}
.item {
flex: 1;
.label {
margin-bottom: vh(10);
font-weight: 400;
font-size: vw(16);
color: rgba(255, 255, 255, 0.9);
}
.value {
font-weight: bold;
font-size: vw(28);
color: #02f9fa;
line-height: vh(33);
}
}
.countup-wrap {
display: inline-block;
width: vw(40);
height: vh(40);
margin-right: vw(4);
border-radius: vw(4);
color: #ffffff;
font-size: vw(28);
font-weight: bold;
display: inline-flex;
align-items: center;
justify-content: center;
background: linear-gradient(180deg, #00b7ff 0%, #0033ff 100%);
}
}
.footer {
display: flex;
width: 100%;
height: vh(120);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
.left {
flex: 1;
display: flex;
& > div {
flex: 1;
height: vh(110);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
.item {
padding: vh(10) vw(24);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.label {
font-weight: 400;
font-size: vw(14);
margin-bottom: vh(10);
color: rgba(255, 255, 255, 0.9);
}
.countup-wrap {
color: #02f9fa;
font-size: vw(28);
font-weight: bold;
}
}
}
.right {
padding: vh(10) vw(24);
.item {
display: flex;
margin-bottom: vh(12);
&:nth-last-child(1) {
margin-bottom: 0;
}
.tag {
padding: 0 vw(16);
font-weight: bold;
font-size: vw(14);
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;
}
}
.content {
margin-left: vw(4);
padding: 0 vw(10);
width: vw(900);
height: vh(24);
line-height: vh(24);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 400;
font-size: vw(14);
color: #ffffff;
border-radius: vw(2);
background: rgba(0, 150, 255, 0.28);
}
}
}
}
}
</style>

View File

@@ -1,329 +1,334 @@
<template> <template>
<div class="box-4-content"> <div class="box-4">
<div class="content-1 content"> <!-- <Title1 title="交通信息" />
<!-- 1 --> <div class="flex justify-evenly pt-10">
<div class="title"> <div v-for="item in list" class="cell">
游客画像 <img class="icon" :src="item.icon" alt="" width="64" height="64" />
</div> <div>
<!-- 2 --> <countup :end-val="item.value" />
<div class="all-man"> <div class="label">{{ item.label }}</div>
</div>
</div> </div>
<ul class="ul-1"> </div>
<li class="li "> <div class="flex">
<div class="li-title"> <div class="box">
<span>年龄性别占比</span> <div class="pt-10">
</div> <Title3 title="拥堵路段总数" />
<div class="chart-1"> <div class="pt-20">
<v-chart class="chart" :option="option1" autoresize /> <Line :width="250" :height="170" />
<span class="sky-1"></span> </div>
</div> </div>
<div class="li-man">总人数123456</div> </div>
</li> <div class="box">
<li class="li"> <div class="pt-10">
<div class="li-title"> <Title3 title="拥堵路段总数" />
<span>客源分析TOP5</span> <Line :width="250" :height="120" />
</div> </div>
<div class="press-box" v-for="item in 5" :key="i"> </div>
<div class="press-title"> <div class="box">
<span>河北</span> <div class="pt-10">
<span>55%</span> <Title3 title="拥堵路段总数" />
</div> <jam :width="250" :height="200" />
</div>
<div class="bg-box"> </div>
<div class="bg-width"></div> </div>
</div> <Title1 title="停车信息" />
</div> <div class="stop-box">
</li> <div>
<li class="li"> <img class="icon" src="@/assets/images/icon-5.png" alt="" />
<div class="li-title"> <div>
<span>购票来源</span> <div class="label">车库总数</div>
</div> <countup class="value" :end-val="500" />
<div class="round-press"> </div>
<div class="round-1"> <div>
<div class="round-width"></div> <div class="label">车库总数</div>
</div> <countup class="value" :end-val="500" />
</div> </div>
<v-chart class="chart" :option="option2" autoresize /> <div>
</li> <div class="label">已使用车位数</div>
</ul> <countup class="value" :end-val="500" />
</div> </div>
<div class="content-2 content"> </div>
<!-- 1 --> <div>
<div class="title"> <div>
停车信息 <div class="label">三峡之巅</div>
</div> <div class="value error">已满</div>
</div>
<ul class="ul-1"> <div>
<li class="li"> <div class="label">白帝城</div>
<div class="li-title"> <div class="value error">已满</div>
<span>年龄性别占比</span> </div>
</div> <div>
<v-chart class="chart" :option="option1" autoresize /> <div class="label">天坑地缝</div>
<div class="li-man">总人数123456</div> <div class="value success">空余</div>
</li> </div>
<li class="li"> <div>
<div class="li-title"> <div class="label">永安宫</div>
<span>客源分析TOP5</span> <div class="value success">空余</div>
</div> </div>
<div class="press-box" v-for="item in 5" :key="i"> </div>
<div class="press-title"> </div>
<span>河北</span> <div class="flex pt-10">
<span>55%</span> <div class="box-1">
</div> <div class="pt-10">
<Title3 title="拥堵路段总数" />
<div class="bg-box"> <div class="pt-20">
<div class="bg-width"></div> <Line :width="250" :height="120" />
</div> </div>
</div> </div>
</li> </div>
<li class="li"> <div class="box-1">
<div class="li-title"> <div class="pt-10">
<span>购票来源</span> <Title3 title="拥堵路段总数" />
</div> <traffic-flow />
<div class="round-press"> </div>
<div class="round-1"> </div>
<div class="round-width"></div> <div class="box-1">
</div> <div class="pt-10">
</div> <Title3 title="景区停车场空位" />
<v-chart class="chart" :option="option2" autoresize /> <vacancy />
</li> </div>
</ul> </div>
</div> </div>
</div> <div class="flex">
<div class="flex-1">
<Title1 title="车船信息" class="title1" />
</div>
<div class="flex-1">
<Title1 title="酒店信息" class="title1" />
</div>
</div> -->
<div class="flex">
<div class="car-ship">
<div class="mb-6">
<div class="car">
<div class="label">车总数</div>
<div class="flex align-center">
<countup class="value" :end-val="130" /> <span class="unit"></span>
</div>
</div>
<div class="table">
<div class="header">
<div>景区</div>
<div>调度</div>
<div>空余</div>
</div>
<div class="cell">
<div class="label">车牌号</div>
<div>车长</div>
<div>车速</div>
</div>
</div>
</div>
<div>
<div class="ship">
<div class="label">车总数</div>
<div class="flex align-center">
<countup class="value" :end-val="130" /> <span class="unit"></span>
</div>
</div>
</div>
</div>
<div class="hotel">
<div> 3 </div>
<div> 4 </div>
</div>
</div>
</div>
</template> </template>
<script setup> <script setup>
import { use } from 'echarts/core'; import jam from './jam.vue'
import { CanvasRenderer } from 'echarts/renderers'; import TrafficFlow from './traffic-flow.vue'
import { GaugeChart } from 'echarts/charts'; import vacancy from './vacancy.vue'
import { PieChart } from 'echarts/charts'; import countup from 'vue-countup-v3'
import { import icon1 from '@/assets/images/icon-1.png'
TitleComponent, import icon2 from '@/assets/images/icon-2.png'
TooltipComponent, import icon3 from '@/assets/images/icon-3.png'
LegendComponent, import icon4 from '@/assets/images/icon-4.png'
} from 'echarts/components';
import VChart, { THEME_KEY } from 'vue-echarts';
import { ref, provide } from 'vue';
use([
GaugeChart,
CanvasRenderer,
PieChart,
TitleComponent,
TooltipComponent,
LegendComponent,
]);
provide(THEME_KEY, 'dark');
const option1 = ref({
backgroundColor:'transparent',
series: [
{
name: 'Access From',
width:300,
height:100,
type: 'pie',
left:-50,
top:10,
radius: ['40%', '50%'],
avoidLabelOverlap: true,
padAngle: 5,
itemStyle: {
borderRadius: 2
},
data: [
{ value: 200, name: '30-40%' },
{ value: 500, name: '19岁以下' },
{ value: 234, name: '18-30岁' },
{ value: 135, name: '60岁以上' },
{ value: 100, name: '30-40岁' },
],
labelLine: {
show: true
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
},
],
});
const option2 = ref({
backgroundColor:'transparent',
});
let list = ref([
{
label: '路段总数',
value: '1234',
icon: icon1
},
{
label: '当前拥堵路段',
value: '1234',
icon: icon2
},
{
label: '总拥堵次数',
value: '1234',
icon: icon3
},
{
label: '最大拥堵时长',
value: '1234',
icon: icon4
}
])
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.box-4-content{ .title1 {
width:vw(792); :deep(.title) {
height:vh(976); width: vw(300) !important;
background: linear-gradient( 180deg, rgba(10,37,75,0) 0%, rgba(10,37,75,0.87) 30%, #0A254B 50%, #0A254B 100%); }
// background: green; }
border-radius: 0px 0px 0px 0px; .box-4 {
.content-2{ margin-top: vh(120);
.all-2-infor{ width: vw(790);
height: vh(950);
.box-1{ padding: vw(8);
display: flex; box-sizing: border-box;
width:vw(384); background-image: url('@/assets/images/bg-5.png');
height: vh(70); background-size: 100% 100%;
background: linear-gradient( 90deg, #0A4190 0%, #0096FF 47%, #004D88 100%); .cell {
border-radius: 0px 0px 0px 0px; display: flex;
border: 1px solid; align-items: center;
border-image: linear-gradient(180deg, rgba(0, 150, 255, 1), rgba(0, 90, 153, 0)) 1 1; .icon {
.info1{ width: vw(64);
text-align: center; height: vh(60);
}
} .countup-wrap {
.t-ico-1{ color: #02f9fa;
justify-content: center; font-size: vw(24);
align-items: center; font-weight: bold;
.t-ico-1{ }
width:vw(45); .label {
height:vh(47); font-weight: 400;
font-size: vw(14);
} margin-top: vh(10);
} color: rgba(255, 255, 255, 0.9);
} }
} }
} .box {
.content{ width: vw(250);
.title{ height: vh(200);
width: vw(220); margin-right: vw(8);
height: vh(32); background-image: url('@/assets/images/bg-3.png');
line-height: vh(32); background-size: 100% 100%;
background: linear-gradient( 180deg, #02A8F7 0%, #05AEFF 49%, #0184E1 51%, #0572C1 77%, #0085E2 100%); }
font-size:vw(16); .box-1 {
text-align: center; width: vw(250);
margin: vh(20) auto; height: vh(200);
} margin-right: vw(8);
.ul-1{ background-image: url('@/assets/images/bg-3.png');
display:flex; background-size: 100% 100%;
.chart-1{ }
width:vw(253); .stop-box {
height:vh(150); display: flex;
position: relative; gap: vw(20);
.sky-1{ .icon {
position: absolute; width: vw(45);
top:vw(60); height: vh(48);
left:50%; }
width:vw(30); & > div {
height:vw(30); flex: 1;
transform: translate(-50%); display: flex;
background-color: #00B1FF; align-items: center;
border-radius: 50%; justify-content: space-evenly;
} height: vh(70);
} background-image: url('@/assets/images/bg-4.png');
background-size: 100% 100%;
.li{ .label {
width:vw(253); font-weight: 400;
height:vh(225); font-size: vw(14);
margin-right:vw(10); color: rgba(255, 255, 255, 0.9);
background: radial-gradient( 0% 70% at 99% 50%, #0A4190 0%, rgba(0,77,136,0.6) 100%); }
border-image: linear-gradient(180deg, rgba(0, 150, 255, 1), rgba(0, 90, 153, 0)) 1 1; .value {
.li-man{ margin-top: vh(10);
font-size:vw(14); font-weight: bold;
color:#fff; font-size: vw(24);
margin:0 auto; color: #ffffff;
text-align: center; }
} .error {
.round-press{ color: #e21b1b;
}
} .success {
.press-box{ color: #02f9fa;
padding:vw(5) vw(10); }
// margin-bottom:vh(10); }
.press-title{ }
display: flex; .car-ship {
justify-content: space-between; flex: 1;
color:rgba(255,255,255,0.9); & > div {
font-size:vw(12); position: relative;
margin-bottom:vh(10) flex: 1;
} height: vh(110);
.bg-box{ display: flex;
width: 100%; align-items: center;
height: vw(10); background-image: url('@/assets/images/bg-4.png');
background: rgba(0,150,255,0.15); background-size: 100% 100%;
border-radius: vw(5); .icon {
} width: vw(352);
.bg-width{ height: vh(70);
background: linear-gradient( to left, #FF7021 0%, rgba(255,112,33,0) 100%); padding-top: vh(10);
border-radius: vw(5); padding-left: vw(90);
border-top-right-radius: 0; background-size: 100% 100%;
border-bottom-right-radius: 0; box-sizing: border-box;
width:50%; }
height:100%; .car {
position: relative; @extend .icon;
} background-image: url('@/assets/images/icon-6.png');
.bg-width::before{ }
display: block; .ship {
content: ''; @extend .icon;
width:vw(10); background-image: url('@/assets/images/icon-7.png');
height:vw(10); }
// border-radius: 50%; .label {
position: absolute; font-weight: 400;
right:0; font-size: vw(14);
top:0; color: rgba(255, 255, 255, 0.9);
width: 0; margin-bottom: vh(6);
height: 0; }
border-left: vw(10) solid red; .value {
// border-right-width: vw(10); font-weight: bold;
// border-bottom-width: vw(10); font-size: vw(24);
// border-left-width: vw(10); color: #02f9fa;
}
.unit {
} font-weight: bold;
} font-size: vw(14);
.li-title{ color: #02f9fa;
background: url(/src/assets/images/t-title-bg.png) no-repeat 100%; margin-top: vh(6);
span{ }
background: linear-gradient(90deg, #FFFFFF 0%, #0096FF 100%); .table {
font-family: Microsoft YaHei, Microsoft YaHei; position: absolute;
font-weight: bold; left: vw(160);
font-size: vw(15); width: vw(200);
line-height: vh(18); height: vh(100);
text-align: left; z-index: 2;
font-style: normal; .header {
text-transform: none; display: flex;
display: inline-block; height: vh(18);
margin-left:vw(20); line-height: vh(18);
margin-bottom:vh(10); text-align: center;
background: rgba(0, 150, 255, 0.4);
} & > div {
flex: 1;
.li-1-echat{ font-weight: 400;
position: relative; font-size: vw(12);
.sty-1{ color: #52b8ff;
position: absolute; }
top:50%; }
left:50%; .cell {
transform: translate(-50%,-50%); display: flex;
width:50rpx; height: vh(30);
height:50rpx; line-height: vh(30);
text-align: center;
background: #074686;
} & > div {
// width: 100%; flex: 1;
// height: 300px; }
.chart { }
}
} }
} }
} .hotel {
} flex: 1;
} }
} }
.chart {
width: vw(253);
height: vh(120);
}
}
</style> </style>

View File

@@ -0,0 +1,190 @@
<template>
<div class="top" id="jam" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import * as echarts from 'echarts'
let topChart = null
let result = [
{ name: '路段1', value: 86 },
{ name: '路段1', value: 83 },
{ name: '路段1', value: 73 },
{ name: '路段1', value: 61 },
{ name: '路段1', value: 61 }
]
let option = {
backgroundColor: 'transparent',
tooltip: {
show: false
},
legend: {
show: false
},
grid: {
left: '4%',
right: '4%',
top: '16%',
bottom: '-10%',
containLabel: true
},
xAxis: [
{
splitLine: {
show: false
},
type: 'value',
show: false
}
],
yAxis: [
{
splitLine: {
show: false
},
axisLine: {
show: false
},
type: 'category',
axisTick: {
show: false
},
data: 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, 6, 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(4),
MaxSize: 0,
showBackground: true,
barBorderRadius: [30, 0, 0, 30],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
label: {
show: true,
offset: [10, -13],
color: '#D3E5FF',
fontWeight: 500,
position: 'left',
align: 'left',
fontSize: fitChartSize(14),
fontFamily: 'Source Han Sans CN',
formatter: function (params) {
return params.data.name
}
},
data: result.map((item, index) => {
return {
name: item.name,
value: item.value,
itemStyle: {
barBorderRadius: [3, 0, 0, 3],
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(255, 112, 33, 0)'
},
{
offset: 1,
color: 'rgba(255, 112, 33, 1)'
}
]
}
}
}
})
},
{
name: '外圆',
type: 'scatter',
emphasis: {
scale: false
},
showSymbol: true,
symbol: 'circle',
symbolSize: fitChartSize(10),
z: 2,
data: result.map((item, index) => {
return {
name: item.name,
value: item.value,
itemStyle: {
color: '#fff',
opacity: 1
}
}
}),
animationDelay: 500
}
]
}
const init = () => {
topChart = echarts.init(document.getElementById('jam'))
topChart.setOption(option)
}
const resize = () => {
if (topChart) {
topChart.dispose()
topChart = null
init()
}
}
onMounted(() => {
init()
window.addEventListener('resize', resize)
})
</script>
<style scoped lang="scss">
.top {
width: 100%;
height: vh(160);
}
</style>

View File

@@ -0,0 +1,74 @@
<template>
<div class="pie" :id="id" />
</template>
<script setup>
import * as echarts from 'echarts'
import { fitChartSize } from '@/utils/dataUtil'
const props = defineProps({
id: {
type: String,
default: () => ''
}
})
let gaugeChart = null
onMounted(() => {
init()
})
const init = () => {
// 基于准备好的dom初始化echarts实例
gaugeChart = echarts.init(document.getElementById(props.id))
gaugeChart.setOption({
tooltip: {
trigger: 'item'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['30%', '40%'],
itemStyle: {
borderColor: 'transparent',
borderRadius: fitChartSize(2),
borderWidth: fitChartSize(2)
},
label: {
color: '#D3F0FE',
fontSize: fitChartSize(12)
},
labelLine: {
normal: {
lineStyle: {
type: 'dashed'
}
}
},
data: [
{ value: 484, name: '19岁以下', labelLine: { length: 2 } },
{ value: 300, name: '18-30岁', labelLine: { length: 2 } },
{ value: 1048, name: '30-40岁', labelLine: { length: 2 } },
{ value: 580, name: '40-60岁', labelLine: { length: 2 } },
{ value: 735, name: '60岁以上', labelLine: { length: 2 } }
]
}
]
})
window.addEventListener('resize', resize)
}
const resize = () => {
if (gaugeChart) {
gaugeChart.dispose()
init()
}
}
</script>
<style lang="scss" scoped>
.pie {
width: vw(253);
height: vh(100);
}
</style>

View File

@@ -0,0 +1,199 @@
<template>
<div class="ticket" id="ticket" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import * as echarts from 'echarts'
let ticketChart = null
var qxzbData = [
{
name: '处理中',
value: 500,
full: 1000 // 最大值
},
{
name: '超期中',
value: 756,
full: 1000
},
{
name: '延期中',
value: 800,
full: 1000
}
]
var returnData = function (qxzbData, name) {
for (var a = 0; a < qxzbData.length; a++) {
if (qxzbData[a].name == name) {
return qxzbData[a].value
}
}
}
let option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'item',
formatter: function (rsp) {
if (rsp.name != '') {
return rsp.name + '<br/>' + rsp.marker + ' ' + rsp.value + ' ' + rsp.percent + ''
}
}
},
legend: {
orient: 'vertical',
x: 'right',
y: 'center',
// textStyle: {
// color: '#4A9AE4'
// },
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: ['30%', '50%'],
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: ['30%', '40%'],
center: ['30%', '50%'],
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: ['45%', '55%'],
center: ['30%', '50%'],
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('ticket'))
ticketChart.setOption(option)
window.addEventListener('resize', resize)
}
const resize = () => {
if (ticketChart) {
ticketChart.dispose()
ticketChart = null
init()
}
}
onMounted(() => {
init()
})
</script>
<style scoped lang="scss">
.ticket {
width: 100%;
height: vh(200);
}
</style>

View File

@@ -0,0 +1,251 @@
<template>
<div class="top" id="top" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import * as echarts from 'echarts'
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 colorListB = [
'rgba(249, 136, 136, 1)',
'rgba(255, 162, 106, 1)',
'rgba(255, 210, 130, 1)',
'rgba(142, 255, 206, 1)',
'rgba(165, 232, 255, 1)'
]
let colorListC = [
'rgba(249, 136, 136, 0.1)',
'rgba(255, 162, 106, 0.1)',
'rgba(255, 210, 130, 0.1)',
'rgba(142, 255, 206, 0.1)',
'rgba(165, 232, 255, 0.1)'
]
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]
}
]
}
}
}
})
}
// {
// name: '外圆',
// type: 'scatter',
// emphasis: {
// scale: false
// },
// showSymbol: true,
// symbol: 'circle',
// symbolSize: 8, // 进度条白点
// z: 2,
// data: result.map((item, index) => {
// return {
// name: item.name,
// value: item.value,
// itemStyle: {
// color: colorListB[index],
// borderColor: colorListC[index],
// borderWidth: 12,
// shadowColor: colorListC[index],
// shadowBlur: 10,
// opacity: 1
// }
// }
// }),
// animationDelay: 500
// },
// {
// name: '外圆',
// type: 'scatter',
// emphasis: {
// scale: false
// },
// showSymbol: true,
// symbol: 'circle',
// symbolSize: 3, // 进度条白点
// z: 3,
// data: result.map((item, index) => {
// return {
// name: item.name,
// value: item.value,
// itemStyle: {
// color: colorListB[index],
// borderColor: colorListC[index],
// borderWidth: 30,
// shadowColor: colorListC[index],
// shadowBlur: 10,
// opacity: 1
// }
// }
// }),
// animationDelay: 500
// }
]
}
const init = () => {
topChart = echarts.init(document.getElementById('top'))
topChart.setOption(option)
}
const resize = () => {
if (topChart) {
topChart.dispose()
topChart = null
init()
}
}
onMounted(() => {
init()
window.addEventListener('resize', resize)
})
</script>
<style scoped lang="scss">
.top {
width: 100%;
height: vh(260);
}
</style>

View File

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

View File

@@ -0,0 +1,189 @@
<template>
<div class="vacancy" id="vacancy" />
</template>
<script setup>
import { fitChartSize } from '@/utils/dataUtil'
import * as echarts from 'echarts'
let topChart = null
let result = [
{ name: '三峡之颠', value: 86 },
{ name: '白帝城', value: 83 },
{ name: '瞿塘峡', value: 73 },
{ name: '天坑地缝', value: 61 }
]
let option = {
backgroundColor: 'transparent',
tooltip: {
show: false
},
legend: {
show: false
},
grid: {
left: '4%',
right: '4%',
top: '20%',
bottom: '-4%',
containLabel: true
},
xAxis: [
{
splitLine: {
show: false
},
type: 'value',
show: false
}
],
yAxis: [
{
splitLine: {
show: false
},
axisLine: {
show: false
},
type: 'category',
axisTick: {
show: false
},
data: result.map((item) => item.name),
axisLabel: {
show: false
}
},
{
type: 'category',
inverse: true,
axisTick: 'none',
axisLine: 'none',
show: true,
axisLabel: {
textStyle: {
color: '#fff',
fontSize: fitChartSize(12)
},
verticalAlign: 'bottom',
padding: [0, 0, 6, 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(4),
MaxSize: 0,
showBackground: true,
barBorderRadius: [30, 0, 0, 30],
backgroundStyle: {
color: 'rgba(0, 150, 255, 0.15)'
},
label: {
show: true,
offset: [10, -13],
color: '#D3E5FF',
fontWeight: 500,
position: 'left',
align: 'left',
fontSize: fitChartSize(14),
fontFamily: 'Source Han Sans CN',
formatter: function (params) {
return params.data.name
}
},
data: result.map((item, index) => {
return {
name: item.name,
value: item.value,
itemStyle: {
barBorderRadius: [3, 0, 0, 3],
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(0, 204, 255, 0)'
},
{
offset: 1,
color: 'rgba(0, 204, 255, 1)'
}
]
}
}
}
})
},
{
name: '外圆',
type: 'scatter',
emphasis: {
scale: false
},
showSymbol: true,
symbol: 'circle',
symbolSize: fitChartSize(10),
z: 2,
data: result.map((item, index) => {
return {
name: item.name,
value: item.value,
itemStyle: {
color: '#fff',
opacity: 1
}
}
}),
animationDelay: 500
}
]
}
const init = () => {
topChart = echarts.init(document.getElementById('vacancy'))
topChart.setOption(option)
}
const resize = () => {
if (topChart) {
topChart.dispose()
topChart = null
init()
}
}
onMounted(() => {
init()
window.addEventListener('resize', resize)
})
</script>
<style scoped lang="scss">
.vacancy {
width: 100%;
height: vh(170);
}
</style>

View File

@@ -14,16 +14,18 @@
<li class="nav-right-item">舆情</li> <li class="nav-right-item">舆情</li>
<li class="nav-right-item">酒店</li> <li class="nav-right-item">酒店</li>
</ul> </ul>
<box4 />
</div> </div>
<box2 /> <!-- <box2 />
<box3 /> -->
<box4 />
</main> </main>
</template> </template>
<script setup> <script setup>
import box1 from './components/box-1.vue' import box1 from './components/box-1.vue'
import box4 from './components/box-4.vue'
import box2 from './components/box-2.vue' import box2 from './components/box-2.vue'
import box3 from './components/box-3.vue'
import box4 from './components/box-4.vue'
onMounted(() => {}) onMounted(() => {})
</script> </script>

View File

@@ -4,6 +4,8 @@ import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx' import vueJsx from '@vitejs/plugin-vue-jsx'
import AutoImport from 'unplugin-auto-import/vite' import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// import vueDevTools from 'vite-plugin-vue-devtools' // import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/ // https://vite.dev/config/
@@ -13,7 +15,11 @@ export default defineConfig({
vueJsx(), vueJsx(),
AutoImport({ AutoImport({
imports: ['vue'], imports: ['vue'],
dts: 'src/auto-import.d.ts' dts: 'src/auto-import.d.ts',
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
}) })
// vueDevTools(), // vueDevTools(),
], ],