数据大屏

This commit is contained in:
曹磊 2025-07-23 16:18:20 +08:00
parent 4a560323a6
commit ee2c7a6738
26 changed files with 8984 additions and 8098 deletions

View File

@ -82,7 +82,11 @@
"vxe-table-plugin-antd": "4.0.8",
"vxe-pc-ui": "4.6.12",
"xe-utils": "3.5.26",
"xss": "^1.0.15"
"xss": "^1.0.15",
"three": "^0.178.0",
"jquery": "^3.7.1",
"element-plus": "^2.10.4",
"@element-plus/icons-vue": "^2.3.1"
},
"devDependencies": {
"@commitlint/cli": "^18.6.1",

File diff suppressed because it is too large Load Diff

BIN
src/assets/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
src/assets/borderbg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
src/assets/btbg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
src/assets/img/baseLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
src/assets/img/baseMap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
src/assets/img/blue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
src/assets/img/green.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
src/assets/img/views.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
src/assets/img/yellow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

119
src/assets/rotation3D.css Normal file
View File

@ -0,0 +1,119 @@
/*
椭圆会使内部失真 transform: rotateX(50deg);
*/
.rotation3D{
position: relative; width: 800px; height: 750px; cursor: move; user-select: none;
margin: 0 auto; margin-top: -100px;
/*border: 1px solid white; border-radius: 100%;*/
}
.rotation3D .center{
display: none;
position: absolute; left: 50%; top: 50%;
transform: translate(-50%, -50%);
}
.rotation3D .itemList{ position: absolute; width: 100%; height: 100%; z-index: 20; }
.rotation3D .lineList{
position: absolute; width: 100%; height: 100%; z-index: 10;
transform-style: preserve-3d;
}
/*---------------------------点样式---------------------------*/
.rotation3D__item{
position: absolute; display: block; cursor: pointer; width: 161px; height: 188px;
text-align: center; line-height: 30px; font-size: 16px; color: white;
/*background: #2292ef; border-radius: 4px;*/
}
.rotation3D__item .scale{ position: absolute; top: 0; width: 100%; height: 100%; }
.rotation3D__item .cont{ position: relative; z-index: 2; }
.rotation3D__item .cont .iconfont { font-size: 28px; margin-top: 14px; margin-bottom: 0px; display: block; }
.rotation3D__item .cont p{ color: #fff; }
.rotation3D__item.blue{ color: #01e9fc; }
.rotation3D__item.green{ color: #02e943; }
.rotation3D__item.yellow{ color: #ffd200; }
/*底座*/
.rotation3D__item .baseImg{ position: absolute; width: 100%; height: 100%; z-index: 1; }
.rotation3D__item.blue .baseImg{ background: url("img/blue.png"); }
.rotation3D__item.green .baseImg{ background: url("img/green.png"); }
.rotation3D__item.yellow .baseImg{ background: url("img/yellow.png"); }
/*---------------------------
线样式
线高为总高的一般
---------------------------*/
.rotation3D__line{
position: absolute; left: 50%; top: 50%;
display: block; width: 2px; height: 50%;
padding-top: 60px; color: #fff; font-size: 50px;
/*background: #fff;*/
/*原点设置在中间*/
transform-origin: 50% 0;
transform-style: preserve-3d;
}
.rotation3D__line .pos{ position: absolute; top: 0; }
.rotation3D__line svg { position: absolute; top: 0; }
.rotation3D__line svg path {
stroke: #fff; fill: none;
stroke-width: 3;
animation: path-animation 100s linear 0s infinite normal;
}
@keyframes path-animation {
0% { stroke-dashoffset:500; }
100% { stroke-dashoffset:0; }
}
.rotation3D__line .dot {
position: absolute; top: 0; left: 0; text-align: center;
/*width: 35px; height: 35px; font-size: 35px; */
width: 24px; height: 24px; font-size: 24px;
}
.rotation3D__line .dot1,.rotation3D__line .dot3,.rotation3D__line .dot4{
animation: svg-path-animation 6s ease-in-out 0s infinite normal;
}
.rotation3D__line .dot1{
offset-path: path("M0 400, 0 0"); offset-distance: 0%;
}
.rotation3D__line .dot2{
offset-path: path("M0 200, 0 0"); offset-distance: 0%;
background: #ffd200; border-radius: 100%;
font-size: 22px; color: #000;
}
.rotation3D__line .dot3{
offset-path: path("M20 400 S 0 200, 20 0"); offset-distance: 0%;
}
.rotation3D__line .dot4{
offset-path: path("M20 0 S 40 200, 20 400"); offset-distance: 0%;
}
@keyframes svg-path-animation {
from {offset-distance: 0%;}
to {offset-distance: 100%;}
}
/*颜色*/
.rotation3D__line.blue { color: #07b2f9; }
.rotation3D__line.green { color: #00ff5b; }
.rotation3D__line.yellow { color: #ffd500; }
.rotation3D__line.blue svg path { stroke: #07b2f9; }
.rotation3D__line.green svg path { stroke: #00ff5b; }
.rotation3D__line.yellow svg path { stroke: #ffd500; }
.rotation3D-baseMap{
position: absolute; left: 10px; right: 0; top: 126px; margin: auto;
width: 900px; height: 516px;
background: url("@/assets/img/baseMap.png") no-repeat;
background-position:center;
}
.rotation3D-baseMap::before{
position: absolute; left: 0px; right: 0; top: 0px; margin: auto; z-index: 99;
width: 342px; height: 318px; display: block; content: '';
background: url("@/assets/img/baseLogo.png") no-repeat;
animation: 10s bounceUpDown infinite;
background-size: 70%;
background-position: center ;
}
.mt-20{
margin-top: 40px;
}

164
src/assets/style.css Normal file
View File

@ -0,0 +1,164 @@
/* 全局样式 */
body {
margin: 0;
padding: 0;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
background-color: #0a192f;
color: #e6f1ff;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: rgba(100, 116, 139, 0.1);
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
background: rgba(148, 163, 184, 0.5);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(148, 163, 184, 0.8);
}
/* 标题样式 */
.section-title {
position: relative;
font-size: 18px;
font-weight: 600;
margin-bottom: 16px;
padding-left: 12px;
}
.section-title::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 4px;
background: linear-gradient(to bottom, #4ce7ff, #165dff);
border-radius: 2px;
}
/* 数字动画 */
.number-counter {
font-family: 'Roboto Mono', monospace;
font-weight: 500;
}
/* 渐变按钮 */
.gradient-btn {
background: linear-gradient(90deg, #4ce7ff 0%, #165dff 100%);
border: none;
color: white;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
.gradient-btn:hover {
transform: translateY(-1px);
box-shadow: 0 5px 15px rgba(22, 93, 255, 0.3);
}
.gradient-btn:active {
transform: translateY(0);
box-shadow: 0 2px 5px rgba(22, 93, 255, 0.3);
}
/* 状态标签 */
.status-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status-tag.up {
background-color: rgba(16, 185, 129, 0.1);
color: #10b981;
}
.status-tag.down {
background-color: rgba(239, 68, 68, 0.1);
color: #ef4444;
}
/* 骨架屏加载 */
.skeleton {
background: linear-gradient(90deg, rgba(255, 255, 255, 0.05) 25%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.05) 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
@font-face {
font-family: "unidreamLED";
src: url('@/assets/fonts/UnidreamLED.ttf');
}
@font-face {
font-family: "iconfont"; /* Project id 3045003 */
src: url('//at.alicdn.com/t/font_3045003_lkxtjaj4m6.woff2?t=1641286939195') format('woff2'),
url('//at.alicdn.com/t/font_3045003_lkxtjaj4m6.woff?t=1641286939195') format('woff'),
url('//at.alicdn.com/t/font_3045003_lkxtjaj4m6.ttf?t=1641286939195') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-liangchang:before {
content: "\e622";
}
.icon-lumianshigong:before {
content: "\e623";
}
.icon-shiyanjiance:before {
content: "\e624";
}
.icon-renyuanguanli:before {
content: "\e625";
}
.icon-tanpuyashifuwu:before {
content: "\e626";
}
.icon-shujufuwuzhongxin:before {
content: "\e627";
}
.icon-a-lujishigong2x:before {
content: "\e620";
}
.icon-GPS:before {
content: "\e621";
}

BIN
src/assets/titlebg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

1
src/assets/vue.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -64,5 +64,15 @@ export const TokenLoginRoute: AppRouteRecordRaw = {
ignoreAuth: true,
},
};
export const newScreenRoute: AppRouteRecordRaw = {
path: '/screen/screen',
name: 'screenIndex',
component: () => import('/@/views/screen/screen.vue'),
meta: {
title: t('routes.basic.login'),
},
};
// Basic routing without permission
export const basicRoutes = [LoginRoute, RootRoute, ...mainOutRoutes, REDIRECT_ROUTE, PAGE_NOT_FOUND_ROUTE, TokenLoginRoute, Oauth2LoginRoute];
export const basicRoutes = [LoginRoute, RootRoute, ...mainOutRoutes, REDIRECT_ROUTE, PAGE_NOT_FOUND_ROUTE, TokenLoginRoute, Oauth2LoginRoute,newScreenRoute];

376
src/utiles/index.js Normal file
View File

@ -0,0 +1,376 @@
import $ from 'jquery'
const cancelFrame = window.cancelAnimationFrame || window.cancelRequestAnimationFrame;
const requestFrame = window.requestAnimationFrame;
export let NumTile = "";
const time = !window.performance || !window.performance.now
? () => +new Date()
: () => performance.now();
/**
* 计算两点距离
* @param points
* @returns {number}
* distance([{x:0,y:0},{x:1,y:1}]);
*/
const distance = (points) => {
const p1 = points[0];
const p2 = points[1];
const a = p2.x - p1.x;
const b = p2.y - p1.y;
return Math.sqrt(a * a + b * b);
};
/**
* 圆公式
* @param rotation 弧度
* 计算公式
* Math.PI; //圆周率
* Math.sin(); //正弦 x -左 +右
* Math.cos; //余弦 y -下 +上
*/
const circleMath = {
/**
* 根据弧度计算角度
* @param rotation 弧度
* rotation, farScale, xs, xr, ys, yr, itemWidth
*/
parseRotate(rotation, self) {
const sin = Math.sin(rotation);
const cos = Math.cos(rotation);
const sin_cos = sin * cos; //得出偏移正负值从0°向左依次 +-+-
let angle = (180 / Math.PI * rotation) - 180;
let lastAngle = angle;
lastAngle = angle + (self.yr * (sin_cos / (Math.PI + 1)));
return lastAngle;
},
/**
* 计算scale,x,y
* scale 最小尺寸 + ((1 - 最小尺寸) * (sin正弦 + 1) * 0.5)
* x x起点 + (尺寸 * cos余弦 * x半径) - 元素宽度一半
* y y起点 + (尺寸 * sin正弦 * x半径) - 元素宽度一半
* farScale, xs, xr, ys, yr, itemWidth
*/
parseSXY(rotation, self) {
const { farScale, itemWidth, xs, xr, ys, yr } = self;
const sin = Math.sin(rotation);
const cos = Math.cos(rotation);
const scale = farScale + ((1 - farScale) * (sin + 1) * 0.5); //单个尺寸
// 使用压缩
const x = xs + (cos * xr) - (itemWidth * 0.5);
const y = ys + (sin * yr) - (itemWidth * 0.5);
const distanceNumber = distance([
{ x: self.$rotation.width() / 2 - self.$item.width() / 2, y: self.$rotation.height() / 2 - self.$item.height() / 2 },
{ x, y }
]);
return { x, y, scale, distanceNumber };
}
};
/**
* 3D旋转
* @param id
*/
export class Rotation3D {
constructor(_opts) {
const self = this;
this.$rotation = $(_opts.id);
this.$lineList = this.$rotation.find('.lineList');
this.$item = this.$rotation.find('.rotation3D__item');
this.$line = this.$rotation.find('.rotation3D__line');
this.itemWidth = this.$item.width();
this.itemHeight = this.$item.height();
this.length = this.$item.length;
// 圆计算
this.rotation = Math.PI / 2; //圆周率/2
this.destRotation = this.rotation;
const xr = this.$rotation.width() * 0.5;
const yr = this.$rotation.height() * 0.5;
const xRadius = _opts.xRadius || 0;
const yRadius = _opts.yRadius || 0;
const opts = Object.assign({
farScale: 1, // 最小尺寸
xs: xr, // x起点
ys: yr, // y起点
xr: xr - xRadius, // x半径-压缩
yr: yr - yRadius, // y半径-压缩
// 播放
autoPlay: false,
autoPlayDelay: 3000,
currenIndex: -1,
fps: 30,
speed: 4,
}, _opts);
Object.assign(this, opts);
// 遍历子元素
this.$item.each(function (index) {
if(NumTile == ''){
setTimeout(() => {
let num = self.$item[0].dataset.num;
let dw = self.$item[0].dataset.dw;
$('.rotation3DNum').html(num);
$('.dw').html(`(${dw})`);
}, 1500);
}
$(this).click(function () {
$(this).addClass('active').siblings().removeClass('active');
let num = $(this).attr('data-num');
let dw = $(this).attr('data-dw');
$('.dw').html(`(${dw})`);
$('.rotation3DNum').css('opacity', 0);
setTimeout(() => {
$('.rotation3DNum').html(num); // 更改数字内容
$('.rotation3DNum').css('opacity', 1);
}, 500);
self.goTo(index);
});
});
// 当前控件进入离开
this.$rotation.mouseenter(function () {
clearInterval(self.autoPlayTimer);
});
this.$rotation.mouseleave(function () {
self.onAutoPlay();
});
this.onAutoPlay();
this.onDrag();
this.render();
}
/**
* item样式
* x x起点 + (尺寸 * 余弦 * x压缩) - 元素宽度一半
* y y起点 + (尺寸 * 正弦 * y压缩) - 元素宽度一半
*/
itemStyle($item, index, rotation) {
const parseSXY = circleMath.parseSXY(rotation, this);
const { scale, x, y } = parseSXY;
const $line = this.$lineList.find('.rotation3D__line').eq(index);
//设置当前子菜单的位置left,top = (x,y)
$item.find('.scale').css({
transform: `scale(${scale})`
});
$item.css({
position: 'absolute',
display: 'inline-block',
'z-index': parseInt(scale * 100),
'transform-origin': '0px 0px',
transform: `translate(${x}px, ${y}px)`
});
/**
* 线样式
*/
$line.css({
height: parseSXY.distanceNumber
});
$line.find('svg').css({
height: parseSXY.distanceNumber
});
$line.find('.dot1').css({
'offset-path': `path("M0 ${parseSXY.distanceNumber}, 0 0")`
});
$line.find('#path1').attr({
d: `M0 ${parseSXY.distanceNumber}, 0 0`
});
$line.find('.dot2').css({
'offset-path': `path("M0 ${parseSXY.distanceNumber/2}, 0 0")`
});
$line.find('#path2').attr({
d: `M0 ${parseSXY.distanceNumber}, 0 0`
});
$line.find('.dot3').css({
'offset-path': `path("M20 ${parseSXY.distanceNumber} S 0 ${parseSXY.distanceNumber/2}, 20 0")`
});
$line.find('#path3').attr({
d: `M20 ${parseSXY.distanceNumber} S 0 ${parseSXY.distanceNumber/2}, 20 0`
});
$line.find('.dot4').css({
'offset-path': `path("M20 0 S 40 ${parseSXY.distanceNumber/2}, 20 ${parseSXY.distanceNumber}")`
});
$line.find('#path4').attr({
d: `M20 0 S 40 ${parseSXY.distanceNumber/2}, 20 ${parseSXY.distanceNumber}`
});
}
/**
* line样式
*/
lineStyle($line, index, rotation) {
const rotate = circleMath.parseRotate(rotation, this);
$line.css({
transform: `rotate(${rotate}deg)`
});
this.$lineList.css({
// transform: `rotateX(${this.yRadius / 3}deg)`,
});
}
/**
* 旋转至index
*/
goTo(index) {
const self = this;
this.currenIndex = index;
/**
* 1.计算floatIndex,用于控死amdiff
*/
const itemsRotated = this.length * ((Math.PI / 2) - this.rotation) / (2 * Math.PI);
let floatIndex = itemsRotated % this.length;
if (floatIndex < 0) { floatIndex = floatIndex + this.length; }
/**
* 2.计算diff,判断方向正反
*/
let diff = index - (floatIndex % this.length);
if (2 * Math.abs(diff) > this.length) {
diff -= (diff > 0) ? this.length : -this.length;
}
// 停止任何正在进行的旋转
this.destRotation += (2 * Math.PI / this.length) * -diff;
this.scheduleNextFrame();
}
/**
* 定时器渐近旋转
*/
scheduleNextFrame() {
const self = this;
this.lastTime = time();
// 暂停
const pause = function () {
cancelFrame ? cancelFrame(this.timer) : clearTimeout(self.timer);
self.timer = 0;
};
// 渐进播放
const playFrame = function () {
const rem = self.destRotation - self.rotation;
const now = time();
const dt = (now - self.lastTime) * 0.002;
self.lastTime = now;
if (Math.abs(rem) < 0.003) {
self.rotation = self.destRotation;
pause();
} else {
// 渐近地接近目的地
self.rotation = self.destRotation - rem / (1 + (self.speed * dt));
self.scheduleNextFrame();
}
self.render();
};
this.timer = cancelFrame
? requestFrame(playFrame)
: setTimeout(playFrame, 1000 / this.fps);
}
/**
* 更新
*/
render() {
const self = this;
// 图形间隔:弧度
const spacing = 2 * Math.PI / this.$item.length;
let itemRotation = this.rotation;
let lineRotation = this.rotation + (Math.PI / 2);
this.$item.each(function (index) {
self.itemStyle($(this), index, itemRotation);
itemRotation += spacing;
});
this.$line.each(function (index) {
self.lineStyle($(this), index, lineRotation);
lineRotation += spacing;
});
}
/**
* 自动播放
*/
onAutoPlay() {
const self = this;
if (this.autoPlay) {
this.autoPlayTimer = setInterval(function () {
if (self.currenIndex < 0) {
self.currenIndex = self.length - 1;
}
self.goTo(self.currenIndex);
self.currenIndex--; //倒叙
}, this.autoPlayDelay);
}
}
/**
* 拖拽
*/
onDrag() {
const self = this;
let startX, startY, moveX, moveY, endX, endY;
// 拖拽:三个事件-按下 移动 抬起
//按下
this.$rotation.mousedown(function (e) {
startX = e.pageX;
startY = e.pageY;
// 移动
$(document).mousemove(function (e) {
endX = e.pageX;
endY = e.pageY;
moveX = endX - startX;
moveY = endY - startY;
});
// 抬起
$(document).mouseup(function (e) {
endX = e.pageX;
endY = e.pageY;
moveX = endX - startX;
moveY = endY - startY;
// 每40旋转一步
const moveIndex = parseInt(Math.abs(moveX) / 50);
if (moveIndex > 0) {
if (moveX < 0) { //向左
self.currenIndex = self.currenIndex - moveIndex;
play(moveIndex);
} else { //向右
self.currenIndex = self.currenIndex + moveIndex;
play(moveIndex);
}
}
// 解绑
$(document).unbind("mousemove");
$(document).unbind("mouseup");
});
});
function play() {
if (self.currenIndex === 0) {
self.currenIndex = self.length - 1;
}
self.goTo(self.currenIndex % self.length);
}
}
}
//window.Rotation3D = Rotation3D;
//export default Rotation3D

View File

@ -9,6 +9,7 @@ enum Api {
xqtd = '/zh/home/getXqtd',
sbtd = '/zh/home/getSbtd',
hytd = '/zh/home/getHytd',
xqqy = '/zh/home/getXqqy',
sstd = '/zh/home/getSstd',
}
/**
@ -59,6 +60,12 @@ export const getSbtd = (params) => defHttp.get({ url: Api.sbtd, params }, { isTr
*/
export const getHytd = (params) => defHttp.get({ url: Api.hytd, params }, { isTransformResponse: false });
/**
*
* @param params
*/
export const getXqqy = (params) => defHttp.get({ url: Api.xqqy, params }, { isTransformResponse: false });
/**
*
* @param params

View File

@ -46,7 +46,15 @@
</a-row>
<a-row :gutter="16" style="padding:16px 0px 0px 16px;width: 100%">
<a-col :span="6">
<a-col :span="4">
<a-card>
<div @click="openDataBigScreen">
<div class="space-between"><span>数据大屏</span></div>
<span style="font-size:45px;padding-left: 80px;"><fund-two-tone /></span>
</div>
</a-card>
</a-col>
<a-col :span="5">
<a-card>
<div>
<div class="space-between"><span>今日投递重量T</span><span>昨日</span></div>
@ -59,7 +67,7 @@
</div>
</a-card>
</a-col>
<a-col :span="6">
<a-col :span="5">
<a-card>
<div class="space-between"><span>今日投递次数</span><span>昨日</span></div>
<div class="space-between"><span class="card-title">{{formData.tdcsT}}</span><span>{{formData.tdcsY}}</span></div>
@ -70,7 +78,7 @@
</div>
</a-card>
</a-col>
<a-col :span="6">
<a-col :span="5">
<a-card>
<div class="space-between"><span>今日清运重量T</span><span>昨日</span></div>
<div class="space-between"><span class="card-title">{{formData.qyzlT}}</span><span>{{formData.qyzlY}}</span></div>
@ -81,7 +89,7 @@
</div>
</a-card>
</a-col>
<a-col :span="6">
<a-col :span="5">
<a-card>
<div class="space-between"><span>今日新增会员</span><span>昨日</span></div>
<div class="space-between"><span class="card-title">{{formData.hyrsT}}</span><span>{{formData.hyrsY}}</span></div>
@ -161,13 +169,15 @@
</template>
<script lang="ts" setup>
import {onMounted, reactive, ref} from 'vue';
import { FundTwoTone } from '@ant-design/icons-vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import Pie from '/@/components/chart/Pie.vue';
import BarAndLine from '/@/components/chart/BarAndLine.vue';
import {getTotalInfo, getTodayInfo, getOrderTypeCn, getXqtd, getSbtd, getHytd, getSstd} from './api';
import {getTotalInfo, getTodayInfo, getOrderTypeCn, getXqtd, getSbtd, getHytd, getSstd, getXqqy} from './api';
import {orderColumns} from './data';
import { useRouter } from 'vue-router';
const router = useRouter();
const queryParam = reactive<any>({});
//table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
@ -350,20 +360,42 @@
try {
dataSource.value = [];
const res = await getSstd({});
console.log(res);
dataSource.value = res.result;
} catch (error) {
}
}
onMounted(() => {
//
async function loadXqqy(){
try {
const res = await getXqqy({});
console.log(res);
} catch (error) {
}
}
async function loadData(){
loadTotalData();
loadTodayData();
loadOrderTypeCn();
loadXqtd();
loadSbtd();
loadHytd();
// loadSstd();
// loadXqqy();
}
setInterval(function() {
reload();
loadData();
}, 30*60*1000);
function openDataBigScreen(){
const url = router.resolve({ path: '/screen/screen' }).href
window.open(url, '_blank')
}
onMounted(() => {
loadData();
})
</script>

View File

@ -0,0 +1,172 @@
<template>
<div class="rotation3D-baseMap"></div>
<div class="muflex">
<label class="rotation3DNum"></label><span class="dw"></span>
</div>
<!--旋转3D-->
<div id="rotation3D" class="rotation3D">
<div class="itemList">
<div class="rotation3D__item" :class="item.type" v-for="item in itemList" :data-num="item.num" :data-dw="item.dw">
<div class="scale">
<div class="baseImg"></div>
<div class="cont">
<!-- <i class="iconfont"> {{ item.num }} </i> -->
<i class="iconfont" :class="item.icon"></i>
<p class="item-name-overflow">{{item.name}}</p>
</div>
</div>
</div>
</div>
<div class="lineList">
<div class="rotation3D__line" v-for="item in itemList" :class="item.type">
<div class="pos" v-if="item.type == 'blue'">
<svg width="10" height="400">
<path id="path1" d="M0 400, 0 0" stroke-dasharray="5,10"/>
</svg>
<div class="dot dot1 el-icon-caret-right"></div>
</div>
<div class="pos" v-if="item.type == 'yellow'">
<svg width="10" height="400">
<path id="path2" d="M0 400, 0 0" stroke-dasharray="5,10"/>
</svg>
<!-- <div class="dot dot2"><i class="el-icon-close"></i></div> -->
</div>
<div class="pos" style="left: -16px;" v-if="item.type == 'green'">
<svg width="50" height="400">
<path id="path3" d="M0 400, 0 0" stroke-dasharray="5,10"/>
</svg>
</div>
</div>
</div>
</div>
</template>
<script setup>
import {onMounted, ref} from 'vue';
import{ Rotation3D } from '@/utiles/index';
import { getTotalInfo } from "../../dashboard/Analysis/api";
const itemList = ref([
{ name:'总投递重量(T)', type:'blue', num:'0', icon:'icon-a-lujishigong2x', dw:'T'},
{ name:'总清运重量(T)', type:'green', num:'0', icon:'icon-a-lujishigong2x',dw:'T'},
{ name:'总会员(人)', type:'yellow', num:'0',icon:'icon-renyuanguanli', dw:'人' },
{ name:'总设备(台)', type:'blue', num:'0', icon:'icon-liangchang',dw:'台'},
{ name:'在线设备(台)', type:'green', num:'0', icon:'icon-tanpuyashifuwu', dw:'台'},
{ name:'离线设备(台)', type:'yellow', num:'0', icon:'icon-lumianshigong',dw:'台'},
{ name:'总区域(个)', type:'blue', num:'0', icon:'icon-GPS ',dw:'个' },
{ name:'总投递次数(次)', type:'green', num:'0', icon:'icon-shiyanjiance',dw:'次'},
]);
const rotation3D = ref();
function createRotation3D(){
rotation3D.value = new Rotation3D({
id: '#rotation3D',
farScale: 0.6,
// farScale: 1,
xRadius: 0, //x
yRadius: 220, //y
// yRadius: 0, //y
//autoPlay:true,
//autoPlayDelay:6000,
})
}
async function loadTotalData() {
const resData = await getTotalInfo({});
const res = resData.result;
itemList.value = [];
itemList.value.push(
{
name: '总投递重量(T)',
type: 'blue',
num: res.tdzlA,
icon:'icon-a-lujishigong2x',
dw:'T'
},
{
name: '总清运重量(T)',
type: 'green',
num: res.qyzlA,
icon:'icon-a-lujishigong2x',
dw:'T'
},
{
name: '总会员(人)',
type: 'yellow',
num: res.hyrsA,
icon:'icon-renyuanguanli',
dw:'人'
},
{
name: '总设备(台)',
type: 'blue',
num: res.sbsA,
icon:'icon-liangchang',
dw:'台'
},
{
name: '在线设备(台)',
type: 'green',
num: res.zxsbsA,
icon:'icon-tanpuyashifuwu',
dw:'台'
},
{
name: '离线设备(台)',
type: 'yellow',
num: res.lxsbsA,
icon:'icon-lumianshigong',
dw:'台'
},
{
name: '总区域(个)',
type: 'blue',
num: res.qysA,
icon:'icon-GPS',
dw:'台'
},
{
name: '总投递次数(次)',
type: 'green',
num: res.tdcsA,
icon:'icon-shiyanjiance',
dw:'次'
},
);
// rotation3D.value.render();
}
setInterval(function() {
loadTotalData();
}, 30*60*1000);
onMounted(()=>{
createRotation3D();
loadTotalData();
})
</script>
<style>
@import url('@/assets/rotation3D.css');
.rotation3DNum{
font-size: 48px;
color: aqua;
font-family: 'unidreamLED';
transition: opacity 0.5s ease-out;
}
.dw{
font-size: 20px;
font-weight: bold;
color: aqua;
}
.muflex{
position: absolute;
left: 2%;
top: 10px;
}
.item-name-overflow{
word-wrap: break-word; /* 或使用 overflow-wrap: break-word; */
width: 60px; /* 设定一个宽度来观察效果 */
margin-left: 50px;
}
</style>

View File

@ -0,0 +1,301 @@
<template>
<div class="data-card" :id="id"></div>
</template>
<script setup>
import {ref, onMounted, reactive} from 'vue'
import * as echarts from 'echarts'
import {getXqtd} from "../../dashboard/Analysis/api";
// import { Info } from '@element-plus/icons-vue'
const props = defineProps({
id: {
type: String,
required: true
},
data:{
type:String
}
});
let quLabel = ref([]);
let quCn = ref([]);
let quWeight = ref([]);
let myChart = reactive({});
function tid(data){
quLabel.value = [];
quCn.value = [];
quWeight.value = [];
data.forEach((item)=>{
quLabel.value.push(item.housingestateName);
quCn.value.push(item.cn);
quWeight.value.push(item.weight)
})
const chartDom = document.getElementById(props.id);
myChart = echarts.init(chartDom);
//
const colors = {
primary: 'rgba(66, 165, 245, 0.9)', //
secondary: 'rgba(129, 199, 132, 0.9)', // 绿
gridLine: 'rgba(100, 150, 255, 0.1)', // 线
axisLine: 'rgba(100, 150, 255, 0.3)', //
textColor: '#a8b2d1', //
tooltipBg: 'rgba(10, 25, 47, 0.8)', //
};
//
const gradientPrimary = {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{offset: 0, color: 'rgba(66, 165, 245, 0.9)'},
{offset: 1, color: 'rgba(66, 165, 245, 0.3)'}
]
};
const option = {
//
legend: {
data: ['投递重量(T)','投递次数'],
textStyle: { color: colors.textColor },
icon: 'rect',
itemWidth: 14,
itemHeight: 8,
top: '3%'
},
//
grid: {
left: '4%',
right: '2%',
bottom: '15%',
containLabel: true,
},
// X
xAxis: {
type: 'category',
data: quLabel.value,
axisTick: { show: false },
axisLine: {
show: true,
lineStyle: { color: colors.axisLine }
},
axisLabel: {
color: colors.textColor,
interval: 0,
rotate: 45
}
},
// Y访
yAxis: [
{
type: 'value',
name: '投递重量(T)',
nameTextStyle: { color: colors.primary },
axisTick: { show: false },
axisLine: {
show: true,
lineStyle: { color: colors.primary }
},
axisLabel: { color: colors.textColor },
splitLine: { show: false }
},
{
type: 'value',
name: '投递次数',
nameTextStyle: { color: colors.secondary },
axisTick: { show: false },
axisLine: {
show: true,
lineStyle: { color: colors.secondary }
},
axisLabel: { color: colors.textColor },
splitLine: {
show: true,
lineStyle: { color: colors.gridLine }
}
},
],
// +线
series: [
// 访
{
name: '投递重量(T)',
type: 'bar',
yAxisIndex: 0,
barWidth: '35%',
data: quWeight.value,
itemStyle: { color: gradientPrimary },
emphasis: { itemStyle: { color: colors.primary } },
animationDelay: idx => idx * 10
},
// 线
{
name: '投递次数',
type: 'line',
yAxisIndex: 1, // Y
data: quCn.value,
symbol: 'circle',
symbolSize: 8,
itemStyle: { color: colors.secondary },
lineStyle: {
color: colors.secondary,
width: 2,
type: 'solid'
},
areaStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{offset: 0, color: 'rgba(129, 199, 132, 0.4)'},
{offset: 1, color: 'rgba(129, 199, 132, 0.05)'}
]
}
},
animationDelay: idx => idx * 10 + 100
}
],
// tooltip
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: 'rgba(100, 255, 218, 0.7)',
width: 1,
type: 'dashed'
}
},
backgroundColor: colors.tooltipBg,
borderColor: 'rgba(100, 150, 255, 0.3)',
borderWidth: 1,
textStyle: { color: '#e6f1ff' },
},
//
//backgroundColor: 'transparent'
};
//
myChart.setOption(option);
}
async function loadXqtd() {
const resData = await getXqtd({});
const res = resData.result;
let xqu = [];
for (let i = 0; i < res.length; i++) {
xqu.push({
housingestateName: res[i].housingestateName,
cn: res[i].cn,
weight: res[i].weight,
});
}
tid(xqu);
}
setInterval(function() {
loadXqtd();
}, 30*60*1000);
//
onMounted(()=>{
window.addEventListener('resize', () => {
myChart.resize();
});
loadXqtd();
})
// const formattedValue = computed(() => {
// if (typeof props.value === 'number') {
// if (props.value >= 100000000) {
// return (props.value / 100000000).toFixed(2) + '亿'
// } else if (props.value >= 10000) {
// return (props.value / 10000).toFixed(2) + ''
// } else {
// return props.value.toLocaleString()
// }
// }
// return props.value
// })
</script>
<style scoped>
.data-card {
width: 100%;
height: 100%;
}
/*
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.card-header h3 {
margin: 0;
font-size: 16px;
font-weight: 500;
}
.number-display {
display: flex;
align-items: baseline;
margin-bottom: 8px;
}
.number-counter {
font-weight: 600;
}
.unit {
margin-left: 4px;
font-size: 14px;
color: #94a3b8;
}
.change-indicator {
display: flex;
align-items: center;
font-size: 14px;
}
.change-desc {
margin-left: 8px;
color: #94a3b8;
}
.list-data .el-list-item {
padding: 8px 0;
border-bottom: 1px solid rgba(100, 116, 139, 0.1);
}
.list-item-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.item-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item-value {
margin: 0 12px;
font-weight: 500;
}
.item-percentage {
min-width: 60px;
text-align: right;
} */
</style>

View File

@ -0,0 +1,273 @@
<template>
<div class="data-card" :id="id"></div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import * as echarts from 'echarts'
import {getHytd} from "../../dashboard/Analysis/api";
// import { Info } from '@element-plus/icons-vue'
const props = defineProps({
id: {
type: String,
required: true,
},
data: {
type: String
}
});
let quLabel = ref([]);
let quCn = ref([]);
let quWeight = ref([]);
let myChart = reactive({});
function tid(data) {
quLabel.value = [];
quCn.value = [];
quWeight.value = [];
data.forEach((item) => {
quLabel.value.push(item.phone);
quCn.value.push(item.cn);
quWeight.value.push(item.weight)
})
const chartDom = document.getElementById(props.id);
myChart = echarts.init(chartDom);
//
const option = {
//
legend: {
data: ['投递重量(KG)','投递次数'],
top: '3%',
textStyle: { color: '#a8b2d1' }, //
icon: 'circle', //
itemWidth: 8
},
//
grid: {
left: '2%',
right: '3%',
bottom: '15%',
containLabel: true,
},
// X
xAxis: {
type: 'category',
data: quLabel.value,
axisLine: {
show: true,
lineStyle: { color: '#2d3748' } // X线
},
axisTick: { show: false }, // 线
axisLabel: {
color: '#a0aec0', //
rotate: 45,
formatter: function (params) {
// return params.slice(-4);
return params;
}
}
},
// Y访
yAxis: [
// Y
{
type: 'value',
name: '投递重量(KG)',
nameTextStyle: { color: 'rgba(129, 199, 132)' },
axisLine: {
show: true,
lineStyle: {
color: 'rgba(129, 199, 132)'
}
},
axisTick: { show: false },
splitLine: { show: false }, // 线
axisLabel: {
color: '#a8b2d1',
// formatter: '{value}%'
}
},
{
type: 'value',
name: '投递次数',
nameTextStyle: { color: '#63b3ed' }, //
axisLine: { show: true,
lineStyle: {
color: 'rgba(66, 165, 245)'
}
},
axisTick: { show: false },
splitLine: {
lineStyle: {
color: 'rgba(74, 85, 104, 0.3)', // 线
type: 'dashed' // 线
}
},
axisLabel: {
color: '#a8b2d1',
// formatter: '{value}'
}
},
],
// +线
series: [
// 访
{
name: '投递重量(KG)',
type: 'bar',
data: quWeight.value,
//
barWidth: 24,
//
itemStyle: {
borderRadius: [6, 6, 0, 0], //
//
//
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{offset: 0, color: '#00FF9D'},
{offset: 1, color: 'rgba(0, 255, 157, 0.4)'}
]
},
},
//
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{offset: 0, color: '#00FF9D'}, //
{offset: 1, color: 'rgba(0, 255, 157, 0.3)'} // c
]),
boxShadow: '0 0 15px rgba(49, 130, 206, 0.6)' //
}
},
//
label: {
show: false,
verticalAlign: 'middle',
align: 'center',
color: '#e0e7ff',
fontSize: 12
}
},
// 线
{
name: '投递次数',
type: 'line',
yAxisIndex: 1, // Y
data: quCn.value,
// 线
lineStyle: {
width: 3,
// color: '#165DFF', //
color: '#16b9ff', //
shadowBlur: 10,
// shadowColor: 'rgba(22, 93, 255, 0.7)' //
shadowColor: 'rgba(22,139,255,0.7)' //
},
//
symbol: 'circle', //
symbolSize: 8, //
itemStyle: {
// color: '#805ad5',
// borderColor: '#fff',
color: '#16b9ff', //
borderColor: '#16b9ff', //
borderWidth: 2
},
//
emphasis: {
symbolSize: 12,
itemStyle: {
color: '#d53f8c',
boxShadow: '0 0 10px rgba(213, 63, 140, 0.8)'
}
},
//
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{offset: 0, color: 'rgba(22, 93, 255, 0.3)'},
{offset: 1, color: 'rgba(22, 93, 255, 0.05)'}
])
},
//
label: {
show: false,
position: 'bottom',
color: '#e9d8fd',
// formatter: '{value}%'
}
}
],
// tooltip
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: 'rgba(100, 255, 218, 0.7)',
width: 1,
type: 'dashed'
}
},
backgroundColor: 'rgba(17, 24, 39, 0.9)', //
borderColor: 'rgba(79, 70, 229, 0.5)',
borderWidth: 1,
textStyle: { color: '#e0e7ff' },
padding: 12,
// 线
extraCssText: 'box-shadow: 0 0 15px rgba(79, 70, 229, 0.3);',
},
//
//backgroundColor: 'transparent'
};
//
myChart.setOption(option);
}
async function loadHytd() {
const resData = await getHytd({});
const res = resData.result;
let Hy = [];
for (let i = 0; i < res.length; i++) {
Hy.push({
phone: res[i].phone,
cn: res[i].cn,
weight: res[i].weight,
});
}
tid(Hy);
}
setInterval(function() {
loadHytd();
}, 30*60*1000);
//
onMounted(() => {
window.addEventListener('resize', () => {
myChart.resize();
});
loadHytd();
})
</script>
<style scoped>
.data-card {
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,178 @@
<template>
<div class="data-card" :id="id"></div>
</template>
<script setup>
import {ref, onMounted, reactive} from 'vue'
import * as echarts from 'echarts'
import {getXqqy} from "../../dashboard/Analysis/api";
// import { Info } from '@element-plus/icons-vue'
const props = defineProps({
id: {
type: String,
required: true
},
data: {
type: String
}
});
let quCn = ref([
]);
let quWeight = ref([]);
let myChart = reactive({});
function tid(data) {
quCn.value = [];
quWeight.value = [];
data.forEach((item, idx) => {
quCn.value.push({
name: item.housingestateName,
value: item.cn,
symbolSize: item.cn / 10,
count: item.cn,
weight: item.weight
});
quWeight.value.push({
source: '中心节点',
target: item.housingestateName,
value: item.weight,
lineStyle: {
width: item.weight / 20,
color: `rgba(100, 255, 218, ${item.weight / 3000 + 0.1})`
}
});
});
const chartDom = document.getElementById(props.id);
myChart = echarts.init(chartDom);
//
const colors = {
primary: 'rgba(66, 165, 245, 0.9)', //
secondary: 'rgba(100, 255, 218, 0.9)', //
accent: 'rgba(255, 102, 102, 0.9)', //
gridLine: 'rgba(100, 150, 255, 0.1)', // 线
axisLine: 'rgba(100, 150, 255, 0.3)', //
textColor: '#a8b2d1', //
tooltipBg: 'rgba(10, 25, 47, 0.8)', //
};
//
//
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'item',
formatter: function (params) {
if (params.dataType === 'edge') {
return `
<div style="color:#64ffda;font-weight:bold">连接</div>
<div>投递重量(T): ${params.data.value}</div>
<div>${params.data.source} ${params.data.target}</div>
`;
}
return `
<div style="color:#64ffda;font-weight:bold">${params.data.name}</div>
<div>投递重量(T): ${params.data.weight}</div>
<div>投递次数: ${params.data.count}</div>
`;
},
backgroundColor: colors.tooltipBg,
borderColor: 'rgba(100, 150, 255, 0.3)',
borderWidth: 1,
textStyle: { color: '#e6f1ff' }
},
series: [
{
type: 'graph',
layout: 'force',
data: quCn.value,
links: quWeight.value,
roam: true,
focusNodeAdjacency: true,
draggable: true,
force: {
repulsion: 100,
edgeLength: [40, 100]
},
label: {
show: true,
position: 'right',
color: colors.textColor
},
lineStyle: {
color: 'source',
curveness: 0.1
},
emphasis: {
lineStyle: {
width: function (edge) {
return edge.value / 120;
}
}
},
itemStyle: {
color: function (params) {
if (params.data.category === 0) {
return colors.primary;
}
//
return `rgba(${params.data.count *0.15}, ${params.data.count *0.4}, ${params.data.count * 0.75}, ${params.data.count / 300 + 0.9})`;
},
shadowBlur: 50,
shadowColor: function (params) {
if (params.data.category === 0) {
return 'rgba(100, 255, 218, 0.7)';
}
return 'rgba(66, 165, 245, 0.5)';
}
},
animationDuration: 500,
animationEasing: 'elasticOut',
animationDelay: function (idx) {
return idx * 100;
}
}
]
};
//
myChart.setOption(option);
}
//
async function loadXqqy(){
const resData = await getXqqy({});
const res = resData.result;
let Qy = [];
for (let i = 0; i < res.length; i++) {
Qy.push({
housingestateName: res[i].housingestateName,
cn: res[i].cn,
weight: res[i].weight,
});
}
tid(Qy);
}
setInterval(function() {
loadXqqy();
}, 30*60*1000);
//
onMounted(() => {
window.addEventListener('resize', () => {
myChart.resize();
});
loadXqqy();
})
</script>
<style scoped>
.data-card {
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,267 @@
<template>
<div class="data-card" :id="id"></div>
</template>
<script setup>
import {ref, onMounted, reactive} from 'vue'
import * as echarts from 'echarts'
import {getSbtd} from "../../dashboard/Analysis/api";
// import { Info } from '@element-plus/icons-vue'
const props = defineProps({
id: {
type: String,
required: true
},
data: {
type: String
}
});
let quLabel = ref([]);
let quCn = ref([]);
let quWeight = ref([]);
let myChart = reactive({});
function tid(data) {
quLabel.value = [];
quCn.value = [];
quWeight.value = [];
data.forEach((item) => {
quLabel.value.push(`${item.housingestateName}-${item.content}`);
//quLabel.value.push(item.content);
quCn.value.push(item.cn);
quWeight.value.push(item.weight)
})
const chartDom = document.getElementById(props.id);
myChart = echarts.init(chartDom);
//
const option = {
//
legend: {
data: ['投递重量(T)','投递次数'],
top: '3%',
textStyle: { color: '#a8b2d1' }, //
icon: 'circle', //
itemWidth: 8
},
//
grid: {
left: '5%',
right: '2%',
bottom: '10%',
containLabel: true,
},
// X
xAxis: {
type: 'category',
data: quLabel.value,
axisLine: {
show: true,
lineStyle: { color: '#2d3748' } // X线
},
axisTick: { show: false }, // 线
axisLabel: {
color: '#a0aec0', //
rotate: 45,
formatter: function (params) {
if (typeof params !== 'string') {
return ''; //
}
const index = params.lastIndexOf('-');
if (index === -1) {
return ''; //
}
return params.substring(index + 1).trim();
}
}
},
// Y访
yAxis: [
{
type: 'value',
name: '投递重量(T)',
nameTextStyle: { color: '#805ad5' }, //
axisLine: { show: true,
lineStyle: {
color: 'rgb(128,90,213)'
}
},
axisTick: { show: false },
splitLine: {
lineStyle: {
color: 'rgba(74, 85, 104, 0.3)', // 线
type: 'dashed' // 线
}
},
axisLabel: {
color: '#a8b2d1',
// formatter: '{value}'
}
},
// Y
{
type: 'value',
name: '投递次数',
nameTextStyle: { color: '#63b3ed' },
axisLine: { show: true,
lineStyle: {
color: 'rgba(66, 165, 245)'
}
},
axisTick: { show: false },
splitLine: { show: false }, // 线
axisLabel: {
color: '#a8b2d1',
// formatter: '{value}%'
}
}
],
// +线
series: [
// 访
{
name: '投递重量(T)',
type: 'bar',
data: quWeight.value,
//
barWidth: 24,
//
itemStyle: {
borderRadius: [6, 6, 0, 0], //
//
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{offset: 0, color: '#9D4EDD'},
{offset: 1, color: 'rgba(22, 93, 255, 0.6)'}
]
},
},
//
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#63b3ed' }, //
{ offset: 1, color: '#3182ce' }
]),
boxShadow: '0 0 15px rgba(49, 130, 206, 0.6)' //
}
},
//
label: {
show: false,
verticalAlign: 'middle',
align: 'center',
color: '#e0e7ff',
fontSize: 12
}
},
// 线
{
name: '投递次数',
type: 'line',
yAxisIndex: 1, // Y
data: quCn.value,
// 线
lineStyle: {
width: 3,
color: '#00E5FF', // 线
shadowBlur: 10,
shadowColor: 'rgba(0, 229, 255, 0.5)' //
},
//
symbol: 'circle', //
symbolSize: 8, //
itemStyle: {
color: '#805ad5',
borderColor: '#fff',
borderWidth: 2
},
//
emphasis: {
symbolSize: 12,
itemStyle: {
color: '#d53f8c',
boxShadow: '0 0 10px rgba(213, 63, 140, 0.8)'
}
},
//
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(128, 90, 213, 0.3)' },
{ offset: 1, color: 'rgba(128, 90, 213, 0)' }
])
},
//
label: {
show: false,
position: 'bottom',
color: '#e9d8fd',
// formatter: '{value}%'
}
}
],
// tooltip
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(17, 24, 39, 0.9)', //
borderColor: 'rgba(79, 70, 229, 0.5)',
borderWidth: 1,
textStyle: { color: '#e0e7ff' },
padding: 12,
// 线
extraCssText: 'box-shadow: 0 0 15px rgba(79, 70, 229, 0.3);',
},
//
//backgroundColor: 'transparent'
};
//
myChart.setOption(option);
}
async function loadSbtd() {
const resData = await getSbtd({});
const res = resData.result;
let Sb = [];
for (let i = 0; i < res.length; i++) {
Sb.push({
housingestateName: res[i].housingestateName,
content: res[i].content,
cn: res[i].cn,
weight: res[i].weight,
})
}
tid(Sb);
}
setInterval(function() {
loadSbtd();
}, 30*60*1000);
//
onMounted(() => {
window.addEventListener('resize', () => {
myChart.resize();
});
loadSbtd();
})
</script>
<style scoped>
.data-card {
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,42 @@
<template>
<div class="page-header">
<div class="header-title">
<h1>{{ title }}</h1>
</div>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
default: '吉林资环数据平台'
},
})
</script>
<style scoped>
.page-header {
display: flex;
height: 60px;
justify-content: center;
align-items: center;
background-image: url('@/assets/titlebg.png');
background-repeat: no-repeat;
background-position: center;
background-size: 100% 100%;
padding: 5px;
}
.header-title h1 {
margin: 0;
font-size: 32px;
font-weight: 600;
letter-spacing: 10px;
background: linear-gradient(to top, #098fe9, #ffffff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
</style>

277
src/views/screen/screen.vue Normal file
View File

@ -0,0 +1,277 @@
<template>
<div class="dashboard-container">
<PageHeader />
<a-row :gutter="20" class="main-content">
<!-- 左侧面板 -->
<a-col :span="6">
<a-card class="panel-card" :body-style="{'width':'100%','height':'100%','padding':'0', 'margin-top': '-34px'}">
<template #title>
<div class="card-header">
<sliders-two-tone style="margin-right: 5px"/>
<span>小区排行</span>
</div>
</template>
<DataCard :id="`echart1`"/>
</a-card>
<a-card class="panel-card mt-20" :body-style="{'width':'100%','height':'100%','padding':'0','margin-top': '-34px'}">
<template #title>
<div class="card-header">
<sliders-two-tone style="margin-right: 5px" />
<span>设备排行</span>
</div>
</template>
<DataCardSet :id="`echart2`" :data="Sb" />
</a-card>
</a-col>
<!-- 中间面板 -->
<a-col :span="12">
<a-card class="mind-card" :body-style="{'padding':'0'}">
<ChartComponent
ref="mainChartRef"
:showControls="true"
:initialConfig="particleConfig"
/>
<div class="statistics-container">
<a-row :gutter="20">
<a-col :span="6" v-for="(item, index) in statistics" :key="index">
<div class="statistics-item">
<div class="title">{{ item.title }}</div>
<div class="flex justify-between items-center">
<div class="today-data">{{ item.today }}</div>
<div class="yesterday">昨日 {{ item.yesterday }}</div>
</div>
<div :style="{ color: item.rateColor }" class="rate">较昨日 {{ item.rate }}</div>
</div>
</a-col>
</a-row>
</div>
</a-card>
</a-col>
<a-col :span="6">
<a-card class="panel-card" :body-style="{'width':'100%','height':'100%','padding':'0', 'margin-top': '-34px'}">
<template #title>
<div class="card-header">
<sliders-two-tone style="margin-right: 5px"/>
<span>会员排行</span>
</div>
</template>
<DataCardHy :id="`echart3`" :data="Hy" />
</a-card>
<a-card class="panel-card mt-20" :body-style="{'width':'100%','height':'100%','padding':'0','margin-top': '-34px'}">
<template #title>
<div class="card-header">
<sliders-two-tone style="margin-right: 5px"/>
<span>小区清运</span>
</div>
</template>
<DataCardQy :id="`echart4`" :data="Qy"/>
</a-card>
</a-col>
</a-row>
</div>
</template>
<script setup>
import {ref, onMounted, computed, reactive} from 'vue'
import {
SlidersTwoTone
} from '@ant-design/icons-vue';
import PageHeader from './components/PageHeader.vue'
import DataCard from './components/DataCard.vue'
import DataCardSet from './components/DataCardSet.vue'
import DataCardHy from './components/DataCardHy.vue'
import DataCardQy from './components/DataCardQy.vue'
import ChartComponent from './components/ChartComponent.vue'
import {getTodayInfo} from "../dashboard/Analysis/api";
const particleConfig = ref({
particleCount: 800,
radius: 30,
particleSize: 0.4,
animationSpeed: 0.015,
colorMode: 'group'
})
async function loadTodayData() {
statistics.value = [];
const resData = await getTodayInfo({});
const res = resData.result;
statistics.value.push(
{
title: '今日投递重量T',
today: res.tdzlT,
yesterday: res.tdzlY,
rate: res.tdzlR+"%",
rateColor: res.tdzlR>=0?'#ff3333':'#00FF9D'
},
{
title: '今日投递次数(次)',
today: res.tdcsT,
yesterday: res.tdcsY,
rate: res.tdcsR+"%",
rateColor: res.tdzlR>=0?'#ff3333':'#00FF9D'
},
{
title: '今日清运重量T',
today: res.qyzlT,
yesterday: res.qyzlY,
rate: res.qyzlR+"%",
rateColor: res.tdzlR>=0?'#ff3333':'#00FF9D'
},
{
title: '今日新增会员(人)',
today: res.hyrsT,
yesterday: res.hyrsY,
rate: res.hyrsR+"%",
rateColor: res.tdzlR>=0?'#ff3333':'#00FF9D'
}
);
}
//
const statistics = ref([])
//
const mainChartRef = ref(null)
const budgetChartRef = ref(null)
const trendChartRef = ref(null)
setInterval(function() {
loadTodayData();
}, 30*60*1000);
onMounted(() => {
loadTodayData();
})
//
const handleResize = () => {
if (mainChartRef.value) mainChartRef.value.resize()
if (budgetChartRef.value) budgetChartRef.value.resize()
if (trendChartRef.value) trendChartRef.value.resize()
}
</script>
<style scoped lang="less">
.dashboard-container {
background-image: url('@/assets/bg.png');
background-repeat: no-repeat;
background-size: 100% 100%;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
}
.main-content {
/*margin-top: 20px;*/
padding: 15px 15px 0px 15px;
}
.panel-card {
background: url('@/assets/borderbg.png') no-repeat center;
background-size: 100% 100%;
border: 1px solid rgba(64, 158, 255, 0.2);
border-radius: 8px;
color: #fff;
height: 43vh;
overflow: hidden;
}
.mind-card{
background: transparent;
border: none;
}
.card-header {
display: flex;
align-items: center;
font-size: 16px;
font-weight: 600;
height: 30px;
padding-left: 20px;
}
::v-deep(.ant-card-head-title){
background: url('@/assets/btbg.png') no-repeat center;
background-size: 100%;
height: 76px;
border: none;
padding: 5px;
color: #fff;
}
::v-deep(.ant-card-head){
margin-left: 0px!important;
margin-right: 0px!important;
padding: 0px;
border: none;
}
.card-header i {
margin-right: 8px;
color: #409eff;
}
.small-cards {
margin-top: 20px;
}
.small-card {
background: rgba(255, 255, 255, 0.05);
text-align: center;
border-radius: 4px;
padding: 10px;
}
.card-value {
font-size: 18px;
font-weight: bold;
}
.card-desc {
font-size: 12px;
color: #aaa;
margin-top: 4px;
}
.statistics-item {
background: #0d0348;
border: 1px solid #064f8b;
padding: 15px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.title {
font-size: 14px;
color: #99F2FF;
margin-bottom: 10px;
}
.today-data {
font-size: 36px;
color: #00FFFF;
font-weight: 700;
margin: 10px 0;
text-shadow: 0 0 8px rgba(0, 255, 255, 0.4);
}
.yesterday {
color: #B3FFFF;
font-size: 12px;
opacity: 0.9;
}
.rate {
font-size: 12px;
}
.statistics-container{
//display: grid;
//grid-template-rows: auto 1fr auto;
//.ant-row{
// align-self: end;
//}
position: fixed;
bottom: 20px;
width: 49%;
}
</style>