fix(settings): use local state in LLMTableRow to avoid props mutation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 11:29:49 +08:00
parent 6966ced359
commit fad41ce94a

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed, watch } from 'vue'
import { Eye, EyeOff, Play, ChevronDown, ChevronRight, Trash2 } from 'lucide-vue-next' import { Eye, EyeOff, Play, ChevronDown, ChevronRight, Trash2 } from 'lucide-vue-next'
import type { LLMModelConfig } from '@/api/settings' import type { LLMModelConfig } from '@/api/settings'
@@ -17,6 +17,14 @@ const emit = defineEmits<{
}>() }>()
const showApiKey = ref(false) const showApiKey = ref(false)
const editingModel = ref<LLMModelConfig>({ ...props.model })
// Reinitialize editing model when expanding (handles editing different rows)
watch(() => props.isExpanded, (expanded, wasExpanded) => {
if (expanded && !wasExpanded) {
editingModel.value = { ...props.model }
}
})
const status = computed(() => { const status = computed(() => {
if (!props.model.api_key || !props.model.model) return 'empty' if (!props.model.api_key || !props.model.model) return 'empty'
@@ -37,8 +45,8 @@ function onProviderChange() {
claude: 'https://api.anthropic.com', claude: 'https://api.anthropic.com',
deepseek: 'https://api.deepseek.com/v1' deepseek: 'https://api.deepseek.com/v1'
} }
if (!props.model.base_url || Object.values(defaults).includes(props.model.base_url)) { if (!editingModel.value.base_url || Object.values(defaults).includes(editingModel.value.base_url)) {
emit('update', { ...props.model, base_url: defaults[props.model.provider] || '' }) editingModel.value.base_url = defaults[editingModel.value.provider] || ''
} }
} }
</script> </script>
@@ -69,7 +77,7 @@ function onProviderChange() {
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
<label>// PROVIDER</label> <label>// PROVIDER</label>
<select v-model="model.provider" @change="onProviderChange"> <select v-model="editingModel.provider" @change="onProviderChange">
<option value="openai">OpenAI</option> <option value="openai">OpenAI</option>
<option value="claude">Claude</option> <option value="claude">Claude</option>
<option value="ollama">Ollama</option> <option value="ollama">Ollama</option>
@@ -79,18 +87,18 @@ function onProviderChange() {
</div> </div>
<div class="form-group"> <div class="form-group">
<label>// MODEL</label> <label>// MODEL</label>
<input v-model="model.model" type="text" placeholder="gpt-4o" /> <input v-model="editingModel.model" type="text" placeholder="gpt-4o" />
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>// BASE URL</label> <label>// BASE URL</label>
<input v-model="model.base_url" type="text" /> <input v-model="editingModel.base_url" type="text" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label>// API KEY</label> <label>// API KEY</label>
<div class="input-with-toggle"> <div class="input-with-toggle">
<input <input
v-model="model.api_key" v-model="editingModel.api_key"
:type="showApiKey ? 'text' : 'password'" :type="showApiKey ? 'text' : 'password'"
placeholder="sk-..." placeholder="sk-..."
/> />
@@ -101,13 +109,13 @@ function onProviderChange() {
</div> </div>
</div> </div>
<div class="panel-actions"> <div class="panel-actions">
<button class="test-btn" @click="emit('test', model)"> <button class="test-btn" @click="emit('test', editingModel)">
<Play :size="12" /> 测试连接 <Play :size="12" /> 测试连接
</button> </button>
<button <button
class="save-btn" class="save-btn"
:disabled="status !== 'available'" :disabled="status !== 'available'"
@click="emit('update', model)" @click="emit('update', editingModel)"
> >
保存 保存
</button> </button>