feat: 接入大屏修复bug
@@ -48,6 +48,7 @@
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "catalog:",
|
||||
"echarts": "^5.5.1",
|
||||
"echarts-gl": "^2.0.9",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"pinia": "catalog:",
|
||||
|
BIN
apps/web-antd/src/assets/digitalIntelligence/bg.png
Normal file
After Width: | Height: | Size: 556 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/center-bg.png
Normal file
After Width: | Height: | Size: 360 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/circle1.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/circle2.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/circle3.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
apps/web-antd/src/assets/digitalIntelligence/circle4.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
apps/web-antd/src/assets/energyConsumptionAnalysis/bg.png
Normal file
After Width: | Height: | Size: 549 KiB |
BIN
apps/web-antd/src/assets/energyConsumptionAnalysis/center-bg.png
Normal file
After Width: | Height: | Size: 570 KiB |
After Width: | Height: | Size: 803 B |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.1 KiB |
BIN
apps/web-antd/src/assets/monitor/bg.png
Normal file
After Width: | Height: | Size: 394 KiB |
BIN
apps/web-antd/src/assets/monitor/device-alerts-bg1.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
apps/web-antd/src/assets/monitor/device-alerts-bg2.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
apps/web-antd/src/assets/monitor/device-alerts-bg3.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
apps/web-antd/src/assets/monitor/device-alerts-button1.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor1.png
Normal file
After Width: | Height: | Size: 167 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor2.png
Normal file
After Width: | Height: | Size: 175 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor3.png
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor4.png
Normal file
After Width: | Height: | Size: 175 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor5.png
Normal file
After Width: | Height: | Size: 184 KiB |
BIN
apps/web-antd/src/assets/monitor/monitor6.png
Normal file
After Width: | Height: | Size: 160 KiB |
BIN
apps/web-antd/src/assets/navigation/科技数据粒子上升蓝色.gif
Normal file
After Width: | Height: | Size: 38 MiB |
BIN
apps/web-antd/src/assets/property/bg.png
Normal file
After Width: | Height: | Size: 438 KiB |
BIN
apps/web-antd/src/assets/property/center-bg.png
Normal file
After Width: | Height: | Size: 360 KiB |
BIN
apps/web-antd/src/assets/property/customer-circle.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
apps/web-antd/src/assets/property/personnel-duty-circle1.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
apps/web-antd/src/assets/property/personnel-duty-circle2.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
apps/web-antd/src/assets/property/personnel-duty-circle3.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
apps/web-antd/src/assets/property/personnel-duty-circle4.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
apps/web-antd/src/assets/security/bg.png
Normal file
After Width: | Height: | Size: 450 KiB |
BIN
apps/web-antd/src/assets/security/button1.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
apps/web-antd/src/assets/security/button2.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
apps/web-antd/src/assets/security/button3.png
Normal file
After Width: | Height: | Size: 773 B |
BIN
apps/web-antd/src/assets/security/center-bg.png
Normal file
After Width: | Height: | Size: 327 KiB |
BIN
apps/web-antd/src/assets/security/monitor1.png
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
apps/web-antd/src/assets/security/monitor2.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
apps/web-antd/src/assets/security/stop-icon.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
@@ -11,6 +11,7 @@ import {
|
||||
GiteeIcon,
|
||||
GitHubOutlined,
|
||||
UserOutlined,
|
||||
|
||||
} from '@vben/icons';
|
||||
import {
|
||||
BasicLayout,
|
||||
@@ -30,6 +31,7 @@ import { resetRoutes } from '#/router';
|
||||
import { useAuthStore, useNotifyStore } from '#/store';
|
||||
import { useTenantStore } from '#/store/tenant';
|
||||
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||
// import { TagOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const authStore = useAuthStore();
|
||||
@@ -83,6 +85,13 @@ const menus = computed(() => {
|
||||
icon: CircleHelp,
|
||||
text: $t('ui.widgets.qa'),
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
router.push('/navigation');
|
||||
},
|
||||
// icon: TagOutlined,
|
||||
text: '返回导航',
|
||||
},
|
||||
];
|
||||
/**
|
||||
* 租户选中状态 不显示个人中心
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { initPreferences } from '@vben/preferences';
|
||||
import { unmountGlobalLoading } from '@vben/utils';
|
||||
import { overridesPreferences } from './preferences';
|
||||
|
||||
import './utils/flexible'
|
||||
/**
|
||||
* 应用初始化完成之后再进行页面加载渲染
|
||||
*/
|
||||
|
@@ -100,6 +100,67 @@ const coreRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/navigation/Navigation.vue'),
|
||||
name: 'navigation',
|
||||
path: '/navigation',
|
||||
meta: {
|
||||
title: '导航页面', // 或者用 $t('page.navigation.title')
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/energyConsumptionAnalysis/index.vue'),
|
||||
name: 'energyAnalysis',
|
||||
path: '/energyAnalysis',
|
||||
meta: {
|
||||
title: '能耗大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
}, {
|
||||
component: () => import('#/views/screen/property/index.vue'),
|
||||
name: 'property',
|
||||
path: '/property',
|
||||
meta: {
|
||||
title: '物业大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
}, {
|
||||
component: () => import('#/views/screen/security/index.vue'),
|
||||
name: 'security',
|
||||
path: '/security',
|
||||
meta: {
|
||||
title: '安防大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/monitor/index.vue'),
|
||||
name: 'monitor',
|
||||
path: '/monitor',
|
||||
meta: {
|
||||
title: '监控大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/security/index.vue'),
|
||||
name: 'security',
|
||||
path: '/security',
|
||||
meta: {
|
||||
title: '安防大屏',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/screen/digitalIntelligence/index.vue'),
|
||||
name: 'digitalIntelligence',
|
||||
path: '/digitalIntelligence',
|
||||
meta: {
|
||||
title: '商务中心数智管理平台',
|
||||
requiresAuth: true, // 如果需要登录验证
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export { coreRoutes, fallbackNotFoundRoute };
|
||||
|
@@ -58,7 +58,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
} else {
|
||||
onSuccess
|
||||
? await onSuccess?.()
|
||||
: await router.push(preferences.app.defaultHomePath);
|
||||
: await router.push('/navigation');
|
||||
}
|
||||
|
||||
if (userInfo?.realName) {
|
||||
|
119
apps/web-antd/src/utils/echartsResize.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { ECharts } from 'echarts'
|
||||
|
||||
/**
|
||||
* ECharts 图表自适应管理器
|
||||
* 用于监听图表容器大小变化并自动调整图表大小
|
||||
*/
|
||||
export class EChartsResizeManager {
|
||||
private charts: Map<ECharts, ResizeObserver> = new Map()
|
||||
private resizeHandler: (() => void) | null = null
|
||||
|
||||
constructor() {
|
||||
this.initResizeListener()
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加图表到管理器
|
||||
* @param chart ECharts 实例
|
||||
*/
|
||||
addChart(chart: ECharts): void {
|
||||
if (chart && !this.charts.has(chart)) {
|
||||
// 获取图表容器元素
|
||||
const container = chart.getDom()
|
||||
if (container) {
|
||||
// 为每个图表创建独立的 ResizeObserver
|
||||
const observer = new ResizeObserver(() => {
|
||||
if (chart && !chart.isDisposed()) {
|
||||
chart.resize()
|
||||
}
|
||||
})
|
||||
|
||||
// 监听图表容器的大小变化
|
||||
observer.observe(container)
|
||||
this.charts.set(chart, observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除图表
|
||||
* @param chart ECharts 实例
|
||||
*/
|
||||
removeChart(chart: ECharts): void {
|
||||
const observer = this.charts.get(chart)
|
||||
if (observer) {
|
||||
observer.disconnect()
|
||||
this.charts.delete(chart)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有图表
|
||||
*/
|
||||
clearCharts(): void {
|
||||
this.charts.forEach(observer => {
|
||||
observer.disconnect()
|
||||
})
|
||||
this.charts.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新调整所有图表大小
|
||||
*/
|
||||
resizeAllCharts(): void {
|
||||
this.charts.forEach((observer, chart) => {
|
||||
if (chart && !chart.isDisposed()) {
|
||||
chart.resize()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化窗口大小变化监听器
|
||||
*/
|
||||
private initResizeListener(): void {
|
||||
// 监听窗口大小变化
|
||||
this.resizeHandler = () => {
|
||||
this.resizeAllCharts()
|
||||
}
|
||||
window.addEventListener('resize', this.resizeHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁监听器
|
||||
*/
|
||||
destroy(): void {
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler)
|
||||
this.resizeHandler = null
|
||||
}
|
||||
|
||||
this.clearCharts()
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局实例
|
||||
export const echartsResizeManager = new EChartsResizeManager()
|
||||
|
||||
/**
|
||||
* 便捷函数:添加图表到管理器
|
||||
* @param chart ECharts 实例
|
||||
*/
|
||||
export function addChartToResizeManager(chart: ECharts): void {
|
||||
echartsResizeManager.addChart(chart)
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷函数:移除图表
|
||||
* @param chart ECharts 实例
|
||||
*/
|
||||
export function removeChartFromResizeManager(chart: ECharts): void {
|
||||
echartsResizeManager.removeChart(chart)
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷函数:手动触发所有图表重新调整大小
|
||||
*/
|
||||
export function resizeAllCharts(): void {
|
||||
echartsResizeManager.resizeAllCharts()
|
||||
}
|
12
apps/web-antd/src/utils/flexible.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
function setRootFontSize(): void {
|
||||
const baseWidth = 1920 // 设计稿宽度
|
||||
const baseFontSize = 16 // 设计稿根字体
|
||||
const screenWidth = window.innerWidth
|
||||
const fontSize = (screenWidth / baseWidth) * baseFontSize
|
||||
document.documentElement.style.fontSize = fontSize + 'px'
|
||||
}
|
||||
|
||||
setRootFontSize()
|
||||
window.addEventListener('resize', setRootFontSize)
|
||||
|
||||
export default setRootFontSize
|
264
apps/web-antd/src/utils/pie3d.ts
Normal file
@@ -0,0 +1,264 @@
|
||||
import * as echarts from 'echarts'
|
||||
import 'echarts-gl'
|
||||
|
||||
interface Pie3DData {
|
||||
name: string
|
||||
value: number
|
||||
itemStyle?: {
|
||||
color?: string
|
||||
opacity?: number
|
||||
}
|
||||
startRatio?: number
|
||||
endRatio?: number
|
||||
}
|
||||
|
||||
interface Pie3DOptions {
|
||||
data: Pie3DData[]
|
||||
height?: number
|
||||
hoverHeightScale?: number
|
||||
selectOffset?: number
|
||||
distance?: number
|
||||
boxHeight?: number
|
||||
radius?: number
|
||||
}
|
||||
|
||||
interface ParametricEquation {
|
||||
u: { min: number; max: number; step: number }
|
||||
v: { min: number; max: number; step: number }
|
||||
x: (u: number, v: number) => number
|
||||
y: (u: number, v: number) => number
|
||||
z: (u: number, v: number) => number
|
||||
}
|
||||
|
||||
interface SeriesItem {
|
||||
name: string
|
||||
type: string
|
||||
parametric: boolean
|
||||
wireframe: { show: boolean }
|
||||
pieData?: Pie3DData
|
||||
pieStatus?: { selected: boolean; hovered: boolean; k: number }
|
||||
itemStyle?: any
|
||||
parametricEquation?: ParametricEquation
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染3D饼图,支持点击选中(外移)、鼠标悬停高亮(升高)、高亮修正,无label/legend。
|
||||
* @param dom - 容器DOM
|
||||
* @param options - 配置项
|
||||
* options.data: [{name, value, itemStyle}]
|
||||
* options.height: 饼图厚度
|
||||
* options.hoverHeightScale: 高亮时高度放大倍数
|
||||
* options.selectOffset: 选中时外移距离(默认0.1)
|
||||
* options.distance: 视角距离
|
||||
* options.boxHeight: grid3D.boxHeight
|
||||
* options.radius: 半径缩放
|
||||
* @returns echartsInstance
|
||||
*/
|
||||
export function renderPie3DChart(dom: HTMLElement, options: Pie3DOptions): echarts.ECharts | undefined {
|
||||
if (!dom) return
|
||||
const myChart = echarts.init(dom)
|
||||
const hoverHeightScale = options.hoverHeightScale || 1.2
|
||||
const selectOffset = options.selectOffset || 0.1
|
||||
const distance = options.distance || 120
|
||||
const gridBoxHeight = options.boxHeight || 10
|
||||
const R = options.radius || 0.8
|
||||
const fixedH = 100
|
||||
|
||||
// 生成扇形的曲面参数方程
|
||||
function getParametricEquation(startRatio: number, endRatio: number, isSelected: boolean, isHovered: boolean, k: number, h: number): ParametricEquation {
|
||||
const midRatio = (startRatio + endRatio) / 2
|
||||
const startRadian = startRatio * Math.PI * 2
|
||||
const endRadian = endRatio * Math.PI * 2
|
||||
const midRadian = midRatio * Math.PI * 2
|
||||
if (startRatio === 0 && endRatio === 1) isSelected = false
|
||||
k = typeof k !== 'undefined' ? k : 1
|
||||
const offsetX = isSelected ? Math.cos(midRadian) * selectOffset : 0
|
||||
const offsetY = isSelected ? Math.sin(midRadian) * selectOffset : 0
|
||||
const hoverRate = isHovered ? 1.05 : 1
|
||||
return {
|
||||
u: { min: -Math.PI, max: Math.PI * 3, step: Math.PI / 32 },
|
||||
v: { min: 0, max: Math.PI * 2, step: Math.PI / 20 },
|
||||
x: function(u: number, v: number) {
|
||||
if (u < startRadian) return offsetX + R * Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
if (u > endRadian) return offsetX + R * Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
return offsetX + R * Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate
|
||||
},
|
||||
y: function(u: number, v: number) {
|
||||
if (u < startRadian) return offsetY + R * Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
if (u > endRadian) return offsetY + R * Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
return offsetY + R * Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate
|
||||
},
|
||||
z: function(u: number, v: number) {
|
||||
if (u < -Math.PI * 0.5) return Math.sin(u)
|
||||
if (u > Math.PI * 2.5) return Math.sin(u) * h * .1
|
||||
return Math.sin(v) > 0 ? 1 * h * .1 : -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 生成series
|
||||
function getPie3D(pieData: Pie3DData[]): SeriesItem[] {
|
||||
const series: SeriesItem[] = []
|
||||
let sumValue = 0
|
||||
let startValue = 0
|
||||
let endValue = 0
|
||||
const k = 1 // 实心饼图
|
||||
for (let i = 0; i < pieData.length; i++) {
|
||||
sumValue += pieData[i].value
|
||||
const seriesItem: SeriesItem = {
|
||||
name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
|
||||
type: 'surface',
|
||||
parametric: true,
|
||||
wireframe: { show: false },
|
||||
pieData: pieData[i],
|
||||
pieStatus: { selected: false, hovered: false, k: k }
|
||||
}
|
||||
if (pieData[i].itemStyle) {
|
||||
const itemStyle: any = {}
|
||||
const itemStyleObj: any = pieData[i].itemStyle
|
||||
if (itemStyleObj.color) itemStyle.color = itemStyleObj.color
|
||||
if (itemStyleObj.opacity !== undefined) itemStyle.opacity = itemStyleObj.opacity
|
||||
seriesItem.itemStyle = itemStyle
|
||||
}
|
||||
series.push(seriesItem)
|
||||
}
|
||||
for (let i = 0; i < series.length; i++) {
|
||||
endValue = startValue + series[i].pieData!.value
|
||||
series[i].pieData!.startRatio = startValue / sumValue
|
||||
series[i].pieData!.endRatio = endValue / sumValue
|
||||
series[i].parametricEquation = getParametricEquation(
|
||||
series[i].pieData!.startRatio!,
|
||||
series[i].pieData!.endRatio!,
|
||||
false,
|
||||
false,
|
||||
k,
|
||||
fixedH // 所有块厚度一样
|
||||
)
|
||||
startValue = endValue
|
||||
}
|
||||
// 透明圆环用于高亮修正
|
||||
series.push({
|
||||
name: 'mouseoutSeries',
|
||||
type: 'surface',
|
||||
parametric: true,
|
||||
wireframe: { show: false },
|
||||
itemStyle: { opacity: 0 },
|
||||
parametricEquation: {
|
||||
u: { min: 0, max: Math.PI * 2, step: Math.PI / 20 },
|
||||
v: { min: 0, max: Math.PI, step: Math.PI / 20 },
|
||||
x: function(u: number, v: number) { return Math.sin(v) * Math.sin(u) + Math.sin(u) },
|
||||
y: function(u: number, v: number) { return Math.sin(v) * Math.cos(u) + Math.cos(u) },
|
||||
z: function(u: number, v: number) { return Math.cos(v) > 0 ? 0.1 : -0.1 }
|
||||
}
|
||||
})
|
||||
return series
|
||||
}
|
||||
|
||||
const series = getPie3D(options.data)
|
||||
// 不加pie2d,不加legend,不加label
|
||||
const option = {
|
||||
backgroundColor: 'rgba(0,0,0,0)',
|
||||
legend: undefined,
|
||||
tooltip: undefined,
|
||||
xAxis3D: { min: -1, max: 1 },
|
||||
yAxis3D: { min: -1, max: 1 },
|
||||
zAxis3D: { min: -1, max: 1 },
|
||||
grid3D: {
|
||||
show: false,
|
||||
boxHeight: gridBoxHeight,
|
||||
viewControl: {
|
||||
alpha: 35,
|
||||
distance: distance,
|
||||
rotateSensitivity: 0,
|
||||
zoomSensitivity: 0,
|
||||
panSensitivity: 0,
|
||||
autoRotate: false
|
||||
}
|
||||
},
|
||||
series: series
|
||||
}
|
||||
myChart.setOption(option, true)
|
||||
|
||||
// 选中/高亮交互
|
||||
let hoveredIndex = ''
|
||||
|
||||
// // 点击选中(外移)
|
||||
// myChart.on('click', function(params) {
|
||||
// if (!series[params.seriesIndex] || series[params.seriesIndex].type !== 'surface') return;
|
||||
// let isSelected = !series[params.seriesIndex].pieStatus.selected;
|
||||
// let isHovered = series[params.seriesIndex].pieStatus.hovered;
|
||||
// let k = series[params.seriesIndex].pieStatus.k;
|
||||
// let startRatio = series[params.seriesIndex].pieData.startRatio;
|
||||
// let endRatio = series[params.seriesIndex].pieData.endRatio;
|
||||
// // 取消之前选中
|
||||
// if (selectedIndex !== '' && selectedIndex !== params.seriesIndex) {
|
||||
// let prev = series[selectedIndex];
|
||||
// prev.parametricEquation = getParametricEquation(
|
||||
// prev.pieData.startRatio, prev.pieData.endRatio, false, prev.pieStatus.hovered, k, prev.pieData.value
|
||||
// );
|
||||
// prev.pieStatus.selected = false;
|
||||
// }
|
||||
// // 当前选中/取消
|
||||
// series[params.seriesIndex].parametricEquation = getParametricEquation(
|
||||
// startRatio, endRatio, isSelected, isHovered, k, series[params.seriesIndex].pieData.value
|
||||
// );
|
||||
// series[params.seriesIndex].pieStatus.selected = isSelected;
|
||||
// isSelected ? selectedIndex = params.seriesIndex : selectedIndex = '';
|
||||
// myChart.setOption({ series });
|
||||
// });
|
||||
|
||||
// 悬停高亮(升高)
|
||||
myChart.on('mouseover', function(params: any) {
|
||||
if (!series[params.seriesIndex] || !series[params.seriesIndex].pieData) return
|
||||
if (hoveredIndex === params.seriesIndex) return
|
||||
// 取消之前高亮
|
||||
if (hoveredIndex !== '') {
|
||||
const prev = series[parseInt(hoveredIndex)]
|
||||
if (!prev || !prev.pieData) return
|
||||
prev.parametricEquation = getParametricEquation(
|
||||
prev.pieData.startRatio!,
|
||||
prev.pieData.endRatio!,
|
||||
prev.pieStatus!.selected,
|
||||
false,
|
||||
prev.pieStatus!.k,
|
||||
fixedH
|
||||
)
|
||||
prev.pieStatus!.hovered = false
|
||||
hoveredIndex = ''
|
||||
}
|
||||
// 当前高亮
|
||||
const cur = series[params.seriesIndex]
|
||||
cur.parametricEquation = getParametricEquation(
|
||||
cur.pieData!.startRatio!,
|
||||
cur.pieData!.endRatio!,
|
||||
cur.pieStatus!.selected,
|
||||
true,
|
||||
cur.pieStatus!.k,
|
||||
fixedH * hoverHeightScale
|
||||
)
|
||||
cur.pieStatus!.hovered = true
|
||||
hoveredIndex = params.seriesIndex.toString()
|
||||
myChart.setOption({ series })
|
||||
})
|
||||
|
||||
// 全局移出,修正高亮残留
|
||||
myChart.on('globalout', function() {
|
||||
if (hoveredIndex !== '') {
|
||||
const prev = series[parseInt(hoveredIndex)]
|
||||
if (!prev || !prev.pieData) return
|
||||
prev.parametricEquation = getParametricEquation(
|
||||
prev.pieData.startRatio!,
|
||||
prev.pieData.endRatio!,
|
||||
prev.pieStatus!.selected,
|
||||
false,
|
||||
prev.pieStatus!.k,
|
||||
fixedH
|
||||
)
|
||||
prev.pieStatus!.hovered = false
|
||||
hoveredIndex = ''
|
||||
myChart.setOption({ series })
|
||||
}
|
||||
})
|
||||
|
||||
return myChart
|
||||
}
|
129
apps/web-antd/src/utils/threeDBarOption.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
interface ThreeDBarOptionConfig {
|
||||
xData: string[]
|
||||
yData: number[]
|
||||
barWidth?: number
|
||||
colorConfig?: {
|
||||
left0?: string
|
||||
left08?: string
|
||||
top03?: string
|
||||
top1?: string
|
||||
}
|
||||
}
|
||||
|
||||
export function getThreeDBarOption({ xData, yData, barWidth = 30, colorConfig = {} }: ThreeDBarOptionConfig) {
|
||||
// 注册 shape 只需一次,防止多次注册报错
|
||||
if (!(echarts.graphic as any).registered3DBar) {
|
||||
const leftShape = echarts.graphic.extendShape({
|
||||
buildPath(ctx: any, shape: any) {
|
||||
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape
|
||||
const WIDTH = 15
|
||||
const OBLIQUE_ANGLE_HEIGHT = 8
|
||||
const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
|
||||
const p2 = [basicsXAxis - WIDTH, bottomYAxis]
|
||||
const p3 = [basicsXAxis, bottomYAxis]
|
||||
const p4 = [basicsXAxis, topBasicsYAxis]
|
||||
ctx.moveTo(p1[0], p1[1])
|
||||
ctx.lineTo(p2[0], p2[1])
|
||||
ctx.lineTo(p3[0], p3[1])
|
||||
ctx.lineTo(p4[0], p4[1])
|
||||
}
|
||||
})
|
||||
const rightShape = echarts.graphic.extendShape({
|
||||
buildPath(ctx: any, shape: any) {
|
||||
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape
|
||||
const WIDTH = 15
|
||||
const OBLIQUE_ANGLE_HEIGHT = 2
|
||||
const p1 = [basicsXAxis, topBasicsYAxis]
|
||||
const p2 = [basicsXAxis, bottomYAxis]
|
||||
const p3 = [basicsXAxis + WIDTH, bottomYAxis]
|
||||
const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
|
||||
ctx.moveTo(p1[0], p1[1])
|
||||
ctx.lineTo(p2[0], p2[1])
|
||||
ctx.lineTo(p3[0], p3[1])
|
||||
ctx.lineTo(p4[0], p4[1])
|
||||
}
|
||||
})
|
||||
const topShape = echarts.graphic.extendShape({
|
||||
buildPath(ctx: any, shape: any) {
|
||||
const { topBasicsYAxis, basicsXAxis } = shape
|
||||
const WIDTH = 15
|
||||
const OBLIQUE_ANGLE_HEIGHT = 8
|
||||
const p1 = [basicsXAxis, topBasicsYAxis]
|
||||
const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
|
||||
const p3 = [basicsXAxis, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT * 2]
|
||||
const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT]
|
||||
ctx.moveTo(p1[0], p1[1])
|
||||
ctx.lineTo(p2[0], p2[1])
|
||||
ctx.lineTo(p3[0], p3[1])
|
||||
ctx.lineTo(p4[0], p4[1])
|
||||
}
|
||||
})
|
||||
echarts.graphic.registerShape('leftShape', leftShape)
|
||||
echarts.graphic.registerShape('rightShape', rightShape)
|
||||
echarts.graphic.registerShape('topShape', topShape)
|
||||
;(echarts.graphic as any).registered3DBar = true
|
||||
}
|
||||
|
||||
return {
|
||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', top: "12%", containLabel: true },
|
||||
xAxis: [{
|
||||
type: 'category',
|
||||
data: xData,
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
}],
|
||||
yAxis: [{
|
||||
type: 'value',
|
||||
axisLine: { show: false },
|
||||
splitLine: { lineStyle: { color: '#1b4a7a' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
}],
|
||||
series: [{
|
||||
type: 'custom',
|
||||
data: yData,
|
||||
barWidth,
|
||||
renderItem(params: any, api: any) {
|
||||
const basicsCoord = api.coord([api.value(0), api.value(1)])
|
||||
const topBasicsYAxis = basicsCoord[1]
|
||||
const basicsXAxis = basicsCoord[0]
|
||||
const bottomYAxis = api.coord([api.value(0), 0])[1]
|
||||
return {
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
type: 'leftShape',
|
||||
shape: { topBasicsYAxis, basicsXAxis, bottomYAxis },
|
||||
style: {
|
||||
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: colorConfig.left0 || '#2ADBEF' },
|
||||
{ offset: 1, color: colorConfig.left08 || '#2677F9 ' },
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'rightShape',
|
||||
shape: { topBasicsYAxis, basicsXAxis, bottomYAxis },
|
||||
style: {
|
||||
fill: '#114EAD '
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'topShape',
|
||||
shape: { topBasicsYAxis, basicsXAxis, bottomYAxis },
|
||||
style: {
|
||||
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0.3, color: colorConfig.top03 || '#6DF0FF' },
|
||||
{ offset: 1, color: colorConfig.top1 || '#6DF0FF' }
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
@@ -12,6 +12,7 @@ import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||
import { modalSchema } from './data';
|
||||
import productDetailModal from './rentalPlan-detial-modal.vue';
|
||||
import { getDictOptions } from '#/utils/dict';
|
||||
import { Item } from 'ant-design-vue/es/menu';
|
||||
|
||||
const emit = defineEmits<{ reload: [] }>();
|
||||
|
||||
@@ -69,7 +70,10 @@ const [BasicModal, modalApi] = useVbenModal({
|
||||
if ((isUpdate.value || isReadonly.value) && id) {
|
||||
const record = await rentalPlanInfo(id);
|
||||
// 后端返回绿植产品包列表结构处理
|
||||
detailTable.value = record.productList.map((item:any) => item.product);
|
||||
detailTable.value = record.productList.map((item:any) => {...item.product,Item.productNum});
|
||||
|
||||
console.log(detailTable.value);
|
||||
|
||||
await formApi.setValues(record);
|
||||
}
|
||||
await markInitialized();
|
||||
|
@@ -87,10 +87,14 @@ onMounted(async () => {
|
||||
data: xAxisData.value,
|
||||
boundaryGap: false,
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
yAxis: { type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (value) => `${value * 100}%`
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '订单数',
|
||||
name: '订单趋势',
|
||||
type: 'line',
|
||||
data: seriesData.value ||[],
|
||||
smooth: true,
|
||||
@@ -123,17 +127,22 @@ onMounted(async () => {
|
||||
],
|
||||
});
|
||||
renderCustomerTypesBar({
|
||||
title: { text: '客户类型分配' },
|
||||
title: { text: '客户类型分布' },
|
||||
tooltip: { trigger: 'axis' },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: countByCusTypeData.type || [],
|
||||
data: ['企业客户','个人客户','政府机构','商业地产','其他'],
|
||||
boundaryGap: true,
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
yAxis: { type: 'value',
|
||||
axisLabel: {
|
||||
// formatter: (value: number) => `${parseInt(value.toString())}`
|
||||
},
|
||||
// interval: 0,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '订单数',
|
||||
name: '客户数',
|
||||
type: 'bar',
|
||||
data: countByCusTypeData.counts || [],
|
||||
},
|
||||
@@ -141,16 +150,28 @@ onMounted(async () => {
|
||||
});
|
||||
renderCustomerRenewalLine({
|
||||
title: { text: '客户续租率趋势' },
|
||||
tooltip: { trigger: 'axis' },
|
||||
tooltip: { trigger: 'axis',
|
||||
formatter: function(params:any) {
|
||||
let result = params[0].axisValue + '<br/>';
|
||||
params.forEach((item:any) => {
|
||||
result += item.marker + item.seriesName + ':' + item.data + '%<br/>';
|
||||
});
|
||||
return result;
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: countRenewRateData.month || [],
|
||||
boundaryGap: false,
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: '{value}%',
|
||||
}, },
|
||||
series: [
|
||||
{
|
||||
name: '订单数',
|
||||
name: '续租率',
|
||||
type: 'line',
|
||||
data: countRenewRateData.rate || [],
|
||||
smooth: true,
|
||||
@@ -162,6 +183,19 @@ onMounted(async () => {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
formatter: function(params:any) {
|
||||
// params 是一个数组,包含每个系列的当前项
|
||||
let result = params[0].axisValue + '<br/>';
|
||||
params.forEach((item:any) => {
|
||||
if (item.seriesName === '完成率') {
|
||||
// 假设原始数据是 80,显示为 80%
|
||||
result += item.marker + item.seriesName + ':' + item.data + '%<br/>';
|
||||
} else {
|
||||
result += item.marker + item.seriesName + ':' + item.data + '<br/>';
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['计划任务数', '已完成数', '完成率'],
|
||||
@@ -169,7 +203,7 @@ onMounted(async () => {
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: countAchievedData.type || [],
|
||||
data: ['修剪整形','肥水管理','中耕除草','病虫害防治','越冬防寒'],
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
@@ -195,7 +229,7 @@ onMounted(async () => {
|
||||
{
|
||||
name: '计划任务数',
|
||||
type: 'bar',
|
||||
data: countAchievedData.toral || [],
|
||||
data: countAchievedData.total || [],
|
||||
},
|
||||
{
|
||||
name: '已完成数',
|
||||
@@ -220,7 +254,7 @@ onMounted(async () => {
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
bottom: 10,
|
||||
data: ['一星', '两星', '三星', '四星', '五星'],
|
||||
data: ['一星', '二星', '三星', '四星', '五星'],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@@ -283,7 +317,7 @@ function formatNumber(num: number | string) {
|
||||
<div class="title">
|
||||
<div class="title-text">绿植租赁业务统计报表:</div>
|
||||
<div class="title-operate">
|
||||
<div class="export">
|
||||
<div class="export" style="display: none;">
|
||||
<Button size="large" style="color: #fff; background-color: #22c55e">
|
||||
导出数据
|
||||
</Button>
|
||||
|
@@ -60,7 +60,8 @@ function generateWeekDates(): void {
|
||||
return startOfWeek.add(i, 'day').format('YYYY-MM-DD');
|
||||
});
|
||||
weekDates.value = dates;
|
||||
selectedDate.value = dates[0] ?? '';
|
||||
// 默认选中今天
|
||||
selectedDate.value = today.format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
// 获取预约数据
|
||||
@@ -137,7 +138,6 @@ async function fetchBookings(): Promise<void> {
|
||||
});
|
||||
bookingTable.value = table;
|
||||
}
|
||||
console.log(bookingTable.value,'bookingTable.value');
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取预约数据失败:', error);
|
||||
@@ -150,7 +150,8 @@ async function fetchBookings(): Promise<void> {
|
||||
function handleViewModeChange(e: RadioChangeEvent): void {
|
||||
viewMode.value = e.target.value;
|
||||
if (viewMode.value === 'date') {
|
||||
selectedDate.value = weekDates.value[0] ?? '';
|
||||
// 默认选中今天
|
||||
selectedDate.value = dayjs().format('YYYY-MM-DD');
|
||||
} else {
|
||||
selectedRoom.value = roomList.value[0]?.id ?? '';
|
||||
}
|
||||
|
1274
apps/web-antd/src/views/screen/digitalIntelligence/index.vue
Normal file
@@ -0,0 +1,890 @@
|
||||
<template>
|
||||
<div class="mian">
|
||||
<div class="title">
|
||||
<div class="left">
|
||||
<div class="left-first" id="time">--:--:--</div>
|
||||
<div class="left-second" id="date">----</div>
|
||||
</div>
|
||||
<div class="center">南川区综合服务中心数智管理平台能耗大屏</div>
|
||||
<div class="right">
|
||||
<div>{{ weekDay }}</div>
|
||||
<div>晴</div>
|
||||
<div>40℃</div>
|
||||
<div class="logout" @click="logout">
|
||||
<img src="../../../assets/return.png" style="width: 1.5rem; height: 1.5rem;">
|
||||
<div>
|
||||
退出
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="header-item">
|
||||
<span class="header-label">今年用电量</span>
|
||||
<span class="header-value orange">180</span>
|
||||
<span class="header-unit">亿kwh</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">本月用电总量</span>
|
||||
<span class="header-value green">1.8</span>
|
||||
<span class="header-unit">亿kwh</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">今年用水总量</span>
|
||||
<span class="header-value blue">2600</span>
|
||||
<span class="header-unit">万吨</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">本月用水总量</span>
|
||||
<span class="header-value purple">30</span>
|
||||
<span class="header-unit">万吨</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">设备总数</span>
|
||||
<span class="header-value green">500</span>
|
||||
<span class="header-unit">台</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contents">
|
||||
<div class="content">
|
||||
<div class="content-left">
|
||||
<div class="first">
|
||||
<div class="first-total">
|
||||
<div class="first-total-title">今日用电量</div>
|
||||
<div class="first-total-value">
|
||||
<div class="first-total-value-number">1</div>
|
||||
<div class="first-total-value-number">2</div>
|
||||
<div class="first-total-value-number">3</div>
|
||||
</div>
|
||||
<div class="first-total-unit">kwh</div>
|
||||
</div>
|
||||
<div ref="barChart" class="bar-chart" style="width:100%;height:80%;"></div>
|
||||
</div>
|
||||
<div class="second">
|
||||
<div ref="powerChart" class="power-chart"></div>
|
||||
</div>
|
||||
<div class="third">
|
||||
<div class="env-cards">
|
||||
<div class="env-card">
|
||||
<div class="env-card-content">
|
||||
<div class="env-title">PM2.5(μg/m³)</div>
|
||||
<div class="env-value">5</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="env-card">
|
||||
<div class="env-card-content">
|
||||
<div class="env-title">PM10(μg/m³)</div>
|
||||
<div class="env-value">120</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="env-card">
|
||||
<div class="env-card-content">
|
||||
<div class="env-title">噪声(dB(A))</div>
|
||||
<div class="env-value">50</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="env-card">
|
||||
<div class="env-card-content">
|
||||
<div class="env-title">一氧化碳(ppm)</div>
|
||||
<div class="env-value">10</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="envChart" class="env-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-center">
|
||||
<div class="content-center-first">
|
||||
<div class="first-item">
|
||||
<div class="item"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-center-second">
|
||||
<div class="second-item">
|
||||
<div class="second-item-text">645</div>
|
||||
<div class="second-item-text">729</div>
|
||||
<div class="second-item-text">648</div>
|
||||
<div class="second-item-text">786</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-right">
|
||||
<div class="first">
|
||||
<div class="first-total">
|
||||
<div class="first-total-title">今日用水量</div>
|
||||
<div class="first-total-value">
|
||||
<div class="first-total-value-number">1</div>
|
||||
<div class="first-total-value-number">2</div>
|
||||
<div class="first-total-value-number">3</div>
|
||||
</div>
|
||||
<div class="first-total-unit">吨</div>
|
||||
</div>
|
||||
<div ref="pie3dChart" class="pie3d-chart"></div>
|
||||
</div>
|
||||
<div class="second">
|
||||
<div ref="waterChart" class="water-chart"></div>
|
||||
</div>
|
||||
<div class="third">
|
||||
<div class="device-cards">
|
||||
<div class="device-card1">
|
||||
<div>
|
||||
<img src="../../../assets/energyConsumptionAnalysis/devices-number-icon.png" style="width: 1.75rem;height: 1.75rem;" alt="">
|
||||
</div>
|
||||
<div class="device-card-text-box">
|
||||
<div class="device-card-text">设备总数</div>
|
||||
<div class="device-card1-value">650</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="device-card2">
|
||||
<div>
|
||||
<img src="../../../assets/energyConsumptionAnalysis/devices-online-icon.png" style="width: 1.75rem;height: 1.75rem;" alt="">
|
||||
</div>
|
||||
<div>
|
||||
<div class="device-card-text">设备在线数</div>
|
||||
<div class="device-card2-value">632</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="device-card3">
|
||||
<div>
|
||||
<img src="../../../assets/energyConsumptionAnalysis/devices-offline-icon.png" style="width: 1.75rem;height: 1.75rem;" alt="">
|
||||
</div>
|
||||
<div>
|
||||
<div class="device-card-text">设备离线数</div>
|
||||
<div class="device-card3-value">18</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="deviceChart" class="device-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import * as echarts from 'echarts'
|
||||
import 'echarts-gl'
|
||||
import { getThreeDBarOption } from '#/utils/threeDBarOption'
|
||||
import { renderPie3DChart } from '#/utils/pie3d'
|
||||
import { addChartToResizeManager, removeChartFromResizeManager } from '#/utils/echartsResize'
|
||||
|
||||
// 路由
|
||||
const router = useRouter()
|
||||
|
||||
// 图表实例
|
||||
const barChart = ref<HTMLElement>()
|
||||
const powerChart = ref<HTMLElement>()
|
||||
const envChart = ref<HTMLElement>()
|
||||
const waterChart = ref<HTMLElement>()
|
||||
const deviceChart = ref<HTMLElement>()
|
||||
const pie3dChart = ref<HTMLElement>()
|
||||
|
||||
// 定时器
|
||||
let timer: number | null = null
|
||||
|
||||
// 星期几
|
||||
const weekDay = ref('')
|
||||
|
||||
// 图表实例
|
||||
let barChartInstance: echarts.ECharts | null = null
|
||||
let powerChartInstance: echarts.ECharts | null = null
|
||||
let envChartInstance: echarts.ECharts | null = null
|
||||
let waterChartInstance: echarts.ECharts | null = null
|
||||
let deviceChartInstance: echarts.ECharts | null = null
|
||||
let pie3dChartInstance: any = null
|
||||
|
||||
// 退出方法
|
||||
const logout = () => {
|
||||
router.push('/navigation')
|
||||
}
|
||||
|
||||
// 更新时间
|
||||
const updateTime = () => {
|
||||
const now = new Date()
|
||||
const time = now.toLocaleTimeString('zh-CN', { hour12: false })
|
||||
const date = now.getFullYear() + '.' +
|
||||
String(now.getMonth() + 1).padStart(2, '0') + '.' +
|
||||
String(now.getDate()).padStart(2, '0')
|
||||
|
||||
// 获取星期几
|
||||
const weekDays = ['日', '一', '二', '三', '四', '五', '六']
|
||||
weekDay.value = '星期' + weekDays[now.getDay()]
|
||||
|
||||
const timeElement = document.getElementById('time')
|
||||
const dateElement = document.getElementById('date')
|
||||
|
||||
if (timeElement) timeElement.innerText = time
|
||||
if (dateElement) dateElement.innerText = date
|
||||
}
|
||||
|
||||
// 初始化柱状图
|
||||
const initBarChart = () => {
|
||||
if (!barChart.value) return
|
||||
|
||||
const chart = echarts.init(barChart.value)
|
||||
const option = getThreeDBarOption({
|
||||
xData: ['A区', 'B区', 'C区', 'D区'],
|
||||
yData: [320, 452, 688, 400]
|
||||
})
|
||||
chart.setOption(option)
|
||||
barChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化电力图表
|
||||
const initPowerChart = () => {
|
||||
if (!powerChart.value) return
|
||||
|
||||
const chart = echarts.init(powerChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 40, right: 20, top: 40, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 19}, (_, i) => i + 6),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [{
|
||||
data: [80, 120, 100, 130, 150, 180, 200, 220, 250, 285, 230, 200, 180, 150, 120, 100, 80, 60, 40],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: { color: '#3ec6ff' },
|
||||
lineStyle: { width: 1 },
|
||||
areaStyle: { color: new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#32B7E9' },
|
||||
{ offset: 1, color: 'rgba(50,183,233,0)' }
|
||||
]
|
||||
), }
|
||||
}]
|
||||
}
|
||||
chart.setOption(option)
|
||||
powerChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化环境图表
|
||||
const initEnvChart = () => {
|
||||
if (!envChart.value) return
|
||||
|
||||
const chart = echarts.init(envChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 40, right: 20, top: 10, bottom: 20 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 8}, (_, i) => (i+1)*3),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '今年',
|
||||
data: [100, 200, 150, 300, 250, 200, 100, 80],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: { color: '#b388ff' },
|
||||
lineStyle: { width: 3 },
|
||||
areaStyle: { color: 'rgba(179,136,255,0.2)' },
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
},
|
||||
{
|
||||
name: '去年',
|
||||
data: [80, 120, 100, 180, 150, 120, 60, 40],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: { color: '#ffb300' },
|
||||
lineStyle: { width: 3 },
|
||||
areaStyle: { color: 'rgba(255,179,0,0.15)' },
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
envChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化水表图表
|
||||
const initWaterChart = () => {
|
||||
if (!waterChart.value) return
|
||||
|
||||
const chart = echarts.init(waterChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: {
|
||||
data: ['今日', '平均'],
|
||||
textStyle: { color: '#fff' },
|
||||
right: 20,
|
||||
top: 10,
|
||||
bottom: 10
|
||||
},
|
||||
grid: { left: 40, right: 20, top: 40, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 19}, (_, i) => i + 6),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '今日',
|
||||
data: [1.2, 2.1, 1.8, 2.5, 3.0, 3.8, 4.2, 4.5, 4.8, 4.8, 4.0, 3.2, 2.5, 2.0, 1.5, 1.0, 0.8, 0.5, 0.2],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: { color: '#3ec6ff' },
|
||||
lineStyle: { width: 1 },
|
||||
areaStyle: { color: new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#32B7E9' },
|
||||
{ offset: 1, color: 'rgba(50,183,233,0)' }
|
||||
]
|
||||
), }
|
||||
},
|
||||
{
|
||||
name: '平均',
|
||||
data: Array(19).fill(2.5),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
itemStyle: { color: '#ff6b00' },
|
||||
lineStyle: { width: 1, type: 'solid', color: '#ff6b00' }
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
waterChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化设备图表
|
||||
const initDeviceChart = () => {
|
||||
if (!deviceChart.value) return
|
||||
|
||||
const chart = echarts.init(deviceChart.value)
|
||||
const option = {
|
||||
grid: { left: 60, right: 40, top: 10, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#8697BA' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { show: false }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: ['门禁', '监控', '水表'],
|
||||
axisLine: { lineStyle: { color: '#8697BA' } },
|
||||
axisLabel: { color: '#A1B2C2' }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '设备数',
|
||||
type: 'bar',
|
||||
data: [650, 120, 40],
|
||||
barWidth: 12,
|
||||
itemStyle: {
|
||||
color: function(params: any) {
|
||||
const colors = [
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#2986B1' },
|
||||
{ offset: 1, color: '#6941FF' }
|
||||
]
|
||||
),
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#33FF99' },
|
||||
{ offset: 1, color: '#00908E' }
|
||||
]
|
||||
),
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#01B4FF' },
|
||||
{ offset: 1, color: '#0336FF' }
|
||||
]
|
||||
),
|
||||
];
|
||||
return colors[params.dataIndex]
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
deviceChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化3D饼图
|
||||
const initPie3DChart = () => {
|
||||
if (!pie3dChart.value) return
|
||||
|
||||
pie3dChartInstance = renderPie3DChart(pie3dChart.value, {
|
||||
data: [
|
||||
{ name: 'A区域', value: 20, itemStyle: { color: '#3ffbff' } },
|
||||
{ name: 'D区域', value: 20, itemStyle: { color: '#b388ff' } },
|
||||
{ name: 'C区域', value: 45, itemStyle: { color: '#ff9900' } },
|
||||
{ name: 'B区域', value: 15, itemStyle: { color: '#3f6bff' } },
|
||||
],
|
||||
hoverHeightScale: 2,
|
||||
selectOffset: 0.1,
|
||||
distance: 220,
|
||||
boxHeight: 5
|
||||
})
|
||||
if (pie3dChartInstance) {
|
||||
addChartToResizeManager(pie3dChartInstance)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时初始化
|
||||
onMounted(() => {
|
||||
updateTime()
|
||||
timer = setInterval(updateTime, 1000)
|
||||
initBarChart()
|
||||
initPowerChart()
|
||||
initEnvChart()
|
||||
initWaterChart()
|
||||
initDeviceChart()
|
||||
initPie3DChart()
|
||||
})
|
||||
|
||||
// 组件卸载时清理
|
||||
onBeforeUnmount(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
|
||||
// 从管理器中移除图表
|
||||
if (barChartInstance) {
|
||||
removeChartFromResizeManager(barChartInstance)
|
||||
barChartInstance.dispose()
|
||||
}
|
||||
if (powerChartInstance) {
|
||||
removeChartFromResizeManager(powerChartInstance)
|
||||
powerChartInstance.dispose()
|
||||
}
|
||||
if (envChartInstance) {
|
||||
removeChartFromResizeManager(envChartInstance)
|
||||
envChartInstance.dispose()
|
||||
}
|
||||
if (waterChartInstance) {
|
||||
removeChartFromResizeManager(waterChartInstance)
|
||||
waterChartInstance.dispose()
|
||||
}
|
||||
if (deviceChartInstance) {
|
||||
removeChartFromResizeManager(deviceChartInstance)
|
||||
deviceChartInstance.dispose()
|
||||
}
|
||||
if (pie3dChartInstance) {
|
||||
removeChartFromResizeManager(pie3dChartInstance)
|
||||
pie3dChartInstance.dispose()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mian{
|
||||
height: 100vh;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/bg.png");
|
||||
background-size: 100% 100%;
|
||||
background-color: #081b3a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.title {
|
||||
height: 5.375rem;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.left{
|
||||
display: flex;
|
||||
width: 14.3125rem;
|
||||
.left-first{
|
||||
padding-left: 2.3125rem;
|
||||
padding-right: 3.5rem;
|
||||
font-size: 1.875rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.left-second{
|
||||
width: 6.5rem;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.25rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
.center{
|
||||
font-size: 1.9rem;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.1em;
|
||||
text-shadow: 0 0 10px #1e90ff, 0 0 20px #1e90ff;
|
||||
}
|
||||
.right{
|
||||
width: 17.3125rem;
|
||||
display: flex;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.25rem;
|
||||
color: #FFFFFF;
|
||||
gap: .75rem;
|
||||
.logout{
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
gap: .2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.header{
|
||||
/* margin-top: 1.125rem; */
|
||||
margin-left: 4.625rem;
|
||||
margin-right: 4.25rem;
|
||||
height: 4.4rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.header-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 0 10px #0ff2, 0 0 20px #0ff2 inset;
|
||||
}
|
||||
.header-label {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
margin-right: 0.5rem;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.header-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
|
||||
.header-value.orange { color: #ffb300; }
|
||||
.header-value.green { color: #00ffb0; }
|
||||
.header-value.blue { color: #3ec6ff; }
|
||||
.header-value.purple { color: #b388ff; }
|
||||
|
||||
.header-unit {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
}
|
||||
.header div{
|
||||
width: 20.125rem;
|
||||
}
|
||||
.content{
|
||||
flex:1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0 2.25rem 2.25rem 2.25rem;
|
||||
.content-left{
|
||||
width: 32.6875rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.first{
|
||||
height: 13.9rem;
|
||||
.first-total{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 1rem 1rem 0 0;
|
||||
font-size: 1.12rem;
|
||||
color: #89EAFF;
|
||||
.first-total-title{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: 400;
|
||||
font-size: 1.12rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
.first-total-value{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.first-total-value-number{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: bold;
|
||||
font-size: 1.3rem;
|
||||
color: #89EAFF;
|
||||
line-height: 1rem;
|
||||
padding: .3rem;
|
||||
margin: 0 .1rem;
|
||||
border: .02rem solid rgba(99,145,180,0.59);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.second{
|
||||
height: 12.5rem;
|
||||
/* margin-top:2.25rem;
|
||||
margin-bottom: 2rem; */
|
||||
.power-chart {
|
||||
height: 100%;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
}
|
||||
.third{
|
||||
height: 16rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* justify-content: space-around; */
|
||||
.env-cards {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 5rem;
|
||||
margin-left: 1rem;
|
||||
margin-right: 1.1rem;
|
||||
.env-card {
|
||||
width: 6.9rem;
|
||||
height: 3.2rem;
|
||||
border-radius: 0.5rem;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
.env-card-content{
|
||||
.env-title {
|
||||
font-size: .9rem;
|
||||
font-weight: 400;
|
||||
color: #4CE2D1;
|
||||
}
|
||||
.env-value {
|
||||
font-size: .8rem;
|
||||
font-weight: bold;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.env-chart {
|
||||
height: 8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-center{
|
||||
width: 47.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.content-center-first{
|
||||
height: 31.9rem;
|
||||
.first-item{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/center-bg.png");
|
||||
background-size: 95% 95%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
}
|
||||
.content-center-second{
|
||||
height: 12.5rem;
|
||||
.second-item{
|
||||
margin-top: 6rem;
|
||||
margin-left: 3.68rem;
|
||||
margin-bottom: 6.5rem;
|
||||
margin-right: 4.9rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.second-item-text{
|
||||
width: 5.37rem;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-right{
|
||||
width: 32.68rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.first{
|
||||
height: 14.9rem;
|
||||
.first-total{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 1rem 1rem 0 0;
|
||||
font-size: 1.12rem;
|
||||
color: #89EAFF;
|
||||
.first-total-title{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: 400;
|
||||
font-size: 1.12rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
.first-total-value{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.first-total-value-number{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: bold;
|
||||
font-size: 1.3rem;
|
||||
color: #89EAFF;
|
||||
line-height: 1rem;
|
||||
padding: .3rem;
|
||||
margin: 0 .1rem;
|
||||
border: .02rem solid rgba(99,145,180,0.59);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.second{
|
||||
height: 12.5rem;
|
||||
/* margin-top:2.25rem;
|
||||
margin-bottom: 2rem; */
|
||||
.water-chart {
|
||||
height: 100%;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
}
|
||||
.third{
|
||||
height: 14rem;
|
||||
.device-cards {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 2rem;
|
||||
margin-right: 1.06rem;
|
||||
/* margin-bottom: 0.5rem; */
|
||||
margin-left: 1.31rem;
|
||||
.device-card1{
|
||||
height: 3.2rem;
|
||||
width: 7.21rem;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/devices-number.png");
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.device-card1-value{
|
||||
font-family: Arial;
|
||||
font-weight: bold;
|
||||
font-size: 1.25rem;
|
||||
color: #02B3F4;
|
||||
}
|
||||
}
|
||||
.device-card2{
|
||||
height: 3.2rem;
|
||||
width: 7.21rem;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/devices-online.png");
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.device-card2-value{
|
||||
font-family: Arial;
|
||||
font-weight: bold;
|
||||
font-size: 1.25rem;
|
||||
color: #1DE39D;
|
||||
}
|
||||
}
|
||||
.device-card3{
|
||||
height: 3.2rem;
|
||||
width: 7.21rem;
|
||||
background: url("../../../assets/energyConsumptionAnalysis/devices-offline.png");
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.device-card3-value{
|
||||
font-family: Arial;
|
||||
font-weight: bold;
|
||||
font-size: 1.25rem;
|
||||
color: #F19315;
|
||||
}
|
||||
}
|
||||
.device-card-text{
|
||||
font-family: Microsoft YaHei UI;
|
||||
font-weight: 400;
|
||||
font-size: 0.75rem;
|
||||
color: #A1B2C2;
|
||||
line-height: 0.75rem;
|
||||
}
|
||||
}
|
||||
.device-chart {
|
||||
height: 8rem;
|
||||
/* margin-top: 0.2rem; */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.pie3d-title {
|
||||
color: #fff;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
.pie3d-water {
|
||||
color: #fff;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
.pie3d-water-num {
|
||||
font-size: 1.3rem;
|
||||
color: #3ec6ff;
|
||||
background: #0a1e3a;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0 0.2rem;
|
||||
margin: 0 0.2rem;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.pie3d-chart {
|
||||
width: 100%;
|
||||
height: 176px;
|
||||
min-height: unset;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
1283
apps/web-antd/src/views/screen/monitor/index.vue
Normal file
307
apps/web-antd/src/views/screen/navigation/Navigation.vue
Normal file
@@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<div class="navigation-bg">
|
||||
<div class="navigation-head">
|
||||
<div class="navigation-head-left">超级管理员 欢迎你</div>
|
||||
<div class="navigation-head-center">南川区综合服务中心数智平台</div>
|
||||
<div class="navigation-head-right" @click="logout">
|
||||
<span>
|
||||
<!-- <img src="../../../assets/return.png" alt="退出"> -->
|
||||
退出
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navigation-body">
|
||||
<div class="navigation-body-left">
|
||||
<div @click="goToProperty()">基础物业</div>
|
||||
<div @click="goToEnergyAnalysis()">能耗分析</div>
|
||||
<div @click="goToSecurity()">安防大屏</div>
|
||||
<div @click="goToMonitor()">监控大屏</div>
|
||||
</div>
|
||||
<div class="navigation-body-center">
|
||||
<div class="top-content">
|
||||
<div class="top-content-text1"><div class="text1">数据管理</div></div>
|
||||
<div class="top-content-text2"><div class="text1">运行监控</div></div>
|
||||
<div class="top-content-text3"><div class="text1">信息管理</div></div>
|
||||
<div class="top-content-text4"><div class="text1">配置管理</div></div>
|
||||
<div class="top-content-text5"><div class="text1">登录管理</div></div>
|
||||
<div class="top-content-text6"><div class="text1">驾驶舱</div></div>
|
||||
</div>
|
||||
<div class="bottom-content">
|
||||
<div class="bottom-content-text1"><div class="text">数字建模</div></div>
|
||||
<div class="bottom-content-text2"><div class="text">指挥中心</div></div>
|
||||
<div class="bottom-content-text3"><div class="text">软件支撑</div></div>
|
||||
<div class="bottom-content-text4"><div class="text">应用管理</div></div>
|
||||
<div class="bottom-content-text5"><div class="text">数据管理</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navigation-body-right">
|
||||
<div @click="goToDigitalIntelligence()">商务中心</div>
|
||||
<div @click="goToHome()">后台管理</div>
|
||||
<div>能源管理</div>
|
||||
<div>能耗分析</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 退出方法
|
||||
const logout = () => {
|
||||
// 这里可以添加清除 token、重定向登录页等逻辑
|
||||
console.log('用户退出')
|
||||
router.push('/login') // 假设登录页面路径为 /login
|
||||
}
|
||||
//物业
|
||||
const goToProperty = () => {
|
||||
router.push('/property')
|
||||
}
|
||||
//能耗分析
|
||||
const goToEnergyAnalysis = () => {
|
||||
router.push('/energyAnalysis')
|
||||
}
|
||||
// 监控大屏
|
||||
const goToSecurity = () => {
|
||||
router.push('/security')
|
||||
}
|
||||
// 监控大屏
|
||||
const goToMonitor = () => {
|
||||
router.push('/monitor')
|
||||
}
|
||||
// 商务中心数智管理平台
|
||||
const goToDigitalIntelligence = () => {
|
||||
router.push('/digitalIntelligence')
|
||||
}
|
||||
//
|
||||
const goToHome = () => {
|
||||
router.push(preferences.app.defaultHomePath);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.navigation-bg {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
background: url("../../../assets/navigation.png");
|
||||
background-size: 100% 100%;
|
||||
color: #fff;
|
||||
text-shadow: 0 0.125rem 0.5rem #000;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.navigation-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: 3.5rem;
|
||||
}
|
||||
|
||||
.navigation-head-center{
|
||||
font-size: 1.9rem;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.navigation-head-left,
|
||||
.navigation-head-right {
|
||||
flex: 1 1 100%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
padding-left: 1.3rem;
|
||||
padding-right: 1.3rem;
|
||||
}
|
||||
|
||||
@media (min-width: 48em) {
|
||||
.navigation-head-left {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
font-size: 0.875rem;
|
||||
padding-left: 1.3rem;
|
||||
}
|
||||
.navigation-head-center {
|
||||
flex: 3;
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.navigation-head-right {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
font-size: 0.875rem;
|
||||
padding-right: 1.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.navigation-body-left {
|
||||
width: 12%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
padding: 2.25rem 1.25rem;
|
||||
height: 35rem;
|
||||
}
|
||||
|
||||
.navigation-body-left div {
|
||||
background: url("../../../assets/shine.png") no-repeat center;
|
||||
background-size: contain;
|
||||
font-size: 1rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
min-width: 7.5rem;
|
||||
max-width: 100%;
|
||||
transition: transform 0.2s ease;
|
||||
cursor: pointer;
|
||||
color: #77DAFF;
|
||||
}
|
||||
.navigation-body-left div:hover {
|
||||
color: #ffffff;
|
||||
background-image: url("../../../assets/shine-lines.png");
|
||||
}
|
||||
|
||||
.navigation-body-center{
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.top-content{
|
||||
height: 70%;
|
||||
padding-top: 26rem;
|
||||
padding-left: 19.5rem;
|
||||
padding-right: 20rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.top-content-text1{
|
||||
font-size: 1.0625rem;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 0.5rem;
|
||||
color: #86B6E6;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text2{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 1.25rem;
|
||||
color: #86ECDE;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text3{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 2.5rem;
|
||||
color: #FECFB4;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text4{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 2.625rem;
|
||||
color: #CDADF4;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text5{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 1.5625rem;
|
||||
color: #86ECDE;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-content-text6{
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
width: 4.6875rem;
|
||||
height: 7.5rem;
|
||||
padding-top: 1.25rem;
|
||||
color: #86B6E6;
|
||||
cursor: pointer;
|
||||
}
|
||||
.text1{
|
||||
padding: 0.3125rem 1.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-content{
|
||||
height: 50%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: 12.5rem;
|
||||
padding-left: 23.625rem;
|
||||
padding-right: 24.25rem;
|
||||
font-size: 1rem;
|
||||
color: #00EEFD;
|
||||
.bottom-content-text1{
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bottom-content-text2{
|
||||
padding-top: 0.75rem;
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bottom-content-text3{
|
||||
padding-top: 1.125rem;
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bottom-content-text4{
|
||||
padding-top: 0.8125rem;
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bottom-content-text5{
|
||||
width: 6.875rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.text{
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.navigation-body-right{
|
||||
width: 12%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
padding: 2.25rem 1.25rem;
|
||||
height: 35rem;
|
||||
}
|
||||
.navigation-body-right div {
|
||||
background: url("../../../assets/shine.png") no-repeat center;
|
||||
background-size: contain;
|
||||
font-size: 1rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
min-width: 7.5rem;
|
||||
max-width: 100%;
|
||||
transition: transform 0.2s ease;
|
||||
cursor: pointer;
|
||||
color: #77DAFF;
|
||||
}
|
||||
.navigation-body-right div:hover {
|
||||
background-image: url("../../../assets/shine-lines.png");
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
804
apps/web-antd/src/views/screen/property/index.vue
Normal file
@@ -0,0 +1,804 @@
|
||||
<template>
|
||||
<div class="mian">
|
||||
<div class="title">
|
||||
<div class="left">
|
||||
<div class="left-first" id="time">--:--:--</div>
|
||||
<div class="left-second" id="date">----</div>
|
||||
</div>
|
||||
<div class="center">南川区综合服务中心数智管理平台物业大屏</div>
|
||||
<div class="right">
|
||||
<div>{{ weekDay }}</div>
|
||||
<div>晴</div>
|
||||
<div>40℃</div>
|
||||
<div class="logout" @click="logout">
|
||||
<img src="../../../assets/return.png" style="width: 1.5rem; height: 1.5rem;">
|
||||
<div>
|
||||
退出
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="header-item">
|
||||
<span class="header-label">今年用电量</span>
|
||||
<span class="header-value orange">180</span>
|
||||
<span class="header-unit">亿kwh</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">本月用电总量</span>
|
||||
<span class="header-value green">1.8</span>
|
||||
<span class="header-unit">亿kwh</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">今年用水总量</span>
|
||||
<span class="header-value blue">2600</span>
|
||||
<span class="header-unit">万吨</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">本月用水总量</span>
|
||||
<span class="header-value purple">30</span>
|
||||
<span class="header-unit">万吨</span>
|
||||
</div>
|
||||
<div class="header-item">
|
||||
<span class="header-label">设备总数</span>
|
||||
<span class="header-value green">500</span>
|
||||
<span class="header-unit">台</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contents">
|
||||
<div class="content">
|
||||
<div class="content-left">
|
||||
<div class="first">
|
||||
<div ref="barChart" class="bar-chart" style="width:100%;height:100%;"></div>
|
||||
</div>
|
||||
<div class="second">
|
||||
<div ref="powerChart" class="power-chart"></div>
|
||||
</div>
|
||||
<div class="third">
|
||||
<div ref="envChart" class="env-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-center">
|
||||
<div class="content-center-first">
|
||||
<div class="first-item">
|
||||
<div class="item"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-center-second">
|
||||
<div class="second-item">
|
||||
<div class="second-item-box1">645</div>
|
||||
<div class="second-item-box2">729</div>
|
||||
<div class="second-item-box3">648</div>
|
||||
<div class="second-item-box4">786</div>
|
||||
<div class="second-item-box5">645</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-right">
|
||||
<div class="first">
|
||||
<div ref="waterChart" class="water-chart"></div>
|
||||
<!-- <div ref="pie3dChart" class="pie3d-chart"></div> -->
|
||||
</div>
|
||||
<div class="second">
|
||||
<div class="second-box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-label">服务数量</div>
|
||||
<div class="box-content-num">132</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-label">服务数量</div>
|
||||
<div class="box-content-num">862</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-label">服务数量</div>
|
||||
<div class="box-content-num">272</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div ref="waterChart" class="water-chart"></div> -->
|
||||
</div>
|
||||
<div class="third">
|
||||
<div ref="deviceChart" class="device-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import * as echarts from 'echarts'
|
||||
import 'echarts-gl'
|
||||
import { getThreeDBarOption } from '#/utils/threeDBarOption'
|
||||
import { renderPie3DChart } from '#/utils/pie3d'
|
||||
import { addChartToResizeManager, removeChartFromResizeManager } from '#/utils/echartsResize'
|
||||
|
||||
// 路由
|
||||
const router = useRouter()
|
||||
|
||||
// 图表实例
|
||||
const barChart = ref<HTMLElement>()
|
||||
const powerChart = ref<HTMLElement>()
|
||||
const envChart = ref<HTMLElement>()
|
||||
const waterChart = ref<HTMLElement>()
|
||||
const deviceChart = ref<HTMLElement>()
|
||||
const pie3dChart = ref<HTMLElement>()
|
||||
|
||||
// 定时器
|
||||
let timer: number | null = null
|
||||
|
||||
// 星期几
|
||||
const weekDay = ref('')
|
||||
|
||||
// 图表实例
|
||||
let barChartInstance: echarts.ECharts | null = null
|
||||
let powerChartInstance: echarts.ECharts | null = null
|
||||
let envChartInstance: echarts.ECharts | null = null
|
||||
let waterChartInstance: echarts.ECharts | null = null
|
||||
let deviceChartInstance: echarts.ECharts | null = null
|
||||
let pie3dChartInstance: any = null
|
||||
|
||||
// 退出方法
|
||||
const logout = () => {
|
||||
router.push('/navigation')
|
||||
}
|
||||
|
||||
// 更新时间
|
||||
const updateTime = () => {
|
||||
const now = new Date()
|
||||
const time = now.toLocaleTimeString('zh-CN', { hour12: false })
|
||||
const date = now.getFullYear() + '.' +
|
||||
String(now.getMonth() + 1).padStart(2, '0') + '.' +
|
||||
String(now.getDate()).padStart(2, '0')
|
||||
|
||||
// 获取星期几
|
||||
const weekDays = ['日', '一', '二', '三', '四', '五', '六']
|
||||
weekDay.value = '星期' + weekDays[now.getDay()]
|
||||
|
||||
const timeElement = document.getElementById('time')
|
||||
const dateElement = document.getElementById('date')
|
||||
|
||||
if (timeElement) timeElement.innerText = time
|
||||
if (dateElement) dateElement.innerText = date
|
||||
}
|
||||
|
||||
// 初始化柱状图
|
||||
const initBarChart = () => {
|
||||
if (!barChart.value) return
|
||||
|
||||
const chart = echarts.init(barChart.value)
|
||||
const option = getThreeDBarOption({
|
||||
xData: ['A区', 'B区', 'C区', 'D区'],
|
||||
yData: [320, 452, 688, 400]
|
||||
})
|
||||
chart.setOption(option)
|
||||
barChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化电力图表
|
||||
const initPowerChart = () => {
|
||||
if (!powerChart.value) return
|
||||
|
||||
const chart = echarts.init(powerChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 40, right: 20, top: 32, bottom: 20 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 19}, (_, i) => i + 6),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [{
|
||||
data: [80, 120, 100, 130, 150, 180, 200, 220, 250, 285, 230, 200, 180, 150, 120, 100, 80, 60, 40],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: { color: '#3ec6ff' },
|
||||
lineStyle: { width: 1 },
|
||||
areaStyle: { color: new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#32B7E9' },
|
||||
{ offset: 1, color: 'rgba(50,183,233,0)' }
|
||||
]
|
||||
), }
|
||||
}]
|
||||
}
|
||||
chart.setOption(option)
|
||||
powerChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化环境图表
|
||||
const initEnvChart = () => {
|
||||
if (!envChart.value) return
|
||||
|
||||
const chart = echarts.init(envChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 40, right: 20, top: 40, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 8}, (_, i) => (i+1)*3),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '今年',
|
||||
data: [100, 200, 150, 300, 250, 200, 100, 80],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: { color: '#b388ff' },
|
||||
lineStyle: { width: 3 },
|
||||
areaStyle: { color: 'rgba(179,136,255,0.2)' },
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
},
|
||||
{
|
||||
name: '去年',
|
||||
data: [80, 120, 100, 180, 150, 120, 60, 40],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
itemStyle: { color: '#ffb300' },
|
||||
lineStyle: { width: 3 },
|
||||
areaStyle: { color: 'rgba(255,179,0,0.15)' },
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
envChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化水表图表
|
||||
const initWaterChart = () => {
|
||||
if (!waterChart.value) return
|
||||
|
||||
const chart = echarts.init(waterChart.value)
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: {
|
||||
data: ['今日', '平均'],
|
||||
textStyle: { color: '#fff' },
|
||||
right: 20,
|
||||
top: 10
|
||||
},
|
||||
grid: { left: 40, right: 20, top: 40, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: 19}, (_, i) => i + 6),
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#3ec6ff' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#1e90ff22' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '今日',
|
||||
data: [1.2, 2.1, 1.8, 2.5, 3.0, 3.8, 4.2, 4.5, 4.8, 4.8, 4.0, 3.2, 2.5, 2.0, 1.5, 1.0, 0.8, 0.5, 0.2],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 0,
|
||||
itemStyle: { color: '#3ec6ff' },
|
||||
lineStyle: { width: 1 },
|
||||
areaStyle: { color: new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#32B7E9' },
|
||||
{ offset: 1, color: 'rgba(50,183,233,0)' }
|
||||
]
|
||||
), }
|
||||
},
|
||||
{
|
||||
name: '平均',
|
||||
data: Array(19).fill(2.5),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
itemStyle: { color: '#ff6b00' },
|
||||
lineStyle: { width: 1, type: 'solid', color: '#ff6b00' }
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
waterChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化设备图表
|
||||
const initDeviceChart = () => {
|
||||
if (!deviceChart.value) return
|
||||
|
||||
const chart = echarts.init(deviceChart.value)
|
||||
const option = {
|
||||
grid: { left: 60, right: 40, top: 10, bottom: 30 },
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
axisLine: { lineStyle: { color: '#8697BA' } },
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { show: false }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: ['门禁', '监控', '水表'],
|
||||
axisLine: { lineStyle: { color: '#8697BA' } },
|
||||
axisLabel: { color: '#A1B2C2' }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '设备数',
|
||||
type: 'bar',
|
||||
data: [650, 120, 40],
|
||||
barWidth: 12,
|
||||
itemStyle: {
|
||||
color: function(params: any) {
|
||||
const colors = [
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#2986B1' },
|
||||
{ offset: 1, color: '#6941FF' }
|
||||
]
|
||||
),
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#33FF99' },
|
||||
{ offset: 1, color: '#00908E' }
|
||||
]
|
||||
),
|
||||
new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1, // 上→下
|
||||
[
|
||||
{ offset: 0, color: '#01B4FF' },
|
||||
{ offset: 1, color: '#0336FF' }
|
||||
]
|
||||
),
|
||||
];
|
||||
return colors[params.dataIndex]
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
chart.setOption(option)
|
||||
deviceChartInstance = chart
|
||||
addChartToResizeManager(chart)
|
||||
}
|
||||
|
||||
// 初始化3D饼图
|
||||
const initPie3DChart = () => {
|
||||
if (!pie3dChart.value) return
|
||||
|
||||
pie3dChartInstance = renderPie3DChart(pie3dChart.value, {
|
||||
data: [
|
||||
{ name: 'A区域', value: 20, itemStyle: { color: '#3ffbff' } },
|
||||
{ name: 'D区域', value: 20, itemStyle: { color: '#b388ff' } },
|
||||
{ name: 'C区域', value: 45, itemStyle: { color: '#ff9900' } },
|
||||
{ name: 'B区域', value: 15, itemStyle: { color: '#3f6bff' } },
|
||||
],
|
||||
hoverHeightScale: 2,
|
||||
selectOffset: 0.1,
|
||||
distance: 220,
|
||||
boxHeight: 5
|
||||
})
|
||||
if (pie3dChartInstance) {
|
||||
addChartToResizeManager(pie3dChartInstance)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时初始化
|
||||
onMounted(() => {
|
||||
updateTime()
|
||||
timer = setInterval(updateTime, 1000)
|
||||
initBarChart()
|
||||
initPowerChart()
|
||||
initEnvChart()
|
||||
initWaterChart()
|
||||
initDeviceChart()
|
||||
initPie3DChart()
|
||||
})
|
||||
|
||||
// 组件卸载时清理
|
||||
onBeforeUnmount(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
|
||||
// 从管理器中移除图表
|
||||
if (barChartInstance) {
|
||||
removeChartFromResizeManager(barChartInstance)
|
||||
barChartInstance.dispose()
|
||||
}
|
||||
if (powerChartInstance) {
|
||||
removeChartFromResizeManager(powerChartInstance)
|
||||
powerChartInstance.dispose()
|
||||
}
|
||||
if (envChartInstance) {
|
||||
removeChartFromResizeManager(envChartInstance)
|
||||
envChartInstance.dispose()
|
||||
}
|
||||
if (waterChartInstance) {
|
||||
removeChartFromResizeManager(waterChartInstance)
|
||||
waterChartInstance.dispose()
|
||||
}
|
||||
if (deviceChartInstance) {
|
||||
removeChartFromResizeManager(deviceChartInstance)
|
||||
deviceChartInstance.dispose()
|
||||
}
|
||||
if (pie3dChartInstance) {
|
||||
removeChartFromResizeManager(pie3dChartInstance)
|
||||
pie3dChartInstance.dispose()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mian{
|
||||
height: 100vh;
|
||||
background: url("../../../assets/property/bg.png");
|
||||
background-size: 100% 100%;
|
||||
background-color: #081b3a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.title {
|
||||
height: 5.375rem;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.left{
|
||||
display: flex;
|
||||
width: 14.3125rem;
|
||||
.left-first{
|
||||
padding-left: 2.3125rem;
|
||||
padding-right: 3.5rem;
|
||||
font-size: 1.875rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.left-second{
|
||||
width: 6.5rem;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.25rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
.center{
|
||||
font-size: 1.9rem;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.1em;
|
||||
text-shadow: 0 0 10px #1e90ff, 0 0 20px #1e90ff;
|
||||
}
|
||||
.right{
|
||||
width: 17.3125rem;
|
||||
display: flex;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.25rem;
|
||||
color: #FFFFFF;
|
||||
gap: .75rem;
|
||||
.logout{
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
gap: .2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.header{
|
||||
margin-top: 1.125rem;
|
||||
margin-left: 4.625rem;
|
||||
margin-right: 4.25rem;
|
||||
height: 6rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.header-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 0 10px #0ff2, 0 0 20px #0ff2 inset;
|
||||
}
|
||||
.header-label {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
margin-right: 0.5rem;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.header-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
|
||||
.header-value.orange { color: #ffb300; }
|
||||
.header-value.green { color: #00ffb0; }
|
||||
.header-value.blue { color: #3ec6ff; }
|
||||
.header-value.purple { color: #b388ff; }
|
||||
|
||||
.header-unit {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
}
|
||||
.header div{
|
||||
width: 20.125rem;
|
||||
}
|
||||
.contents{
|
||||
flex: 1;
|
||||
.content{
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0 2.25rem 2.25rem 2.25rem;
|
||||
.content-left{
|
||||
width: 32.6875rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.first{
|
||||
height: 16.81rem;
|
||||
.pie3d-title {
|
||||
color: #fff;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
.pie3d-water {
|
||||
color: #fff;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
.pie3d-water-num {
|
||||
font-size: 1.3rem;
|
||||
color: #3ec6ff;
|
||||
background: #0a1e3a;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0 0.2rem;
|
||||
margin: 0 0.2rem;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.pie3d-chart {
|
||||
width: 100%;
|
||||
height: 176px;
|
||||
min-height: unset;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
.second{
|
||||
height: 12rem;
|
||||
/* margin-top:2.25rem; */
|
||||
/* margin-bottom: 2rem; */
|
||||
.power-chart {
|
||||
height: 100%;
|
||||
/* margin-top: 0.2rem; */
|
||||
}
|
||||
}
|
||||
.third{
|
||||
height: 12.12rem;
|
||||
.env-chart {
|
||||
height: 12rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-center{
|
||||
width: 47.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.content-center-first{
|
||||
height: 30.9rem;
|
||||
.first-item{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: url("../../../assets/property/center-bg.png");
|
||||
background-size: 95% 95%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
}
|
||||
.content-center-second{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 12.5rem;
|
||||
.second-item{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
.second-item-box1{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle1.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.second-item-box2{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle2.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.second-item-box3{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle3.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.second-item-box4{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle1.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.second-item-box5{
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: ShiShangZhongHeiJianTi;
|
||||
font-weight: 400;
|
||||
font-size: 1.37rem;
|
||||
color: #FFFFFF;
|
||||
background: url("../../../assets/property/personnel-duty-circle4.png");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-right{
|
||||
width: 32.68rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.first{
|
||||
height: 16.81rem;
|
||||
.water-chart {
|
||||
height: 100%;
|
||||
/* margin-top: 0.2rem; */
|
||||
}
|
||||
}
|
||||
.second{
|
||||
height: 11rem;
|
||||
margin-top:2rem;
|
||||
margin-bottom: 2rem;
|
||||
.second-box{
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
.box-content{
|
||||
height: 8rem;
|
||||
width: 8rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.box-content-label{
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: 400;
|
||||
font-size: .8rem;
|
||||
color: #C4D6FF;
|
||||
line-height: 2rem;
|
||||
}
|
||||
.box-content-num{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
background: url("../../../assets/property/customer-circle.png");
|
||||
background-size: 100% 100%;
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: 400;
|
||||
font-size: 1.2rem;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.third{
|
||||
height: 13.12rem;
|
||||
.device-chart {
|
||||
height: 10rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.pie3d-title {
|
||||
color: #fff;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
.pie3d-water {
|
||||
color: #fff;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
.pie3d-water-num {
|
||||
font-size: 1.3rem;
|
||||
color: #3ec6ff;
|
||||
background: #0a1e3a;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0 0.2rem;
|
||||
margin: 0 0.2rem;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.pie3d-chart {
|
||||
width: 100%;
|
||||
height: 176px;
|
||||
min-height: unset;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
1182
apps/web-antd/src/views/screen/security/index.vue
Normal file
@@ -28,7 +28,7 @@ export default defineConfig(async () => {
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// mock代理目标地址
|
||||
target: 'http://192.168.43.169:8080',
|
||||
target: 'http://192.168.1.101:8080',
|
||||
// target: 'http://192.168.0.108:8080',
|
||||
// target: 'http://192.168.0.106:8080',
|
||||
// target: 'http://47.109.37.87:3010',
|
||||
|