feat: 接入形态图库与 Seedream 生图操作

展示已有主图、候选图和失败记录,支持单图及批量生成、主图选择。
补充正式 ID 校验、费用确认、图片加载占位和回归测试。
依赖后端 eac8dc3 的项目形态图库只读接口。
This commit is contained in:
GouJ
2026-08-28 15:16:25 +08:00
parent bae69c3c62
commit 47443c0efe
26 changed files with 1338 additions and 25 deletions
+2 -1
View File
@@ -11,7 +11,7 @@ import {
import { X } from '@lucide/vue'
/** 统一弹窗容器,Reka UI 负责焦点锁定、Escape 和无障碍语义。 */
defineProps<{ title: string; description: string; busy?: boolean }>()
defineProps<{ title: string; description: string; busy?: boolean; wide?: boolean }>()
const open = defineModel<boolean>('open', { default: false })
</script>
@@ -22,6 +22,7 @@ const open = defineModel<boolean>('open', { default: false })
<DialogOverlay class="dialog-overlay" />
<DialogContent
class="dialog-content"
:class="{ 'dialog-wide': wide }"
@interact-outside="busy && $event.preventDefault()"
@escape-key-down="busy && $event.preventDefault()"
>
+40
View File
@@ -0,0 +1,40 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { ImageOff } from '@lucide/vue'
import { referenceImageUrl } from '../../lib/assets'
/** 安全图片展示,资源失效或地址不合法时提供明确占位,不自动改用临时 Provider URL。 */
const props = withDefaults(
defineProps<{ src?: string | null; alt: string; emptyText?: string; retryable?: boolean }>(),
{ retryable: true }
)
const failed = ref(false)
const url = computed(() => (props.src ? referenceImageUrl(props.src) : null))
watch(
() => props.src,
() => {
failed.value = false
}
)
</script>
<template>
<div class="asset-image">
<img
v-if="url && !failed"
:src="url"
:alt="alt"
loading="lazy"
referrerpolicy="no-referrer"
@error="failed = true"
/>
<span v-else class="asset-image-empty"
><ImageOff :size="22" :stroke-width="1.3" /><span>{{
src ? '图片无法加载' : emptyText || '尚未生成图片'
}}</span
><button v-if="failed && retryable" type="button" class="text-button" @click.stop="failed = false">
重试加载
</button></span
>
</div>
</template>
+1
View File
@@ -2,3 +2,4 @@
export { default as AppDialog } from './AppDialog.vue'
export { default as EmptyState } from './EmptyState.vue'
export { default as StatusBadge } from './StatusBadge.vue'
export { default as AssetImage } from './AssetImage.vue'