133 lines
3.2 KiB
Vue
133 lines
3.2 KiB
Vue
<template>
|
|
<el-select
|
|
v-model="selectedValue"
|
|
:placeholder="placeholder"
|
|
:clearable="clearable"
|
|
:disabled="disabled"
|
|
:multiple="multiple"
|
|
:filterable="filterable"
|
|
:remote="remote"
|
|
:remote-method="remoteMethod"
|
|
:loading="loading"
|
|
:size="size"
|
|
@change="handleChange"
|
|
@visible-change="handleVisibleChange"
|
|
>
|
|
<el-option v-for="item in packageList" :key="item.id" :label="item.name" :value="item.id">
|
|
<div class="package-option">
|
|
<span class="package-name">{{ item.name }}</span>
|
|
<span class="package-info">
|
|
{{ formatFlow(item.flow) }} / {{ formatMoney(item.price) }}
|
|
</span>
|
|
</div>
|
|
</el-option>
|
|
</el-select>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import type { Package } from '@/types/api'
|
|
import { formatFlow, formatMoney } from '@/utils/business'
|
|
|
|
interface Props {
|
|
modelValue?: string | number | (string | number)[]
|
|
placeholder?: string
|
|
clearable?: boolean
|
|
disabled?: boolean
|
|
multiple?: boolean
|
|
filterable?: boolean
|
|
remote?: boolean
|
|
size?: 'large' | 'default' | 'small'
|
|
// 预加载的套餐列表
|
|
packages?: Package[]
|
|
// 远程搜索方法
|
|
fetchMethod?: (query: string) => Promise<Package[]>
|
|
}
|
|
|
|
interface Emits {
|
|
(e: 'update:modelValue', value: string | number | (string | number)[] | undefined): void
|
|
(e: 'change', value: string | number | (string | number)[] | undefined): void
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
placeholder: '请选择套餐',
|
|
clearable: true,
|
|
disabled: false,
|
|
multiple: false,
|
|
filterable: true,
|
|
remote: false,
|
|
size: 'default'
|
|
})
|
|
|
|
const emit = defineEmits<Emits>()
|
|
|
|
const loading = ref(false)
|
|
const packageList = ref<Package[]>([])
|
|
|
|
const selectedValue = computed({
|
|
get: () => props.modelValue,
|
|
set: (val) => {
|
|
emit('update:modelValue', val)
|
|
}
|
|
})
|
|
|
|
const handleChange = (value: string | number | (string | number)[] | undefined) => {
|
|
emit('change', value)
|
|
}
|
|
|
|
const handleVisibleChange = (visible: boolean) => {
|
|
if (visible && !props.remote && packageList.value.length === 0) {
|
|
loadPackages()
|
|
}
|
|
}
|
|
|
|
const loadPackages = async () => {
|
|
if (props.packages) {
|
|
packageList.value = props.packages
|
|
} else if (props.fetchMethod) {
|
|
loading.value = true
|
|
try {
|
|
packageList.value = await props.fetchMethod('')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
}
|
|
|
|
const remoteMethod = async (query: string) => {
|
|
if (!props.fetchMethod) return
|
|
|
|
loading.value = true
|
|
try {
|
|
packageList.value = await props.fetchMethod(query)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (props.packages) {
|
|
packageList.value = props.packages
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.package-option {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
|
|
.package-name {
|
|
font-weight: 500;
|
|
color: var(--el-text-color-primary);
|
|
}
|
|
|
|
.package-info {
|
|
margin-left: 12px;
|
|
font-size: 12px;
|
|
color: var(--el-text-color-secondary);
|
|
}
|
|
}
|
|
</style>
|