!70 fix: 自定义标签融入原生标签栏,点击切换而非消失

Merge pull request !70 from 郭永昊/fix/tag-labels-and-click
This commit is contained in:
2026-05-25 12:05:05 +00:00
committed by Gitee
8 changed files with 50 additions and 57 deletions
+6 -3
View File
@@ -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'
@@ -91,9 +91,6 @@ export default function CreateProjectModal({
</label>
<div style={{ marginBottom: 16 }}>
<span style={{ display: 'block', fontSize: 13, fontWeight: 600, marginBottom: 8, color: 'var(--text-secondary)' }}>
工程类型
</span>
<StyleSelector
value={style}
onChange={handleStyleChange}
@@ -103,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>
+3 -40
View File
@@ -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,51 +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 => (
<span
key={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)',
}}
>
{tag}
<button
type="button"
style={{
background: 'transparent',
border: 'none',
color: 'var(--text-muted)',
fontSize: 14,
padding: 0,
lineHeight: 1,
cursor: 'pointer',
}}
onClick={() => onChange(removeCustomTag(style, tag))}
>
x
</button>
</span>
))}
</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
+2 -2
View File
@@ -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'
@@ -73,9 +73,6 @@ export default function ProjectConfigPanel() {
</div>
<div style={{ flex: 1, overflow: 'auto', padding: '12px 16px' }}>
<div style={{ fontSize: 13, fontWeight: 600, marginBottom: 8, color: 'var(--text-secondary)' }}>
工程类型
</div>
<StyleSelector
value={draftStyle}
onChange={updateDraft}
@@ -85,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>
+4 -2
View File
@@ -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(', ')
+30 -1
View File
@@ -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>
)
}
+1 -1
View File
@@ -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)