diff --git a/src/platforms/painting/controls/dimension.vue b/src/platforms/painting/controls/dimension.vue new file mode 100644 index 0000000..1a70538 --- /dev/null +++ b/src/platforms/painting/controls/dimension.vue @@ -0,0 +1,247 @@ + + + + + diff --git a/src/platforms/painting/controls/proportion.vue b/src/platforms/painting/controls/proportion.vue new file mode 100644 index 0000000..e86235f --- /dev/null +++ b/src/platforms/painting/controls/proportion.vue @@ -0,0 +1,465 @@ + + + + + diff --git a/src/platforms/painting/controls/quality.vue b/src/platforms/painting/controls/quality.vue new file mode 100644 index 0000000..d5351ff --- /dev/null +++ b/src/platforms/painting/controls/quality.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/src/platforms/painting/controls/quantity.vue b/src/platforms/painting/controls/quantity.vue new file mode 100644 index 0000000..319a032 --- /dev/null +++ b/src/platforms/painting/controls/quantity.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/src/platforms/painting/imageUploader.vue b/src/platforms/painting/imageUploader.vue new file mode 100644 index 0000000..120d2bb --- /dev/null +++ b/src/platforms/painting/imageUploader.vue @@ -0,0 +1,312 @@ + + + + + diff --git a/src/platforms/painting/index.js b/src/platforms/painting/index.js new file mode 100644 index 0000000..57afbfc --- /dev/null +++ b/src/platforms/painting/index.js @@ -0,0 +1,254 @@ +import { ref, reactive, computed, markRaw } from 'vue' +import { fetchPlatformModels, getPlatformCode } from '@/utils/modelApi' +import { getModelConfig } from '@/config/models/index.js' +import PaintingModelSelector from './modelSelector.vue' +import PaintingProportion from './controls/proportion.vue' +import DimensionInput from './controls/dimension.vue' +import QualitySelect from './controls/quality.vue' +import Quantity from './controls/quantity.vue' +import ImageUploader from './imageUploader.vue' +import { registerPlatform } from '../registry.js' + +function getDimConfig(config) { + if (!config) return null + const dimParam = config.params.find(p => p.ui === 'dimension') + if (dimParam) return { type: 'combined', config: dimParam.dimension, paramName: dimParam.name } + const wParam = config.params.find(p => p.ui === 'dimensionWidth') + const hParam = config.params.find(p => p.ui === 'dimensionHeight') + if (wParam && hParam) return { type: 'split', wParam, hParam } + return null +} + +export function definePaintingPlatform() { + const model = ref('Flux 2') + const modelType = ref('text') + const proportion = ref('1:1') + const resolution = ref('2k') + const customWidth = ref(1024) + const customHight = ref(1024) + const dimWidth = ref(1024) + const dimHeight = ref(1024) + const quantity = ref(1) + const quality = ref('medium') + const modelConfig = ref(null) + const promptPlaceholder = ref('描述你想生成的画面和动作。') + const paramValues = reactive({}) + + const state = { + model, modelType, + proportion, resolution, + customWidth, customHight, + dimWidth, dimHeight, + quantity, quality, + paramValues, modelConfig, + } + + function syncDefaults(config) { + modelConfig.value = config + if (!config) return + config.params.forEach(p => { + if (!(p.name in paramValues)) { + paramValues[p.name] = p.default ?? (p.name === 'outputFormat' ? 'png' : '') + } + }) + const ratioParam = config.params.find(p => p.ui === 'proportion') + if (ratioParam) proportion.value = ratioParam.default || '1:1' + const resParam = config.params.find(p => p.ui === 'resolution') + if (resParam) resolution.value = resParam.default || '2k' + const qtyParam = config.params.find(p => p.ui === 'quantity') + if (qtyParam) quantity.value = qtyParam.default || 1 + const cwParam = config.params.find(p => p.name === 'customWidth') + if (cwParam) customWidth.value = cwParam.default || 1024 + const chParam = config.params.find(p => p.name === 'customHight') + if (chParam) customHight.value = chParam.default || 1024 + const qualityParam = config.params.find(p => p.name === 'quality') + if (qualityParam) quality.value = qualityParam.default || 'medium' + const dc = getDimConfig(config) + if (dc?.type === 'split') { + dimWidth.value = dc.wParam.default || 1024 + dimHeight.value = dc.hParam.default || 1024 + } else if (dc?.type === 'combined') { + const dimParam = config.params.find(p => p.name === dc.paramName) + const raw = dimParam?.default || '' + const parsed = dc.config.parse(raw) + dimWidth.value = parsed.width + dimHeight.value = parsed.height + } + } + + function syncParamValues() { + const ratioParam = modelConfig.value?.params?.find(p => p.ui === 'proportion') + if (ratioParam) paramValues[ratioParam.name] = proportion.value + const resParam = modelConfig.value?.params?.find(p => p.ui === 'resolution') + if (resParam) paramValues[resParam.name] = resolution.value + const qtyParam = modelConfig.value?.params?.find(p => p.ui === 'quantity') + if (qtyParam) paramValues[qtyParam.name] = quantity.value + if (modelConfig.value?.params?.find(p => p.name === 'customWidth')) { + paramValues.customWidth = customWidth.value + } + if (modelConfig.value?.params?.find(p => p.name === 'customHight')) { + paramValues.customHight = customHight.value + } + if (modelConfig.value?.params?.find(p => p.name === 'quality')) { + paramValues.quality = quality.value + } + const dc = getDimConfig(modelConfig.value) + if (dc?.type === 'split') { + paramValues[dc.wParam.name] = dimWidth.value + paramValues[dc.hParam.name] = dimHeight.value + } else if (dc?.type === 'combined') { + paramValues[dc.paramName] = dc.config.format(dimWidth.value, dimHeight.value) + } + } + + const controls = [ + { + name: 'proportion', + component: markRaw(PaintingProportion), + show: (config) => !!config?.params?.find(p => p.ui === 'proportion'), + props: (config) => { + const ratioParam = config?.params?.find(p => p.ui === 'proportion') + const resParam = config?.params?.find(p => p.ui === 'resolution') + return { + modelValue: proportion.value, + 'onUpdate:modelValue': (v) => { proportion.value = v }, + resolution: resolution.value, + 'onUpdate:resolution': (v) => { resolution.value = v }, + width: customWidth.value, + 'onUpdate:width': (v) => { customWidth.value = v }, + height: customHight.value, + 'onUpdate:height': (v) => { customHight.value = v }, + proportionOptions: ratioParam?.options + ?.filter(o => o !== 'custom') + .map(o => ({ value: o, label: o })) || [], + resolutionOptions: resParam?.options + ?.map(o => ({ value: o, label: o.toUpperCase() })) || [], + allowCustom: ratioParam?.options?.includes('custom') || false, + } + }, + }, + { + name: 'dimension', + component: markRaw(DimensionInput), + show: (config) => !!config?.params?.find(p => p.ui === 'dimension' || p.ui === 'dimensionWidth'), + props: (config) => { + const dc = getDimConfig(config) + return { + width: dimWidth.value, + 'onUpdate:width': (v) => { dimWidth.value = v }, + height: dimHeight.value, + 'onUpdate:height': (v) => { dimHeight.value = v }, + minW: dc?.config?.width?.min || dc?.wParam?.min || 256, + maxW: dc?.config?.width?.max || dc?.wParam?.max || 6197, + minH: dc?.config?.height?.min || dc?.hParam?.min || 256, + maxH: dc?.config?.height?.max || dc?.hParam?.max || 4096, + } + }, + }, + { + name: 'quality', + component: markRaw(QualitySelect), + show: (config) => !!config?.params?.find(p => p.name === 'quality'), + props: (config) => { + const q = config?.params?.find(p => p.name === 'quality') + return { + modelValue: quality.value, + 'onUpdate:modelValue': (v) => { quality.value = v }, + options: q?.options?.map(o => ({ value: o, label: o })) || [], + } + }, + }, + { + name: 'quantity', + component: markRaw(Quantity), + show: (config) => !!config?.params?.find(p => p.ui === 'quantity'), + props: (config) => { + const qtyParam = config?.params?.find(p => p.ui === 'quantity') + return { + modelValue: quantity.value, + 'onUpdate:modelValue': (v) => { quantity.value = v }, + max: qtyParam?.options?.length ? Math.max(...qtyParam.options) : 4, + } + }, + }, + ] + + const platform = { + id: 'painting', + label: 'AI绘画2026', + ModelSelector: markRaw(PaintingModelSelector), + modelSelectorProps: null, + controls, + ImageUploader: markRaw(ImageUploader), + state, + model, + modelType, + modelConfig, + promptPlaceholder, + + async loadModels() { + const code = getPlatformCode('Painting') + return fetchPlatformModels(code) + }, + + async loadConfig(modelName) { + const config = getModelConfig(modelName) + syncDefaults(config) + return config + }, + + getDefaultModel() { + return 'Flux 2' + }, + + showImageUploader() { + if (modelType.value !== 'text') return true + return modelConfig.value?.inputType === 'image' || modelConfig.value?.inputType === 'both' + }, + + imageUploadLimit() { + if (!modelConfig.value) return 4 + const imageParam = modelConfig.value.params.find(p => p.ui === 'imageUpload') + return imageParam?.maxCount || modelConfig.value.maxImages || 4 + }, + + isImageRequired() { + return !!(modelConfig.value?.params?.find(p => p.ui === 'imageUpload')) + }, + + buildTaskBody(shared) { + syncParamValues() + const modelParams = { ...paramValues } + if (shared.prompt.value) modelParams.prompt = shared.prompt.value + return modelParams + }, + + fillFromResult(resultData) { + if (resultData.model !== undefined) model.value = resultData.model + if (resultData.modelType !== undefined) modelType.value = resultData.modelType + if (resultData.proportion !== undefined) proportion.value = resultData.proportion + if (resultData.resolution !== undefined) resolution.value = resultData.resolution + if (resultData.customWidth !== undefined) customWidth.value = resultData.customWidth + if (resultData.customHight !== undefined) customHight.value = resultData.customHight + if (resultData.quantity !== undefined) quantity.value = resultData.quantity + if (resultData.modelParams !== undefined) Object.assign(paramValues, resultData.modelParams) + const dc = getDimConfig(modelConfig.value) + if (dc?.type === 'split') { + if (paramValues[dc.wParam.name] !== undefined) dimWidth.value = paramValues[dc.wParam.name] + if (paramValues[dc.hParam.name] !== undefined) dimHeight.value = paramValues[dc.hParam.name] + } else if (dc?.type === 'combined') { + if (paramValues[dc.paramName]) { + const parsed = dc.config.parse(paramValues[dc.paramName]) + dimWidth.value = parsed.width + dimHeight.value = parsed.height + } + } + if (paramValues.quality !== undefined) quality.value = paramValues.quality + }, + } + + return platform +} + +// 自注册 +registerPlatform('Painting', definePaintingPlatform) diff --git a/src/platforms/painting/modelSelector.vue b/src/platforms/painting/modelSelector.vue new file mode 100644 index 0000000..ddb0b77 --- /dev/null +++ b/src/platforms/painting/modelSelector.vue @@ -0,0 +1,168 @@ + + + + +