param builder component is ready

This commit is contained in:
GeekMaster
2025-09-10 17:04:37 +08:00
parent 1ca58606da
commit 896b5de0a4
29 changed files with 850 additions and 656 deletions

View File

@@ -0,0 +1,258 @@
<template>
<div class="param-builder flex flex-col">
<ParamEmpty
v-if="items.length === 0"
:progress="progress"
:title="title"
:status-text="statusText"
:description="description"
/>
<div v-else class="flex flex-col w-full space-y-5">
<el-select
v-model="selectedModel"
placeholder="请选择模型"
@change="changeModel"
popper-class="model-select"
value-key="name"
>
<template #prefix>
<i class="iconfont icon-model"></i>
</template>
<el-option v-for="item in items" :key="item.name" :label="item.name" :value="item">
<div class="flex justify-start">
<span
class="flex items-center justify-center text-white !text-xl model-version mr-2 w-[40px] h-[40px] rounded-lg"
>{{ item.version }}</span
>
<div class="flex !items-start flex-col py-2 space-y-1">
<span class="label text-sm">{{ item.name }}</span>
<div class="whitespace-pre-line">
<span
class="text-xs text-gray-500 break-words whitespace-pre-line line-clamp-1"
:title="item.label"
>{{ item.label }}</span
>
</div>
</div>
</div>
</el-option>
</el-select>
<div v-for="param in selectedModel.params" :key="param.name" class="w-full">
<div class="w-full flex flex-col !items-start space-y-2" v-if="param.type === 'switch'">
<div class="w-full flex justify-between">
<label class="label font-bold">{{ param.label }}</label>
<el-switch v-model="modelValue[param.name]" size="large" />
</div>
<p v-if="param.info" class="text-xs text-gray-500 mb-1">{{ param.info }}</p>
</div>
<div class="w-full flex flex-col !items-start space-y-2" v-else>
<label class="label font-bold">
{{ param.label }}
<span v-if="param.required" class="text-red-500 ml-1">*</span>
</label>
<p v-if="param.info" class="text-xs text-gray-500 mb-1">{{ param.info }}</p>
<div class="flex w-full">
<el-input
v-if="param.type === 'text'"
v-model="modelValue[param.name]"
:placeholder="param.placeholder"
/>
<el-input-number
v-if="param.type === 'number'"
v-model="modelValue[param.name]"
class="!w-full"
:placeholder="param.placeholder"
:min="param.min"
:max="param.max"
:step="param.step"
/>
<el-slider
v-if="param.type === 'slider'"
v-model="modelValue[param.name]"
:min="param.min"
:max="param.max"
:step="param.step"
/>
<el-date-picker
v-if="param.type === 'date'"
v-model="modelValue[param.name]"
:placeholder="param.placeholder"
/>
<el-time-picker
v-if="param.type === 'time'"
v-model="modelValue[param.name]"
:placeholder="param.placeholder"
/>
<el-select
v-if="param.type === 'select'"
v-model="modelValue[param.name]"
:placeholder="param.placeholder"
:popper-class="param.popperClass"
>
<el-option
v-for="option in param.options"
:key="option.value"
:label="option.label"
:value="option.value"
>
<div class="flex justify-start" v-if="option.image">
<span class="flex py-3 mr-2">
<img :src="option.image" class="w-[54px] h-[54px] rounded-lg"
/></span>
<div class="flex !items-start flex-col py-2 space-y-1">
<span class="label text-sm">{{ option.label }}</span>
<span class="text-xs text-gray-500">{{ option.value }}</span>
</div>
</div>
<div class="flex justify-start items-center h-full" v-else>
<span class="label text-sm">{{ option.label }}</span>
</div>
</el-option>
</el-select>
<el-input
type="textarea"
v-if="param.type === 'textarea'"
v-model="modelValue[param.name]"
:autosize="param.autosize"
:maxlength="param.maxlength"
:show-word-limit="param.showWordLimit"
:placeholder="param.placeholder"
/>
<ImageUpload
v-if="param.type === 'image'"
v-model="modelValue[param.name]"
:max-count="param.maxCount"
:multiple="param.multiple"
:max-size="param.maxSize"
:accept="param.accept"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import ImageUpload from './ImageUpload.vue'
import ParamEmpty from './ui/ParamEmpty.vue'
const title = ref('参数构建器')
const statusText = ref('功能正在开发中')
const description = ref('我们正在努力完善当前功能,敬请期待!')
const props = defineProps({
modelValue: {
type: Object,
required: true,
},
items: {
type: Array,
required: true,
},
progress: {
type: Number,
default: 65,
validator: (value) => value >= 0 && value <= 100,
},
})
const selectedModel = ref({ label: '请选择模型' })
const emit = defineEmits(['update:modelValue'])
// 初始化 modelValue 默认值
const initModelValue = (model) => {
const defaultValues = {}
if (model && model.params) {
model.params.forEach((param) => {
// 根据参数类型设置默认值
switch (param.type) {
case 'text':
case 'textarea':
defaultValues[param.name] = param.value || ''
break
case 'number':
defaultValues[param.name] = param.value || 0
break
case 'slider':
defaultValues[param.name] = param.value || param.min || 0
break
case 'select':
// 如果有选项,选择第一个选项作为默认值
defaultValues[param.name] =
param.value || (param.options && param.options[0] ? param.options[0].value : '')
break
case 'checkbox':
case 'switch':
defaultValues[param.name] = param.value || false
break
case 'date':
case 'time':
defaultValues[param.name] = param.value || null
break
case 'image':
defaultValues[param.name] = param.value || []
break
default:
defaultValues[param.name] = param.value || ''
}
})
}
return defaultValues
}
// 初始化默认值
const modelValue = ref(initModelValue(selectedModel.value))
// 监听 modelValue 变化,通知父组件
watch(
modelValue,
(newValue) => {
emit('update:modelValue', newValue)
},
{ deep: true }
)
// 组件挂载时初始化
onMounted(() => {
// 确保初始值被正确设置
if (props.modelValue && Object.keys(props.modelValue).length > 0) {
modelValue.value = { ...props.modelValue }
} else {
modelValue.value = initModelValue(selectedModel.value)
}
})
const changeModel = (item) => {
if (item) {
selectedModel.value = item
// 更新 modelValue 为选中模型的默认值
modelValue.value = initModelValue(item)
}
}
</script>
<style lang="scss">
.param-builder {
.model-version {
background: url('@/assets/img/model-version.png') no-repeat center center;
background-size: cover;
}
.el-select__wrapper {
min-height: 34px;
line-height: 25px;
}
}
.model-select {
.el-select-dropdown__item {
height: auto !important;
}
.model-version {
background: url('@/assets/img/model-version.png') no-repeat center center;
background-size: cover;
}
}
</style>