<script lang="ts" setup>
|
import type { TrendPopupOption } from '../helpers/trendOptionPopup';
|
|
import { computed, ref } from 'vue';
|
|
import { ChevronDown, Search } from '@vben/icons';
|
|
import { Empty as AEmpty, Modal as AModal } from 'ant-design-vue';
|
|
import { filterTrendOptions, getTrendOptionSummary, normalizeTrendOptionValue } from '../helpers/trendOptionPopup';
|
|
defineOptions({ name: 'SxtTrendOptionPopup' });
|
|
const props = withDefaults(
|
defineProps<{
|
disabled?: boolean;
|
multiple?: boolean;
|
options: TrendPopupOption[];
|
placeholder?: string;
|
popupTitle: string;
|
value?: string | string[];
|
}>(),
|
{
|
disabled: false,
|
multiple: false,
|
placeholder: '请选择',
|
value: undefined,
|
},
|
);
|
|
const emit = defineEmits<{
|
change: [value: string | string[] | undefined];
|
'update:value': [value: string | string[] | undefined];
|
}>();
|
|
const open = ref(false);
|
const keyword = ref('');
|
const draftKeys = ref<string[]>([]);
|
|
const displayText = computed(() => getTrendOptionSummary(props.value, props.options));
|
const filteredOptions = computed(() => filterTrendOptions(props.options, keyword.value));
|
|
function openPopup() {
|
if (props.disabled) return;
|
keyword.value = '';
|
draftKeys.value = normalizeTrendOptionValue(props.value);
|
open.value = true;
|
}
|
|
function closePopup() {
|
open.value = false;
|
}
|
|
function setOption(value: string, checked = true) {
|
if (!props.multiple) {
|
draftKeys.value = [value];
|
return;
|
}
|
if (checked && !draftKeys.value.includes(value)) {
|
draftKeys.value = [...draftKeys.value, value];
|
} else if (!checked) {
|
draftKeys.value = draftKeys.value.filter((key) => key !== value);
|
}
|
}
|
|
function selectAll() {
|
const keys = new Set(draftKeys.value);
|
filteredOptions.value.forEach((option) => keys.add(option.value));
|
draftKeys.value = [...keys];
|
}
|
|
function clearDraft() {
|
draftKeys.value = [];
|
}
|
|
function emitValue(value: string | string[] | undefined) {
|
emit('update:value', value);
|
emit('change', value);
|
}
|
|
function confirmSelection() {
|
emitValue(props.multiple ? [...draftKeys.value] : draftKeys.value[0]);
|
closePopup();
|
}
|
|
function confirmSingleOption(value: string) {
|
if (props.multiple) return;
|
draftKeys.value = [value];
|
emitValue(value);
|
closePopup();
|
}
|
</script>
|
|
<template>
|
<div class="trend-option-popup">
|
<a-input :disabled="disabled" :placeholder="placeholder" :title="displayText" :value="displayText" class="popup-trigger" readonly @click="openPopup">
|
<template #suffix>
|
<ChevronDown :size="16" class="dropdown-icon" />
|
</template>
|
</a-input>
|
|
<AModal
|
v-model:open="open"
|
:body-style="{ padding: '16px' }"
|
:destroy-on-close="true"
|
:mask-closable="false"
|
:title="popupTitle"
|
:width="640"
|
@cancel="closePopup"
|
@ok="confirmSelection">
|
<div class="popup-content">
|
<div class="popup-toolbar" :class="{ 'is-multiple': multiple }">
|
<a-input v-model:value="keyword" allow-clear placeholder="请输入名称或编码搜索">
|
<template #prefix><Search :size="16" /></template>
|
</a-input>
|
<template v-if="multiple">
|
<a-button @click="selectAll">全选</a-button>
|
<a-button @click="clearDraft">清空</a-button>
|
</template>
|
</div>
|
|
<div class="option-list">
|
<button
|
v-for="option in filteredOptions"
|
:key="option.value"
|
class="option-row"
|
type="button"
|
@click="setOption(option.value, !draftKeys.includes(option.value))"
|
@dblclick="confirmSingleOption(option.value)">
|
<a-checkbox v-if="multiple" :checked="draftKeys.includes(option.value)" @change="setOption(option.value, $event.target.checked)" @click.stop />
|
<a-radio v-else :checked="draftKeys.includes(option.value)" @click.stop="setOption(option.value)" />
|
<span class="option-label">{{ option.label }}</span>
|
</button>
|
<AEmpty v-if="!filteredOptions.length" :image="undefined" description="暂无可选数据" />
|
</div>
|
</div>
|
</AModal>
|
</div>
|
</template>
|
|
<style lang="scss" scoped>
|
.trend-option-popup,
|
.popup-trigger {
|
width: 100%;
|
}
|
|
.popup-trigger {
|
cursor: pointer;
|
|
:deep(input) {
|
overflow: hidden;
|
text-overflow: ellipsis;
|
cursor: pointer;
|
}
|
}
|
|
.dropdown-icon {
|
color: rgb(0 0 0 / 45%);
|
pointer-events: none;
|
}
|
|
.popup-toolbar {
|
display: grid;
|
grid-template-columns: minmax(0, 1fr);
|
margin-bottom: 12px;
|
}
|
|
.popup-toolbar.is-multiple {
|
grid-template-columns: minmax(0, 1fr) auto auto;
|
gap: 8px;
|
}
|
|
.option-list {
|
height: min(440px, 55vh);
|
overflow-y: auto;
|
border: 1px solid #e5e7eb;
|
}
|
|
.option-row {
|
display: flex;
|
align-items: center;
|
width: 100%;
|
min-height: 42px;
|
padding: 8px 12px;
|
text-align: left;
|
cursor: pointer;
|
background: #fff;
|
border: 0;
|
border-bottom: 1px solid #f0f0f0;
|
gap: 10px;
|
}
|
|
.option-row:hover {
|
background: #f5f8fc;
|
}
|
|
.option-label {
|
min-width: 0;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
}
|
</style>
|