157 lines
3.6 KiB
Vue
157 lines
3.6 KiB
Vue
|
|
/**
|
||
|
|
* @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>
|