feat: 画风选择卡片用静态图片替换 emoji 占位,支持选中/未选中状态切换

- 导入 8 张画风图片(卡通/水彩/水墨/彩铅 × 选中/未选中)
- 根据选中状态动态切换预览图
- 删除 emoji SVG fallback 逻辑
- 隐藏油画、剪贴画(hidden: true)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
En 2026-04-10 11:16:14 +08:00
parent 1fff56d700
commit 387ee5ccfb
9 changed files with 30 additions and 32 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

View File

@ -1,3 +1,6 @@
<script lang="ts">
export default { name: 'StyleSelectView' }
</script>
<template>
<div class="style-page page-fullscreen">
<PageHeader title="选择画风" subtitle="为绘本挑选一种你喜欢的画风" :step="3" />
@ -11,7 +14,7 @@
<div class="style-grid">
<div
v-for="s in styles"
v-for="s in visibleStyles"
:key="s.styleId"
class="style-card"
:class="{ selected: selected === s.styleId }"
@ -24,10 +27,9 @@
<div class="style-preview">
<img
:src="previewUrl(s.styleId)"
:src="selected === s.styleId ? s.imgActive : s.img"
:alt="s.styleName"
class="style-preview-img"
@error="(e) => onPreviewError(e, s.hue)"
/>
</div>
<div class="style-info">
@ -58,6 +60,16 @@ import {
import PageHeader from '@/components/aicreate/PageHeader.vue'
import { useAicreateStore } from '@/stores/aicreate'
// {style}-normal.png {style}-active.png
import styleCartoonNormal from '@/assets/images/style-cartoon-normal.png'
import styleCartoonActive from '@/assets/images/style-cartoon-active.png'
import styleWatercolorNormal from '@/assets/images/style-watercolor-normal.png'
import styleWatercolorActive from '@/assets/images/style-watercolor-active.png'
import styleInkNormal from '@/assets/images/style-ink-normal.png'
import styleInkActive from '@/assets/images/style-ink-active.png'
import stylePencilNormal from '@/assets/images/style-pencil-normal.png'
import stylePencilActive from '@/assets/images/style-pencil-active.png'
const router = useRouter()
const store = useAicreateStore()
const selected = ref('')
@ -66,40 +78,26 @@ interface StyleItem {
styleId: string
styleName: string
desc: string
/** 用于预览图加载失败时的 fallback 渐变色调HSL 色相) */
hue: number
/** 未选中态图片 */
img: string
/** 选中态图片 */
imgActive: string
/** hidden=true 时暂不展示,后续开发开放 */
hidden?: boolean
}
const styles: StyleItem[] = [
{ styleId: 'style_cartoon', styleName: '卡通风格', desc: '色彩鲜明,充满童趣', hue: 30 },
{ styleId: 'style_watercolor', styleName: '水彩风格', desc: '柔和透明,梦幻浪漫', hue: 200 },
{ styleId: 'style_ink', styleName: '水墨国风', desc: '古韵悠长,意境深远', hue: 270 },
{ styleId: 'style_pencil', styleName: '彩铅风格', desc: '细腻温暖,自然亲切', hue: 50 },
{ styleId: 'style_oilpaint', styleName: '油画风格', desc: '色彩浓郁,质感丰富', hue: 25 },
{ styleId: 'style_collage', styleName: '剪贴画', desc: '趣味拼贴,创意满满', hue: 320 },
{ styleId: 'style_cartoon', styleName: '卡通风格', desc: '色彩鲜明,充满童趣', img: styleCartoonNormal, imgActive: styleCartoonActive },
{ styleId: 'style_watercolor', styleName: '水彩风格', desc: '柔和透明,梦幻浪漫', img: styleWatercolorNormal, imgActive: styleWatercolorActive },
{ styleId: 'style_ink', styleName: '水墨国风', desc: '古韵悠长,意境深远', img: styleInkNormal, imgActive: styleInkActive },
{ styleId: 'style_pencil', styleName: '彩铅风格', desc: '细腻温暖,自然亲切', img: stylePencilNormal, imgActive: stylePencilActive },
// hidden
{ styleId: 'style_oilpaint', styleName: '油画风格', desc: '色彩浓郁,质感丰富', img: '', imgActive: '', hidden: true },
{ styleId: 'style_collage', styleName: '剪贴画', desc: '趣味拼贴,创意满满', img: '', imgActive: '', hidden: true },
]
/** 预览图路径约定:/public/aicreate/styles/{styleId}.jpg */
const previewUrl = (id: string) => `/aicreate/styles/${id}.jpg`
/** 加载失败时切换为对应色调的 SVG 渐变占位 */
const fallbackSvg = (hue: number) =>
'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(
`<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" viewBox="0 0 240 240">` +
`<defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">` +
`<stop offset="0" stop-color="hsl(${hue},65%,84%)"/>` +
`<stop offset="1" stop-color="hsl(${(hue + 30) % 360},70%,66%)"/>` +
`</linearGradient></defs>` +
`<rect width="240" height="240" fill="url(#g)"/>` +
`</svg>`
)
const onPreviewError = (e: Event, hue: number) => {
const img = e.target as HTMLImageElement
if (img.dataset.fallback === '1') return // fallback
img.dataset.fallback = '1'
img.src = fallbackSvg(hue)
}
/** 页面可见的风格列表 */
const visibleStyles = styles.filter(s => !s.hidden)
const goNext = () => {
store.selectedStyle = selected.value