157 lines
3.6 KiB
Vue
Raw Normal View History

2026-06-26 17:55:13 +08:00
/**
* @file index.vue
* @description: 下拉表格选择
* @author: w
* @since: 2026-06-18
*/
<template>
<div class="dropdown-table-select" :style="{ width: inputWidth }">
<el-dropdown ref="dropdownRef" trigger="manual" placement="bottom-start" style="width:100%">
<div class="input-wrap" style="width:100%">
<el-input v-model="inputText" :placeholder="placeholder" @keydown.enter.prevent="handleInputEnter" />
<span class="arrow-icon" @click.stop="toggleDropdown">
<el-icon>
<ArrowDown />
</el-icon>
</span>
</div>
<template #dropdown>
<div class="table-panel" :style="{ width: panelWidth }">
<el-table :data="tableData" border :height="tableHeight" @row-click="handleRowClick" v-loading="tableLoading">
<el-table-column v-for="col in columnList" :key="col.prop" :label="col.label" :prop="col.prop"
:width="col.width" :align="col.align || 'center'" />
</el-table>
</div>
</template>
</el-dropdown>
</div>
</template>
<script setup lang="ts">
export interface TableColumnItem {
label: string
prop: string
width?: string
align?: 'left' | 'center' | 'right'
}
export type TableRowItem = Record<string, any>
const props = defineProps({
// 绑定值
modelValue: {
type: String,
default: ''
},
// 占位符
placeholder: {
type: String,
default: ''
},
// 绑定值字段
labelField: {
type: String,
default: 'billNo'
},
// 输入框宽度
inputWidth: {
type: String,
default: '100%'
},
// 下拉面板宽度
panelWidth: {
type: String,
default: '700px'
},
//表格高度
tableHeight: {
type: String,
default: '150px'
},
// 表格列 父必传,无默认值
columnList: {
type: Array as () => TableColumnItem[],
required: true
},
// 表格数据 父必传
tableData: {
type: Array as () => TableRowItem[],
required: true
}
})
const emit = defineEmits<{
'update:modelValue': [val: string]
change: [row: TableRowItem]
open: []
search: [val: string]
}>()
const bindField = props.labelField ?? 'billNo'
const dropdownRef = useTemplateRef('dropdownRef')
const showPanel = ref(false)
const inputText = ref('')
const tableLoading = ref(false)
watch(
() => props.modelValue,
(val) => {
inputText.value = val ?? ''
},
{ immediate: true }
)
watch(inputText, (val) => {
emit('update:modelValue', val)
})
// 回车处理
const handleInputEnter = (e: KeyboardEvent) => {
e.stopPropagation();
emit('search', inputText.value)
}
// 仅点击箭头才切换下拉
const toggleDropdown = () => {
if (!dropdownRef.value) return
showPanel.value = !showPanel.value
if (showPanel.value) {
dropdownRef.value.handleOpen()
tableLoading.value = true
emit('open')
setTimeout(() => {
tableLoading.value = false
}, 300)
} else {
dropdownRef.value.handleClose()
}
}
// 行点击选中
const handleRowClick = (row: TableRowItem) => {
const text = row[bindField] ?? ''
inputText.value = text
emit('update:modelValue', text)
emit('change', row)
dropdownRef.value?.handleClose()
showPanel.value = false
}
</script>
<style scoped lang="scss">
.dropdown-table-select {
.input-wrap {
position: relative;
}
.arrow-icon {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
color: #909399;
z-index: 999 !important;
pointer-events: auto !important;
font-size: 16px;
transition: transform 0.2s;
}
.table-panel {
padding: 12px;
}
}
</style>