Merge branch 'szq' of http://192.168.110.2:3000/taoge_xiaodi/maintenance_system into lx
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
151
src/components/AutoScroll/index.vue
Normal file
@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div class="auto-scroll-container" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" ref="container">
|
||||
<div class="auto-scroll-content" ref="content" :style="{ transform: scrollEnabled ? `translate3d(0, ${Math.round(scrollY)}px, 0)` : 'none' }">
|
||||
<div class="auto-scroll-item" :class="safety ? 'safety' : ''" v-for="(item, index) in displayList" :key="index">
|
||||
<slot :item="item" :index="index">{{ item }}</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
interface Props {
|
||||
items: any[];
|
||||
speed?: number;
|
||||
height?: number;
|
||||
minItems?: number;
|
||||
autoScroll?: boolean;
|
||||
safety?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
speed: 0.4,
|
||||
minItems: 2,
|
||||
autoScroll: true
|
||||
});
|
||||
|
||||
const container = ref<HTMLElement | null>(null);
|
||||
const content = ref<HTMLElement | null>(null);
|
||||
|
||||
let scrollY = 0;
|
||||
let animationFrameId: number | null = null;
|
||||
let manualPaused = false;
|
||||
let manualControl = false;
|
||||
|
||||
// 是否满足滚动条件
|
||||
const scrollEnabled = computed(() => props.items.length >= props.minItems);
|
||||
|
||||
// 展示数据列表(数据足够时复制一份)
|
||||
const displayList = computed(() => (scrollEnabled.value ? [...props.items, ...props.items] : props.items));
|
||||
|
||||
// 修正 scrollY 范围
|
||||
function normalizeScrollY(contentHeight: number) {
|
||||
scrollY = (((scrollY % contentHeight) + contentHeight) % contentHeight) - contentHeight;
|
||||
}
|
||||
|
||||
// 滚动逻辑
|
||||
function step() {
|
||||
if (!content.value) return;
|
||||
const contentHeight = content.value.offsetHeight / 2;
|
||||
scrollY -= props.speed;
|
||||
normalizeScrollY(contentHeight);
|
||||
content.value.style.transform = `translate3d(0, ${Math.round(scrollY)}px, 0)`;
|
||||
animationFrameId = requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
function startScroll() {
|
||||
if (!animationFrameId) {
|
||||
animationFrameId = requestAnimationFrame(step);
|
||||
}
|
||||
}
|
||||
|
||||
function pauseScroll() {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
animationFrameId = null;
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseEnter() {
|
||||
if (!manualControl) pauseScroll();
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
if (!manualControl && props.autoScroll && scrollEnabled.value) startScroll();
|
||||
}
|
||||
|
||||
function onWheel(e: WheelEvent) {
|
||||
if (!content.value || !container.value) return;
|
||||
|
||||
const contentHeight = content.value.offsetHeight / (scrollEnabled.value ? 2 : 1);
|
||||
const containerHeight = container.value.offsetHeight;
|
||||
|
||||
if (contentHeight <= containerHeight) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
manualPaused = true;
|
||||
pauseScroll();
|
||||
scrollY -= e.deltaY;
|
||||
normalizeScrollY(contentHeight);
|
||||
content.value.style.transform = `translate3d(0, ${Math.round(scrollY)}px, 0)`;
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
if (scrollEnabled.value && props.autoScroll) {
|
||||
startScroll();
|
||||
}
|
||||
container.value?.addEventListener('wheel', onWheel, { passive: false });
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
pauseScroll();
|
||||
container.value?.removeEventListener('wheel', onWheel);
|
||||
});
|
||||
|
||||
// 监听数据是否滚动条件满足
|
||||
watch(scrollEnabled, (newVal) => {
|
||||
if (!newVal) {
|
||||
pauseScroll();
|
||||
scrollY = 0;
|
||||
if (content.value) content.value.style.transform = 'none';
|
||||
} else if (props.autoScroll && !manualPaused && !manualControl) {
|
||||
startScroll();
|
||||
}
|
||||
});
|
||||
|
||||
// 暴露控制方法
|
||||
defineExpose({
|
||||
pause: (): void => {
|
||||
manualControl = true;
|
||||
pauseScroll();
|
||||
},
|
||||
resume: (): void => {
|
||||
manualControl = false;
|
||||
if (scrollEnabled.value) startScroll();
|
||||
},
|
||||
reset: (): void => {
|
||||
scrollY = 0;
|
||||
if (content.value) {
|
||||
content.value.style.transform = 'translate3d(0, 0, 0)';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.auto-scroll-container {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.auto-scroll-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
will-change: transform;
|
||||
}
|
||||
.safety:nth-child(odd) {
|
||||
background-color: rgba(67, 226, 203, 0.2);
|
||||
}
|
||||
</style>
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="right_box">
|
||||
<div class="data_box">
|
||||
<div class="top_box">
|
||||
<Title title="年发电量" />
|
||||
<div class="echarts">
|
||||
<EchartBoxTwo :option="option_fdssgl" ref="barChart" />
|
||||
@ -47,19 +47,20 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import EchartBoxTwo from '@/components/EchartBox/index.vue';
|
||||
import AutoScroller from '@/components/AutoScroll/index.vue';
|
||||
import { option1 } from './options';
|
||||
import Title from './title.vue';
|
||||
import { dayjs } from 'element-plus';
|
||||
|
||||
const option_fdssgl = ref(option1);
|
||||
|
||||
const handleWheel = (event: WheelEvent) => {
|
||||
const errorList = (event.target as HTMLElement).closest('.error_list');
|
||||
|
||||
if (errorList) {
|
||||
event.preventDefault(); // 阻止页面或父元素的垂直滚动
|
||||
errorList.scrollLeft += event.deltaY; // 横向滚动
|
||||
}
|
||||
};
|
||||
const bjList = ref([
|
||||
{createTime: '2025-11-11 11:11:11'},
|
||||
{createTime: '2025-11-11 11:11:11'},
|
||||
{createTime: '2025-11-11 11:11:11'},
|
||||
{createTime: '2025-11-11 11:11:11'},
|
||||
{createTime: '2025-11-11 11:11:11'},
|
||||
])
|
||||
const activebj = ref('')
|
||||
|
||||
const reszieFont = () => {
|
||||
const fontSize = Math.sqrt(window.innerWidth) / 3;
|
||||
@ -70,14 +71,10 @@ const reszieFont = () => {
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', reszieFont);
|
||||
|
||||
window.addEventListener('wheel', handleWheel, { passive: false });
|
||||
// passive: false 是关键,允许我们在事件处理函数中调用 event.preventDefault()
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', reszieFont);
|
||||
window.removeEventListener('wheel', handleWheel);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 月份选择器 -->
|
||||
<div class="picker-group">
|
||||
<div class="picker-input" @click="isMonthOpen = !isMonthOpen" :class="{ 'open': isMonthOpen }">
|
||||
@ -205,7 +204,7 @@ $vh_base: 1080;
|
||||
right: 0;
|
||||
max-height: vh(200); /* 限制最大高度并可滚动 */
|
||||
overflow-y: auto;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: vw(4);
|
||||
box-shadow: 0 vh(4) vh(12) rgba(0, 0, 0, 0.15); /* 更明显的阴影 */
|
||||
list-style: none;
|
||||
@ -229,7 +228,7 @@ $vh_base: 1080;
|
||||
}
|
||||
|
||||
.options li.selected {
|
||||
// background-color: #e6f7ff;
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="left">
|
||||
<span class="top">
|
||||
<span class="num">365</span>
|
||||
<span class="unit">day</span>
|
||||
<!-- <span class="unit">day</span> -->
|
||||
</span>
|
||||
<span class="desc">
|
||||
运行天数
|
||||
@ -103,7 +103,6 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import SmallTitle from './smalltitle.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
// 模拟数据量,用于拖动条渲染
|
||||
const operationData = ref({
|
||||
@ -118,31 +117,31 @@ const infoList = ref([
|
||||
name: '变压器220KV',
|
||||
value: 564,
|
||||
unit: '(台)',
|
||||
icon: 'src/assets/ueimg/1.png',
|
||||
icon: '/assets/ue1.png',
|
||||
},
|
||||
{
|
||||
name: '开关柜220KV',
|
||||
value: 100,
|
||||
unit: '(台)',
|
||||
icon: 'src/assets/ueimg/2.png',
|
||||
icon: '/assets/ue2.png',
|
||||
},
|
||||
{
|
||||
name: '接地变110v',
|
||||
value: 564,
|
||||
unit: '(台)',
|
||||
icon: 'src/assets/ueimg/3.png',
|
||||
icon: '/assets/ue3.png',
|
||||
},
|
||||
{
|
||||
name: '电缆110v',
|
||||
value: 768,
|
||||
unit: '(回)',
|
||||
icon: 'src/assets/ueimg/4.png',
|
||||
icon: '/assets/ue4.png',
|
||||
},
|
||||
{
|
||||
name: 'GIS',
|
||||
value: 100,
|
||||
unit: '(个)',
|
||||
icon: 'src/assets/ueimg/5.png',
|
||||
icon: '/assets/ue5.png',
|
||||
},
|
||||
])
|
||||
|
||||
@ -389,19 +388,19 @@ const powerGenerationData = ref([
|
||||
|
||||
.infoName {
|
||||
margin-left: vw(17);
|
||||
font-size: vh(20);
|
||||
font-size: vw(16);
|
||||
width: vw(200);
|
||||
height: vh(28);
|
||||
}
|
||||
|
||||
.infoValue {
|
||||
margin-left: vw(62);
|
||||
font-size: vh(20);
|
||||
font-size: vw(16);
|
||||
}
|
||||
|
||||
.infoUnit {
|
||||
margin-left: vw(34);
|
||||
font-size: vh(18);
|
||||
font-size: vw(16);
|
||||
color: rgba(125, 255, 253, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,7 +115,8 @@ $vh_base: 1080;
|
||||
// 关键修改:设置一个最大宽度,防止文本过长导致容器被撑破
|
||||
// 你可以根据需要调整这个值,vw(300) 表示在1920px宽的屏幕下,最大宽度是300px
|
||||
max-width: 12vw;
|
||||
width: 100%; /* 让输入框尽可能利用可用空间,但不超过max-width */
|
||||
width: 100%;
|
||||
/* 让输入框尽可能利用可用空间,但不超过max-width */
|
||||
display: flex;
|
||||
gap: vw(8);
|
||||
justify-content: space-between;
|
||||
@ -123,16 +124,21 @@ $vh_base: 1080;
|
||||
padding: vh(8) vw(16);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
box-sizing: border-box; /* 确保padding不会增加元素总宽度 */
|
||||
box-sizing: border-box;
|
||||
/* 确保padding不会增加元素总宽度 */
|
||||
}
|
||||
|
||||
.picker-input .value {
|
||||
color: #fff;
|
||||
// 关键修改:添加文本省略样式
|
||||
white-space: nowrap; /* 强制文本在一行显示 */
|
||||
overflow: hidden; /* 隐藏溢出的文本 */
|
||||
text-overflow: ellipsis; /* 显示省略号 */
|
||||
flex-grow: 1; /* 让 .value 元素占据可用空间,将箭头推到最右边 */
|
||||
white-space: nowrap;
|
||||
/* 强制文本在一行显示 */
|
||||
overflow: hidden;
|
||||
/* 隐藏溢出的文本 */
|
||||
text-overflow: ellipsis;
|
||||
/* 显示省略号 */
|
||||
flex-grow: 1;
|
||||
/* 让 .value 元素占据可用空间,将箭头推到最右边 */
|
||||
}
|
||||
|
||||
.picker-input.open .arrow {
|
||||
@ -155,11 +161,13 @@ $vh_base: 1080;
|
||||
top: 110%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-height: vh(200); /* 限制最大高度并可滚动 */
|
||||
max-height: vh(200);
|
||||
/* 限制最大高度并可滚动 */
|
||||
overflow-y: auto;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: vw(4);
|
||||
box-shadow: 0 vh(4) vh(12) rgba(0, 0, 0, 0.15); /* 更明显的阴影 */
|
||||
box-shadow: 0 vh(4) vh(12) rgba(0, 0, 0, 0.15);
|
||||
/* 更明显的阴影 */
|
||||
list-style: none;
|
||||
z-index: 10;
|
||||
padding: vh(4) 0;
|
||||
@ -181,6 +189,7 @@ $vh_base: 1080;
|
||||
}
|
||||
|
||||
.options li.selected {
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@ -49,9 +49,9 @@ import EchartBoxTwo from '@/components/EchartBox/index.vue';
|
||||
import { option1, option2, option3 } from './options';
|
||||
import Smalltitle from './smalltitle.vue';
|
||||
|
||||
const option_fdssgl = ref(option1);
|
||||
const option_fdzlqs = ref(option2);
|
||||
const option_dzfhqx = ref(option3);
|
||||
const option_fdssgl = ref(null);
|
||||
const option_fdzlqs = ref(null);
|
||||
const option_dzfhqx = ref(null);
|
||||
const errorListRef = ref<HTMLElement | null>(null);
|
||||
const errorList = ref([1, 2, 3, 4, 5, 6, 7]);
|
||||
|
||||
@ -67,23 +67,27 @@ const handleWheel = (event: WheelEvent) => {
|
||||
const reszieFont = () => {
|
||||
const fontSize = Math.sqrt(window.innerWidth) / 3;
|
||||
|
||||
option_fdssgl.value.xAxis.axisLabel.fontSize = fontSize;
|
||||
option_fdssgl.value.yAxis.axisLabel.fontSize = fontSize;
|
||||
option_fdssgl.value.legend.textStyle.fontSize = fontSize;
|
||||
option1.xAxis.axisLabel.fontSize = fontSize;
|
||||
option1.yAxis.axisLabel.fontSize = fontSize;
|
||||
option1.legend.textStyle.fontSize = fontSize;
|
||||
|
||||
option_fdzlqs.value.xAxis.axisLabel.fontSize = fontSize;
|
||||
option_fdzlqs.value.yAxis.axisLabel.fontSize = fontSize;
|
||||
option_fdzlqs.value.legend.textStyle.fontSize = fontSize;
|
||||
option_fdzlqs.value.yAxis.nameTextStyle.fontSize = fontSize;
|
||||
option2.xAxis.axisLabel.fontSize = fontSize;
|
||||
option2.yAxis.axisLabel.fontSize = fontSize;
|
||||
option2.legend.textStyle.fontSize = fontSize;
|
||||
option2.yAxis.nameTextStyle.fontSize = fontSize;
|
||||
|
||||
option_dzfhqx.value.xAxis.axisLabel.fontSize = fontSize;
|
||||
option_dzfhqx.value.yAxis.axisLabel.fontSize = fontSize;
|
||||
option_dzfhqx.value.legend.textStyle.fontSize = fontSize;
|
||||
option3.xAxis.axisLabel.fontSize = fontSize;
|
||||
option3.yAxis.axisLabel.fontSize = fontSize;
|
||||
option3.legend.textStyle.fontSize = fontSize;
|
||||
|
||||
option_fdssgl.value = option1;
|
||||
option_fdzlqs.value = option2;
|
||||
option_dzfhqx.value = option3;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
reszieFont();
|
||||
window.addEventListener('resize', reszieFont);
|
||||
|
||||
window.addEventListener('wheel', handleWheel, { passive: false });
|
||||
// passive: false 是关键,允许我们在事件处理函数中调用 event.preventDefault()
|
||||
});
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
<div class="ueScreen">
|
||||
<Header :isFull="isFull" @changePage="handleChangePage" />
|
||||
<div class="content_box">
|
||||
<LeftPage class="left" :style="{ left: isHideOther ? '-25vw' : '0' }" />
|
||||
<RightPage class="right" :style="{ right: isHideOther ? '-25vw' : '0' }" />
|
||||
<LeftPage class="left" :style="{ left: isHideOther ? '-25vw' : '1vw' }" />
|
||||
<RightPage class="right" :style="{ right: isHideOther ? '-25vw' : '1vw' }" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -20,7 +20,6 @@ import usePermissionStore from '@/store/modules/permission';
|
||||
import to from 'await-to-js';
|
||||
|
||||
const userStore = useUserStoreHook();
|
||||
const initDone = ref(false); // ✅ 控制页面是否渲染
|
||||
const isHideOther = ref(false);
|
||||
const isFull = ref(false);
|
||||
|
||||
@ -38,12 +37,12 @@ const handleChangePage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 调用静默登录接口
|
||||
const silentLogin = async (isExpired = false) => {
|
||||
const token = getToken();
|
||||
if (token && !isExpired) return true;
|
||||
|
||||
try {
|
||||
// 调用静默登录接口
|
||||
await to(
|
||||
userStore.login({
|
||||
username: 'admin',
|
||||
@ -62,12 +61,9 @@ const silentLogin = async (isExpired = false) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
// ✅ 页面初始化逻辑
|
||||
const initPage = async () => {
|
||||
const logged = await silentLogin();
|
||||
if (!logged) return;
|
||||
|
||||
initDone.value = true; // ✅ 准备完成
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
||||