refactor(ui): 重构组件架构并新增上下文模板类型支持

- feat(TemplateManager): 新增3个上下文相关模板类型
  - contextSystemOptimize: 上下文系统优化模板
  - contextUserOptimize: 上下文用户优化模板
  - contextIterate: 上下文迭代模板
  - 完善类型映射和选择逻辑

- refactor(App): 组件统一化重构
  - 将ModelSelectUI和TemplateSelectUI替换为SelectWithConfig
  - 实现统一的选择器组件,提高代码复用性
  - 新增响应式数据管理和状态同步机制
  - 优化模板和模型选择的刷新逻辑
  - 改进v-model绑定方式,符合Vue 3最佳实践

- improve: 状态管理优化
  - 新增templateOptions和textModelOptions响应式数据
  - 实现自动状态同步和选择验证
  - 优化错误处理和边界情况处理
This commit is contained in:
linshen committed 2025-09-27 22:59:44 +08:00
1 parent 0d2322b118
commit 285b2c50bf
2 files changed
+226 -45

No files matched your search

+19 -1
View File
@@ -593,7 +593,16 @@ const props = defineProps<{
selectedSystemOptimizeTemplate?: Template,
selectedUserOptimizeTemplate?: Template,
selectedIterateTemplate?: Template,
templateType: 'optimize' | 'userOptimize' | 'iterate' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate',
templateType:
| 'optimize'
| 'userOptimize'
| 'iterate'
| 'text2imageOptimize'
| 'image2imageOptimize'
| 'imageIterate'
| 'contextSystemOptimize'
| 'contextUserOptimize'
| 'contextIterate',
show: boolean
}>()
@@ -638,10 +647,13 @@ const migrationDialog = ref<{
const selectedTemplate = computed(() => {
switch (props.templateType) {
case 'optimize':
case 'contextSystemOptimize':
return props.selectedSystemOptimizeTemplate
case 'userOptimize':
case 'contextUserOptimize':
return props.selectedUserOptimizeTemplate
case 'iterate':
case 'contextIterate':
return props.selectedIterateTemplate
default:
return null
@@ -663,6 +675,12 @@ function getCategoryFromProps() {
return 'image-image2image-optimize'
case 'imageIterate':
return 'image-iterate'
case 'contextSystemOptimize':
return 'context-system-optimize'
case 'contextUserOptimize':
return 'context-user-optimize'
case 'contextIterate':
return 'context-iterate'
default:
return 'system-optimize'
}
+207 -44
View File
@@ -17,8 +17,8 @@
<!-- Core Navigation Slot -->
<template #core-nav>
<FunctionModeSelector
v-model="functionMode"
@change="handleModeSelect"
:modelValue="functionMode"
@update:modelValue="handleModeSelect"
/>
</template>
@@ -125,23 +125,44 @@
/>
</template>
<template #model-select>
<ModelSelectUI
:ref="(el) => modelSelectRefs.optimizeModelSelect = el"
:modelValue="modelManager.selectedOptimizeModel"
@update:modelValue="modelManager.selectedOptimizeModel = $event"
<SelectWithConfig
v-model="modelManager.selectedOptimizeModel"
:options="textModelOptions"
:getPrimary="(opt: any) => typeof opt.label === 'string' ? opt.label.replace(/\s*\(.*\)\s*$/, '') : ''"
:getSecondary="(opt: any) => {
const s = typeof opt.label === 'string' ? opt.label : ''
const m = s.match(/\((.*)\)\s*$/)
return m ? m[1] : ''
}"
:getValue="(opt: any) => opt.value"
:placeholder="t('model.select.placeholder')"
size="medium"
:disabled="optimizer.isOptimizing"
filterable
:show-config-action="true"
:show-empty-config-c-t-a="true"
@focus="refreshTextModels"
@config="modelManager.showConfig = true"
/>
</template>
<template #template-select>
<TemplateSelectUI
v-if="services && services.templateManager"
ref="templateSelectRef"
v-model="currentSelectedTemplate"
:type="templateSelectType"
:optimization-mode="selectedOptimizationMode"
@manage="openTemplateManager"
/>
<template v-if="services && services.templateManager">
<SelectWithConfig
v-model="selectedTemplateIdForSelect"
:options="templateOptions as any"
:getPrimary="(tpl: any) => tpl?.name || ''"
:getSecondary="(tpl: any) => tpl?.metadata?.description || ''"
:getValue="(tpl: any) => tpl?.id"
:placeholder="t('template.select')"
size="medium"
:disabled="optimizer.isOptimizing"
filterable
:show-config-action="true"
:show-empty-config-c-t-a="true"
@focus="refreshOptimizeTemplates"
@config="handleOpenOptimizeTemplateManager"
/>
</template>
<NText v-else depth="3" class="p-2 text-sm">
{{ t('template.loading') || '加载中...' }}
</NText>
@@ -214,11 +235,22 @@
>
<!-- 模型选择插槽 -->
<template #model-select>
<ModelSelectUI
:ref="(el) => modelSelectRefs.testModelSelect = el"
:modelValue="modelManager.selectedTestModel"
@update:modelValue="modelManager.selectedTestModel = $event"
:disabled="false"
<SelectWithConfig
v-model="modelManager.selectedTestModel"
:options="textModelOptions"
:getPrimary="(opt: any) => typeof opt.label === 'string' ? opt.label.replace(/\s*\(.*\)\s*$/, '') : ''"
:getSecondary="(opt: any) => {
const s = typeof opt.label === 'string' ? opt.label : ''
const m = s.match(/\((.*)\)\s*$/)
return m ? m[1] : ''
}"
:getValue="(opt: any) => opt.value"
:placeholder="t('model.select.placeholder')"
size="medium"
filterable
:show-config-action="true"
:show-empty-config-c-t-a="true"
@focus="refreshTextModels"
@config="modelManager.showConfig = true"
/>
</template>
@@ -335,7 +367,7 @@ hljs.registerLanguage('json', jsonLang)
// UI Components
MainLayoutUI, ThemeToggleUI, ActionButtonUI, ModelManagerUI, TemplateManagerUI, HistoryDrawerUI,
LanguageSwitchDropdown, DataManagerUI, InputPanelUI, PromptPanelUI, OptimizationModeSelectorUI,
ModelSelectUI, TemplateSelectUI, TestAreaPanel, UpdaterIcon, VariableManagerModal,
SelectWithConfig, TestAreaPanel, UpdaterIcon, VariableManagerModal,
ImageWorkspace, FunctionModeSelector,
ConversationManager, OutputDisplay, ContextEditor,
@@ -365,7 +397,7 @@ hljs.registerLanguage('json', jsonLang)
// Quick Template Manager
quickTemplateManager,
} from '@prompt-optimizer/ui'
import type { IPromptService } from '@prompt-optimizer/core'
import type { IPromptService, Template, ModelConfig } from '@prompt-optimizer/core'
// 1. 基础 composables
// highlight.js for Naive NCode
@@ -401,7 +433,6 @@ hljs.registerLanguage('json', jsonLang)
const showDataManager = ref(false)
const optimizeModelSelect = ref(null)
const testPanelRef = ref(null)
const templateSelectRef = ref<{ refresh?: () => void } | null>(null)
const promptPanelRef = ref<{ refreshIterateTemplateSelect?: () => void } | null>(null)
// 高级模式状态
@@ -710,7 +741,156 @@ hljs.registerLanguage('json', jsonLang)
selectedIterateTemplate: toRef(optimizer, 'selectedIterateTemplate')
}
)
const currentSelectedTemplate = computed({
get() {
return selectedOptimizationMode.value === 'system'
? optimizer.selectedOptimizeTemplate
: optimizer.selectedUserOptimizeTemplate
},
set(newValue) {
if (!newValue) return
if (selectedOptimizationMode.value === 'system') {
optimizer.selectedOptimizeTemplate = newValue
} else {
optimizer.selectedUserOptimizeTemplate = newValue
}
}
})
const templateOptions = ref<Template[]>([])
const textModelOptions = ref<Array<{ label: string; value: string; raw: ModelConfig & { key: string } }>>([])
const handleOpenOptimizeTemplateManager = () => {
const type = templateSelectType.value
console.log('[App] Opening template manager for type:', type)
openTemplateManager(type as any)
}
const clearCurrentTemplateSelection = () => {
if (selectedOptimizationMode.value === 'system') {
optimizer.selectedOptimizeTemplate = null
} else {
optimizer.selectedUserOptimizeTemplate = null
}
}
const ensureTemplateSelection = () => {
const current = currentSelectedTemplate.value
const available = templateOptions.value
if (current) {
const matched = available.find(t => t.id === current.id)
if (matched) {
if (matched !== current) {
currentSelectedTemplate.value = matched
}
return
}
}
if (available.length > 0) {
currentSelectedTemplate.value = available[0]
} else {
clearCurrentTemplateSelection()
}
}
const refreshOptimizeTemplates = async () => {
if (!services.value?.templateManager) {
templateOptions.value = []
clearCurrentTemplateSelection()
return
}
try {
const list = await services.value.templateManager.listTemplatesByType(templateSelectType.value as any)
templateOptions.value = list || []
} catch (error) {
console.warn('[App] Failed to refresh optimize templates:', error)
templateOptions.value = []
}
ensureTemplateSelection()
}
const refreshTextModels = async () => {
if (!services.value?.modelManager) {
textModelOptions.value = []
return
}
try {
const manager = services.value.modelManager
if (typeof (manager as any).ensureInitialized === 'function') {
await (manager as any).ensureInitialized()
}
const enabledModels = await manager.getEnabledModels()
textModelOptions.value = enabledModels.map((m: ModelConfig & { key: string }) => {
const providerName = (m as any)?.provider ?? (m as any)?.providerId ?? 'Unknown'
return {
label: `${m.name} (${providerName})`,
value: m.key,
raw: m
}
})
const availableKeys = new Set(textModelOptions.value.map(opt => opt.value))
const fallbackValue = textModelOptions.value[0]?.value || ''
if (fallbackValue) {
if (!availableKeys.has(modelManager.selectedOptimizeModel)) {
modelManager.selectedOptimizeModel = fallbackValue
}
if (!availableKeys.has(modelManager.selectedTestModel)) {
modelManager.selectedTestModel = fallbackValue
}
}
} catch (error) {
console.warn('[App] Failed to refresh text models:', error)
textModelOptions.value = []
}
}
const selectedTemplateIdForSelect = computed<string>({
get() {
const current = currentSelectedTemplate.value
if (!current) return ''
return templateOptions.value.some(t => t.id === current.id) ? current.id : ''
},
set(id: string) {
if (!id) {
clearCurrentTemplateSelection()
return
}
const tpl = templateOptions.value.find(t => t.id === id)
if (tpl) {
currentSelectedTemplate.value = tpl
}
}
})
watch(() => services.value?.templateManager, async (manager) => {
if (manager) {
await refreshOptimizeTemplates()
} else {
templateOptions.value = []
clearCurrentTemplateSelection()
}
}, { immediate: true })
watch(() => services.value?.modelManager, async (manager) => {
if (manager) {
await refreshTextModels()
} else {
textModelOptions.value = []
}
}, { immediate: true })
watch(() => templateSelectType.value, async () => {
await refreshOptimizeTemplates()
})
// 7. 监听服务初始化
watch(services, async (newServices) => {
if (!newServices) return
@@ -746,23 +926,6 @@ hljs.registerLanguage('json', jsonLang)
}, 1500)
}
// 8. 计算属性和方法
const currentSelectedTemplate = computed({
get() {
return selectedOptimizationMode.value === 'system'
? optimizer.selectedOptimizeTemplate
: optimizer.selectedUserOptimizeTemplate
},
set(newValue) {
if (!newValue) return
if (selectedOptimizationMode.value === 'system') {
optimizer.selectedOptimizeTemplate = newValue
} else {
optimizer.selectedUserOptimizeTemplate = newValue
}
}
})
// 处理优化提示词
const handleOptimizePrompt = () => {
// 检查是否需要传递高级上下文
@@ -912,9 +1075,7 @@ hljs.registerLanguage('json', jsonLang)
console.log('[App] 模板语言已切换:', newLanguage)
// 刷新主界面的模板选择组件
if (templateSelectRef.value?.refresh) {
templateSelectRef.value.refresh()
}
refreshOptimizeTemplates()
// 刷新迭代页面的模板选择组件
if (promptPanelRef.value?.refreshIterateTemplateSelect) {
@@ -933,10 +1094,11 @@ hljs.registerLanguage('json', jsonLang)
// 模板管理器关闭回调:刷新基础模式选择,同时通知图像模式刷新模板列表
const handleTemplateManagerClosed = () => {
try {
templateManagerState.handleTemplateManagerClose(() => templateSelectRef.value?.refresh?.())
templateManagerState.handleTemplateManagerClose(() => { refreshOptimizeTemplates() })
} catch (e) {
console.warn('[App] Failed to run template manager close handler:', e)
}
refreshOptimizeTemplates()
if (typeof window !== 'undefined') {
window.dispatchEvent(new Event('image-workspace-refresh-templates'))
}
@@ -962,6 +1124,7 @@ hljs.registerLanguage('json', jsonLang)
} catch (e) {
console.warn('[App] Failed to refresh text models after manager close:', e)
}
await refreshTextModels()
// 图像模式:广播刷新图像模型事件(ImageWorkspace 监听并执行刷新)
if (typeof window !== 'undefined') {
window.dispatchEvent(new Event('image-workspace-refresh-image-models'))