|
179 | 179 | </div> |
180 | 180 |
|
181 | 181 | <div class="modal-models-section"> |
182 | | - <div class="model-search"> |
| 182 | + <!-- 警告:检测到失效模型 --> |
| 183 | + <div v-if="unsupportedModels.length > 0" class="simple-notice warning" style="margin-bottom: 20px;"> |
| 184 | + <p>检测到配置中包含当前供应商列表中不存在的模型。以下模型可能已失效或被供应商移除:</p> |
| 185 | + <div class="unsupported-list"> |
| 186 | + <a-tag |
| 187 | + closable |
| 188 | + v-for="model in unsupportedModels" |
| 189 | + :key="model" |
| 190 | + color="error" |
| 191 | + @close="removeModel(model)" |
| 192 | + style="margin-bottom: 4px;" |
| 193 | + > |
| 194 | + {{ model }} |
| 195 | + </a-tag> |
| 196 | + </div> |
| 197 | + <a-button size="small" type="primary" danger ghost @click="removeAllUnsupported" class="clear-btn"> |
| 198 | + 一键移除所有失效模型 |
| 199 | + </a-button> |
| 200 | + </div> |
| 201 | + |
| 202 | + <div class="model-search" v-if="providerConfig.allModels.length > 0"> |
183 | 203 | <a-input |
184 | 204 | v-model:value="providerConfig.searchQuery" |
185 | 205 | placeholder="搜索模型..." |
|
192 | 212 | </div> |
193 | 213 |
|
194 | 214 | <!-- 显示选中统计信息 --> |
195 | | - <div class="selection-summary"> |
| 215 | + <div class="selection-summary" v-if="providerConfig.allModels.length > 0"> |
196 | 216 | <span>已选择 {{ providerConfig.selectedModels.length }} 个模型</span> |
197 | 217 | <span v-if="providerConfig.searchQuery" class="filter-info"> |
198 | 218 | (当前筛选显示 {{ filteredModels.length }} 个) |
199 | 219 | </span> |
200 | 220 | </div> |
201 | 221 |
|
202 | | - <div class="modal-checkbox-list"> |
| 222 | + <div class="modal-checkbox-list" v-if="providerConfig.allModels.length > 0"> |
203 | 223 | <div v-for="(model, index) in filteredModels" :key="index" class="modal-checkbox-item"> |
204 | 224 | <a-checkbox |
205 | 225 | :checked="providerConfig.selectedModels.includes(model.id)" |
|
209 | 229 | </a-checkbox> |
210 | 230 | </div> |
211 | 231 | </div> |
212 | | - <div v-if="providerConfig.allModels.length === 0" class="modal-no-models"> |
213 | | - <a-alert v-if="!modelStatus[providerConfig.provider]" type="warning" message="请在 .env 中配置对应的 APIKEY,并重新启动服务" /> |
214 | | - <div v-else> |
215 | | - <a-alert type="warning" message="该提供商暂未适配获取模型列表的方法,如需添加模型,请编辑 src/config/static/models.py 。"> |
216 | | - <template #description> |
217 | | - <a href="https://xerrors.github.io/Yuxi-Know/latest/intro/model-config.html" target="_blank">模型配置</a> |
218 | | - </template> |
219 | | - </a-alert> |
| 232 | + |
| 233 | + <!-- 手动管理模式 (当无法获取模型列表时) --> |
| 234 | + <div v-if="providerConfig.allModels.length === 0" class="modal-manual-manage"> |
| 235 | + <div v-if="!modelStatus[providerConfig.provider]" class="simple-notice warning" style="margin-bottom: 16px;"> |
| 236 | + 请在 .env 中配置对应的 APIKEY,并重新启动服务 |
| 237 | + </div> |
| 238 | + |
| 239 | + <div class="manual-manage-container"> |
| 240 | + <div class="manual-header"> |
| 241 | + <div class="simple-notice info"> |
| 242 | + 无法获取模型列表,您可以手动管理模型配置。该提供商暂未适配自动获取模型列表,或者网络请求失败。您可以在下方手动添加或移除模型。 |
| 243 | + </div> |
| 244 | + </div> |
| 245 | + |
| 246 | + <div class="manual-add-box" style="margin: 16px 0;"> |
| 247 | + <a-input-search |
| 248 | + v-model:value="manualModelInput" |
| 249 | + placeholder="请输入模型ID(如:gpt-4)" |
| 250 | + enter-button="添加模型" |
| 251 | + @search="addManualModel" |
| 252 | + /> |
| 253 | + </div> |
| 254 | + |
| 255 | + <div class="current-models-list"> |
| 256 | + <h4 style="margin-bottom: 10px; font-weight: 600;">当前配置的模型 ({{ providerConfig.selectedModels.length }})</h4> |
| 257 | + <div v-if="providerConfig.selectedModels.length === 0" class="empty-text" style="color: var(--gray-500); padding: 8px 0;">暂无配置模型</div> |
| 258 | + <div class="tags-container"> |
| 259 | + <a-tag |
| 260 | + v-for="model in providerConfig.selectedModels" |
| 261 | + :key="model" |
| 262 | + closable |
| 263 | + color="blue" |
| 264 | + @close="removeModel(model)" |
| 265 | + style="margin-bottom: 8px; padding: 4px 8px;" |
| 266 | + > |
| 267 | + {{ model }} |
| 268 | + </a-tag> |
| 269 | + </div> |
| 270 | + </div> |
220 | 271 | </div> |
221 | 272 | </div> |
222 | 273 | </div> |
|
306 | 357 | import { computed, reactive, watch, h, ref } from 'vue' |
307 | 358 | import { message } from 'ant-design-vue'; |
308 | 359 | import { |
309 | | - InfoCircleOutlined, |
| 360 | + InfoCircleOutlined, // Keep if still used for other things, if not, remove. For now assume it might be used elsewhere. |
310 | 361 | SettingOutlined, |
311 | 362 | DownCircleOutlined, |
312 | 363 | LoadingOutlined, |
@@ -479,6 +530,46 @@ const filteredModels = computed(() => { |
479 | 530 | return allModels.filter(model => model.id.toLowerCase().includes(searchQuery)); |
480 | 531 | }); |
481 | 532 |
|
| 533 | +// 计算不支持/已失效的模型 |
| 534 | +const unsupportedModels = computed(() => { |
| 535 | + if (providerConfig.allModels.length === 0) return []; |
| 536 | + const availableIds = new Set(providerConfig.allModels.map(m => m.id)); |
| 537 | + return providerConfig.selectedModels.filter(id => !availableIds.has(id)); |
| 538 | +}); |
| 539 | +
|
| 540 | +// 手动管理相关 |
| 541 | +const manualModelInput = ref(''); |
| 542 | +
|
| 543 | +// 添加手动输入的模型 |
| 544 | +const addManualModel = () => { |
| 545 | + const val = manualModelInput.value.trim(); |
| 546 | + if (!val) return; |
| 547 | +
|
| 548 | + if (providerConfig.selectedModels.includes(val)) { |
| 549 | + message.warning('该模型已存在'); |
| 550 | + return; |
| 551 | + } |
| 552 | +
|
| 553 | + providerConfig.selectedModels.push(val); |
| 554 | + manualModelInput.value = ''; |
| 555 | + message.success('添加成功'); |
| 556 | +}; |
| 557 | +
|
| 558 | +// 移除模型 |
| 559 | +const removeModel = (modelId) => { |
| 560 | + const idx = providerConfig.selectedModels.indexOf(modelId); |
| 561 | + if (idx > -1) { |
| 562 | + providerConfig.selectedModels.splice(idx, 1); |
| 563 | + } |
| 564 | +}; |
| 565 | +
|
| 566 | +// 移除所有不支持的模型 |
| 567 | +const removeAllUnsupported = () => { |
| 568 | + const toRemove = unsupportedModels.value; |
| 569 | + providerConfig.selectedModels = providerConfig.selectedModels.filter(id => !toRemove.includes(id)); |
| 570 | + message.success(`已移除 ${toRemove.length} 个失效模型`); |
| 571 | +}; |
| 572 | +
|
482 | 573 | // 自定义供应商管理 |
483 | 574 | const customProviderForm = ref(); |
484 | 575 | const customProviderModal = reactive({ |
@@ -998,8 +1089,8 @@ const testCustomProvider = async (providerId, modelName) => { |
998 | 1089 | } |
999 | 1090 |
|
1000 | 1091 | .model-icon { |
1001 | | - width: 28px; |
1002 | | - height: 28px; |
| 1092 | + width: 36px; |
| 1093 | + height: 36px; |
1003 | 1094 | border-radius: 6px; |
1004 | 1095 | overflow: hidden; |
1005 | 1096 | filter: grayscale(100%); |
@@ -1438,4 +1529,43 @@ const testCustomProvider = async (providerId, modelName) => { |
1438 | 1529 | } |
1439 | 1530 | } |
1440 | 1531 | } |
| 1532 | +
|
| 1533 | +// Simple Notice Styles |
| 1534 | +.simple-notice { |
| 1535 | + padding: 8px 12px; |
| 1536 | + border-radius: 4px; |
| 1537 | + font-size: 12px; |
| 1538 | + line-height: 1.5; |
| 1539 | + margin-bottom: 12px; |
| 1540 | + border: 1px solid transparent; // Keep a subtle border |
| 1541 | +
|
| 1542 | + &.warning { |
| 1543 | + background-color: var(--color-warning-50); |
| 1544 | + color: var(--color-warning-700); |
| 1545 | + border-color: var(--color-warning-100); |
| 1546 | + } |
| 1547 | +
|
| 1548 | + &.info { |
| 1549 | + background-color: var(--color-info-50); |
| 1550 | + color: var(--color-info-700); |
| 1551 | + border-color: var(--color-info-100); |
| 1552 | + } |
| 1553 | +
|
| 1554 | + p { // For warning message, if it's multiline |
| 1555 | + margin: 0; |
| 1556 | + } |
| 1557 | +
|
| 1558 | + .unsupported-list { |
| 1559 | + margin-top: 8px; |
| 1560 | + display: flex; |
| 1561 | + flex-wrap: wrap; |
| 1562 | + gap: 4px; |
| 1563 | + } |
| 1564 | + .clear-btn { |
| 1565 | + margin-top: 8px; |
| 1566 | + font-size: 12px; |
| 1567 | + height: 24px; |
| 1568 | + padding: 0 8px; |
| 1569 | + } |
| 1570 | +} |
1441 | 1571 | </style> |
0 commit comments