feat【能源场景】:新增边框组件 导入方式import Background from '../Background/index.vue'

This commit is contained in:
Free-sss 2025-09-05 12:58:19 +08:00
parent b0aea5a9e0
commit 10128c3498
5 changed files with 378 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,378 @@
<template>
<div class="go-energy-border" ref="containerRef">
<div class="background container flex" :style="{ backgroundImage: 'url(' + currentBackground + ')' }">
<div class="header flex">
<span class="title">
{{ titleText }}
</span>
<!-- 平行按钮模式 -->
<div v-if="props.selectOptionMode === 1" class="button-content">
<span class="button" v-for="item in localSelectOptions.dataset" :key="item.value"
:class="{ 'selected': item.value === localSelectOptions.selectedValue }" @click="handleSelect(item.value)">
{{ item.label }}
</span>
</div>
<!-- 下拉菜单模式 -->
<div class="button-content" v-if="props.selectOptionMode === 2 || props.selectOptionMode === 4">
<!-- 下拉按钮容器 -->
<div class="dropdown-wrapper">
<!-- 下拉按钮使用激活的button样式 -->
<span class="button selected dropdown-trigger" @click.stop="toggleDropdown">
<span>{{ getSelectedLabel(1) }}</span>
<span class="custom-arrow" :class="{ 'rotate': isDropdownOpen }"></span>
</span>
<!-- 下拉选项列表 -->
<div class="dropdown-menu" v-if="isDropdownOpen">
<span class="button down" v-for="item in localSelectOptions.dataset" :key="item.value"
:class="{ 'selected': item.value === localSelectOptions.selectedValue }"
@click.stop="handleSelect(item.value)">
{{ item.label }}
</span>
</div>
</div>
</div>
</div>
<div class="content flex">
<!-- 绝对定位容器内按钮 -->
<div class="in-button-container" v-if="selectOptionMode == 3 || selectOptionMode === 4">
<div class="button background" v-for="item in localInSelectOptions.dataset" :key="item.value"
:class="{ 'selected': item.value === localInSelectOptions.selectedValue }" :style="{
backgroundImage: `url(${item.value === localInSelectOptions.selectedValue ? selectedIMG : unselectIMG})`
}" @click.stop="handleInSelect(item.value)">
{{ item.label }}
</div>
</div>
<slot></slot>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, computed, watch } from 'vue';
import background_L from './assets/background_L.png'
import background_XXL from './assets/background_XXL.png'
import unselectIMG from './assets/unselect.png'
import selectedIMG from './assets/selected.png'
const props = defineProps({
titleText: {
type: String,
required: true
},
selectOptionMode: {
type: Number,
required: false,
default: 0
},
selectOptions: {
type: Object as () => {
selectedValue: string;
dataset: Array<{ label: string; value: string }>;
},
required: false,
default: () => ({
selectedValue: 'electricsupply',
dataset: [
{ label: '电力', value: 'electricsupply' },
{ label: '供水', value: 'watersupply' },
{ label: '燃气', value: 'gassupply' }
]
})
},
inSelectOptions: {
type: Object as () => {
selectedValue: string;
dataset: Array<{ label: string; value: string }>;
},
required: false,
default: () => ({
selectedValue: 'consumption',
dataset: [
{ label: '消耗量', value: 'consumption' },
{ label: '费用', value: 'cost' },
{ label: '最大流量', value: 'maximumFlow' }
]
})
}
});
const localSelectOptions = ref({ ...props.selectOptions });
const localInSelectOptions = ref({ ...props.inSelectOptions });
const isDropdownOpen = ref(false);
const containerWidth = ref(0)
const containerRef = ref<HTMLElement | null>(null)
let resizeObserver: ResizeObserver | null = null
const emit = defineEmits<{
(e: 'mode1-2-select', value: string): void;
(e: 'mode3-select', value: string): void;
}>();
//
const getSelectedLabel = (mode: number) => {
if (mode === 1) {
return localSelectOptions.value.dataset.find(
item => item.value === localSelectOptions.value.selectedValue
)?.label ?? '选择数据';
}
else {
return localInSelectOptions.value.dataset.find(
item => item.value === localInSelectOptions.value.selectedValue
)?.label ?? '选择数据';
}
}
//
const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value;
}
//
const handleSelect = (value: string) => {
localSelectOptions.value.selectedValue = value;
emit('mode1-2-select', value);
isDropdownOpen.value = false;
};
const handleInSelect = (value: string) => {
localInSelectOptions.value.selectedValue = value;
emit('mode3-select', value);
};
//
const currentBackground = computed(() => {
return containerWidth.value > 800 ? background_XXL : background_L
})
//
const handleClickOutside = (event: Event) => {
if (props.selectOptionMode === 2 && isDropdownOpen.value) {
const target = event.target as HTMLElement;
if (!target.closest('.dropdown-wrapper')) {
isDropdownOpen.value = false;
}
}
}
//
const observeContainerSize = () => {
if (containerRef.value) {
containerWidth.value = containerRef.value.offsetWidth
if (resizeObserver) {
resizeObserver.disconnect();
}
resizeObserver = new ResizeObserver(entries => {
containerWidth.value = entries[0].contentRect.width
})
resizeObserver.observe(containerRef.value)
}
}
watch(() => props.selectOptions,
(newVal) => {
localSelectOptions.value = { ...newVal };
},
{ deep: true }
);
watch(() => props.inSelectOptions,
(newVal) => {
localInSelectOptions.value = { ...newVal };
},
{ deep: true }
);
//
watch(() => props.selectOptionMode, () => {
isDropdownOpen.value = false;
});
//
onMounted(() => {
document.addEventListener('click', handleClickOutside)
observeContainerSize()
})
//
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside)
resizeObserver?.disconnect()
})
</script>
<style scoped>
.go-energy-border {
width: 100%;
height: 100%;
.flex {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.background {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.container {
padding: 0 5px 10px 15px;
box-sizing: border-box;
.header {
height: 64px;
flex-direction: row;
position: relative;
/* background-color: #B9F1FE; */
.title {
font-size: 22px;
flex-grow: 1;
text-align: left;
padding-left: 28px;
color: #eee;
letter-spacing: 3px;
margin-bottom: 4px;
}
.button-content {
display: flex;
position: relative;
}
.button {
font-size: 16px;
padding: 9.5px 7px;
color: #2a99ff;
letter-spacing: 1px;
margin-left: 1px;
cursor: pointer;
display: inline-block;
justify-content: center;
box-sizing: border-box;
text-align: center;
user-select: none;
&:hover {
box-shadow: #2a99ff 0px 0px 10px;
}
&:active {
transform: scale(0.98);
background-color: rgba(22, 124, 232, 0.2);
box-shadow: #2a99ff 0px 0px 5px;
}
&.selected {
background: linear-gradient(to top, #167CE8, transparent);
color: #f8f8f8;
border-bottom: #B9F1FE 3px solid;
}
}
/* 下拉菜单样式 */
.dropdown-wrapper {
position: relative;
}
.dropdown-trigger {
padding-left: 10px;
display: inline-flex;
justify-content: center;
align-items: center;
gap: 6px;
}
.custom-arrow {
display: inline-block;
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 5px solid #f8f8f8;
transition: transform 0.1s ease;
}
/* 旋转类:箭头向上(对应下拉菜单展开) */
.custom-arrow.rotate {
transform: rotate(180deg);
/* 旋转180度箭头方向反转 */
}
.fa-chevron-down {
transition: transform 0.2s ease;
font-size: 12px;
margin-left: 5px;
}
.fa-chevron-down.rotate {
transform: rotate(180deg);
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 1px;
z-index: 100;
display: flex;
flex-direction: column;
min-width: 100%;
}
.down {
background: linear-gradient(to top, #167CE8, transparent);
box-shadow: #0a5685 0 0 5px;
color: #f8f8f8;
}
.dropdown-menu .button {
margin-left: 0;
margin-top: 1px;
width: 100%;
}
}
.content {
position: relative;
box-sizing: border-box;
/* background-color: #167CE8; */
/* margin-top: 10px; */
.in-button-container {
display: flex;
flex-direction: row;
height: 22px;
width: fit-content;
padding: 10px;
position: absolute;
top: 0px;
right: 0px;
z-index: 10;
.button {
margin-left: 10px;
user-select: none;
cursor: pointer;
font-size: 12px;
padding: 2px 12px;
background-size: 100% 100%;
color: #cbcbcb;
&.selected {
color: #ffffff;
}
}
}
}
}
}
</style>