diff --git a/frontend/src/components/canvas/CanvasNodeView.vue b/frontend/src/components/canvas/CanvasNodeView.vue index e151fe1..801149d 100644 --- a/frontend/src/components/canvas/CanvasNodeView.vue +++ b/frontend/src/components/canvas/CanvasNodeView.vue @@ -24,7 +24,7 @@ const editing = ref(false) let imgController = null async function loadSrc() { - if (props.node.type !== 'image' || !props.node.src) return + if (!['image', 'video'].includes(props.node.type) || !props.node.src) return imgController?.abort() imgController = new AbortController() imgLoaded.value = false @@ -79,6 +79,19 @@ function onImgLoad(e) { }) } } +function onVidMeta(e) { + imgLoaded.value = true + imgBroken.value = false + const v = e.target + if (v.videoWidth && v.videoHeight) { + const long = Math.max(v.videoWidth, v.videoHeight) + const k = 512 / long + emit('imgload', { + w: Math.round(v.videoWidth * k), + h: Math.round(v.videoHeight * k), + }) + } +} function startEdit() { if (props.node.type !== 'note') return @@ -164,6 +177,23 @@ defineExpose({ startEdit, commitNote })
{{ node.prompt }}
+ + +
runningGens.value > 0) const genMsg = ref('') -const imageModels = computed(() => - (allModels.value || []).filter((m) => m.enabled !== false && m.type === 'image'), +const kindModels = computed(() => + (allModels.value || []).filter((m) => m.enabled !== false && m.type === gen.kind), ) -const model = computed(() => imageModels.value.find((m) => m.id === gen.modelId) || null) +const model = computed(() => kindModels.value.find((m) => m.id === gen.modelId) || null) const ratios = computed(() => model.value?.ratios?.length ? model.value.ratios : ['1:1']) const resolutions = computed(() => model.value?.resolutions?.length ? model.value.resolutions : Object.keys(model.value?.prices || {})) +const durations = computed(() => model.value?.durations?.length ? model.value.durations : ['5s']) const maxRefs = computed(() => Math.min(8, Math.max( Number(model.value?.max_reference_images || 0), model.value?.image_to_image ? 1 : 0, ))) -// 换模型时重置参数默认值(和画图台一致) +// 换类型/模型时重置参数默认值(和画图台一致) +watch(() => gen.kind, () => { + const first = kindModels.value[0] + gen.modelId = first?.id || '' +}) watch(model, (m) => { if (!m) return gen.ratio = m.ratios?.[0] || '1:1' const res = m.resolutions?.length ? m.resolutions : Object.keys(m.prices || {}) gen.resolution = res[0] || '1K' + gen.duration = m.durations?.[0] || '5s' }) const selectedNotes = computed(() => selectionIds.value @@ -551,7 +557,7 @@ async function generate() { } genMsg.value = '' - const n = Math.max(1, Math.min(4, Number(gen.count) || 1)) + const n = gen.kind === 'image' ? Math.max(1, Math.min(4, Number(gen.count) || 1)) : 1 const size = ratioToSize(gen.ratio || ratios.value[0]) const sources = [...selectedNotes.value] // 落点:参与源节点的包围盒右侧;无源则视口中心。多张时横向排开。 @@ -599,6 +605,7 @@ async function runGeneration(node, prompt) { model: gen.modelId, prompt, ratio: gen.ratio || ratios.value[0], resolution: gen.resolution || resolutions.value[0] || '1K', } + if (gen.kind === 'video') payload.duration = gen.duration || durations.value[0] if (refImages.value.length) { payload.reference_images = refImages.value.map((r) => r.dataUrl.replace(/^data:[^,]*,/, '')) } @@ -606,7 +613,7 @@ async function runGeneration(node, prompt) { opts.headers['Idempotency-Key'] = node.id const r = await api('/generate', opts) if (r.ok && r.data?.url) { - node.type = 'image' + node.type = gen.kind === 'video' ? 'video' : 'image' node.src = r.data.url.replace(/^\/images\//, '') node.prompt = prompt node.model = model.value?.alias || model.value?.name || model.value?.id @@ -746,7 +753,7 @@ onMounted(async () => { document.addEventListener('visibilitychange', onVisibility) const mm = await api('/managed-models') allModels.value = mm.data?.data || [] - const first = imageModels.value[0] + const first = kindModels.value[0] if (first) { gen.modelId = first.id gen.ratio = (first.ratios && first.ratios[0]) || '1:1' @@ -874,44 +881,54 @@ const saveLabel = computed(() => ({ placeholder="描述你要生成的画面…(留空则使用选中的笔记)" />
+ + -
⚠ 还没有可用的图像模型,请到管理后台添加
-
- -
-
- + + +
+ ⚠ 还没有可用的{{ gen.kind === 'video' ? '视频' : '图像' }}模型