master
parent
655bb65d00
commit
d637db70f6
@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
variant?: 'default' | 'dashboard' | 'radar'
|
||||
}>(),
|
||||
{ variant: 'default' },
|
||||
)
|
||||
|
||||
const route = useRoute()
|
||||
const title = computed(() => String(route.meta.title || ''))
|
||||
|
||||
const className = computed(() => {
|
||||
if (props.variant === 'dashboard') return 'dashboard-page-title'
|
||||
if (props.variant === 'radar') return 'radar-page-title'
|
||||
return 'page-title'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 :class="className">{{ title }}</h1>
|
||||
</template>
|
||||
@ -0,0 +1,122 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
export type RemotePagedItem = { id: number; label: string }
|
||||
|
||||
export type RemotePagedFetchParams = {
|
||||
keyword: string
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
export type RemotePagedFetchResult = {
|
||||
items: RemotePagedItem[]
|
||||
total: number
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue?: number
|
||||
placeholder?: string
|
||||
pageSize?: number
|
||||
initialOption?: RemotePagedItem | null
|
||||
fetchPage: (params: RemotePagedFetchParams) => Promise<RemotePagedFetchResult>
|
||||
}>(),
|
||||
{
|
||||
placeholder: '请搜索选择',
|
||||
pageSize: 20,
|
||||
initialOption: null,
|
||||
},
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: number | undefined]
|
||||
}>()
|
||||
|
||||
const loading = ref(false)
|
||||
const options = ref<RemotePagedItem[]>([])
|
||||
const keyword = ref('')
|
||||
const page = ref(1)
|
||||
const total = ref(0)
|
||||
|
||||
function mergeInitialOption(items: RemotePagedItem[]) {
|
||||
const seed = props.initialOption
|
||||
if (!seed) return items
|
||||
if (items.some((item) => item.id === seed.id)) return items
|
||||
return [seed, ...items]
|
||||
}
|
||||
|
||||
async function load(pageNum = 1, kw = keyword.value) {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await props.fetchPage({ keyword: kw, page: pageNum, pageSize: props.pageSize })
|
||||
options.value = mergeInitialOption(res.items)
|
||||
total.value = res.total
|
||||
page.value = pageNum
|
||||
keyword.value = kw
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onRemoteMethod(kw: string) {
|
||||
void load(1, kw)
|
||||
}
|
||||
|
||||
function onPageChange(nextPage: number) {
|
||||
void load(nextPage, keyword.value)
|
||||
}
|
||||
|
||||
function onVisibleChange(visible: boolean) {
|
||||
if (visible) {
|
||||
void load(1, keyword.value)
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.initialOption,
|
||||
() => {
|
||||
options.value = mergeInitialOption(options.value)
|
||||
},
|
||||
)
|
||||
|
||||
defineExpose({ reload: () => load(1, keyword.value) })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-select
|
||||
:model-value="modelValue"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
:placeholder="placeholder"
|
||||
:remote-method="onRemoteMethod"
|
||||
:loading="loading"
|
||||
style="width: 100%"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
@visible-change="onVisibleChange"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.label" :value="item.id" />
|
||||
<template v-if="total > pageSize" #footer>
|
||||
<div class="remote-paged-select-footer" @click.stop @mousedown.stop>
|
||||
<el-pagination
|
||||
small
|
||||
layout="total, prev, pager, next"
|
||||
:total="total"
|
||||
:page-size="pageSize"
|
||||
:current-page="page"
|
||||
@current-change="onPageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.remote-paged-select-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 6px 8px 4px;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in new issue