Files
one-pipe-system/src/components/business/PackageSelector.vue
2026-01-23 17:18:24 +08:00

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>