首次提交
This commit is contained in:
commit
adb06231ea
4
.env.development
Normal file
4
.env.development
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# .env.development
|
||||||
|
NODE_ENV=development
|
||||||
|
VUE_APP_BASE_API=/dev-api
|
||||||
|
VUE_APP_TITLE=大屏数据可视化系统
|
3
.env.production
Normal file
3
.env.production
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
NODE_ENV=production
|
||||||
|
VUE_APP_BASE_API=http://your-ruoyi-server.com # 生产环境若依后端地址
|
||||||
|
VUE_APP_TITLE=大屏数据可视化系统
|
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
507
1.html
Normal file
507
1.html
Normal file
@ -0,0 +1,507 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>我是战斗人 - 精英战斗展示</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--primary: #e63946;
|
||||||
|
--secondary: #f1faee;
|
||||||
|
--accent: #a8dadc;
|
||||||
|
--dark: #1d3557;
|
||||||
|
--darker: #0d1b2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: linear-gradient(135deg, var(--darker) 0%, var(--dark) 100%);
|
||||||
|
color: var(--secondary);
|
||||||
|
min-height: 100vh;
|
||||||
|
overflow-x: hidden;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1.5rem 0;
|
||||||
|
border-bottom: 1px solid rgba(241, 250, 238, 0.1);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--secondary);
|
||||||
|
text-shadow: 0 0 10px rgba(230, 57, 70, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo i {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
color: var(--secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 2px;
|
||||||
|
bottom: -5px;
|
||||||
|
left: 0;
|
||||||
|
background-color: var(--primary);
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover::after {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
background: linear-gradient(to right, var(--primary), var(--accent));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
text-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero p {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
max-width: 600px;
|
||||||
|
color: var(--accent);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto 3rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-container:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-container::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(45deg, rgba(29, 53, 87, 0.2), rgba(168, 218, 220, 0.1));
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-controls {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
background: linear-gradient(to top, rgba(13, 27, 42, 0.9), transparent);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 1rem;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-container:hover .video-controls {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn {
|
||||||
|
background: rgba(241, 250, 238, 0.2);
|
||||||
|
border: none;
|
||||||
|
color: var(--secondary);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn:hover {
|
||||||
|
background: var(--primary);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.features {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 2rem;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card {
|
||||||
|
background: rgba(29, 53, 87, 0.4);
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
border: 1px solid rgba(168, 218, 220, 0.1);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card:hover {
|
||||||
|
transform: translateY(-10px);
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
border-color: rgba(168, 218, 220, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-icon {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
color: var(--primary);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card h3 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2rem;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
padding: 2rem;
|
||||||
|
background: rgba(29, 53, 87, 0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
background: linear-gradient(135deg, rgba(29, 53, 87, 0.6), rgba(13, 27, 42, 0.8));
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta h2 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta p {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto 2rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.8rem 2rem;
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--secondary);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 4px 15px rgba(230, 57, 70, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background: #c1121f;
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 6px 20px rgba(230, 57, 70, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem 0;
|
||||||
|
border-top: 1px solid rgba(241, 250, 238, 0.1);
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links a {
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links a:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
header {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动画效果 */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 1s ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delay-1 { animation-delay: 0.2s; }
|
||||||
|
.delay-2 { animation-delay: 0.4s; }
|
||||||
|
.delay-3 { animation-delay: 0.6s; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header>
|
||||||
|
<div class="logo">
|
||||||
|
<i class="fas fa-fist-raised"></i>
|
||||||
|
<span>战斗精英</span>
|
||||||
|
</div>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#">首页</a></li>
|
||||||
|
<li><a href="#">关于我们</a></li>
|
||||||
|
<li><a href="#">训练课程</a></li>
|
||||||
|
<li><a href="#">战斗展示</a></li>
|
||||||
|
<li><a href="#">联系我们</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="hero">
|
||||||
|
<h1 class="fade-in">我是战斗人</h1>
|
||||||
|
<p class="fade-in delay-1">探索极限,挑战自我,体验战斗艺术的精髓。我们的训练结合传统与现代技术,打造最强大的战斗精英。</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="video-container fade-in delay-2">
|
||||||
|
<video id="mainVideo" src="https://robotstorm.tech/_nuxt/videos/videoDemo.1589869.MP4" controls></video>
|
||||||
|
<div class="video-controls">
|
||||||
|
<button class="control-btn" id="playBtn"><i class="fas fa-play"></i></button>
|
||||||
|
<button class="control-btn" id="pauseBtn"><i class="fas fa-pause"></i></button>
|
||||||
|
<button class="control-btn" id="volumeUp"><i class="fas fa-volume-up"></i></button>
|
||||||
|
<button class="control-btn" id="volumeDown"><i class="fas fa-volume-down"></i></button>
|
||||||
|
<button class="control-btn" id="fullscreenBtn"><i class="fas fa-expand"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="features">
|
||||||
|
<div class="feature-card fade-in delay-1">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-shield-alt"></i>
|
||||||
|
</div>
|
||||||
|
<h3>防御技巧</h3>
|
||||||
|
<p>学习最先进的防御技术,保护自己免受攻击。我们的课程涵盖各种场景下的防御策略。</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card fade-in delay-2">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-bolt"></i>
|
||||||
|
</div>
|
||||||
|
<h3>快速反应</h3>
|
||||||
|
<p>训练你的反应速度和决策能力,在关键时刻做出正确判断,掌握战斗主动权。</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card fade-in delay-3">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-users"></i>
|
||||||
|
</div>
|
||||||
|
<h3>团队协作</h3>
|
||||||
|
<p>学习如何在团队环境中协调行动,发挥集体力量,实现更高效的战斗配合。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="stats">
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number">500+</div>
|
||||||
|
<div class="stat-label">训练课程</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number">10K+</div>
|
||||||
|
<div class="stat-label">满意学员</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number">15</div>
|
||||||
|
<div class="stat-label">专业教练</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-number">98%</div>
|
||||||
|
<div class="stat-label">成功率</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="cta fade-in delay-2">
|
||||||
|
<h2>准备好成为战斗精英了吗?</h2>
|
||||||
|
<p>加入我们,开启你的战斗之旅。无论你是初学者还是经验丰富的战士,我们都有适合你的课程。</p>
|
||||||
|
<a href="#" class="btn">立即加入</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="social-links">
|
||||||
|
<a href="#"><i class="fab fa-weibo"></i></a>
|
||||||
|
<a href="#"><i class="fab fa-weixin"></i></a>
|
||||||
|
<a href="#"><i class="fab fa-qq"></i></a>
|
||||||
|
<a href="#"><i class="fab fa-tiktok"></i></a>
|
||||||
|
</div>
|
||||||
|
<p>© 2023 战斗精英 | 专业战斗训练平台</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 视频控制功能
|
||||||
|
const video = document.getElementById('mainVideo');
|
||||||
|
const playBtn = document.getElementById('playBtn');
|
||||||
|
const pauseBtn = document.getElementById('pauseBtn');
|
||||||
|
const volumeUp = document.getElementById('volumeUp');
|
||||||
|
const volumeDown = document.getElementById('volumeDown');
|
||||||
|
const fullscreenBtn = document.getElementById('fullscreenBtn');
|
||||||
|
|
||||||
|
playBtn.addEventListener('click', () => {
|
||||||
|
video.play();
|
||||||
|
});
|
||||||
|
|
||||||
|
pauseBtn.addEventListener('click', () => {
|
||||||
|
video.pause();
|
||||||
|
});
|
||||||
|
|
||||||
|
volumeUp.addEventListener('click', () => {
|
||||||
|
if (video.volume < 1) video.volume += 0.1;
|
||||||
|
});
|
||||||
|
|
||||||
|
volumeDown.addEventListener('click', () => {
|
||||||
|
if (video.volume > 0) video.volume -= 0.1;
|
||||||
|
});
|
||||||
|
|
||||||
|
fullscreenBtn.addEventListener('click', () => {
|
||||||
|
if (video.requestFullscreen) {
|
||||||
|
video.requestFullscreen();
|
||||||
|
} else if (video.webkitRequestFullscreen) {
|
||||||
|
video.webkitRequestFullscreen();
|
||||||
|
} else if (video.msRequestFullscreen) {
|
||||||
|
video.msRequestFullscreen();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加滚动动画效果
|
||||||
|
const observerOptions = {
|
||||||
|
threshold: 0.1,
|
||||||
|
rootMargin: '0px 0px -50px 0px'
|
||||||
|
};
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('fade-in');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, observerOptions);
|
||||||
|
|
||||||
|
// 观察所有具有动画潜力的元素
|
||||||
|
document.querySelectorAll('.feature-card, .stat-item, .cta').forEach(el => {
|
||||||
|
observer.observe(el);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
221
README.md
Normal file
221
README.md
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
**通知:最新的低代码大屏系统GoView已开源,详见:[https://gitee.com/MTrun/go-view](https://gitee.com/MTrun/go-view)**
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://gitee.com/MTrun/go-view/raw/master/readme/logo-t-y.png" alt="go-view" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
**长期赞助商**
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<a href="http://www.ccflow.org/?from=vueBigScreenGitee" target="_blank">
|
||||||
|
<img src="https://gitee.com/dromara/go-view/raw/master/readme/sponsors/ccflow-banner.png" alt="go-view" style="width: 320px!important;" width="320px!important;" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 一、项目描述
|
||||||
|
|
||||||
|
- 一个基于 Vue、Datav、Echart 框架的 " **数据大屏项目** ",通过 Vue 组件实现数据动态刷新渲染,内部图表可实现自由替换。部分图表使用 DataV 自带组件,可进行更改,详情请点击下方 DataV 文档。
|
||||||
|
|
||||||
|
- [**Vue3 版本请点击这里查看,使用 Hooks+TypeScript 实现,全新内容等你探索!**](https://gitee.com/MTrun/vue-big-screen-plugin)
|
||||||
|
- [**React 版本请点击这里查看,全新界面超级好看!!!**](https://gitee.com/MTrun/react-big-screen)
|
||||||
|
- 项目需要全屏展示(按 F11)。
|
||||||
|
- 项目部分区域使用了全局注册方式,增加了打包体积,在实际运用中请使用 **按需引入**。
|
||||||
|
- 拉取项目之后,建议按照自己的功能区域重命名文件,现以简单的位置进行区分。
|
||||||
|
- 项目环境:Vue-cli-5.x、DataV-2.7.3、Echarts-4.6.0(如果5.x版本有问题,请切换到4.x版本)、Webpack-4.0、Npm-9.x、Node-v18。
|
||||||
|
- 请拉取 master 分支的代码,其余分支是开发分支。
|
||||||
|
- 需要其它地图数据的,请查看我的其它项目(有一个地图合集)
|
||||||
|
|
||||||
|
友情链接:
|
||||||
|
|
||||||
|
1. [Vue 官方文档](https://cn.vuejs.org/v2/guide/instance.html)
|
||||||
|
2. [DataV 官方文档](http://datav.jiaminghi.com/guide/)
|
||||||
|
3. [echarts 实例](https://echarts.apache.org/examples/zh/index.html),[echarts API 文档](https://echarts.apache.org/zh/api.html#echarts)
|
||||||
|
|
||||||
|
项目展示
|
||||||
|

|
||||||
|
|
||||||
|
## 二、主要文件介绍
|
||||||
|
|
||||||
|
| 文件 | 作用/功能 |
|
||||||
|
| ------------------- | --------------------------------------------------------------------- |
|
||||||
|
| main.js | 主目录文件,引入 Echart/DataV 等文件 |
|
||||||
|
| utils | 工具函数与 mixins 函数等 |
|
||||||
|
| views/ index.vue | 项目主结构 |
|
||||||
|
| views/其余文件 | 界面各个区域组件(按照位置来命名) |
|
||||||
|
| assets | 静态资源目录,放置 logo 与背景图片 |
|
||||||
|
| assets / style.scss | 通用 CSS 文件,全局项目快捷样式调节 |
|
||||||
|
| assets / index.scss | Index 界面的 CSS 文件 |
|
||||||
|
| components/echart | 所有 echart 图表(按照位置来命名) |
|
||||||
|
| common/... | 全局封装的 ECharts 和 flexible 插件代码(适配屏幕尺寸,可定制化修改) |
|
||||||
|
|
||||||
|
## 三、使用介绍
|
||||||
|
|
||||||
|
### 启动项目
|
||||||
|
|
||||||
|
需要提前安装好 `nodejs` 与 `pnpm`,下载项目后在项目主目录下运行 `pnpm` 拉取依赖包。安装完依赖包之后然后使用 `vue-cli` 或者直接使用命令`npm run serve`,就可以启动项目,启动项目后需要手动全屏(按 F11)。如果编译项目的时候提示没有 DataV 框架的依赖,输入 `npm install @jiaminghi/data-view` 或者 `yarn add @jiaminghi/data-view` 进行手动安装。
|
||||||
|
|
||||||
|
### 封装组件渲染图表
|
||||||
|
|
||||||
|
所有的 ECharts 图表都是基于 `common/echart/index.vue` 封装组件创建的,已经对数据和屏幕改动进行了监听,能够动态渲染图表数据和大小。在监听窗口小大的模块,使用了防抖函数来控制更新频率,节约浏览器性能。
|
||||||
|
|
||||||
|
项目配置了默认的 ECharts 图表样式,文件地址:`common/echart/theme.json`。
|
||||||
|
|
||||||
|
封装的渲染图表组件支持传入以下参数,可根据业务需求自行添加/删除。
|
||||||
|
|
||||||
|
参数名称 | 类型 | 作用/功能 |
|
||||||
|
| -------------------| --------- | ------------------------------|
|
||||||
|
| id | String | 唯一 id,渲染图表的节点(非必填,使用了 $el)|
|
||||||
|
| className | String | class样式名称(非必填) |
|
||||||
|
| options | Object | ECharts 配置(必填) |
|
||||||
|
| height | String | 图表高度(建议填) |
|
||||||
|
| width | String | 图表宽度(建议填) |
|
||||||
|
|
||||||
|
### 动态渲染图表
|
||||||
|
|
||||||
|
动态渲染图表案例为 `components` 目录下各个图表组件,index 文件负责数据获取和处理,chart 文件负责监听和数据渲染。
|
||||||
|
|
||||||
|
chart 文件的主要逻辑为:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Echart :options="options" id="id" height="height" width="width" ></Echart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 引入封装组件
|
||||||
|
import Echart from '@/common/echart'
|
||||||
|
export default {
|
||||||
|
// 定义配置数据
|
||||||
|
data(){ return { options: {}}},
|
||||||
|
// 声明组件
|
||||||
|
components: { Echart},
|
||||||
|
// 接收数据
|
||||||
|
props: {
|
||||||
|
cdata: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 进行监听,也可以使用 computed 计算属性实现此功能
|
||||||
|
watch: {
|
||||||
|
cdata: {
|
||||||
|
handler (newData) {
|
||||||
|
this.options ={
|
||||||
|
// 这里编写 ECharts 配置
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 立即监听
|
||||||
|
immediate: true,
|
||||||
|
// 深度监听
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 复用图表组件
|
||||||
|
|
||||||
|
复用图表组件案例为中间部分的 `任务通过率与任务达标率` 模块,两个图表类似,区别在于颜色和主要渲染数据。只需要传入对应的唯一 id 和样式,然后在复用的组件 `components/echart/center/centerChartRate` 里进行接收并在对应位置赋值即可。
|
||||||
|
|
||||||
|
如:在调用处 `views/center.vue` 里去定义好数据并传入组件
|
||||||
|
|
||||||
|
```js
|
||||||
|
//组件调用
|
||||||
|
<span>今日任务通过率</span>
|
||||||
|
<centerChart :id="rate[0].id" :tips="rate[0].tips" :colorObj="rate[0].colorData" />
|
||||||
|
|
||||||
|
<span>今日任务达标率</span>
|
||||||
|
<centerChart :id="rate[1].id" :tips="rate[1].tips" :colorObj="rate[1].colorData" />
|
||||||
|
|
||||||
|
...
|
||||||
|
import centerChart from "@/components/echart/center/centerChartRate";
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
rate: [
|
||||||
|
{
|
||||||
|
id: "centerRate1",
|
||||||
|
tips: 60,
|
||||||
|
...
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "centerRate2",
|
||||||
|
tips: 40,
|
||||||
|
colorData: {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 更换边框
|
||||||
|
|
||||||
|
边框是使用了 DataV 自带的组件,只需要去 views 目录下去寻找对应的位置去查找并替换就可以,具体的种类请去 DavaV 官网查看
|
||||||
|
如:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<dv-border-box-1></dv-border-box-1>
|
||||||
|
<dv-border-box-2></dv-border-box-2>
|
||||||
|
<dv-border-box-3></dv-border-box-3>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 更换图表
|
||||||
|
|
||||||
|
直接进入 `components/echart` 下的文件修改成你要的 echarts 模样,可以去[echarts 官方社区](https://gallery.echartsjs.com/explore.html#sort=rank~timeframe=all~author=all)里面查看案例。
|
||||||
|
|
||||||
|
### Mixins 解决自适应适配功能
|
||||||
|
|
||||||
|
使用 mixins 注入解决了界面大小变动图表自适应适配的功能,函数在 `utils/resizeMixins.js` 中,应用在 `common/echart/index.vue` 的封装渲染组件,主要是对 `this.chart` 进行了功能注入。
|
||||||
|
|
||||||
|
### 屏幕适配
|
||||||
|
|
||||||
|
1.5 版本项目放弃了 flexible 插件方案,将 rem 改回px,使用更流程通用的 `css3:scale` 缩放方案,通过 `ref` 指向 `views/index`,屏幕改变时缩放内容。项目的基准尺寸是 `1920px*1080px`,所以支持同比例屏幕 100% 填充,如果非同比例则会自动计算比例居中填充,不足的部分则留白。实现代码在 `src/utils/userDraw` ,如果有其它的适配方案,欢迎交流。
|
||||||
|
|
||||||
|
### 请求数据
|
||||||
|
|
||||||
|
现在的项目未使用前后端数据请求,建议使用 axios 进行数据请求,在 main.js 位置进行全局配置。
|
||||||
|
|
||||||
|
- axios 的 main.js 配置参考范例(因人而异)
|
||||||
|
|
||||||
|
```js
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
//把方法放到vue的原型上,这样就可以全局使用了
|
||||||
|
Vue.prototype.$http = axios.create({
|
||||||
|
//设置20秒超时时间
|
||||||
|
timeout: 20000,
|
||||||
|
baseURL: 'http://172.0.0.1:80080', //这里写后端地址
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、更新情况
|
||||||
|
|
||||||
|
1. 增加了 Echart 组件复用的功能,如:中间任务达标率的两个百分比图使用的是同一个组件。
|
||||||
|
2. 修复了头部右侧的图案条不对称的问题。
|
||||||
|
3. 使用 Mixins 注入图表响应式代码(scale方案之后无需使用)。
|
||||||
|
4. vue-awesome 改成按需引入的方式。
|
||||||
|
5. 封装渲染函数,抽离了数据使逻辑更加清晰。
|
||||||
|
6. 新增地图组件,并添加自动轮播功能
|
||||||
|
7. 将适配方案从 rem 改成 scale
|
||||||
|
|
||||||
|
## 五、反馈
|
||||||
|
|
||||||
|
QQ群二维码:1032272034
|
||||||
|
|
||||||
|
<img src="public/QQ3.png" width="200px" />
|
||||||
|
|
||||||
|
## 六、相关大屏案例
|
||||||
|
(以下案例基于此项目二次开发):
|
||||||
|
|
||||||
|
1. (支持地图下钻)https://gitee.com/memeda520/IofTV-Screen
|
||||||
|

|
||||||
|
2. (重写结构,支持响应式布局)https://gitee.com/BigCatHome/koi-screen
|
||||||
|

|
||||||
|
## 七、其余
|
||||||
|
|
||||||
|
这个项目是个人的作品,难免会有问题和 BUG,如果有问题请进行评论,我也会尽力去更新,自己也在前端学习的路上,欢迎交流,非常感谢!
|
5
babel.config.js
Normal file
5
babel.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
19108
package-lock.json
generated
Normal file
19108
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
51
package.json
Normal file
51
package.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"version": "1.5.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@jiaminghi/data-view": "^2.7.3",
|
||||||
|
"@types/echarts": "^4.4.3",
|
||||||
|
"core-js": "^3.6.4",
|
||||||
|
"echarts": "^4.6.0",
|
||||||
|
"vue": "^2.6.11",
|
||||||
|
"vue-awesome": "^4.0.2",
|
||||||
|
"vue-router": "^3.1.5",
|
||||||
|
"vuex": "^3.1.2",
|
||||||
|
"axios": "^0.21.4",
|
||||||
|
"element-ui": "^2.15.13",
|
||||||
|
"file-saver": "^2.0.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "^4.2.0",
|
||||||
|
"@vue/cli-plugin-eslint": "^4.2.0",
|
||||||
|
"@vue/cli-service": "^5.0.8",
|
||||||
|
"babel-eslint": "^10.0.3",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-vue": "^6.1.2",
|
||||||
|
"sass": "^1.25.0",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
|
"vue-template-compiler": "^2.6.11"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/essential",
|
||||||
|
"eslint:recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "babel-eslint"
|
||||||
|
},
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions"
|
||||||
|
]
|
||||||
|
}
|
9565
pnpm-lock.yaml
generated
Normal file
9565
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/QQ3.png
Normal file
BIN
public/QQ3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 395 KiB |
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
18
public/index.html
Normal file
18
public/index.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title>设备数据大屏可视化系统</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
public/other_image.png
Normal file
BIN
public/other_image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
BIN
public/other_image2.png
Normal file
BIN
public/other_image2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 MiB |
14
src/App.vue
Normal file
14
src/App.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#app {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #020308;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
81
src/api/data.js
Normal file
81
src/api/data.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 获取设备总按摩时间统计
|
||||||
|
export function getDeviceTotalMassageTime(params) {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/massage/getDeviceTotalMassageTime',
|
||||||
|
method: 'get',
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取按摩头类型分布统计
|
||||||
|
export function getHeadTypeStats() {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/massage/headTypeDistribution',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取中心面板统计数据
|
||||||
|
export function getCenterInfoStats() {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/device/deviceCenter/deviceStats',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备使用时长统计
|
||||||
|
export function getDeviceUsageStats() {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/device/massageInfo/getDeviceUsageStats',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取仪表盘图表数据 - 新增接口
|
||||||
|
export function getDashboardChartData() {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/device/dashboard/chartData',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备趋势数据 - 新增接口
|
||||||
|
export function getDeviceTrendData() {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/device/dashboard/trendData',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取中心面板综合数据
|
||||||
|
export function getComprehensiveCenterStats() {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/device/deviceCenter/comprehensiveStats',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备使用分析数据
|
||||||
|
export function getDeviceUsageAnalysis() {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/device/deviceUsageAnalysis',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取雷达图统计数据
|
||||||
|
export function getRadarStats() {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/massage/radarStats',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDeviceCountByProvince() {
|
||||||
|
return request({
|
||||||
|
url: '/dev-api/device/device/getDeviceCountByProvince',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
BIN
src/assets/pageBg.png
Normal file
BIN
src/assets/pageBg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 289 KiB |
98
src/assets/scss/_variables.scss
Normal file
98
src/assets/scss/_variables.scss
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// 颜色
|
||||||
|
$colors: (
|
||||||
|
"primary": #1A5CD7,
|
||||||
|
"info-1": #4394e4,
|
||||||
|
"info": #4b67af,
|
||||||
|
"white": #ffffff,
|
||||||
|
"light": #f9f9f9,
|
||||||
|
"grey-1": #999999,
|
||||||
|
"grey": #666666,
|
||||||
|
"dark-1": #5f5f5f,
|
||||||
|
"dark": #222222,
|
||||||
|
"black-1": #171823,
|
||||||
|
"black": #000000,
|
||||||
|
"icon": #5cd9e8
|
||||||
|
);
|
||||||
|
|
||||||
|
// 字体大小
|
||||||
|
$base-font-size: 0.2rem;
|
||||||
|
$font-sizes: (
|
||||||
|
xxs: 0.1,
|
||||||
|
//8px
|
||||||
|
xs: 0.125,
|
||||||
|
//10px
|
||||||
|
sm: 0.2875,
|
||||||
|
//12px
|
||||||
|
md: 0.1625,
|
||||||
|
//13px
|
||||||
|
lg: 0.175,
|
||||||
|
//14px
|
||||||
|
xl: 0.2,
|
||||||
|
//16px
|
||||||
|
xxl: 0.225,
|
||||||
|
//18px
|
||||||
|
xxxl: 0.25 //20px,,,,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 宽高
|
||||||
|
.w-100 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.h-100 {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
//flex
|
||||||
|
.d-flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.flex-column {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.flex-wrap {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.flex-nowrap {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
$flex-jc: (
|
||||||
|
start: flex-start,
|
||||||
|
end: flex-end,
|
||||||
|
center: center,
|
||||||
|
between: space-between,
|
||||||
|
around: space-around,
|
||||||
|
evenly: space-evenly,
|
||||||
|
);
|
||||||
|
|
||||||
|
$flex-ai: (
|
||||||
|
start: flex-start,
|
||||||
|
end: flex-end,
|
||||||
|
center: center,
|
||||||
|
stretch: stretch,
|
||||||
|
);
|
||||||
|
|
||||||
|
.flex-1 {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//.mt-1 => margin top
|
||||||
|
//spacing
|
||||||
|
$spacing-types: (
|
||||||
|
m: margin,
|
||||||
|
p: padding,
|
||||||
|
);
|
||||||
|
$spacing-directions: (
|
||||||
|
t: top,
|
||||||
|
r: right,
|
||||||
|
b: bottom,
|
||||||
|
l: left,
|
||||||
|
);
|
||||||
|
$spacing-base-size: 0.5rem;
|
||||||
|
$spacing-sizes: (
|
||||||
|
0: 0,
|
||||||
|
1: 0.5,
|
||||||
|
2: 1,
|
||||||
|
3: 1.5,
|
||||||
|
4: 2,
|
||||||
|
5: 2.5,
|
||||||
|
);
|
144
src/assets/scss/index.scss
Normal file
144
src/assets/scss/index.scss
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#index {
|
||||||
|
color: #d3d6dd;
|
||||||
|
width: 1920px;
|
||||||
|
height: 1080px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
transform-origin: left top;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 16px 16px 0 16px;
|
||||||
|
background-image: url("../assets/pageBg.png");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.host-body {
|
||||||
|
.dv-dec-10,
|
||||||
|
.dv-dec-10-s {
|
||||||
|
width: 33.3%;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
.dv-dec-10-s {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
.dv-dec-8 {
|
||||||
|
width: 200px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
position: relative;
|
||||||
|
width: 500px;
|
||||||
|
text-align: center;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
font-size: 24px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dv-dec-6 {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -30px;
|
||||||
|
left: 50%;
|
||||||
|
width: 250px;
|
||||||
|
height: 8px;
|
||||||
|
transform: translate(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二行
|
||||||
|
.aside-width {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
.react-r-s,
|
||||||
|
.react-l-s {
|
||||||
|
background-color: #0f1325;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 平行四边形
|
||||||
|
.react-right {
|
||||||
|
&.react-l-s {
|
||||||
|
text-align: right;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
font-size: 18px;
|
||||||
|
width: 300px;
|
||||||
|
line-height: 50px;
|
||||||
|
text-align: center;
|
||||||
|
transform: skewX(-45deg);
|
||||||
|
|
||||||
|
.react-after {
|
||||||
|
position: absolute;
|
||||||
|
right: -25px;
|
||||||
|
top: 0;
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
background-color: #0f1325;
|
||||||
|
transform: skewX(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: inline-block;
|
||||||
|
transform: skewX(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-left {
|
||||||
|
&.react-l-s {
|
||||||
|
width: 500px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
font-size: 18px;
|
||||||
|
width: 300px;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
text-align: center;
|
||||||
|
transform: skewX(45deg);
|
||||||
|
background-color: #0f1325;
|
||||||
|
|
||||||
|
.react-left {
|
||||||
|
position: absolute;
|
||||||
|
left: -25px;
|
||||||
|
top: 0;
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
background-color: #0f1325;
|
||||||
|
transform: skewX(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: inline-block;
|
||||||
|
transform: skewX(-45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.body-box {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
//下方区域的布局
|
||||||
|
.content-box {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2fr 3fr 5fr 3fr 2fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 底部数据
|
||||||
|
.bottom-box {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
186
src/assets/scss/style.scss
Normal file
186
src/assets/scss/style.scss
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
@import "./variables";
|
||||||
|
|
||||||
|
// 全局样式
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
line-height: 1.2em;
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #343440;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix {
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
height: 0;
|
||||||
|
line-height: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//浮动
|
||||||
|
.float-r {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
//浮动
|
||||||
|
.float-l {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字体加粗
|
||||||
|
.fw-b {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
//文章一行显示,多余省略号显示
|
||||||
|
.title-item {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-color-black {
|
||||||
|
background-color: rgba(19, 25, 47, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-color-blue {
|
||||||
|
background-color: #1a5cd7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorBlack {
|
||||||
|
color: #272727 !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #272727 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorGrass {
|
||||||
|
color: #33cea0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #33cea0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorRed {
|
||||||
|
color: #ff5722;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #ff5722 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorText {
|
||||||
|
color: #d3d6dd !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #d3d6dd !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorBlue {
|
||||||
|
color: #257dff !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #257dff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//颜色
|
||||||
|
@each $colorkey, $color in $colors {
|
||||||
|
.text-#{$colorkey} {
|
||||||
|
color: $color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-#{$colorkey} {
|
||||||
|
background-color: $color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//对齐
|
||||||
|
@each $var in (left, center, right) {
|
||||||
|
.text-#{$var} {
|
||||||
|
text-align: $var !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//flex
|
||||||
|
@each $key, $value in $flex-jc {
|
||||||
|
.jc-#{$key} {
|
||||||
|
justify-content: $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $key, $value in $flex-ai {
|
||||||
|
.ai-#{$key} {
|
||||||
|
align-items: $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//字体
|
||||||
|
@each $fontkey, $fontvalue in $font-sizes {
|
||||||
|
.fs-#{$fontkey} {
|
||||||
|
font-size: $fontvalue * $base-font-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//.mt-1 => margin top
|
||||||
|
//spacing
|
||||||
|
|
||||||
|
@each $typekey, $type in $spacing-types {
|
||||||
|
//.m-1
|
||||||
|
@each $sizekey, $size in $spacing-sizes {
|
||||||
|
.#{$typekey}-#{$sizekey} {
|
||||||
|
#{$type}: $size * $spacing-base-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//.mx-1
|
||||||
|
@each $sizekey, $size in $spacing-sizes {
|
||||||
|
.#{$typekey}x-#{$sizekey} {
|
||||||
|
#{$type}-left: $size * $spacing-base-size;
|
||||||
|
#{$type}-right: $size * $spacing-base-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$typekey}y-#{$sizekey} {
|
||||||
|
#{$type}-top: $size * $spacing-base-size;
|
||||||
|
#{$type}-bottom: $size * $spacing-base-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//.mt-1
|
||||||
|
@each $directionkey, $direction in $spacing-directions {
|
||||||
|
@each $sizekey, $size in $spacing-sizes {
|
||||||
|
.#{$typekey}#{$directionkey}-#{$sizekey} {
|
||||||
|
#{$type}-#{$direction}: $size * $spacing-base-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$typekey} {
|
||||||
|
#{$type}: 0;
|
||||||
|
}
|
||||||
|
}
|
66
src/common/echart/index.vue
Normal file
66
src/common/echart/index.vue
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<template>
|
||||||
|
<div :id="id" :class="className" :style="{ height: height, width: width }" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tdTheme from './theme.json' // 引入默认主题
|
||||||
|
import '../map/fujian.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'echart',
|
||||||
|
props: {
|
||||||
|
className: {
|
||||||
|
type: String,
|
||||||
|
default: 'chart'
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: 'chart'
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '100%'
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '2.5rem'
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Object,
|
||||||
|
default: ()=>({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
chart: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
options: {
|
||||||
|
handler (options) {
|
||||||
|
// 设置true清空echart缓存
|
||||||
|
this.chart.setOption(options, true)
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$echarts.registerTheme('tdTheme', tdTheme); // 覆盖默认主题
|
||||||
|
this.initChart();
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
this.chart.dispose()
|
||||||
|
this.chart = null
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initChart () {
|
||||||
|
// 初始化echart
|
||||||
|
this.chart = this.$echarts.init(this.$el, 'tdTheme')
|
||||||
|
this.chart.setOption(this.options, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
490
src/common/echart/theme.json
Normal file
490
src/common/echart/theme.json
Normal file
@ -0,0 +1,490 @@
|
|||||||
|
{
|
||||||
|
"color": [
|
||||||
|
"#2d8cf0",
|
||||||
|
"#19be6b",
|
||||||
|
"#ff9900",
|
||||||
|
"#E46CBB",
|
||||||
|
"#9A66E4",
|
||||||
|
"#ed3f14"
|
||||||
|
],
|
||||||
|
"backgroundColor": "rgba(0,0,0,0)",
|
||||||
|
"textStyle": {},
|
||||||
|
"title": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#516b91"
|
||||||
|
},
|
||||||
|
"subtextStyle": {
|
||||||
|
"color": "#93b7e3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": "2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lineStyle": {
|
||||||
|
"normal": {
|
||||||
|
"width": "2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"symbolSize": "6",
|
||||||
|
"symbol": "emptyCircle",
|
||||||
|
"smooth": true
|
||||||
|
},
|
||||||
|
"radar": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": "2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lineStyle": {
|
||||||
|
"normal": {
|
||||||
|
"width": "2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"symbolSize": "6",
|
||||||
|
"symbol": "emptyCircle",
|
||||||
|
"smooth": true
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"barBorderWidth": 0,
|
||||||
|
"barBorderColor": "#ccc"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"barBorderWidth": 0,
|
||||||
|
"barBorderColor": "#ccc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pie": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scatter": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"boxplot": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parallel": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sankey": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"funnel": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gauge": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"candlestick": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"color": "#edafda",
|
||||||
|
"color0": "transparent",
|
||||||
|
"borderColor": "#d680bc",
|
||||||
|
"borderColor0": "#8fd3e8",
|
||||||
|
"borderWidth": "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"graph": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderWidth": 0,
|
||||||
|
"borderColor": "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lineStyle": {
|
||||||
|
"normal": {
|
||||||
|
"width": 1,
|
||||||
|
"color": "#aaa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"symbolSize": "6",
|
||||||
|
"symbol": "emptyCircle",
|
||||||
|
"smooth": true,
|
||||||
|
"color": [
|
||||||
|
"#2d8cf0",
|
||||||
|
"#19be6b",
|
||||||
|
"#f5ae4a",
|
||||||
|
"#9189d5",
|
||||||
|
"#56cae2",
|
||||||
|
"#cbb0e3"
|
||||||
|
],
|
||||||
|
"label": {
|
||||||
|
"normal": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eee"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"map": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"areaColor": "#f3f3f3",
|
||||||
|
"borderColor": "#516b91",
|
||||||
|
"borderWidth": 0.5
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"areaColor": "rgba(165,231,240,1)",
|
||||||
|
"borderColor": "#516b91",
|
||||||
|
"borderWidth": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"normal": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "rgb(81,107,145)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geo": {
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"areaColor": "#f3f3f3",
|
||||||
|
"borderColor": "#516b91",
|
||||||
|
"borderWidth": 0.5
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"areaColor": "rgba(165,231,240,1)",
|
||||||
|
"borderColor": "#516b91",
|
||||||
|
"borderWidth": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"normal": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "rgb(81,107,145)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"categoryAxis": {
|
||||||
|
"axisLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#cccccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisTick": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#333"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisLabel": {
|
||||||
|
"show": true,
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#fff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitLine": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": [
|
||||||
|
"#eeeeee"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitArea": {
|
||||||
|
"show": false,
|
||||||
|
"areaStyle": {
|
||||||
|
"color": [
|
||||||
|
"rgba(250,250,250,0.05)",
|
||||||
|
"rgba(200,200,200,0.02)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"valueAxis": {
|
||||||
|
"axisLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#cccccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisTick": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#333"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisLabel": {
|
||||||
|
"show": true,
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#fff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitLine": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": [
|
||||||
|
"#eeeeee"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitArea": {
|
||||||
|
"show": false,
|
||||||
|
"areaStyle": {
|
||||||
|
"color": [
|
||||||
|
"rgba(250,250,250,0.05)",
|
||||||
|
"rgba(200,200,200,0.02)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"logAxis": {
|
||||||
|
"axisLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#cccccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisTick": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#333"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisLabel": {
|
||||||
|
"show": true,
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#999999"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": [
|
||||||
|
"#eeeeee"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitArea": {
|
||||||
|
"show": false,
|
||||||
|
"areaStyle": {
|
||||||
|
"color": [
|
||||||
|
"rgba(250,250,250,0.05)",
|
||||||
|
"rgba(200,200,200,0.02)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timeAxis": {
|
||||||
|
"axisLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#cccccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisTick": {
|
||||||
|
"show": false,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#333"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axisLabel": {
|
||||||
|
"show": true,
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#999999"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitLine": {
|
||||||
|
"show": true,
|
||||||
|
"lineStyle": {
|
||||||
|
"color": [
|
||||||
|
"#eeeeee"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"splitArea": {
|
||||||
|
"show": false,
|
||||||
|
"areaStyle": {
|
||||||
|
"color": [
|
||||||
|
"rgba(250,250,250,0.05)",
|
||||||
|
"rgba(200,200,200,0.02)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"toolbox": {
|
||||||
|
"iconStyle": {
|
||||||
|
"normal": {
|
||||||
|
"borderColor": "#999"
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"borderColor": "#666"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"legend": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#fff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"axisPointer": {
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#ccc",
|
||||||
|
"width": 1
|
||||||
|
},
|
||||||
|
"crossStyle": {
|
||||||
|
"color": "#ccc",
|
||||||
|
"width": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timeline": {
|
||||||
|
"lineStyle": {
|
||||||
|
"color": "#8fd3e8",
|
||||||
|
"width": 1
|
||||||
|
},
|
||||||
|
"itemStyle": {
|
||||||
|
"normal": {
|
||||||
|
"color": "#8fd3e8",
|
||||||
|
"borderWidth": 1
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"color": "#8fd3e8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"controlStyle": {
|
||||||
|
"normal": {
|
||||||
|
"color": "#8fd3e8",
|
||||||
|
"borderColor": "#8fd3e8",
|
||||||
|
"borderWidth": 0.5
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"color": "#8fd3e8",
|
||||||
|
"borderColor": "#8fd3e8",
|
||||||
|
"borderWidth": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkpointStyle": {
|
||||||
|
"color": "#8fd3e8",
|
||||||
|
"borderColor": "rgba(138,124,168,0.37)"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"normal": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#8fd3e8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#8fd3e8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"visualMap": {
|
||||||
|
"color": [
|
||||||
|
"#516b91",
|
||||||
|
"#59c4e6",
|
||||||
|
"#a5e7f0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dataZoom": {
|
||||||
|
"backgroundColor": "rgba(0,0,0,0)",
|
||||||
|
"dataBackgroundColor": "rgba(255,255,255,0.3)",
|
||||||
|
"fillerColor": "rgba(167,183,204,0.4)",
|
||||||
|
"handleColor": "#a7b7cc",
|
||||||
|
"handleSize": "100%",
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#333"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"markPoint": {
|
||||||
|
"label": {
|
||||||
|
"normal": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emphasis": {
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#eee"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
src/common/map/fujian.js
Normal file
48
src/common/map/fujian.js
Normal file
File diff suppressed because one or more lines are too long
177
src/components/echart/bottom/bottomLeftChart/chart.vue
Normal file
177
src/components/echart/bottom/bottomLeftChart/chart.vue
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Echart
|
||||||
|
:options="options"
|
||||||
|
id="bottomLeftChart"
|
||||||
|
height="480px"
|
||||||
|
width="100%"
|
||||||
|
></Echart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Echart from '@/common/echart'
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Echart,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
cdata: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
cdata: {
|
||||||
|
handler (newData) {
|
||||||
|
this.options = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
backgroundColor: "rgba(255,255,255,0.1)",
|
||||||
|
axisPointer: {
|
||||||
|
type: "shadow",
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
backgroundColor: "#7B7DDC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatter: function(params) {
|
||||||
|
let result = params[0].name + '<br/>';
|
||||||
|
params.forEach(function(item) {
|
||||||
|
var value = item.value;
|
||||||
|
var seriesName = item.seriesName;
|
||||||
|
if (seriesName === '使用率') {
|
||||||
|
value = (value * 100).toFixed(1) + '%';
|
||||||
|
} else {
|
||||||
|
value = value + ' 分钟';
|
||||||
|
}
|
||||||
|
result += '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + item.color + '"></span>' + seriesName + ':' + value + '<br/>';
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ["实际使用时长", "计划使用时长", "使用率"],
|
||||||
|
textStyle: {
|
||||||
|
color: "#B4B4B4"
|
||||||
|
},
|
||||||
|
top: "0%"
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
x: "8%",
|
||||||
|
width: "88%",
|
||||||
|
y: "4%"
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
data: newData.category,
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: "#B4B4B4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
interval: 0, // 强制显示所有标签
|
||||||
|
rotate: 45, // 标签旋转45度避免重叠
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
name: '使用时长(分钟)',
|
||||||
|
splitLine: { show: false },
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: "#B4B4B4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
formatter: '{value}'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
name: '使用率',
|
||||||
|
splitLine: { show: false },
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: "#B4B4B4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
formatter: function(value) {
|
||||||
|
return (value * 100).toFixed(0) + '%';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
min: 0,
|
||||||
|
max: 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "使用率",
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
showAllSymbol: true,
|
||||||
|
symbol: "emptyCircle",
|
||||||
|
symbolSize: 8,
|
||||||
|
yAxisIndex: 1,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: "#F02FC2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: newData.rateData
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "实际使用时长",
|
||||||
|
type: "bar",
|
||||||
|
barWidth: 10,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
barBorderRadius: 5,
|
||||||
|
color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{ offset: 0, color: "#956FD4" },
|
||||||
|
{ offset: 1, color: "#3EACE5" }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: newData.barData
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "计划使用时长",
|
||||||
|
type: "bar",
|
||||||
|
barGap: "-100%",
|
||||||
|
barWidth: 10,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
barBorderRadius: 5,
|
||||||
|
color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{ offset: 0, color: "rgba(156,107,211,0.8)" },
|
||||||
|
{ offset: 0.2, color: "rgba(156,107,211,0.5)" },
|
||||||
|
{ offset: 1, color: "rgba(156,107,211,0.2)" }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
z: -12,
|
||||||
|
data: newData.lineData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
58
src/components/echart/bottom/bottomLeftChart/index.vue
Normal file
58
src/components/echart/bottom/bottomLeftChart/index.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Chart :cdata="cdata" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getDeviceUsageStats } from '@/api/data'
|
||||||
|
import Chart from './chart.vue'
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
cdata: {
|
||||||
|
category: [],
|
||||||
|
lineData: [],
|
||||||
|
barData: [],
|
||||||
|
rateData: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Chart,
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.setData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setData () {
|
||||||
|
// 调用设备使用统计接口
|
||||||
|
getDeviceUsageStats().then(response => {
|
||||||
|
console.log('接口响应:', response);
|
||||||
|
if (response.code === 200) {
|
||||||
|
console.log('实际数据:', response.data);
|
||||||
|
this.cdata = response.data;
|
||||||
|
} else {
|
||||||
|
console.error('获取设备使用统计失败:', response.msg);
|
||||||
|
this.setDefaultData();
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取设备使用统计失败', error);
|
||||||
|
this.setDefaultData();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setDefaultData () {
|
||||||
|
// 设置一些默认数据避免页面空白
|
||||||
|
this.cdata = {
|
||||||
|
category: ["设备1", "设备2", "设备3", "设备4", "设备5"],
|
||||||
|
lineData: [1000, 1200, 800, 1500, 900],
|
||||||
|
barData: [600, 800, 400, 1000, 500],
|
||||||
|
rateData: [0.60, 0.67, 0.50, 0.67, 0.56]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
359
src/components/echart/bottom/bottomRightChart/chart.vue
Normal file
359
src/components/echart/bottom/bottomRightChart/chart.vue
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Echart
|
||||||
|
:options="options"
|
||||||
|
id="centreLeft1Chart"
|
||||||
|
height="480px"
|
||||||
|
width="100%"
|
||||||
|
></Echart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Echart from '@/common/echart'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: {},
|
||||||
|
// 定义颜色
|
||||||
|
colorList: {
|
||||||
|
linearYtoG: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 1,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: '#f5b44d'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: '#28f8de'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
linearGtoB: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 1,
|
||||||
|
y2: 0,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: '#43dfa2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: '#28f8de'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
linearBtoG: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 1,
|
||||||
|
y2: 0,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: '#1c98e8'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: '#28f8de'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
areaBtoG: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(35,184,210,.2)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(35,184,210,0)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Echart
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
cdata: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
cdata: {
|
||||||
|
handler(newData) {
|
||||||
|
this.options = {
|
||||||
|
title: {
|
||||||
|
text: '设备运行分析',
|
||||||
|
textStyle: {
|
||||||
|
color: '#D3D6DD',
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: 'normal'
|
||||||
|
},
|
||||||
|
subtext: newData.year + '/' + newData.weekCategory[6],
|
||||||
|
subtextStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 16
|
||||||
|
},
|
||||||
|
top: 50,
|
||||||
|
left: 80
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
top: 120,
|
||||||
|
left: 80,
|
||||||
|
orient: 'vertical',
|
||||||
|
itemGap: 15,
|
||||||
|
itemWidth: 12,
|
||||||
|
itemHeight: 12,
|
||||||
|
data: ['设备平均', '当前设备'],
|
||||||
|
textStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 14
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item'
|
||||||
|
},
|
||||||
|
radar: {
|
||||||
|
center: ['68%', '27%'],
|
||||||
|
radius: '40%',
|
||||||
|
name: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
splitNumber: 8,
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: this.colorList.linearYtoG,
|
||||||
|
opacity: 0.6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: this.colorList.linearYtoG,
|
||||||
|
opacity: 0.6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitArea: {
|
||||||
|
areaStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
opacity: 0.1,
|
||||||
|
shadowBlur: 25,
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowOffsetY: 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
indicator: [
|
||||||
|
{
|
||||||
|
name: '在线率',
|
||||||
|
max: 10000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '使用频率',
|
||||||
|
max: 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '按摩时长',
|
||||||
|
max: 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '设备评分',
|
||||||
|
max: 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 90,
|
||||||
|
right: 80,
|
||||||
|
bottom: 40,
|
||||||
|
top: '60%'
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
position: 'bottom',
|
||||||
|
axisLine: true,
|
||||||
|
axisLabel: {
|
||||||
|
color: 'rgba(255,255,255,.8)',
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
data: newData.weekCategory
|
||||||
|
},
|
||||||
|
// 下方Y轴
|
||||||
|
yAxis: {
|
||||||
|
name: '按摩时长(分钟)',
|
||||||
|
nameLocation: 'end',
|
||||||
|
nameGap: 24,
|
||||||
|
nameTextStyle: {
|
||||||
|
color: 'rgba(255,255,255,.5)',
|
||||||
|
fontSize: 14
|
||||||
|
},
|
||||||
|
max: newData.maxData,
|
||||||
|
splitNumber: 4,
|
||||||
|
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
opacity: 0.1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: 'rgba(255,255,255,.8)',
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
type: 'radar',
|
||||||
|
symbolSize: 0,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: newData.radarDataAvg && newData.radarDataAvg.length > 6 ? newData.radarDataAvg[6] : [5000, 7, 9, 3.2],
|
||||||
|
name: '设备平均',
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#f8d351'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
normal: {
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#f8d351',
|
||||||
|
shadowBlur: 25,
|
||||||
|
shadowColor: 'rgba(248,211,81,.3)',
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowOffsetY: -10,
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: newData.radarData && newData.radarData.length > 6 ? newData.radarData[6] : [6000, 8, 10, 4.0],
|
||||||
|
name: '当前设备',
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#43dfa2'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
normal: {
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
normal: {
|
||||||
|
color: this.colorList.linearGtoB,
|
||||||
|
shadowBlur: 15,
|
||||||
|
shadowColor: 'rgba(0,0,0,.2)',
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowOffsetY: 5,
|
||||||
|
opacity: 0.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '每日按摩时长',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'emptyCircle',
|
||||||
|
symbolSize: 8,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#fff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
normal: {
|
||||||
|
color: this.colorList.linearBtoG,
|
||||||
|
width: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
normal: {
|
||||||
|
color: this.colorList.areaBtoG
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: newData.weekLineData || [],
|
||||||
|
lineSmooth: true,
|
||||||
|
markLine: {
|
||||||
|
silent: true,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'average',
|
||||||
|
name: '平均值'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
precision: 0,
|
||||||
|
label: {
|
||||||
|
normal: {
|
||||||
|
formatter: '平均值: \n {c}分钟'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
normal: {
|
||||||
|
color: 'rgba(248,211,81,.7)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
position: 'top',
|
||||||
|
formatter: '{c} 分钟',
|
||||||
|
backgroundColor: 'rgba(28,152,232,.2)',
|
||||||
|
padding: 6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '占位背景',
|
||||||
|
type: 'bar',
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
show: true,
|
||||||
|
color: '#000',
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
silent: true,
|
||||||
|
barWidth: '50%',
|
||||||
|
data: newData.weekMaxData || [],
|
||||||
|
animation: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
110
src/components/echart/bottom/bottomRightChart/index.vue
Normal file
110
src/components/echart/bottom/bottomRightChart/index.vue
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
[file name]: index.vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Chart :cdata="cdata" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Chart from './chart.vue'
|
||||||
|
import { getDashboardChartData } from '@/api/data' // 引入新的接口
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
drawTiming: null,
|
||||||
|
cdata: {
|
||||||
|
year: null,
|
||||||
|
weekCategory: [],
|
||||||
|
radarData: [],
|
||||||
|
radarDataAvg: [],
|
||||||
|
maxData: 12000,
|
||||||
|
weekMaxData: [],
|
||||||
|
weekLineData: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Chart,
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.drawTimingFn();
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
clearInterval(this.drawTiming);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
drawTimingFn () {
|
||||||
|
this.getDashboardData(); // 使用新的数据获取方法
|
||||||
|
this.drawTiming = setInterval(() => {
|
||||||
|
this.getDashboardData();
|
||||||
|
}, 6000);
|
||||||
|
},
|
||||||
|
|
||||||
|
async getDashboardData() {
|
||||||
|
try {
|
||||||
|
const response = await getDashboardChartData();
|
||||||
|
if (response && response.code === 200) {
|
||||||
|
this.cdata = response.data;
|
||||||
|
console.log('仪表盘数据获取成功:', this.cdata);
|
||||||
|
} else {
|
||||||
|
console.error('获取仪表盘数据失败,使用模拟数据');
|
||||||
|
this.setData(); // 使用原有的模拟数据作为后备
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取仪表盘数据异常,使用模拟数据:', error);
|
||||||
|
this.setData(); // 使用原有的模拟数据作为后备
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 保留原有的setData方法作为后备
|
||||||
|
setData () {
|
||||||
|
// 清空轮询数据
|
||||||
|
this.cdata.weekCategory = [];
|
||||||
|
this.cdata.weekMaxData = [];
|
||||||
|
this.cdata.weekLineData = [];
|
||||||
|
this.cdata.radarData = [];
|
||||||
|
this.cdata.radarDataAvg = [];
|
||||||
|
|
||||||
|
let dateBase = new Date();
|
||||||
|
this.cdata.year = dateBase.getFullYear();
|
||||||
|
// 周数据
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
// 日期
|
||||||
|
let date = new Date();
|
||||||
|
this.cdata.weekCategory.unshift([date.getMonth() + 1, date.getDate()-i].join("/"));
|
||||||
|
|
||||||
|
// 折线图数据
|
||||||
|
this.cdata.weekMaxData.push(this.cdata.maxData);
|
||||||
|
let distance = Math.round(Math.random() * 11000 + 500);
|
||||||
|
this.cdata.weekLineData.push(distance);
|
||||||
|
|
||||||
|
// 雷达图数据
|
||||||
|
// 我的指标
|
||||||
|
let averageSpeed = +(Math.random() * 5 + 3).toFixed(3);
|
||||||
|
let maxSpeed = averageSpeed + +(Math.random() * 3).toFixed(2);
|
||||||
|
let hour = +(distance / 1000 / averageSpeed).toFixed(1);
|
||||||
|
let radarDayData = [distance, averageSpeed, maxSpeed, hour];
|
||||||
|
this.cdata.radarData.unshift(radarDayData);
|
||||||
|
|
||||||
|
// 平均指标
|
||||||
|
let distanceAvg = Math.round(Math.random() * 8000 + 4000);
|
||||||
|
let averageSpeedAvg = +(Math.random() * 4 + 4).toFixed(3);
|
||||||
|
let maxSpeedAvg = averageSpeedAvg + +(Math.random() * 2).toFixed(2);
|
||||||
|
let hourAvg = +(distance / 1000 / averageSpeed).toFixed(1);
|
||||||
|
let radarDayDataAvg = [
|
||||||
|
distanceAvg,
|
||||||
|
averageSpeedAvg,
|
||||||
|
maxSpeedAvg,
|
||||||
|
hourAvg
|
||||||
|
];
|
||||||
|
this.cdata.radarDataAvg.unshift(radarDayDataAvg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
||||||
|
[file content end]
|
104
src/components/echart/center/centerChartRate/index.vue
Normal file
104
src/components/echart/center/centerChartRate/index.vue
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 通过率/达标率 -->
|
||||||
|
<Echart
|
||||||
|
:options="options"
|
||||||
|
:id="id"
|
||||||
|
height="100px"
|
||||||
|
width="100px"
|
||||||
|
></Echart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Echart from '@/common/echart'
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Echart,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
default: "chartRate"
|
||||||
|
},
|
||||||
|
tips: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 50
|
||||||
|
},
|
||||||
|
colorObj: {
|
||||||
|
type: Object,
|
||||||
|
default: function () {
|
||||||
|
return {
|
||||||
|
textStyle: "#3fc0fb",
|
||||||
|
series: {
|
||||||
|
color: ["#00bcd44a", "transparent"],
|
||||||
|
dataColor: {
|
||||||
|
normal: "#03a9f4",
|
||||||
|
shadowColor: "#97e2f5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// tips 是会变更的数据,所以进行监听
|
||||||
|
tips: {
|
||||||
|
handler (newData) {
|
||||||
|
this.options = {
|
||||||
|
title:{
|
||||||
|
text: newData * 1 + "%",
|
||||||
|
x: "center",
|
||||||
|
y: "center",
|
||||||
|
textStyle: {
|
||||||
|
color: this.colorObj.textStyle,
|
||||||
|
fontSize: 16
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: "pie",
|
||||||
|
radius: ["75%", "80%"],
|
||||||
|
center: ["50%", "50%"],
|
||||||
|
hoverAnimation: false,
|
||||||
|
color: this.colorObj.series.color,
|
||||||
|
label: {
|
||||||
|
normal: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: newData,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: this.colorObj.series.dataColor.normal,
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowColor: this.colorObj.series.dataColor.shadowColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 100 - newData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
82
src/components/echart/centerLeft/centerLeft1Chart/chart.vue
Normal file
82
src/components/echart/centerLeft/centerLeft1Chart/chart.vue
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Echart
|
||||||
|
:options="options"
|
||||||
|
id="centreLeft1Chart"
|
||||||
|
height="220px"
|
||||||
|
width="260px"
|
||||||
|
></Echart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Echart from '@/common/echart'
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Echart,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
cdata: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
cdata: {
|
||||||
|
handler (newData) {
|
||||||
|
this.options = {
|
||||||
|
color: [
|
||||||
|
"#37a2da",
|
||||||
|
"#32c5e9",
|
||||||
|
"#9fe6b8",
|
||||||
|
"#ffdb5c",
|
||||||
|
"#ff9f7f",
|
||||||
|
"#fb7293",
|
||||||
|
"#e7bcf3",
|
||||||
|
"#8378ea"
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
trigger: "item",
|
||||||
|
// 修改这里:移除 {a}(系列名称),只显示 {b}(数据项名称)
|
||||||
|
formatter: "{b} : {c} ({d}%)"
|
||||||
|
},
|
||||||
|
toolbox: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
calculable: true,
|
||||||
|
legend: {
|
||||||
|
orient: "horizontal",
|
||||||
|
icon: "circle",
|
||||||
|
bottom: 0,
|
||||||
|
x: "center",
|
||||||
|
data: newData.xData,
|
||||||
|
textStyle: {
|
||||||
|
color: "#fff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "通过率统计", // 这个名称在legend中不会显示,只在某些配置中用到
|
||||||
|
type: "pie",
|
||||||
|
radius: [10, 50],
|
||||||
|
roseType: "area",
|
||||||
|
center: ["50%", "40%"],
|
||||||
|
data: newData.seriesData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
86
src/components/echart/centerLeft/centerLeft1Chart/index.vue
Normal file
86
src/components/echart/centerLeft/centerLeft1Chart/index.vue
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Chart :cdata="cdata" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Chart from './chart.vue';
|
||||||
|
import { getHeadTypeStats } from '@/api/data.js'; // 根据你的API文件调整路径
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
cdata: {
|
||||||
|
xData: [],
|
||||||
|
seriesData: []
|
||||||
|
},
|
||||||
|
// 按摩头类型映射
|
||||||
|
massageTypeMap: {
|
||||||
|
thermotherapy: '深部热疗',
|
||||||
|
shockwave: '点阵按摩',
|
||||||
|
ball: '全能滚珠',
|
||||||
|
touch: '触觉按摩',
|
||||||
|
finger: '指疗通络',
|
||||||
|
roller: '滚滚刺疗',
|
||||||
|
stone: '温砭舒揉',
|
||||||
|
ion: '离子光灸'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Chart,
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.loadChartData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 根据映射关系转换数据
|
||||||
|
transformData(apiData) {
|
||||||
|
const transformed = {
|
||||||
|
xData: [],
|
||||||
|
seriesData: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// 转换xData和seriesData中的名称
|
||||||
|
if (apiData.xData && apiData.seriesData) {
|
||||||
|
transformed.xData = apiData.xData.map(item => this.massageTypeMap[item] || item);
|
||||||
|
transformed.seriesData = apiData.seriesData.map(item => ({
|
||||||
|
...item,
|
||||||
|
name: this.massageTypeMap[item.name] || item.name
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformed;
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadChartData() {
|
||||||
|
try {
|
||||||
|
const response = await getHeadTypeStats();
|
||||||
|
if (response.code === 200) {
|
||||||
|
// 在设置数据前进行转换
|
||||||
|
this.cdata = this.transformData(response.data);
|
||||||
|
} else {
|
||||||
|
console.error('获取数据失败:', response.msg);
|
||||||
|
this.setDefaultData();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
this.setDefaultData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setDefaultData() {
|
||||||
|
// 失败时使用默认数据(已经是中文)
|
||||||
|
this.cdata = {
|
||||||
|
xData: ["默认数据1", "默认数据2", "默认数据3"],
|
||||||
|
seriesData: [
|
||||||
|
{ value: 30, name: "默认数据1" },
|
||||||
|
{ value: 20, name: "默认数据2" },
|
||||||
|
{ value: 50, name: "默认数据3" }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
359
src/components/echart/centerLeft/centerLeft2Chart/chart.vue
Normal file
359
src/components/echart/centerLeft/centerLeft2Chart/chart.vue
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Echart
|
||||||
|
id="centreLeft2Chart"
|
||||||
|
ref="centreLeft2ChartRef"
|
||||||
|
:options="options"
|
||||||
|
height="360px"
|
||||||
|
width="330px"
|
||||||
|
></Echart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Echart from '@/common/echart';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: {},
|
||||||
|
intervalId: null,
|
||||||
|
preSelectMapIndex: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Echart,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
cdata: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
cdata: {
|
||||||
|
handler(newData) {
|
||||||
|
this.initMap(newData);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initMap(newData) {
|
||||||
|
console.log('接收到的地图数据:', newData); // 添加调试日志
|
||||||
|
|
||||||
|
// 设置各份的中心点坐标(用于散点图)
|
||||||
|
const geoCoordMap = {
|
||||||
|
'北京市': [116.405285, 39.904989],
|
||||||
|
'天津市': [117.190182, 39.125596],
|
||||||
|
'河北省': [114.502461, 38.045474],
|
||||||
|
'山西省': [112.549248, 37.857014],
|
||||||
|
'内蒙古自治区': [111.670801, 40.818311],
|
||||||
|
'辽宁省': [123.429096, 41.796767],
|
||||||
|
'吉林省': [125.3245, 43.886841],
|
||||||
|
'黑龙江省': [126.642464, 45.756967],
|
||||||
|
'上海市': [121.472644, 31.231706],
|
||||||
|
'江苏省': [118.767413, 32.041544],
|
||||||
|
'浙江省': [120.153576, 30.287459],
|
||||||
|
'安徽省': [117.283042, 31.86119],
|
||||||
|
'福建省': [119.306239, 26.075302],
|
||||||
|
'江西省': [115.892151, 28.676493],
|
||||||
|
'山东省': [117.000923, 36.675807],
|
||||||
|
'河南省': [113.665412, 34.757975],
|
||||||
|
'湖北省': [114.298572, 30.584355],
|
||||||
|
'湖南省': [112.982279, 28.19409],
|
||||||
|
'广东省': [113.280637, 23.125178],
|
||||||
|
'广西壮族自治区': [108.320004, 22.82402],
|
||||||
|
'海南省': [110.33119, 20.031971],
|
||||||
|
'重庆市': [106.504962, 29.533155],
|
||||||
|
'四川省': [104.065735, 30.659462],
|
||||||
|
'贵州省': [106.713478, 26.578343],
|
||||||
|
'云南省': [102.712251, 25.040609],
|
||||||
|
'西藏自治区': [91.132212, 29.660361],
|
||||||
|
'陕西省': [108.948024, 34.263161],
|
||||||
|
'甘肃省': [103.823557, 36.058039],
|
||||||
|
'青海省': [101.778916, 36.623178],
|
||||||
|
'宁夏回族自治区': [106.278179, 38.46637],
|
||||||
|
'新疆维吾尔自治区': [87.617733, 43.792818],
|
||||||
|
'台湾': [121.509062, 25.044332],
|
||||||
|
'香港': [114.173355, 22.320048],
|
||||||
|
'澳门': [113.54909, 22.198951]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确保数据格式正确 - 处理可能的数据问题
|
||||||
|
const processedData = (newData || []).map(item => {
|
||||||
|
// 确保每个数据项都有必要的字段
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
value: item.value || 0,
|
||||||
|
onlineCount: item.onlineCount || 0,
|
||||||
|
offlineCount: item.offlineCount || 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('处理后的地图数据:', processedData); // 调试日志
|
||||||
|
|
||||||
|
// 生成散点数据
|
||||||
|
let convertData = (data) => {
|
||||||
|
let scatterData = [];
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
if (!data[i] || !data[i].name) continue;
|
||||||
|
|
||||||
|
var geoCoord = geoCoordMap[data[i].name];
|
||||||
|
if (geoCoord) {
|
||||||
|
scatterData.push({
|
||||||
|
name: data[i].name,
|
||||||
|
value: geoCoord.concat(data[i].value || 0),
|
||||||
|
onlineCount: data[i].onlineCount || 0,
|
||||||
|
offlineCount: data[i].offlineCount || 0
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn(`未找到 ${data[i].name} 的坐标信息`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scatterData;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 修复配置
|
||||||
|
this.options = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||||||
|
borderColor: '#00f2fc',
|
||||||
|
borderWidth: 1,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 12,
|
||||||
|
lineHeight: 18,
|
||||||
|
color: '#fff'
|
||||||
|
},
|
||||||
|
formatter: (params) => {
|
||||||
|
console.log('Tooltip params:', params); // 调试日志
|
||||||
|
|
||||||
|
if (!params || !params.data) {
|
||||||
|
return '<div style="color: #999;">暂无数据</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = params.data;
|
||||||
|
const name = data.name || '未知区域';
|
||||||
|
const total = data.value || 0;
|
||||||
|
const online = data.onlineCount || 0;
|
||||||
|
const offline = data.offlineCount || 0;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div style="font-size: 13px; color: #fff; margin-bottom: 4px;">
|
||||||
|
<strong>${name}</strong>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 12px; color: #ccc; margin-bottom: 2px;">
|
||||||
|
设备总数: <strong style="color: #ffd700">${total}台</strong>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 12px; color: #19be6b; margin-bottom: 2px;">
|
||||||
|
在线设备: <strong>${online}台</strong>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 12px; color: #ed3f14;">
|
||||||
|
离线设备: <strong>${offline}台</strong>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visualMap: {
|
||||||
|
min: 0,
|
||||||
|
max: this.getMaxValue(processedData),
|
||||||
|
show: true,
|
||||||
|
left: 'left',
|
||||||
|
top: 'bottom',
|
||||||
|
text: ['高', '低'],
|
||||||
|
seriesIndex: [0],
|
||||||
|
inRange: {
|
||||||
|
color: ['#e0f7ff', '#0066cc']
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
color: '#fff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
geo: {
|
||||||
|
show: true,
|
||||||
|
map: 'china',
|
||||||
|
roam: false,
|
||||||
|
zoom: 1.2,
|
||||||
|
top: '10%',
|
||||||
|
left: '5%',
|
||||||
|
right: '5%',
|
||||||
|
label: {
|
||||||
|
emphasis: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
areaColor: 'rgba(19, 54, 162, 0.5)',
|
||||||
|
borderColor: 'rgba(0, 242, 252, 0.3)',
|
||||||
|
borderWidth: 1
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
areaColor: 'rgba(79, 127, 255, 0.8)',
|
||||||
|
borderColor: 'rgba(0, 242, 252, 0.6)',
|
||||||
|
borderWidth: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: '#4f7fff'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
// 主地图系列 - 负责区域颜色和悬停
|
||||||
|
name: '设备数量',
|
||||||
|
type: 'map',
|
||||||
|
map: 'china', // 使用 map 而不是 mapType
|
||||||
|
geoIndex: 0,
|
||||||
|
data: processedData, // 使用处理后的数据
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: '#4f7fff'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 散点效果系列
|
||||||
|
name: '散点效果',
|
||||||
|
type: 'effectScatter',
|
||||||
|
coordinateSystem: 'geo',
|
||||||
|
data: convertData(processedData),
|
||||||
|
symbolSize: function(val) {
|
||||||
|
return Math.max(6, Math.min(15, (val[2] || 0) / 10));
|
||||||
|
},
|
||||||
|
showEffectOn: 'render',
|
||||||
|
rippleEffect: {
|
||||||
|
brushType: 'stroke',
|
||||||
|
scale: 3,
|
||||||
|
period: 4
|
||||||
|
},
|
||||||
|
hoverAnimation: false,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#99FBFE',
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowColor: '#fff'
|
||||||
|
},
|
||||||
|
zlevel: 1,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('ECharts 配置:', this.options); // 调试日志
|
||||||
|
|
||||||
|
// 重新选择区域
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.handleMapRandomSelect();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getMaxValue(data) {
|
||||||
|
if (!data || !Array.isArray(data) || data.length === 0) return 10;
|
||||||
|
const values = data.map(item => item.value || 0).filter(val => !isNaN(val));
|
||||||
|
return values.length > 0 ? Math.max(...values) : 10;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 开启定时器
|
||||||
|
startInterval() {
|
||||||
|
const _self = this;
|
||||||
|
const time = 4000;
|
||||||
|
if (this.intervalId !== null) {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
}
|
||||||
|
this.intervalId = setInterval(() => {
|
||||||
|
_self.reSelectMapRandomArea();
|
||||||
|
}, time);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重新随机选中地图区域
|
||||||
|
reSelectMapRandomArea() {
|
||||||
|
const length = this.cdata ? this.cdata.length : 0;
|
||||||
|
if (length === 0) return;
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
try {
|
||||||
|
const map = this.$refs.centreLeft2ChartRef.chart;
|
||||||
|
let index = Math.floor(Math.random() * length);
|
||||||
|
while (index === this.preSelectMapIndex || index >= length) {
|
||||||
|
index = Math.floor(Math.random() * length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 高亮新的区域
|
||||||
|
map.dispatchAction({
|
||||||
|
type: 'highlight',
|
||||||
|
seriesIndex: 0, // 地图系列
|
||||||
|
dataIndex: index,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 显示tooltip
|
||||||
|
map.dispatchAction({
|
||||||
|
type: 'showTip',
|
||||||
|
seriesIndex: 0, // 地图系列
|
||||||
|
dataIndex: index,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.preSelectMapIndex = index;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('地图操作错误:', error)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleMapRandomSelect() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
try {
|
||||||
|
const map = this.$refs.centreLeft2ChartRef.chart;
|
||||||
|
const _self = this;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
_self.reSelectMapRandomArea();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// 鼠标悬停在地图区域上的事件
|
||||||
|
map.on('mouseover', { seriesIndex: 0 }, function (params) {
|
||||||
|
clearInterval(_self.intervalId);
|
||||||
|
// 高亮悬停的区域
|
||||||
|
map.dispatchAction({
|
||||||
|
type: 'highlight',
|
||||||
|
seriesIndex: 0,
|
||||||
|
dataIndex: params.dataIndex,
|
||||||
|
});
|
||||||
|
_self.preSelectMapIndex = params.dataIndex;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 鼠标离开地图区域的事件
|
||||||
|
map.on('mouseout', { seriesIndex: 0 }, function () {
|
||||||
|
// 取消高亮
|
||||||
|
map.dispatchAction({
|
||||||
|
type: 'downplay',
|
||||||
|
seriesIndex: 0
|
||||||
|
});
|
||||||
|
_self.startInterval();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.startInterval();
|
||||||
|
} catch (error) {
|
||||||
|
console.log('地图事件绑定错误:', error)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.intervalId) {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
83
src/components/echart/centerLeft/centerLeft2Chart/index.vue
Normal file
83
src/components/echart/centerLeft/centerLeft2Chart/index.vue
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Chart :cdata="cdata" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Chart from './chart.vue';
|
||||||
|
import { getDeviceCountByProvince } from '@/api/data.js'; // 引入封装好的接口
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
cdata: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Chart,
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.loadChinaMapData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadChinaMapData() {
|
||||||
|
try {
|
||||||
|
// 调用封装好的接口
|
||||||
|
const response = await getDeviceCountByProvince();
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.cdata = response.data;
|
||||||
|
} else {
|
||||||
|
console.error('加载数据失败:', response.msg);
|
||||||
|
// 使用模拟数据
|
||||||
|
this.cdata = this.getMockData();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载全国地图数据失败:', error);
|
||||||
|
// 使用模拟数据
|
||||||
|
this.cdata = this.getMockData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getMockData() {
|
||||||
|
// 模拟数据 - 保持与福建地图相同的格式
|
||||||
|
return [
|
||||||
|
{ name: '北京市', value: 45 },
|
||||||
|
{ name: '天津市', value: 18 },
|
||||||
|
{ name: '河北省', value: 23 },
|
||||||
|
{ name: '山西省', value: 9 },
|
||||||
|
{ name: '内蒙古自治区', value: 6 },
|
||||||
|
{ name: '辽宁省', value: 20 },
|
||||||
|
{ name: '吉林省', value: 8 },
|
||||||
|
{ name: '黑龙江省', value: 15 },
|
||||||
|
{ name: '上海市', value: 42 },
|
||||||
|
{ name: '江苏省', value: 78 },
|
||||||
|
{ name: '浙江省', value: 89 },
|
||||||
|
{ name: '安徽省', value: 28 },
|
||||||
|
{ name: '福建省', value: 45 },
|
||||||
|
{ name: '江西省', value: 25 },
|
||||||
|
{ name: '山东省', value: 67 },
|
||||||
|
{ name: '河南省', value: 38 },
|
||||||
|
{ name: '湖北省', value: 35 },
|
||||||
|
{ name: '湖南省', value: 32 },
|
||||||
|
{ name: '广东省', value: 156 },
|
||||||
|
{ name: '广西壮族自治区', value: 14 },
|
||||||
|
{ name: '海南省', value: 8 },
|
||||||
|
{ name: '重庆市', value: 25 },
|
||||||
|
{ name: '四川省', value: 42 },
|
||||||
|
{ name: '贵州省', value: 10 },
|
||||||
|
{ name: '云南省', value: 12 },
|
||||||
|
{ name: '西藏自治区', value: 2 },
|
||||||
|
{ name: '陕西省', value: 18 },
|
||||||
|
{ name: '甘肃省', value: 7 },
|
||||||
|
{ name: '青海省', value: 3 },
|
||||||
|
{ name: '宁夏回族自治区', value: 4 },
|
||||||
|
{ name: '新疆维吾尔自治区', value: 5 }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
132
src/components/echart/centerRight/centerRightChart/chart.vue
Normal file
132
src/components/echart/centerRight/centerRightChart/chart.vue
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Echart
|
||||||
|
:options="options"
|
||||||
|
id="centreRight2Chart1"
|
||||||
|
height="200px"
|
||||||
|
width="260px"
|
||||||
|
></Echart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Echart from '@/common/echart'
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Echart,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
cdata: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
cdata: {
|
||||||
|
handler (newData) {
|
||||||
|
// 固定样式数据
|
||||||
|
let lineStyle = {
|
||||||
|
normal: {
|
||||||
|
width: 1,
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.options = {
|
||||||
|
radar: {
|
||||||
|
indicator: newData.indicatorData,
|
||||||
|
shape: "circle",
|
||||||
|
splitNumber: 5,
|
||||||
|
radius: ["0%", "65%"],
|
||||||
|
name: {
|
||||||
|
textStyle: {
|
||||||
|
color: "rgb(238, 197, 102)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: [
|
||||||
|
"rgba(238, 197, 102, 0.1)",
|
||||||
|
"rgba(238, 197, 102, 0.2)",
|
||||||
|
"rgba(238, 197, 102, 0.4)",
|
||||||
|
"rgba(238, 197, 102, 0.6)",
|
||||||
|
"rgba(238, 197, 102, 0.8)",
|
||||||
|
"rgba(238, 197, 102, 1)"
|
||||||
|
].reverse()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitArea: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: "rgba(238, 197, 102, 0.5)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "北京",
|
||||||
|
type: "radar",
|
||||||
|
lineStyle: lineStyle,
|
||||||
|
data: newData.dataBJ,
|
||||||
|
symbol: "none",
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: "#F9713C"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
normal: {
|
||||||
|
opacity: 0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "上海",
|
||||||
|
type: "radar",
|
||||||
|
lineStyle: lineStyle,
|
||||||
|
data: newData.dataSH,
|
||||||
|
symbol: "none",
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: "#B3E4A1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
normal: {
|
||||||
|
opacity: 0.05
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "广州",
|
||||||
|
type: "radar",
|
||||||
|
lineStyle: lineStyle,
|
||||||
|
data: newData.dataGZ,
|
||||||
|
symbol: "none",
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: "rgb(238, 197, 102)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
normal: {
|
||||||
|
opacity: 0.05
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //end
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
61
src/components/echart/centerRight/centerRightChart/index.vue
Normal file
61
src/components/echart/centerRight/centerRightChart/index.vue
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Chart :cdata="cdata" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Chart from './chart.vue';
|
||||||
|
import { getRadarStats } from '@/api/data'; // 导入新接口
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
cdata: {
|
||||||
|
indicatorData: [],
|
||||||
|
dataBJ: [],
|
||||||
|
dataSH: [],
|
||||||
|
dataGZ: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Chart,
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadRadarData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadRadarData() {
|
||||||
|
try {
|
||||||
|
const response = await getRadarStats();
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.cdata = response.data;
|
||||||
|
} else {
|
||||||
|
console.error('获取雷达图数据失败:', response.msg);
|
||||||
|
this.loadDefaultData();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取雷达图数据异常:', error);
|
||||||
|
this.loadDefaultData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadDefaultData() {
|
||||||
|
// 备用默认数据
|
||||||
|
this.cdata = {
|
||||||
|
indicatorData: [
|
||||||
|
{ name: "按摩次数", max: 100 },
|
||||||
|
{ name: "总时长(分)", max: 250 },
|
||||||
|
{ name: "按摩头类型", max: 5 },
|
||||||
|
{ name: "身体部位", max: 6 },
|
||||||
|
{ name: "平均时长", max: 200 },
|
||||||
|
{ name: "使用频率", max: 50 }
|
||||||
|
],
|
||||||
|
dataBJ: [[94, 69, 3, 4, 114, 39]],
|
||||||
|
dataSH: [[91, 45, 2, 3, 125, 23]],
|
||||||
|
dataGZ: [[84, 94, 4, 5, 140, 18]]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
34
src/main.js
Normal file
34
src/main.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import App from './App.vue';
|
||||||
|
import router from './router';
|
||||||
|
import store from './store';
|
||||||
|
import dataV from '@jiaminghi/data-view';
|
||||||
|
// 引入全局css
|
||||||
|
import './assets/scss/style.scss';
|
||||||
|
// 按需引入vue-awesome图标
|
||||||
|
import Icon from 'vue-awesome/components/Icon';
|
||||||
|
import 'vue-awesome/icons/chart-bar.js';
|
||||||
|
import 'vue-awesome/icons/chart-area.js';
|
||||||
|
import 'echarts/map/js/china.js'
|
||||||
|
import 'vue-awesome/icons/chart-pie.js';
|
||||||
|
import 'vue-awesome/icons/chart-line.js';
|
||||||
|
import 'vue-awesome/icons/align-left.js';
|
||||||
|
import ElementUI from 'element-ui'
|
||||||
|
//引入echart
|
||||||
|
//4.x 引用方式
|
||||||
|
import echarts from 'echarts'
|
||||||
|
//5.x 引用方式为按需引用
|
||||||
|
//希望使用5.x版本的话,需要在package.json中更新版本号,并切换引用方式
|
||||||
|
//import * as echarts from 'echarts'
|
||||||
|
Vue.prototype.$echarts = echarts
|
||||||
|
Vue.config.productionTip = false;
|
||||||
|
import 'echarts/map/js/china.js' // 确保这个路径正确
|
||||||
|
// 全局注册
|
||||||
|
Vue.component('icon', Icon);
|
||||||
|
Vue.use(dataV);
|
||||||
|
Vue.use(ElementUI);
|
||||||
|
new Vue({
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
render: (h) => h(App),
|
||||||
|
}).$mount('#app');
|
15
src/router/index.js
Normal file
15
src/router/index.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import VueRouter from 'vue-router'
|
||||||
|
|
||||||
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
|
const routes = [{
|
||||||
|
path: '/',
|
||||||
|
name: 'index',
|
||||||
|
component: () => import('../views/index.vue')
|
||||||
|
}]
|
||||||
|
const router = new VueRouter({
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
15
src/store/index.js
Normal file
15
src/store/index.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
export default new Vuex.Store({
|
||||||
|
state: {
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
},
|
||||||
|
modules: {
|
||||||
|
}
|
||||||
|
})
|
36
src/utils/auth.js
Normal file
36
src/utils/auth.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// 认证相关工具函数 - 简化版本
|
||||||
|
|
||||||
|
// 获取token(从若依系统共享)
|
||||||
|
export function getToken() {
|
||||||
|
// 尝试从不同位置获取若依的token
|
||||||
|
return (
|
||||||
|
localStorage.getItem('Admin-Token') ||
|
||||||
|
sessionStorage.getItem('Admin-Token') ||
|
||||||
|
getCookie('Admin-Token') ||
|
||||||
|
''
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置token
|
||||||
|
export function setToken(token) {
|
||||||
|
localStorage.setItem('Admin-Token', token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除token
|
||||||
|
export function removeToken() {
|
||||||
|
localStorage.removeItem('Admin-Token')
|
||||||
|
sessionStorage.removeItem('Admin-Token')
|
||||||
|
deleteCookie('Admin-Token')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cookie操作辅助函数
|
||||||
|
function getCookie(name) {
|
||||||
|
const value = `; ${document.cookie}`
|
||||||
|
const parts = value.split(`; ${name}=`)
|
||||||
|
if (parts.length === 2) return parts.pop().split(';').shift()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCookie(name) {
|
||||||
|
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
|
||||||
|
}
|
77
src/utils/cache.js
Normal file
77
src/utils/cache.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// 缓存工具 - 提供sessionStorage和localStorage的封装
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// sessionStorage
|
||||||
|
session: {
|
||||||
|
// 设置sessionStorage
|
||||||
|
set(key, value) {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
value = JSON.stringify(value)
|
||||||
|
}
|
||||||
|
sessionStorage.setItem(key, value)
|
||||||
|
},
|
||||||
|
// 获取sessionStorage
|
||||||
|
get(key) {
|
||||||
|
const value = sessionStorage.getItem(key)
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取sessionStorage(JSON格式)
|
||||||
|
getJSON(key) {
|
||||||
|
const value = sessionStorage.getItem(key)
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 移除sessionStorage
|
||||||
|
remove(key) {
|
||||||
|
sessionStorage.removeItem(key)
|
||||||
|
},
|
||||||
|
// 清空sessionStorage
|
||||||
|
clear() {
|
||||||
|
sessionStorage.clear()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// localStorage
|
||||||
|
local: {
|
||||||
|
// 设置localStorage
|
||||||
|
set(key, value) {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
value = JSON.stringify(value)
|
||||||
|
}
|
||||||
|
localStorage.setItem(key, value)
|
||||||
|
},
|
||||||
|
// 获取localStorage
|
||||||
|
get(key) {
|
||||||
|
const value = localStorage.getItem(key)
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取localStorage(JSON格式)
|
||||||
|
getJSON(key) {
|
||||||
|
const value = localStorage.getItem(key)
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 移除localStorage
|
||||||
|
remove(key) {
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
},
|
||||||
|
// 清空localStorage
|
||||||
|
clear() {
|
||||||
|
localStorage.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/utils/drawMixin.js
Normal file
57
src/utils/drawMixin.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 屏幕适配 mixin 函数
|
||||||
|
|
||||||
|
// * 默认缩放值
|
||||||
|
const scale = {
|
||||||
|
width: '1',
|
||||||
|
height: '1',
|
||||||
|
}
|
||||||
|
|
||||||
|
// * 设计稿尺寸(px)
|
||||||
|
const baseWidth = 1920
|
||||||
|
const baseHeight = 1080
|
||||||
|
|
||||||
|
// * 需保持的比例(默认1.77778)
|
||||||
|
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// * 定时函数
|
||||||
|
drawTiming: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.calcRate()
|
||||||
|
window.addEventListener('resize', this.resize)
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('resize', this.resize)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
calcRate () {
|
||||||
|
const appRef = this.$refs["appRef"]
|
||||||
|
if (!appRef) return
|
||||||
|
// 当前宽高比
|
||||||
|
const currentRate = parseFloat((window.innerWidth / window.innerHeight).toFixed(5))
|
||||||
|
if (appRef) {
|
||||||
|
if (currentRate > baseProportion) {
|
||||||
|
// 表示更宽
|
||||||
|
scale.width = ((window.innerHeight * baseProportion) / baseWidth).toFixed(5)
|
||||||
|
scale.height = (window.innerHeight / baseHeight).toFixed(5)
|
||||||
|
appRef.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`
|
||||||
|
} else {
|
||||||
|
// 表示更高
|
||||||
|
scale.height = ((window.innerWidth / baseProportion) / baseHeight).toFixed(5)
|
||||||
|
scale.width = (window.innerWidth / baseWidth).toFixed(5)
|
||||||
|
appRef.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resize () {
|
||||||
|
clearTimeout(this.drawTiming)
|
||||||
|
this.drawTiming = setTimeout(() => {
|
||||||
|
this.calcRate()
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
14
src/utils/errorCode.js
Normal file
14
src/utils/errorCode.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// 错误码映射
|
||||||
|
export default {
|
||||||
|
'401': '认证失败,无法访问系统资源',
|
||||||
|
'403': '当前操作没有权限',
|
||||||
|
'404': '访问资源不存在',
|
||||||
|
'default': '系统未知错误,请反馈给管理员',
|
||||||
|
'500': '服务器内部错误',
|
||||||
|
'501': '网络未实现',
|
||||||
|
'502': '网络错误',
|
||||||
|
'503': '服务不可用',
|
||||||
|
'504': '网络超时',
|
||||||
|
'505': 'http版本不支持该请求',
|
||||||
|
'601': '警告信息'
|
||||||
|
}
|
51
src/utils/index.js
Normal file
51
src/utils/index.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* @param {Function} fn 防抖函数
|
||||||
|
* @param {Number} delay 延迟时间
|
||||||
|
*/
|
||||||
|
export function debounce(fn, delay) {
|
||||||
|
var timer;
|
||||||
|
return function () {
|
||||||
|
var context = this;
|
||||||
|
var args = arguments;
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(function () {
|
||||||
|
fn.apply(context, args);
|
||||||
|
}, delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {date} time 需要转换的时间
|
||||||
|
* @param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss
|
||||||
|
*/
|
||||||
|
export function formatTime(time, fmt) {
|
||||||
|
if (!time) return '';
|
||||||
|
else {
|
||||||
|
const date = new Date(time);
|
||||||
|
const o = {
|
||||||
|
'M+': date.getMonth() + 1,
|
||||||
|
'd+': date.getDate(),
|
||||||
|
'H+': date.getHours(),
|
||||||
|
'm+': date.getMinutes(),
|
||||||
|
's+': date.getSeconds(),
|
||||||
|
'q+': Math.floor((date.getMonth() + 3) / 3),
|
||||||
|
S: date.getMilliseconds(),
|
||||||
|
};
|
||||||
|
if (/(y+)/.test(fmt))
|
||||||
|
fmt = fmt.replace(
|
||||||
|
RegExp.$1,
|
||||||
|
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
|
||||||
|
);
|
||||||
|
for (const k in o) {
|
||||||
|
if (new RegExp('(' + k + ')').test(fmt)) {
|
||||||
|
fmt = fmt.replace(
|
||||||
|
RegExp.$1,
|
||||||
|
RegExp.$1.length === 1
|
||||||
|
? o[k]
|
||||||
|
: ('00' + o[k]).substr(('' + o[k]).length)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
}
|
74
src/utils/request.js
Normal file
74
src/utils/request.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import { Notification, Message } from 'element-ui'
|
||||||
|
import { getToken } from '@/utils/auth'
|
||||||
|
import errorCode from '@/utils/errorCode'
|
||||||
|
|
||||||
|
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||||
|
|
||||||
|
// 创建axios实例
|
||||||
|
const service = axios.create({
|
||||||
|
// 开发环境使用空baseURL,让代理处理
|
||||||
|
baseURL: process.env.NODE_ENV === 'development' ? '' : process.env.VUE_APP_BASE_API,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
|
||||||
|
// request拦截器
|
||||||
|
service.interceptors.request.use(config => {
|
||||||
|
const isToken = (config.headers || {}).isToken === false
|
||||||
|
|
||||||
|
if (getToken() && !isToken) {
|
||||||
|
config.headers['Authorization'] = 'Bearer ' + getToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
service.interceptors.response.use(res => {
|
||||||
|
const code = res.data.code || 200
|
||||||
|
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||||
|
|
||||||
|
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code === 401) {
|
||||||
|
Message({
|
||||||
|
message: '认证失败,请确保已在若依系统登录',
|
||||||
|
type: 'error',
|
||||||
|
duration: 5000
|
||||||
|
})
|
||||||
|
return Promise.reject(new Error('认证失败'))
|
||||||
|
} else if (code === 500) {
|
||||||
|
console.log(res)
|
||||||
|
Message({ message: msg, type: 'error' })
|
||||||
|
return Promise.reject(new Error(msg))
|
||||||
|
} else if (code === 601) {
|
||||||
|
Message({ message: msg, type: 'warning' })
|
||||||
|
return Promise.reject(new Error(msg))
|
||||||
|
} else if (code !== 200) {
|
||||||
|
Notification.error({ title: msg })
|
||||||
|
return Promise.reject('error')
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(res.data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.log('err' + error)
|
||||||
|
let { message } = error
|
||||||
|
if (message == "Network Error") {
|
||||||
|
message = "后端接口连接异常"
|
||||||
|
} else if (message.includes("timeout")) {
|
||||||
|
message = "系统接口请求超时"
|
||||||
|
} else if (message.includes("Request failed with status code")) {
|
||||||
|
message = "系统接口" + message.substr(message.length - 3) + "异常"
|
||||||
|
}
|
||||||
|
Message({ message: message, type: 'error', duration: 5 * 1000 })
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default service
|
33
src/utils/resizeMixin.js
Normal file
33
src/utils/resizeMixin.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 混入代码 resize-mixins.js
|
||||||
|
// 改成 Scale 缩放之后,没有使用这个代码,但是保留
|
||||||
|
import { debounce } from '@/utils';
|
||||||
|
const resizeChartMethod = '$__resizeChartMethod';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
// 在组件内部将图表 init 的引用映射到 chart 属性上
|
||||||
|
return {
|
||||||
|
chart: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
window.addEventListener('resize', this[resizeChartMethod], false);
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
// 防止 keep-alive 之后图表变形
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.resize()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('reisze', this[resizeChartMethod]);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 防抖函数来控制 resize 的频率
|
||||||
|
[resizeChartMethod]: debounce(function() {
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.resize();
|
||||||
|
}
|
||||||
|
}, 300),
|
||||||
|
},
|
||||||
|
};
|
40
src/utils/ruoyi.js
Normal file
40
src/utils/ruoyi.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// 参数处理工具函数
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数处理
|
||||||
|
* @param {*} params 参数
|
||||||
|
*/
|
||||||
|
export function tansParams(params) {
|
||||||
|
let result = ''
|
||||||
|
for (const propName of Object.keys(params)) {
|
||||||
|
const value = params[propName]
|
||||||
|
const part = encodeURIComponent(propName) + "="
|
||||||
|
if (value !== null && value !== "" && typeof (value) !== "undefined") {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
for (const key of Object.keys(value)) {
|
||||||
|
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
|
||||||
|
const params = propName + '[' + key + ']'
|
||||||
|
const subPart = encodeURIComponent(params) + "="
|
||||||
|
result += subPart + encodeURIComponent(value[key]) + "&"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result += part + encodeURIComponent(value) + "&"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证是否为blob格式
|
||||||
|
* @param {*} data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function blobValidate(data) {
|
||||||
|
// 简单的blob验证
|
||||||
|
if (!data) return false
|
||||||
|
if (data instanceof Blob) return true
|
||||||
|
if (data.type && data.size !== undefined) return true
|
||||||
|
return false
|
||||||
|
}
|
52
src/views/bottomLeft.vue
Normal file
52
src/views/bottomLeft.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div id="bottomLeft">
|
||||||
|
<div class="bg-color-black">
|
||||||
|
<div class="d-flex pt-2 pl-2">
|
||||||
|
<span>
|
||||||
|
<icon name="chart-bar" class="text-icon"></icon>
|
||||||
|
</span>
|
||||||
|
<div class="d-flex">
|
||||||
|
<span class="fs-xl text mx-2">数据统计图</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<BottomLeftChart />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import BottomLeftChart from '@/components/echart/bottom/bottomLeftChart'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BottomLeftChart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$box-height: 520px;
|
||||||
|
$box-width: 100%;
|
||||||
|
#bottomLeft {
|
||||||
|
padding: 20px 16px;
|
||||||
|
height: $box-height;
|
||||||
|
width: $box-width;
|
||||||
|
border-radius: 5px;
|
||||||
|
.bg-color-black {
|
||||||
|
height: $box-height - 35px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
color: #c3cbde;
|
||||||
|
}
|
||||||
|
.chart-box {
|
||||||
|
margin-top: 16px;
|
||||||
|
width: 170px;
|
||||||
|
height: 170px;
|
||||||
|
.active-ring-name {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
60
src/views/bottomRight.vue
Normal file
60
src/views/bottomRight.vue
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<div id="bottomRight">
|
||||||
|
<div class="bg-color-black">
|
||||||
|
<div class="d-flex pt-2 pl-2">
|
||||||
|
<span>
|
||||||
|
<icon name="chart-area" class="text-icon"></icon>
|
||||||
|
</span>
|
||||||
|
<div class="d-flex">
|
||||||
|
<span class="fs-xl text mx-2">工单修复以及满意度统计图</span>
|
||||||
|
<div class="decoration2">
|
||||||
|
<dv-decoration-2 :reverse="true" style="width:5px;height:6rem;" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<BottomRightChart />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import BottomRightChart from "@/components/echart/bottom/bottomRightChart";
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BottomRightChart
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" class>
|
||||||
|
$box-height: 520px;
|
||||||
|
$box-width: 100%;
|
||||||
|
#bottomRight {
|
||||||
|
padding: 14px 16px;
|
||||||
|
height: $box-height;
|
||||||
|
width: $box-width;
|
||||||
|
border-radius: 5px;
|
||||||
|
.bg-color-black {
|
||||||
|
height: $box-height - 30px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
color: #c3cbde;
|
||||||
|
}
|
||||||
|
//下滑线动态
|
||||||
|
.decoration2 {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.125rem;
|
||||||
|
}
|
||||||
|
.chart-box {
|
||||||
|
margin-top: 16px;
|
||||||
|
width: 170px;
|
||||||
|
height: 170px;
|
||||||
|
.active-ring-name {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
335
src/views/center.vue
Normal file
335
src/views/center.vue
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
<template>
|
||||||
|
<div id="center">
|
||||||
|
<div class="up">
|
||||||
|
<div
|
||||||
|
class="bg-color-black item"
|
||||||
|
v-for="item in titleItem"
|
||||||
|
:key="item.title"
|
||||||
|
>
|
||||||
|
<p class="ml-3 colorBlue fw-b fs-xl">{{ item.title }}</p>
|
||||||
|
<div>
|
||||||
|
<dv-digital-flop
|
||||||
|
class="dv-dig-flop ml-1 mt-2 pl-3"
|
||||||
|
:config="item.number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="down">
|
||||||
|
<div class="ranking bg-color-black">
|
||||||
|
<span>
|
||||||
|
<icon name="chart-pie" class="text-icon"></icon>
|
||||||
|
</span>
|
||||||
|
<span class="fs-xl text mx-2 mb-1 pl-3">设备使用排名榜</span>
|
||||||
|
<dv-scroll-ranking-board class="dv-scr-rank-board mt-1" :config="ranking" />
|
||||||
|
</div>
|
||||||
|
<div class="percent">
|
||||||
|
<div class="item bg-color-black">
|
||||||
|
<span>设备在线率</span>
|
||||||
|
<CenterChart
|
||||||
|
:id="rate[0].id"
|
||||||
|
:tips="rate[0].tips"
|
||||||
|
:colorObj="rate[0].colorData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="item bg-color-black">
|
||||||
|
<span>设备使用率</span>
|
||||||
|
<CenterChart
|
||||||
|
:id="rate[1].id"
|
||||||
|
:tips="rate[1].tips"
|
||||||
|
:colorObj="rate[1].colorData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="water">
|
||||||
|
<dv-water-level-pond class="dv-wa-le-po" :config="water" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CenterChart from '@/components/echart/center/centerChartRate'
|
||||||
|
import { getComprehensiveCenterStats } from '@/api/data'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
titleItem: [
|
||||||
|
{
|
||||||
|
title: '总设备数',
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}次',
|
||||||
|
style: {
|
||||||
|
fontSize: 26,
|
||||||
|
fill: '#3fc0fb'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '在线设备',
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}台',
|
||||||
|
style: {
|
||||||
|
fontSize: 26,
|
||||||
|
fill: '#67e0e3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '今日按摩次数',
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}次',
|
||||||
|
style: {
|
||||||
|
fontSize: 26,
|
||||||
|
fill: '#ff9800'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '离线设备',
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}台',
|
||||||
|
style: {
|
||||||
|
fontSize: 26,
|
||||||
|
fill: '#f56c6c'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '本月按摩时长',
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}分钟',
|
||||||
|
style: {
|
||||||
|
fontSize: 26,
|
||||||
|
fill: '#67c23a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '活跃设备',
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}台',
|
||||||
|
style: {
|
||||||
|
fontSize: 26,
|
||||||
|
fill: '#409eff'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
ranking: {
|
||||||
|
data: [],
|
||||||
|
carousel: 'single',
|
||||||
|
unit: '次'
|
||||||
|
},
|
||||||
|
water: {
|
||||||
|
data: [0],
|
||||||
|
shape: 'roundRect',
|
||||||
|
formatter: '{value}%',
|
||||||
|
waveNum: 3
|
||||||
|
},
|
||||||
|
rate: [
|
||||||
|
{
|
||||||
|
id: 'centerRate1',
|
||||||
|
tips: 0,
|
||||||
|
colorData: {
|
||||||
|
textStyle: '#3fc0fb',
|
||||||
|
series: {
|
||||||
|
color: ['#00bcd44a', 'transparent'],
|
||||||
|
dataColor: {
|
||||||
|
normal: '#03a9f4',
|
||||||
|
shadowColor: '#97e2f5'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'centerRate2',
|
||||||
|
tips: 0,
|
||||||
|
colorData: {
|
||||||
|
textStyle: '#67e0e3',
|
||||||
|
series: {
|
||||||
|
color: ['#faf3a378', 'transparent'],
|
||||||
|
dataColor: {
|
||||||
|
normal: '#ff9800',
|
||||||
|
shadowColor: '#fcebad'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
CenterChart
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.loadCenterData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadCenterData() {
|
||||||
|
try {
|
||||||
|
const response = await getComprehensiveCenterStats()
|
||||||
|
if (response.code === 200) {
|
||||||
|
const data = response.data
|
||||||
|
|
||||||
|
// 更新标题项 - 使用设备相关数据
|
||||||
|
this.updateTitleItems(data)
|
||||||
|
|
||||||
|
// 更新排名数据 - 设备使用排名
|
||||||
|
this.ranking = data.ranking
|
||||||
|
|
||||||
|
// 更新率数据 - 设备在线率和使用率
|
||||||
|
this.updateRateData(data)
|
||||||
|
|
||||||
|
// 更新水位图数据 - 设备总体运行状态
|
||||||
|
if (data.waterData) {
|
||||||
|
this.water = data.waterData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载中心面板数据失败:', error)
|
||||||
|
// 使用默认数据
|
||||||
|
this.setDefaultDeviceData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTitleItems(data) {
|
||||||
|
// 根据后端返回的设备统计数据更新标题项
|
||||||
|
if (data.titleItems && data.titleItems.length >= 6) {
|
||||||
|
this.titleItem = data.titleItems.map((item, index) => {
|
||||||
|
const config = {
|
||||||
|
number: [item.number?.number?.[0] || 0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}' + this.getUnitByIndex(index),
|
||||||
|
style: {
|
||||||
|
fontSize: 26,
|
||||||
|
fill: this.getColorByIndex(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: item.title,
|
||||||
|
number: config
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRateData(data) {
|
||||||
|
// 更新设备在线率和使用率
|
||||||
|
if (data.rateData && data.rateData.length >= 2) {
|
||||||
|
this.rate[0].tips = data.rateData[0].tips || 0 // 设备在线率
|
||||||
|
this.rate[1].tips = data.rateData[1].tips || 0 // 设备使用率
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getUnitByIndex(index) {
|
||||||
|
const units = ['台', '台', '次', '台', '分钟', '台']
|
||||||
|
return units[index] || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
getColorByIndex(index) {
|
||||||
|
const colors = ['#3fc0fb', '#67e0e3', '#ff9800', '#f56c6c', '#67c23a', '#409eff']
|
||||||
|
return colors[index] || '#3fc0fb'
|
||||||
|
},
|
||||||
|
|
||||||
|
setDefaultDeviceData() {
|
||||||
|
// 设置默认设备数据
|
||||||
|
this.ranking.data = [
|
||||||
|
{ name: '设备A', value: 156 },
|
||||||
|
{ name: '设备B', value: 128 },
|
||||||
|
{ name: '设备C', value: 97 },
|
||||||
|
{ name: '设备D', value: 85 },
|
||||||
|
{ name: '设备E', value: 73 }
|
||||||
|
]
|
||||||
|
|
||||||
|
this.rate[0].tips = 75 // 默认在线率75%
|
||||||
|
this.rate[1].tips = 60 // 默认使用率60%
|
||||||
|
this.water.data = [68] // 默认总体运行率68%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.up {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
.item {
|
||||||
|
border-radius: 6px;
|
||||||
|
padding-top: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 32%;
|
||||||
|
height: 70px;
|
||||||
|
.dv-dig-flop {
|
||||||
|
width: 150px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.down {
|
||||||
|
padding: 6px 4px;
|
||||||
|
padding-bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
height: 255px;
|
||||||
|
justify-content: space-between;
|
||||||
|
.bg-color-black {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.ranking {
|
||||||
|
padding: 10px;
|
||||||
|
width: 59%;
|
||||||
|
.dv-scr-rank-board {
|
||||||
|
height: 225px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.percent {
|
||||||
|
width: 40%;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.item {
|
||||||
|
width: 50%;
|
||||||
|
height: 120px;
|
||||||
|
span {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.water {
|
||||||
|
width: 100%;
|
||||||
|
.dv-wa-le-po {
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
205
src/views/centerLeft1.vue
Normal file
205
src/views/centerLeft1.vue
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
<template>
|
||||||
|
<div id="centerLeft1">
|
||||||
|
<div class="bg-color-black">
|
||||||
|
<div class="d-flex pt-2 pl-2">
|
||||||
|
<span>
|
||||||
|
<icon name="chart-bar" class="text-icon"></icon>
|
||||||
|
</span>
|
||||||
|
<div class="d-flex">
|
||||||
|
<span class="fs-xl text mx-2">设备运行统计</span>
|
||||||
|
<dv-decoration-3 class="dv-dec-3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex jc-center">
|
||||||
|
<CenterLeft1Chart />
|
||||||
|
</div>
|
||||||
|
<!-- 4个主要的数据 -->
|
||||||
|
<div class="bottom-data">
|
||||||
|
<div
|
||||||
|
class="item-box mt-2"
|
||||||
|
v-for="(item, index) in numberData"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<div class="d-flex">
|
||||||
|
<span class="coin">●</span>
|
||||||
|
<dv-digital-flop class="dv-digital-flop" :config="item.number" />
|
||||||
|
</div>
|
||||||
|
<p class="text" style="text-align: center;">
|
||||||
|
{{ item.text }}
|
||||||
|
<span class="colorYellow">({{ getUnit(item.type) }})</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CenterLeft1Chart from '@/components/echart/centerLeft/centerLeft1Chart'
|
||||||
|
import { getCenterInfoStats } from '@/api/data'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
numberData: [
|
||||||
|
{
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}',
|
||||||
|
style: {
|
||||||
|
fontSize: 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: '今日按摩任务',
|
||||||
|
type: 'todayTaskCount'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}',
|
||||||
|
style: {
|
||||||
|
fontSize: 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: '累计完成数量',
|
||||||
|
type: 'totalTaskCount'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}',
|
||||||
|
style: {
|
||||||
|
fontSize: 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: '在线设备数',
|
||||||
|
type: 'onlineDeviceCount'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: {
|
||||||
|
number: [0],
|
||||||
|
toFixed: 0,
|
||||||
|
textAlign: 'left',
|
||||||
|
content: '{nt}',
|
||||||
|
style: {
|
||||||
|
fontSize: 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: '离线设备数',
|
||||||
|
type: 'offlineDeviceCount'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
CenterLeft1Chart
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadData()
|
||||||
|
this.changeTiming()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getUnit(type) {
|
||||||
|
const units = {
|
||||||
|
todayTaskCount: '次',
|
||||||
|
totalTaskCount: '次',
|
||||||
|
onlineDeviceCount: '台',
|
||||||
|
offlineDeviceCount: '台'
|
||||||
|
}
|
||||||
|
return units[type] || '件'
|
||||||
|
},
|
||||||
|
changeTiming() {
|
||||||
|
setInterval(() => {
|
||||||
|
this.loadData()
|
||||||
|
}, 5000)
|
||||||
|
},
|
||||||
|
async loadData() {
|
||||||
|
try {
|
||||||
|
const response = await getCenterInfoStats()
|
||||||
|
if (response.code === 200) {
|
||||||
|
const data = response.data
|
||||||
|
console.log('获取到的数据:', data)
|
||||||
|
|
||||||
|
// 直接使用接口返回的对象数据,而不是数组
|
||||||
|
if (data) {
|
||||||
|
// 更新每个数据项
|
||||||
|
this.numberData.forEach((item) => {
|
||||||
|
if (data[item.type] !== undefined) {
|
||||||
|
item.number.number[0] = data[item.type]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 触发数字更新
|
||||||
|
this.numberData.forEach((item) => {
|
||||||
|
item.number = { ...item.number }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取设备数据失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$box-width: 300px;
|
||||||
|
$box-height: 410px;
|
||||||
|
|
||||||
|
#centerLeft1 {
|
||||||
|
padding: 16px;
|
||||||
|
height: $box-height;
|
||||||
|
width: $box-width;
|
||||||
|
border-radius: 10px;
|
||||||
|
.bg-color-black {
|
||||||
|
height: $box-height - 30px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
color: #c3cbde;
|
||||||
|
}
|
||||||
|
.dv-dec-3 {
|
||||||
|
position: relative;
|
||||||
|
width: 100px;
|
||||||
|
height: 20px;
|
||||||
|
top: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-data {
|
||||||
|
.item-box {
|
||||||
|
& > div {
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
font-size: 14px;
|
||||||
|
float: right;
|
||||||
|
position: relative;
|
||||||
|
width: 50%;
|
||||||
|
color: #d3d6dd;
|
||||||
|
.dv-digital-flop {
|
||||||
|
width: 120px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
// 修改为圆点
|
||||||
|
.coin {
|
||||||
|
position: relative;
|
||||||
|
top: 6px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #ffc107;
|
||||||
|
}
|
||||||
|
.colorYellow {
|
||||||
|
color: yellowgreen;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
59
src/views/centerLeft2.vue
Normal file
59
src/views/centerLeft2.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div id="centerLeft1">
|
||||||
|
<div class="bg-color-black">
|
||||||
|
<div class="d-flex pt-2 pl-2">
|
||||||
|
<span>
|
||||||
|
<icon name="chart-pie" class="text-icon"></icon>
|
||||||
|
</span>
|
||||||
|
<div class="d-flex">
|
||||||
|
<span class="fs-xl text mx-2">地图数据</span>
|
||||||
|
<dv-decoration-1 class="dv-dec-1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex jc-center">
|
||||||
|
<CenterLeft2Chart />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CenterLeft2Chart from "@/components/echart/centerLeft/centerLeft2Chart";
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CenterLeft2Chart
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#centerLeft1 {
|
||||||
|
$box-width: 300px;
|
||||||
|
$box-height: 410px;
|
||||||
|
padding: 16px;
|
||||||
|
height: $box-height;
|
||||||
|
min-width: $box-width;
|
||||||
|
border-radius: 5px;
|
||||||
|
.bg-color-black {
|
||||||
|
height: $box-height - 30px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
color: #c3cbde;
|
||||||
|
}
|
||||||
|
.dv-dec-1 {
|
||||||
|
position: relative;
|
||||||
|
width: 100px;
|
||||||
|
height: 20px;
|
||||||
|
top: -3px;
|
||||||
|
}
|
||||||
|
.chart-box {
|
||||||
|
margin-top: 16px;
|
||||||
|
width: 170px;
|
||||||
|
height: 170px;
|
||||||
|
.active-ring-name {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
127
src/views/centerRight1.vue
Normal file
127
src/views/centerRight1.vue
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div id="centerRight1">
|
||||||
|
<div class="bg-color-black">
|
||||||
|
<div class="d-flex pt-2 pl-2">
|
||||||
|
<span>
|
||||||
|
<icon name="chart-line" class="text-icon"></icon>
|
||||||
|
</span>
|
||||||
|
<div class="d-flex">
|
||||||
|
<span class="fs-xl text mx-2">设备按摩时长排行榜</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex jc-center body-box">
|
||||||
|
<dv-scroll-board class="dv-scr-board" :config="config" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getDeviceTotalMassageTime } from '@/api/data'
|
||||||
|
import { Message } from 'element-ui'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
config: {
|
||||||
|
header: ['排名', '设备名称', '总按摩时长(分钟)'],
|
||||||
|
data: [],
|
||||||
|
rowNum: 7,
|
||||||
|
headerHeight: 35,
|
||||||
|
headerBGC: '#0f1325',
|
||||||
|
oddRowBGC: '#0f1325',
|
||||||
|
evenRowBGC: '#171c33',
|
||||||
|
index: false, // 禁用默认索引
|
||||||
|
columnWidth: [60, 120, 120],
|
||||||
|
align: ['center']
|
||||||
|
},
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadDeviceUsageData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadDeviceUsageData() {
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
console.log('开始请求设备数据...')
|
||||||
|
|
||||||
|
const response = await getDeviceTotalMassageTime()
|
||||||
|
console.log('API响应:', response)
|
||||||
|
|
||||||
|
if (response && response.code === 200) {
|
||||||
|
const deviceData = response.data || []
|
||||||
|
|
||||||
|
const tableData = this.formatDeviceData(deviceData)
|
||||||
|
this.config = {
|
||||||
|
...this.config,
|
||||||
|
data: tableData
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('设备按摩数据加载成功:', deviceData)
|
||||||
|
Message.success('数据加载成功')
|
||||||
|
} else {
|
||||||
|
console.error('API返回错误:', response)
|
||||||
|
Message.error('数据加载失败: ' + (response.msg || '未知错误'))
|
||||||
|
this.useMockData()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('请求设备数据异常:', error)
|
||||||
|
Message.error('设备数据加载失败: ' + error.message)
|
||||||
|
this.useMockData()
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
formatDeviceData(deviceData) {
|
||||||
|
if (!deviceData || !Array.isArray(deviceData)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortedData = deviceData.sort((a, b) => {
|
||||||
|
const timeA = a.totalMassageTime || a.duration || a.value || 0
|
||||||
|
const timeB = b.totalMassageTime || b.duration || b.value || 0
|
||||||
|
return timeB - timeA
|
||||||
|
})
|
||||||
|
|
||||||
|
return sortedData.map((item, index) => {
|
||||||
|
const deviceName = item.deviceName || item.name || item.deviceId || `设备${index + 1}`
|
||||||
|
const massageTime = item.totalMassageTime || item.duration || item.value || 0
|
||||||
|
const minutes = Math.round(massageTime)
|
||||||
|
|
||||||
|
return [
|
||||||
|
`${index + 1}`, // 手动添加排名数字
|
||||||
|
deviceName,
|
||||||
|
`<span class='colorGrass'>${minutes}分钟</span>`
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
useMockData() {
|
||||||
|
const mockData = [
|
||||||
|
{ deviceName: '按摩设备A', totalMassageTime: 456 },
|
||||||
|
{ deviceName: '按摩设备B', totalMassageTime: 389 },
|
||||||
|
{ deviceName: '按摩设备C', totalMassageTime: 342 },
|
||||||
|
{ deviceName: '按摩设备D', totalMassageTime: 298 },
|
||||||
|
{ deviceName: '按摩设备E', totalMassageTime: 267 },
|
||||||
|
{ deviceName: '按摩设备F', totalMassageTime: 234 },
|
||||||
|
{ deviceName: '按摩设备G', totalMassageTime: 198 },
|
||||||
|
{ deviceName: '按摩设备H', totalMassageTime: 167 },
|
||||||
|
{ deviceName: '按摩设备I', totalMassageTime: 145 },
|
||||||
|
{ deviceName: '按摩设备J', totalMassageTime: 123 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const tableData = this.formatDeviceData(mockData)
|
||||||
|
this.config = {
|
||||||
|
...this.config,
|
||||||
|
data: tableData
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('使用模拟数据:', mockData)
|
||||||
|
Message.info('使用模拟数据展示')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
188
src/views/centerRight2.vue
Normal file
188
src/views/centerRight2.vue
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<template>
|
||||||
|
<div id="centerRight2">
|
||||||
|
<div class="bg-color-black">
|
||||||
|
<div class="d-flex pt-2 pl-2">
|
||||||
|
<span>
|
||||||
|
<icon name="align-left" class="text-icon"></icon>
|
||||||
|
</span>
|
||||||
|
<span class="fs-xl text mx-2">设备使用分析</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex ai-center flex-column body-box">
|
||||||
|
<!-- 胶囊图:显示设备使用排名 -->
|
||||||
|
<dv-capsule-chart class="dv-cap-chart" :config="capsuleConfig" />
|
||||||
|
<!-- 雷达图:显示设备多维度分析 -->
|
||||||
|
<centerRight2Chart1 :cdata="radarData" />
|
||||||
|
|
||||||
|
<!-- 统计指标 -->
|
||||||
|
<div class="stats-container">
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-label">总设备数</span>
|
||||||
|
<span class="stat-value">{{ stats.totalDevices }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-label">在线率</span>
|
||||||
|
<span class="stat-value">{{ stats.onlineRate.toFixed(1) }}%</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-label">今日任务</span>
|
||||||
|
<span class="stat-value">{{ stats.todayTasks }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import centerRight2Chart1 from '@/components/echart/centerRight/centerRightChart'
|
||||||
|
import { getDeviceUsageAnalysis } from '@/api/data'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
capsuleConfig: {
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
radarData: {
|
||||||
|
indicatorData: [],
|
||||||
|
dataBJ: [],
|
||||||
|
dataSH: [],
|
||||||
|
dataGZ: []
|
||||||
|
},
|
||||||
|
stats: {
|
||||||
|
totalDevices: 0,
|
||||||
|
onlineDevices: 0,
|
||||||
|
offlineDevices: 0,
|
||||||
|
onlineRate: 0,
|
||||||
|
totalTasks: 0,
|
||||||
|
todayTasks: 0,
|
||||||
|
avgTasksPerDevice: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: { centerRight2Chart1 },
|
||||||
|
mounted() {
|
||||||
|
this.loadDeviceUsageData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadDeviceUsageData() {
|
||||||
|
try {
|
||||||
|
const response = await getDeviceUsageAnalysis()
|
||||||
|
const data = response.data
|
||||||
|
|
||||||
|
// 更新胶囊图数据
|
||||||
|
this.capsuleConfig = {
|
||||||
|
data: data.capsuleData
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新雷达图数据
|
||||||
|
this.radarData = {
|
||||||
|
indicatorData: data.radarData.indicatorData,
|
||||||
|
dataBJ: [data.radarData.device1],
|
||||||
|
dataSH: [data.radarData.device2],
|
||||||
|
dataGZ: [data.radarData.device3]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新统计指标
|
||||||
|
this.stats = data.stats
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载设备使用分析数据失败:', error)
|
||||||
|
this.setDefaultData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setDefaultData() {
|
||||||
|
this.capsuleConfig = {
|
||||||
|
data: [
|
||||||
|
{ name: '设备A', value: 167 },
|
||||||
|
{ name: '设备B', value: 123 },
|
||||||
|
{ name: '设备C', value: 98 },
|
||||||
|
{ name: '设备D', value: 87 },
|
||||||
|
{ name: '设备E', value: 76 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.radarData = {
|
||||||
|
indicatorData: [
|
||||||
|
{ name: "使用频率", max: 100 },
|
||||||
|
{ name: "运行时长", max: 100 },
|
||||||
|
{ name: "在线率", max: 100 },
|
||||||
|
{ name: "按摩次数", max: 100 },
|
||||||
|
{ name: "设备健康", max: 100 },
|
||||||
|
{ name: "任务效率", max: 100 }
|
||||||
|
],
|
||||||
|
dataBJ: [[85, 70, 90, 60, 75, 50]],
|
||||||
|
dataSH: [[70, 85, 65, 80, 60, 70]],
|
||||||
|
dataGZ: [[90, 60, 75, 70, 85, 55]]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stats = {
|
||||||
|
totalDevices: 50,
|
||||||
|
onlineDevices: 35,
|
||||||
|
offlineDevices: 15,
|
||||||
|
onlineRate: 70.0,
|
||||||
|
totalTasks: 1200,
|
||||||
|
todayTasks: 25,
|
||||||
|
avgTasksPerDevice: 24.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#centerRight2 {
|
||||||
|
$box-height: 410px;
|
||||||
|
$box-width: 340px;
|
||||||
|
padding: 5px;
|
||||||
|
height: $box-height;
|
||||||
|
width: $box-width;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
.bg-color-black {
|
||||||
|
padding: 5px;
|
||||||
|
height: $box-height;
|
||||||
|
width: $box-width;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
color: #c3cbde;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body-box {
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.dv-cap-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
padding: 10px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #c3cbde;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #ffd700;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
153
src/views/index.vue
Normal file
153
src/views/index.vue
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<template>
|
||||||
|
<div id="index" ref="appRef">
|
||||||
|
<div class="bg">
|
||||||
|
<dv-loading v-if="loading">Loading...</dv-loading>
|
||||||
|
<div v-else class="host-body">
|
||||||
|
<div class="d-flex jc-center">
|
||||||
|
<dv-decoration-10 class="dv-dec-10" />
|
||||||
|
<div class="d-flex jc-center">
|
||||||
|
<dv-decoration-8 class="dv-dec-8" :color="decorationColor" />
|
||||||
|
<div class="title">
|
||||||
|
<span class="title-text">设备BI大数据看板</span>
|
||||||
|
<dv-decoration-6
|
||||||
|
class="dv-dec-6"
|
||||||
|
:reverse="true"
|
||||||
|
:color="['#50e3c2', '#67a1e5']"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<dv-decoration-8
|
||||||
|
class="dv-dec-8"
|
||||||
|
:reverse="true"
|
||||||
|
:color="decorationColor"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<dv-decoration-10 class="dv-dec-10-s" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第二行 -->
|
||||||
|
<div class="d-flex jc-between px-2">
|
||||||
|
<div class="d-flex aside-width">
|
||||||
|
<div class="react-left ml-4 react-l-s">
|
||||||
|
<span class="react-left"></span>
|
||||||
|
<span class="text">设备数据展示</span>
|
||||||
|
</div>
|
||||||
|
<div class="react-left ml-3">
|
||||||
|
<span class="text">设备使用情况展示</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex aside-width">
|
||||||
|
<div class="react-right bg-color-blue mr-3">
|
||||||
|
<span class="text fw-b">设备管理BI数据大屏</span>
|
||||||
|
</div>
|
||||||
|
<div class="react-right mr-4 react-l-s">
|
||||||
|
<span class="react-after"></span>
|
||||||
|
<span class="text"
|
||||||
|
>{{ dateYear }} {{ dateWeek }} {{ dateDay }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body-box">
|
||||||
|
<!-- 第三行数据 -->
|
||||||
|
<div class="content-box">
|
||||||
|
<div>
|
||||||
|
<dv-border-box-12>
|
||||||
|
<centerLeft1 />
|
||||||
|
</dv-border-box-12>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dv-border-box-12>
|
||||||
|
<centerLeft2 />
|
||||||
|
</dv-border-box-12>
|
||||||
|
</div>
|
||||||
|
<!-- 中间 -->
|
||||||
|
<div>
|
||||||
|
<center />
|
||||||
|
</div>
|
||||||
|
<!-- 中间 -->
|
||||||
|
<div>
|
||||||
|
<centerRight2 />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dv-border-box-13>
|
||||||
|
<centerRight1 />
|
||||||
|
</dv-border-box-13>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第四行数据 -->
|
||||||
|
<div class="bottom-box">
|
||||||
|
<dv-border-box-13>
|
||||||
|
<bottomLeft />
|
||||||
|
</dv-border-box-13>
|
||||||
|
<dv-border-box-12>
|
||||||
|
<bottomRight />
|
||||||
|
</dv-border-box-12>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import drawMixin from "../utils/drawMixin";
|
||||||
|
import { formatTime } from '../utils/index.js'
|
||||||
|
import centerLeft1 from './centerLeft1'
|
||||||
|
import centerLeft2 from './centerLeft2'
|
||||||
|
import centerRight1 from './centerRight1'
|
||||||
|
import centerRight2 from './centerRight2'
|
||||||
|
import center from './center'
|
||||||
|
import bottomLeft from './bottomLeft'
|
||||||
|
import bottomRight from './bottomRight'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [ drawMixin ],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
timing: null,
|
||||||
|
loading: true,
|
||||||
|
dateDay: null,
|
||||||
|
dateYear: null,
|
||||||
|
dateWeek: null,
|
||||||
|
weekday: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
||||||
|
decorationColor: ['#568aea', '#000000']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
centerLeft1,
|
||||||
|
centerLeft2,
|
||||||
|
centerRight1,
|
||||||
|
centerRight2,
|
||||||
|
center,
|
||||||
|
bottomLeft,
|
||||||
|
bottomRight
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.timeFn()
|
||||||
|
this.cancelLoading()
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
clearInterval(this.timing)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
timeFn() {
|
||||||
|
this.timing = setInterval(() => {
|
||||||
|
this.dateDay = formatTime(new Date(), 'HH: mm: ss')
|
||||||
|
this.dateYear = formatTime(new Date(), 'yyyy-MM-dd')
|
||||||
|
this.dateWeek = this.weekday[new Date().getDay()]
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
cancelLoading() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loading = false
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '../assets/scss/index.scss';
|
||||||
|
</style>
|
44
vue.config.js
Normal file
44
vue.config.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const resolve = dir => {
|
||||||
|
return path.join(__dirname, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = defineConfig({
|
||||||
|
publicPath: './',
|
||||||
|
transpileDependencies: [],
|
||||||
|
|
||||||
|
chainWebpack: config => {
|
||||||
|
config.resolve.alias
|
||||||
|
.set('_c', resolve('src/components'))
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修正 devServer 配置
|
||||||
|
devServer: {
|
||||||
|
port: 81,
|
||||||
|
host: 'localhost',
|
||||||
|
open: true,
|
||||||
|
// 添加禁用主机检查,避免 Invalid Host header 错误
|
||||||
|
allowedHosts: 'all',
|
||||||
|
// 添加更严格的代理配置
|
||||||
|
proxy: {
|
||||||
|
'/dev-api': {
|
||||||
|
target: 'http://localhost:8080', // 后端在8080端口
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false, // 如果后端是http,需要这个
|
||||||
|
logLevel: 'debug', // 可以查看代理日志
|
||||||
|
pathRewrite: {
|
||||||
|
'^/dev-api': '' // 移除 /dev-api 前缀,直接转发到后端根路径
|
||||||
|
},
|
||||||
|
// 添加headers确保正确传递
|
||||||
|
headers: {
|
||||||
|
Connection: 'keep-alive'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
outputDir: 'dist',
|
||||||
|
assetsDir: 'static'
|
||||||
|
})
|
Loading…
x
Reference in New Issue
Block a user