|
|
@@ -1,49 +1,78 @@
|
|
|
<template>
|
|
|
<div class="filter-group">
|
|
|
- <label class="filter-label">{{ text }}</label>
|
|
|
- <div class="filter-select" @click="handleSelect">
|
|
|
- <span class="filter-select-text">{{ selectedText }}</span>
|
|
|
- <span class="filter-select-arrow"><img src="@/assets/images/Vector.png"></span>
|
|
|
- </div>
|
|
|
+ <label v-if="text" class="filter-label">{{ text }}</label>
|
|
|
+ <div class="filter-select-wrap" :style="selectWrapStyle">
|
|
|
+ <div class="filter-select" @click="handleSelect">
|
|
|
+ <span class="filter-select-text" :title="displayText">{{ displayText }}</span>
|
|
|
+ <span class="filter-select-arrow"><img src="@/assets/images/Vector.png" alt=""></span>
|
|
|
+ </div>
|
|
|
|
|
|
- <div
|
|
|
- class="filter-select-options"
|
|
|
- v-if="showOptions"
|
|
|
- :style="optionsPanelStyle"
|
|
|
- @mouseleave="handleOptionMouseLeave"
|
|
|
- >
|
|
|
- <div class="filter-select-option" v-for="option in options" :key="option.value" @click="handleOptionClick(option)">
|
|
|
- <span class="filter-select-option-text">{{ option.text }}</span>
|
|
|
+ <div
|
|
|
+ class="filter-select-options"
|
|
|
+ v-if="showOptions"
|
|
|
+ :style="optionsPanelStyle"
|
|
|
+ @mouseleave="handleOptionMouseLeave"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="filter-select-option"
|
|
|
+ v-for="option in options"
|
|
|
+ :key="option.value"
|
|
|
+ :title="option.text"
|
|
|
+ @click="handleOptionClick(option)"
|
|
|
+ >
|
|
|
+ <span class="filter-select-option-text">{{ option.text }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { defineProps, ref, defineEmits, watch, computed } from 'vue'
|
|
|
-const emit = defineEmits(['selected'])
|
|
|
-const showOptions = ref(false)
|
|
|
+import { defineProps, ref, defineEmits, watch, computed } from 'vue';
|
|
|
+
|
|
|
+const emit = defineEmits(['selected']);
|
|
|
+const showOptions = ref(false);
|
|
|
|
|
|
const props = defineProps({
|
|
|
text: {
|
|
|
type: String,
|
|
|
+ default: '',
|
|
|
},
|
|
|
selectedText: {
|
|
|
type: String,
|
|
|
+ default: '',
|
|
|
},
|
|
|
options: {
|
|
|
type: Array,
|
|
|
default: () => [],
|
|
|
},
|
|
|
+ /** 下拉框固定宽度,数字为 px */
|
|
|
+ selectWidth: {
|
|
|
+ type: [Number, String],
|
|
|
+ default: 128,
|
|
|
+ },
|
|
|
/** 下拉选项区最大高度,超出滚动 */
|
|
|
maxOptionsHeight: {
|
|
|
type: [Number, String],
|
|
|
default: 0,
|
|
|
},
|
|
|
-})
|
|
|
+});
|
|
|
|
|
|
-const selectedText = ref(props.selectedText)
|
|
|
-const options = ref(props.options)
|
|
|
+const selectedText = ref(props.selectedText);
|
|
|
+const options = ref(props.options);
|
|
|
+
|
|
|
+const resolveWidth = (val) => (typeof val === 'number' ? `${val}px` : val);
|
|
|
+
|
|
|
+const selectWrapStyle = computed(() => {
|
|
|
+ const width = resolveWidth(props.selectWidth);
|
|
|
+ return {
|
|
|
+ width,
|
|
|
+ minWidth: width,
|
|
|
+ maxWidth: width,
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+const displayText = computed(() => props.selectedText || selectedText.value || '');
|
|
|
|
|
|
const optionsPanelStyle = computed(() => {
|
|
|
if (!props.maxOptionsHeight) return undefined;
|
|
|
@@ -51,21 +80,21 @@ const optionsPanelStyle = computed(() => {
|
|
|
? `${props.maxOptionsHeight}px`
|
|
|
: props.maxOptionsHeight;
|
|
|
return { maxHeight: height };
|
|
|
-})
|
|
|
+});
|
|
|
|
|
|
const handleSelect = () => {
|
|
|
- showOptions.value = !showOptions.value
|
|
|
-}
|
|
|
+ showOptions.value = !showOptions.value;
|
|
|
+};
|
|
|
|
|
|
const handleOptionMouseLeave = () => {
|
|
|
- showOptions.value = false
|
|
|
-}
|
|
|
+ showOptions.value = false;
|
|
|
+};
|
|
|
|
|
|
const handleOptionClick = (option) => {
|
|
|
- selectedText.value = option.text
|
|
|
- showOptions.value = false
|
|
|
- emit('selected', option.value)
|
|
|
-}
|
|
|
+ selectedText.value = option.text;
|
|
|
+ showOptions.value = false;
|
|
|
+ emit('selected', option.value);
|
|
|
+};
|
|
|
|
|
|
watch(() => props.selectedText, (newVal) => {
|
|
|
selectedText.value = newVal;
|
|
|
@@ -79,14 +108,14 @@ watch(() => props.options, (newVal) => {
|
|
|
<style scoped>
|
|
|
.filter-group {
|
|
|
height: 36px;
|
|
|
- position: relative;
|
|
|
+ position: relative;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 8.01px;
|
|
|
+ gap: 8px;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.filter-label {
|
|
|
- /* width: 59px; */
|
|
|
height: 16px;
|
|
|
font-family: var(--font-regular);
|
|
|
font-weight: 400;
|
|
|
@@ -96,58 +125,70 @@ watch(() => props.options, (newVal) => {
|
|
|
vertical-align: middle;
|
|
|
color: var(--color-text-primary);
|
|
|
white-space: nowrap;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-select-wrap {
|
|
|
+ position: relative;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.filter-select {
|
|
|
+ width: 100%;
|
|
|
height: 36px;
|
|
|
border-radius: var(--radius-sm);
|
|
|
- border-width: 1px;
|
|
|
padding: 8px 12px;
|
|
|
gap: 8px;
|
|
|
background: var(--color-surface);
|
|
|
border: 1px solid var(--color-input-border);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- justify-content: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ box-sizing: border-box;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
|
|
|
-.filter-select-text{
|
|
|
+.filter-select-text {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
height: 16px;
|
|
|
font-family: var(--font-regular);
|
|
|
font-weight: 400;
|
|
|
font-size: 14px;
|
|
|
line-height: 16px;
|
|
|
- letter-spacing: 0px;
|
|
|
- vertical-align: middle;
|
|
|
+ letter-spacing: 0;
|
|
|
color: var(--color-text-body);
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
|
|
|
|
-.filter-select-arrow{
|
|
|
+.filter-select-arrow {
|
|
|
width: 18px;
|
|
|
height: 18px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- justify-content: center;
|
|
|
+ justify-content: center;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
-.filter-select-options{
|
|
|
+.filter-select-options {
|
|
|
position: absolute;
|
|
|
top: calc(100% + 4px);
|
|
|
left: 0;
|
|
|
- min-width: 100%;
|
|
|
- width: max-content;
|
|
|
- max-width: 280px;
|
|
|
+ width: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
background: var(--color-surface);
|
|
|
border: 1px solid var(--color-input-border);
|
|
|
border-radius: var(--radius-sm);
|
|
|
padding: 4px 0;
|
|
|
z-index: 1000;
|
|
|
overflow-y: auto;
|
|
|
+ overflow-x: hidden;
|
|
|
box-shadow: var(--shadow-dropdown);
|
|
|
}
|
|
|
|
|
|
-.filter-select-option{
|
|
|
+.filter-select-option {
|
|
|
min-height: 36px;
|
|
|
border-radius: 0;
|
|
|
padding: 8px 12px;
|
|
|
@@ -156,6 +197,7 @@ watch(() => props.options, (newVal) => {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
cursor: pointer;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
|
|
|
.filter-select-option:last-child {
|
|
|
@@ -166,14 +208,18 @@ watch(() => props.options, (newVal) => {
|
|
|
background: var(--color-accent-hover);
|
|
|
}
|
|
|
|
|
|
-.filter-select-option-text{
|
|
|
+.filter-select-option-text {
|
|
|
+ width: 100%;
|
|
|
+ min-width: 0;
|
|
|
height: 16px;
|
|
|
font-family: var(--font-regular);
|
|
|
font-weight: 400;
|
|
|
font-size: 14px;
|
|
|
line-height: 16px;
|
|
|
- letter-spacing: 0px;
|
|
|
- vertical-align: middle;
|
|
|
+ letter-spacing: 0;
|
|
|
color: var(--color-text-body);
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|