1717 <!-- Core Navigation Slot -->
1818 <template #core-nav >
1919 <FunctionModeSelector
20- v-model =" functionMode"
21- @change =" handleModeSelect"
20+ :modelValue =" functionMode"
21+ @update:modelValue =" handleModeSelect"
2222 />
2323 </template >
2424
125125 />
126126 </template >
127127 <template #model-select >
128- <ModelSelectUI
129- :ref =" (el) => modelSelectRefs.optimizeModelSelect = el"
130- :modelValue =" modelManager.selectedOptimizeModel"
131- @update:modelValue =" modelManager.selectedOptimizeModel = $event"
128+ <SelectWithConfig
129+ v-model =" modelManager.selectedOptimizeModel"
130+ :options =" textModelOptions"
131+ :getPrimary =" (opt: any) => typeof opt.label === 'string' ? opt.label.replace(/\s*\(.*\)\s*$/, '') : ''"
132+ :getSecondary =" (opt: any) => {
133+ const s = typeof opt.label === 'string' ? opt.label : ''
134+ const m = s.match(/\((.*)\)\s*$/)
135+ return m ? m[1] : ''
136+ }"
137+ :getValue =" (opt: any) => opt.value"
138+ :placeholder =" t('model.select.placeholder')"
139+ size =" medium"
132140 :disabled =" optimizer.isOptimizing"
141+ filterable
142+ :show-config-action =" true"
143+ :show-empty-config-c-t-a =" true"
144+ @focus =" refreshTextModels"
133145 @config =" modelManager.showConfig = true"
134146 />
135147 </template >
136148 <template #template-select >
137- <TemplateSelectUI
138- v-if =" services && services.templateManager"
139- ref =" templateSelectRef"
140- v-model =" currentSelectedTemplate"
141- :type =" templateSelectType"
142- :optimization-mode =" selectedOptimizationMode"
143- @manage =" openTemplateManager"
144- />
149+ <template v-if =" services && services .templateManager " >
150+ <SelectWithConfig
151+ v-model =" selectedTemplateIdForSelect"
152+ :options =" templateOptions as any"
153+ :getPrimary =" (tpl: any) => tpl?.name || ''"
154+ :getSecondary =" (tpl: any) => tpl?.metadata?.description || ''"
155+ :getValue =" (tpl: any) => tpl?.id"
156+ :placeholder =" t('template.select')"
157+ size =" medium"
158+ :disabled =" optimizer.isOptimizing"
159+ filterable
160+ :show-config-action =" true"
161+ :show-empty-config-c-t-a =" true"
162+ @focus =" refreshOptimizeTemplates"
163+ @config =" handleOpenOptimizeTemplateManager"
164+ />
165+ </template >
145166 <NText v-else depth =" 3" class =" p-2 text-sm" >
146167 {{ t('template.loading') || '加载中...' }}
147168 </NText >
214235 >
215236 <!-- 模型选择插槽 -->
216237 <template #model-select >
217- <ModelSelectUI
218- :ref =" (el) => modelSelectRefs.testModelSelect = el"
219- :modelValue =" modelManager.selectedTestModel"
220- @update:modelValue =" modelManager.selectedTestModel = $event"
221- :disabled =" false"
238+ <SelectWithConfig
239+ v-model =" modelManager.selectedTestModel"
240+ :options =" textModelOptions"
241+ :getPrimary =" (opt: any) => typeof opt.label === 'string' ? opt.label.replace(/\s*\(.*\)\s*$/, '') : ''"
242+ :getSecondary =" (opt: any) => {
243+ const s = typeof opt.label === 'string' ? opt.label : ''
244+ const m = s.match(/\((.*)\)\s*$/)
245+ return m ? m[1] : ''
246+ }"
247+ :getValue =" (opt: any) => opt.value"
248+ :placeholder =" t('model.select.placeholder')"
249+ size =" medium"
250+ filterable
251+ :show-config-action =" true"
252+ :show-empty-config-c-t-a =" true"
253+ @focus =" refreshTextModels"
222254 @config =" modelManager.showConfig = true"
223255 />
224256 </template >
@@ -335,7 +367,7 @@ hljs.registerLanguage('json', jsonLang)
335367 // UI Components
336368 MainLayoutUI , ThemeToggleUI , ActionButtonUI , ModelManagerUI , TemplateManagerUI , HistoryDrawerUI ,
337369 LanguageSwitchDropdown , DataManagerUI , InputPanelUI , PromptPanelUI , OptimizationModeSelectorUI ,
338- ModelSelectUI , TemplateSelectUI , TestAreaPanel , UpdaterIcon , VariableManagerModal ,
370+ SelectWithConfig , TestAreaPanel , UpdaterIcon , VariableManagerModal ,
339371 ImageWorkspace , FunctionModeSelector ,
340372 ConversationManager , OutputDisplay , ContextEditor ,
341373
@@ -365,7 +397,7 @@ hljs.registerLanguage('json', jsonLang)
365397 // Quick Template Manager
366398 quickTemplateManager ,
367399 } from ' @prompt-optimizer/ui'
368- import type { IPromptService } from ' @prompt-optimizer/core'
400+ import type { IPromptService , Template , ModelConfig } from ' @prompt-optimizer/core'
369401
370402 // 1. 基础 composables
371403 // highlight.js for Naive NCode
@@ -401,7 +433,6 @@ hljs.registerLanguage('json', jsonLang)
401433 const showDataManager = ref (false )
402434 const optimizeModelSelect = ref (null )
403435 const testPanelRef = ref (null )
404- const templateSelectRef = ref <{ refresh? : () => void } | null >(null )
405436 const promptPanelRef = ref <{ refreshIterateTemplateSelect? : () => void } | null >(null )
406437
407438 // 高级模式状态
@@ -710,7 +741,156 @@ hljs.registerLanguage('json', jsonLang)
710741 selectedIterateTemplate: toRef (optimizer , ' selectedIterateTemplate' )
711742 }
712743 )
713-
744+
745+ const currentSelectedTemplate = computed ({
746+ get() {
747+ return selectedOptimizationMode .value === ' system'
748+ ? optimizer .selectedOptimizeTemplate
749+ : optimizer .selectedUserOptimizeTemplate
750+ },
751+ set(newValue ) {
752+ if (! newValue ) return
753+ if (selectedOptimizationMode .value === ' system' ) {
754+ optimizer .selectedOptimizeTemplate = newValue
755+ } else {
756+ optimizer .selectedUserOptimizeTemplate = newValue
757+ }
758+ }
759+ })
760+
761+ const templateOptions = ref <Template []>([])
762+ const textModelOptions = ref <Array <{ label: string ; value: string ; raw: ModelConfig & { key: string } }>>([])
763+
764+ const handleOpenOptimizeTemplateManager = () => {
765+ const type = templateSelectType .value
766+ console .log (' [App] Opening template manager for type:' , type )
767+ openTemplateManager (type as any )
768+ }
769+
770+ const clearCurrentTemplateSelection = () => {
771+ if (selectedOptimizationMode .value === ' system' ) {
772+ optimizer .selectedOptimizeTemplate = null
773+ } else {
774+ optimizer .selectedUserOptimizeTemplate = null
775+ }
776+ }
777+
778+ const ensureTemplateSelection = () => {
779+ const current = currentSelectedTemplate .value
780+ const available = templateOptions .value
781+
782+ if (current ) {
783+ const matched = available .find (t => t .id === current .id )
784+ if (matched ) {
785+ if (matched !== current ) {
786+ currentSelectedTemplate .value = matched
787+ }
788+ return
789+ }
790+ }
791+
792+ if (available .length > 0 ) {
793+ currentSelectedTemplate .value = available [0 ]
794+ } else {
795+ clearCurrentTemplateSelection ()
796+ }
797+ }
798+
799+ const refreshOptimizeTemplates = async () => {
800+ if (! services .value ?.templateManager ) {
801+ templateOptions .value = []
802+ clearCurrentTemplateSelection ()
803+ return
804+ }
805+
806+ try {
807+ const list = await services .value .templateManager .listTemplatesByType (templateSelectType .value as any )
808+ templateOptions .value = list || []
809+ } catch (error ) {
810+ console .warn (' [App] Failed to refresh optimize templates:' , error )
811+ templateOptions .value = []
812+ }
813+
814+ ensureTemplateSelection ()
815+ }
816+
817+ const refreshTextModels = async () => {
818+ if (! services .value ?.modelManager ) {
819+ textModelOptions .value = []
820+ return
821+ }
822+
823+ try {
824+ const manager = services .value .modelManager
825+ if (typeof (manager as any ).ensureInitialized === ' function' ) {
826+ await (manager as any ).ensureInitialized ()
827+ }
828+ const enabledModels = await manager .getEnabledModels ()
829+ textModelOptions .value = enabledModels .map ((m : ModelConfig & { key: string }) => {
830+ const providerName = (m as any )?.provider ?? (m as any )?.providerId ?? ' Unknown'
831+ return {
832+ label: ` ${m .name } (${providerName }) ` ,
833+ value: m .key ,
834+ raw: m
835+ }
836+ })
837+
838+ const availableKeys = new Set (textModelOptions .value .map (opt => opt .value ))
839+ const fallbackValue = textModelOptions .value [0 ]?.value || ' '
840+
841+ if (fallbackValue ) {
842+ if (! availableKeys .has (modelManager .selectedOptimizeModel )) {
843+ modelManager .selectedOptimizeModel = fallbackValue
844+ }
845+ if (! availableKeys .has (modelManager .selectedTestModel )) {
846+ modelManager .selectedTestModel = fallbackValue
847+ }
848+ }
849+ } catch (error ) {
850+ console .warn (' [App] Failed to refresh text models:' , error )
851+ textModelOptions .value = []
852+ }
853+ }
854+
855+ const selectedTemplateIdForSelect = computed <string >({
856+ get() {
857+ const current = currentSelectedTemplate .value
858+ if (! current ) return ' '
859+ return templateOptions .value .some (t => t .id === current .id ) ? current .id : ' '
860+ },
861+ set(id : string ) {
862+ if (! id ) {
863+ clearCurrentTemplateSelection ()
864+ return
865+ }
866+ const tpl = templateOptions .value .find (t => t .id === id )
867+ if (tpl ) {
868+ currentSelectedTemplate .value = tpl
869+ }
870+ }
871+ })
872+
873+ watch (() => services .value ?.templateManager , async (manager ) => {
874+ if (manager ) {
875+ await refreshOptimizeTemplates ()
876+ } else {
877+ templateOptions .value = []
878+ clearCurrentTemplateSelection ()
879+ }
880+ }, { immediate: true })
881+
882+ watch (() => services .value ?.modelManager , async (manager ) => {
883+ if (manager ) {
884+ await refreshTextModels ()
885+ } else {
886+ textModelOptions .value = []
887+ }
888+ }, { immediate: true })
889+
890+ watch (() => templateSelectType .value , async () => {
891+ await refreshOptimizeTemplates ()
892+ })
893+
714894 // 7. 监听服务初始化
715895 watch (services , async (newServices ) => {
716896 if (! newServices ) return
@@ -746,23 +926,6 @@ hljs.registerLanguage('json', jsonLang)
746926 }, 1500 )
747927 }
748928
749- // 8. 计算属性和方法
750- const currentSelectedTemplate = computed ({
751- get() {
752- return selectedOptimizationMode .value === ' system'
753- ? optimizer .selectedOptimizeTemplate
754- : optimizer .selectedUserOptimizeTemplate
755- },
756- set(newValue ) {
757- if (! newValue ) return
758- if (selectedOptimizationMode .value === ' system' ) {
759- optimizer .selectedOptimizeTemplate = newValue
760- } else {
761- optimizer .selectedUserOptimizeTemplate = newValue
762- }
763- }
764- })
765-
766929 // 处理优化提示词
767930 const handleOptimizePrompt = () => {
768931 // 检查是否需要传递高级上下文
@@ -912,9 +1075,7 @@ hljs.registerLanguage('json', jsonLang)
9121075 console .log (' [App] 模板语言已切换:' , newLanguage )
9131076
9141077 // 刷新主界面的模板选择组件
915- if (templateSelectRef .value ?.refresh ) {
916- templateSelectRef .value .refresh ()
917- }
1078+ refreshOptimizeTemplates ()
9181079
9191080 // 刷新迭代页面的模板选择组件
9201081 if (promptPanelRef .value ?.refreshIterateTemplateSelect ) {
@@ -933,10 +1094,11 @@ hljs.registerLanguage('json', jsonLang)
9331094 // 模板管理器关闭回调:刷新基础模式选择,同时通知图像模式刷新模板列表
9341095 const handleTemplateManagerClosed = () => {
9351096 try {
936- templateManagerState .handleTemplateManagerClose (() => templateSelectRef . value ?. refresh ?.() )
1097+ templateManagerState .handleTemplateManagerClose (() => { refreshOptimizeTemplates () } )
9371098 } catch (e ) {
9381099 console .warn (' [App] Failed to run template manager close handler:' , e )
9391100 }
1101+ refreshOptimizeTemplates ()
9401102 if (typeof window !== ' undefined' ) {
9411103 window .dispatchEvent (new Event (' image-workspace-refresh-templates' ))
9421104 }
@@ -962,6 +1124,7 @@ hljs.registerLanguage('json', jsonLang)
9621124 } catch (e ) {
9631125 console .warn (' [App] Failed to refresh text models after manager close:' , e )
9641126 }
1127+ await refreshTextModels ()
9651128 // 图像模式:广播刷新图像模型事件(ImageWorkspace 监听并执行刷新)
9661129 if (typeof window !== ' undefined' ) {
9671130 window .dispatchEvent (new Event (' image-workspace-refresh-image-models' ))
0 commit comments