255 lines
5.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="selector-component">
<el-input :modelValue="modelValue" :placeholder="placeholder" readonly @click="openDialog">
<template #suffix>
<el-icon><ArrowDown /></el-icon>
</template>
</el-input>
<el-dialog v-model="dialogVisible" :title="dialogTitle" :width="dialogWidth" :append-to-body="true" :modal="true" @close="handleDialogClose">
<div class="dialog-content">
<div class="title">已选择地点路线{{ tempSelectedItem?.routeCode }}</div>
<el-input v-model="searchKeyword" :placeholder="searchPlaceholder" clearable @input="handleSearch" />
<el-table :data="dataList" v-loading="loading" height="350" @row-click="handleRowClick" highlight-current-row>
<el-table-column prop="routeCode" label="线路编号" />
</el-table>
<div class="pagination-wrapper" v-if="total > 0">
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
:page-sizes="pageSizes"
layout="prev, pager, next, sizes"
@current-change="handlePageChange"
@size-change="handleSizeChange"
/>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, computed, onUnmounted, watch } from 'vue'
import { ArrowDown } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { request } from '@shared/utils/request'
// ==================== Props ====================
const props = defineProps({
// v-model 绑定的值
modelValue: {
type: [String, Number],
default: ''
},
// 占位符文本
placeholder: {
type: String,
default: '请选择'
},
// 弹窗标题
dialogTitle: {
type: String,
default: '选择地点路线'
},
// 弹窗宽度
dialogWidth: {
type: String,
default: '500px'
},
// 搜索框占位符
searchPlaceholder: {
type: String,
default: '请输入关键词搜索'
},
// 请求接口地址
apiUrl: {
type: String,
default: '/snow-ops-platform/infrastructure-asset/routes'
},
// 每页大小
pageSize: {
type: Number,
default: 10
},
// 每页大小选项
pageSizes: {
type: Array,
default: () => [10, 20, 50]
},
// 额外请求参数
extraParams: {
type: Object,
default: () => ({})
},
// 搜索防抖延迟时间(ms)
searchDelay: {
type: Number,
default: 1000
}
})
const emit = defineEmits(['update:modelValue', 'change'])
// ==================== 状态 ====================
const dialogVisible = ref(false)
const searchKeyword = ref('')
const dataList = ref([])
const loading = ref(false)
const currentPage = ref(1)
const pageSize = ref(props.pageSize)
const total = ref(0)
// 临时选中的数据(确定前暂存)
const tempSelectedItem = ref(null)
// 防抖定时器
let searchTimer = null
// 已确认选中的值
const selectedValue = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
watch(() => props.modelValue, () => {
if(props.modelValue != tempSelectedItem.value?.routeCode) {
tempSelectedItem.value = null
}
})
// ==================== API 请求 ====================
const fetchData = async () => {
loading.value = true
try {
const params = {
pageNum: currentPage.value,
pageSize: pageSize.value,
lxbh: props.modelValue,
xzdj: props.extraParams?.xzdj, // 行政等级
qxid: props.extraParams?.qxid // 区县编码
}
// 如果有搜索关键词,添加到参数中
if (searchKeyword.value) {
params.lxbh = searchKeyword.value
}
const response = await request({
url: props.apiUrl,
method: 'get',
params
})
if (response?.code === '00000') {
const records = response.data.records || []
total.value = response.data.total || 0
// 统一格式化数据,便于内部使用
dataList.value = records
} else {
ElMessage.error(response.message || '加载失败')
dataList.value = []
total.value = 0
}
} catch (error) {
console.error('请求失败:', error)
ElMessage.error('加载失败,请重试')
dataList.value = []
total.value = 0
} finally {
loading.value = false
}
}
// 带防抖的搜索函数
const debouncedSearch = () => {
// 清除之前的定时器
if (searchTimer) {
clearTimeout(searchTimer)
}
// 设置新的定时器
searchTimer = setTimeout(() => {
currentPage.value = 1
fetchData()
}, props.searchDelay)
}
// ==================== 事件处理 ====================
const openDialog = () => {
dialogVisible.value = true
// 重置状态
searchKeyword.value = tempSelectedItem.value?.routeCode || ''
currentPage.value = 1
fetchData()
}
const handleDialogClose = () => {
dialogVisible.value = false
// 关闭弹窗时清除防抖定时器
if (searchTimer) {
clearTimeout(searchTimer)
}
}
const handleSearch = () => {
// 使用防抖处理搜索
debouncedSearch()
}
const handlePageChange = (page) => {
currentPage.value = page
fetchData()
}
const handleSizeChange = (size) => {
pageSize.value = size
currentPage.value = 1
fetchData()
}
const handleRowClick = (row) => {
tempSelectedItem.value = row
selectedValue.value = tempSelectedItem.value.routeCode
dialogVisible.value = false
emit('change', tempSelectedItem.value)
}
// ==================== 清理定时器 ====================
onUnmounted(() => {
if (searchTimer) {
clearTimeout(searchTimer)
}
})
</script>
<style scoped lang="scss">
.selector-component {
width: 100%;
:deep(.el-input__wrapper) {
cursor: pointer;
.el-input__inner {
cursor: pointer;
}
}
}
.dialog-content {
display: flex;
flex-direction: column;
gap: 16px;
.pagination-wrapper {
display: flex;
justify-content: flex-end;
margin-top: 8px;
}
}
</style>