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:
1 parent
0d2322b118
commit
285b2c50bf
2 files changed
+226
-45
No files matched your search
@@ -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
@@ -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'))
|
||||
|
||||
Reference in new issue
Block a user