Initial commit: One Pipe System
完整的管理系统,包含账户管理、卡片管理、套餐管理、财务管理等功能模块。 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
137
src/components/business/PackageSelector.vue
Normal file
137
src/components/business/PackageSelector.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<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;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.package-name {
|
||||
font-weight: 500;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.package-info {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user