fix: 自定义标签融入原生标签栏,点击切换而非消失
- StyleSelector 新增 customTags 属性,自定义标签作为「自定义标签」行展示在原生标签栏中 - 自定义标签点击切换 true/false,不再移除 key,解决点击消失问题 - CustomTagsEditor 简化为仅输入框+添加按钮 - extractTags 仅提取激活态自定义标签(value=true) - 导出 CUSTOM_PREFIX 供组件使用
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { post } from './client'
|
||||
import { STYLE_CATEGORIES, PROJECT_TYPES, PROJECT_TYPE_KEY, getCustomTags } from '../utils/style'
|
||||
import { STYLE_CATEGORIES, PROJECT_TYPES, PROJECT_TYPE_KEY, CUSTOM_PREFIX, getCustomTags } from '../utils/style'
|
||||
|
||||
interface OptimizePromptParams {
|
||||
tags: string[]
|
||||
@@ -34,8 +34,11 @@ export function extractTags(style: Record<string, string>): string[] {
|
||||
if (option) tags.push(option.label)
|
||||
}
|
||||
|
||||
// 自定义标签
|
||||
tags.push(...getCustomTags(style))
|
||||
// 自定义标签(仅包含激活的)
|
||||
const customTags = getCustomTags(style).filter(
|
||||
tag => style[`${CUSTOM_PREFIX}${tag}`] === 'true'
|
||||
)
|
||||
tags.push(...customTags)
|
||||
return tags
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import { PROJECT_TYPES, PROJECT_TYPE_KEY } from '../utils/style'
|
||||
import { PROJECT_TYPES, PROJECT_TYPE_KEY, getCustomTags } from '../utils/style'
|
||||
import StyleSelector from './StyleSelector'
|
||||
import CustomTagsEditor from './CustomTagsEditor'
|
||||
|
||||
@@ -100,6 +100,7 @@ export default function CreateProjectModal({
|
||||
label: '工程类型',
|
||||
options: PROJECT_TYPES.map(p => ({ value: p.value, label: p.label })),
|
||||
}]}
|
||||
customTags={getCustomTags(style)}
|
||||
/>
|
||||
<CustomTagsEditor style={style} onChange={setStyle} />
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import { getCustomTags, addCustomTag, removeCustomTag } from '../utils/style'
|
||||
import { getCustomTags, addCustomTag } from '../utils/style'
|
||||
|
||||
interface CustomTagsEditorProps {
|
||||
style: Record<string, string>
|
||||
@@ -26,49 +26,14 @@ export default function CustomTagsEditor({ style, onChange }: CustomTagsEditorPr
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: 12 }}>
|
||||
<div style={{ fontSize: 13, fontWeight: 600, marginBottom: 8, color: 'var(--text-secondary)' }}>
|
||||
自定义标签
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 8 }}>
|
||||
{tags.map(tag => (
|
||||
<button
|
||||
key={tag}
|
||||
type="button"
|
||||
onClick={() => onChange(removeCustomTag(style, tag))}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
fontSize: 12,
|
||||
padding: '3px 10px',
|
||||
borderRadius: 999,
|
||||
background: 'var(--bg-input)',
|
||||
border: '1px solid var(--border)',
|
||||
color: 'var(--text-primary)',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
{tag}
|
||||
<span
|
||||
style={{
|
||||
color: 'var(--text-muted)',
|
||||
fontSize: 14,
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
x
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<input
|
||||
type="text"
|
||||
value={input}
|
||||
onChange={e => setInput(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="输入标签后按 Enter 添加"
|
||||
placeholder="输入自定义标签后按 Enter 添加"
|
||||
style={{ flex: 1, padding: '6px 10px', fontSize: 13 }}
|
||||
/>
|
||||
<button
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { AssetType } from '../api/types'
|
||||
import { useProjectStore } from '../stores/project'
|
||||
import { useTaskStore } from '../stores/task'
|
||||
import { mergeStyles } from '../utils/style'
|
||||
import { mergeStyles, getCustomTags } from '../utils/style'
|
||||
import StyleSelector from './StyleSelector'
|
||||
import CustomTagsEditor from './CustomTagsEditor'
|
||||
import PromptEditor from './PromptEditor'
|
||||
@@ -122,7 +122,7 @@ export default function GenerateForm({ onSubmit, submitting }: GenerateFormProps
|
||||
<h3 style={{ marginBottom: 12, fontSize: 14, color: 'var(--text-secondary)' }}>
|
||||
素材风格
|
||||
</h3>
|
||||
<StyleSelector value={taskStyle} onChange={toggleTaskStyle} compact />
|
||||
<StyleSelector value={taskStyle} onChange={toggleTaskStyle} compact customTags={getCustomTags(taskStyle)} />
|
||||
<CustomTagsEditor style={taskStyle} onChange={setTaskStyle} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useProjectStore } from '../stores/project'
|
||||
import { useToastStore } from '../stores/toast'
|
||||
import { PROJECT_TYPES, PROJECT_TYPE_KEY } from '../utils/style'
|
||||
import { PROJECT_TYPES, PROJECT_TYPE_KEY, getCustomTags } from '../utils/style'
|
||||
import StyleSelector from './StyleSelector'
|
||||
import CustomTagsEditor from './CustomTagsEditor'
|
||||
import Skeleton from './Skeleton'
|
||||
@@ -82,6 +82,7 @@ export default function ProjectConfigPanel() {
|
||||
label: '工程类型',
|
||||
options: PROJECT_TYPES.map(p => ({ value: p.value, label: p.label })),
|
||||
}]}
|
||||
customTags={getCustomTags(draftStyle)}
|
||||
/>
|
||||
<CustomTagsEditor style={draftStyle} onChange={setDraftStyle} />
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { STYLE_CATEGORIES, PROJECT_TYPES, PROJECT_TYPE_KEY, getCustomTags } from '../utils/style'
|
||||
import { STYLE_CATEGORIES, PROJECT_TYPES, PROJECT_TYPE_KEY, CUSTOM_PREFIX, getCustomTags } from '../utils/style'
|
||||
import styles from './GenerateForm.module.css'
|
||||
|
||||
interface PromptEditorProps {
|
||||
@@ -32,7 +32,9 @@ function styleToDescription(kvPairs: Record<string, string>): string {
|
||||
if (option) parts.push(`${cat.label}: ${option.label}`)
|
||||
}
|
||||
|
||||
const customTags = getCustomTags(kvPairs)
|
||||
const customTags = getCustomTags(kvPairs).filter(
|
||||
tag => kvPairs[`${CUSTOM_PREFIX}${tag}`] === 'true'
|
||||
)
|
||||
if (customTags.length > 0) parts.push(`标签: ${customTags.join(', ')}`)
|
||||
|
||||
return parts.join(', ')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { StyleCategory } from '../utils/style'
|
||||
import { STYLE_CATEGORIES } from '../utils/style'
|
||||
import { STYLE_CATEGORIES, CUSTOM_PREFIX } from '../utils/style'
|
||||
import styles from './StyleSelector.module.css'
|
||||
|
||||
interface StyleSelectorProps {
|
||||
@@ -7,6 +7,13 @@ interface StyleSelectorProps {
|
||||
onChange: (key: string, value: string) => void
|
||||
compact?: boolean
|
||||
categories?: StyleCategory[]
|
||||
customTags?: string[]
|
||||
}
|
||||
|
||||
const CUSTOM_TAG_CATEGORY: StyleCategory = {
|
||||
key: '', // unused, pills use custom: prefix directly
|
||||
label: '自定义标签',
|
||||
options: [],
|
||||
}
|
||||
|
||||
export default function StyleSelector({
|
||||
@@ -14,6 +21,7 @@ export default function StyleSelector({
|
||||
onChange,
|
||||
compact,
|
||||
categories,
|
||||
customTags,
|
||||
}: StyleSelectorProps) {
|
||||
const cats = categories ?? STYLE_CATEGORIES
|
||||
return (
|
||||
@@ -37,6 +45,27 @@ export default function StyleSelector({
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{customTags && customTags.length > 0 && (
|
||||
<div key="_custom" className={styles.category}>
|
||||
<span className={styles.label}>{CUSTOM_TAG_CATEGORY.label}</span>
|
||||
<div className={styles.options}>
|
||||
{customTags.map(tag => {
|
||||
const key = `${CUSTOM_PREFIX}${tag}`
|
||||
const isActive = value[key] === 'true'
|
||||
return (
|
||||
<button
|
||||
key={tag}
|
||||
type="button"
|
||||
className={`${styles.pill} ${isActive ? styles.pillActive : ''}`}
|
||||
onClick={() => onChange(key, isActive ? 'false' : 'true')}
|
||||
>
|
||||
{tag}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export function mergeStyles(
|
||||
return { ...projectStyle, ...taskOverrides }
|
||||
}
|
||||
|
||||
const CUSTOM_PREFIX = 'custom:'
|
||||
export const CUSTOM_PREFIX = 'custom:'
|
||||
|
||||
export function isCustomKey(key: string): boolean {
|
||||
return key.startsWith(CUSTOM_PREFIX)
|
||||
|
||||
Reference in New Issue
Block a user