171 lines
4.0 KiB
Vue
Raw Normal View History

<script setup lang="ts">
2025-07-16 15:12:35 +08:00
import { ref, computed } from 'vue';
import { Select, Divider, Row } from 'ant-design-vue';
import { ChevronLeft, ChevronRight } from '@vben/icons';
import { postMetersPage } from '#/api-client';
import { $t } from '#/locales';
import { useDebounceFn } from '@vueuse/core';
interface Props {
value?: string;
placeholder?: string;
disabled?: boolean;
allowClear?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
placeholder: $t('common.pleaseSelect') + $t('abp.log.deviceInfo'),
disabled: false,
allowClear: true,
});
const emit = defineEmits<{
'update:value': [string];
change: [string];
'device-change': [any]; // 添加设备信息变化事件
}>();
const VNodes = (_, { attrs }: any) => {
return attrs.vnodes;
};
const options = ref<any[]>([]);
const query = ref({
pageIndex: 1,
pageSize: 10,
2025-07-16 15:37:23 +08:00
SearchKeyword: '',
});
const total = ref(0);
const loading = ref<boolean>(false);
// 最大页码
const maxPage = computed(() => {
return Math.ceil(total.value / query.value.pageSize);
});
/**
* 获取设备列表数据
*/
const fetchData = async () => {
loading.value = true;
try {
const { data } = await postMetersPage({
body: {
pageIndex: query.value.pageIndex,
pageSize: query.value.pageSize,
2025-07-16 15:37:23 +08:00
SearchKeyword: query.value.SearchKeyword,
},
});
if (data?.items) {
options.value = data.items.map((item) => ({
label: item.meterName,
value: item.id,
...item, // 保留完整数据
}));
total.value = data.totalCount || 0;
}
loading.value = false;
} catch (error) {
console.error('获取设备列表失败:', error);
loading.value = false;
}
};
/**
* 上下页
* @param type 1: 下一页, 0: 上一页
*/
const changePage = (type: number) => {
if (type === 1) {
if (query.value.pageIndex >= maxPage.value) return;
query.value.pageIndex += 1;
fetchData();
} else {
if (query.value.pageIndex <= 1) return;
query.value.pageIndex -= 1;
fetchData();
}
};
/**
* 设备搜索
*/
const fetchDevice = useDebounceFn((value: string) => {
query.value.pageIndex = 1;
2025-07-16 15:37:23 +08:00
query.value.SearchKeyword = value;
fetchData();
}, 500);
// 处理值变化
const handleValueChange = (value: string) => {
emit('update:value', value);
emit('change', value);
// 发送选中的设备信息
if (value) {
const selectedDevice = options.value.find(option => option.value === value);
if (selectedDevice) {
emit('device-change', selectedDevice);
}
} else {
emit('device-change', null);
}
};
// 初始化加载数据
fetchData();
// 暴露方法给父组件调用
defineExpose({
getSelectedDevice: () => {
if (props.value) {
return options.value.find(option => option.value === props.value);
}
return null;
},
});
</script>
<template>
<Select
:value="value"
:showSearch="true"
@search="fetchDevice"
:options="options"
:placeholder="placeholder"
:filter-option="false"
:disabled="disabled"
:allowClear="allowClear"
:loading="loading"
@change="handleValueChange"
>
<template #dropdownRender="{ menuNode: menu }">
<v-nodes :vnodes="menu" />
<Divider style="margin: 4px 0" />
<div @mousedown="(e) => e.preventDefault()">
<Row type="flex" justify="space-around" align="middle">
<ChevronLeft
@click="changePage(0)"
:class="{ 'text-gray-400': query.pageIndex <= 1 }"
2025-07-16 15:12:35 +08:00
style="cursor: pointer; width: 16px; height: 16px;"
/>
<div>{{ `${query.pageIndex}/${maxPage}` }}</div>
<ChevronRight
@click="changePage(1)"
:class="{ 'text-gray-400': query.pageIndex >= maxPage }"
2025-07-16 15:12:35 +08:00
style="cursor: pointer; width: 16px; height: 16px;"
/>
</Row>
</div>
</template>
</Select>
</template>
<style lang="less" scoped>
.text-gray-400 {
color: #9ca3af;
cursor: not-allowed !important;
}
</style>