feat: 模型 vision 能力检测,不支持时禁用图片上传
- model-bar.js: 新增 setVisionAvailable() 控制图片按钮状态 - model-bar.js: 导出 isVisionAvailable() 供其他组件查询 - input-area.js: 点击图片按钮时校验 vision 能力 - input-area.js: 发送消息时二次校验,防止绕过 - style.css: .icon-btn.disabled 灰显不可点击样式
This commit is contained in:
@@ -226,6 +226,15 @@ body::before {
|
|||||||
box-shadow: 0 4px 18px rgba(0, 245, 212, 0.4);
|
box-shadow: 0 4px 18px rgba(0, 245, 212, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-btn.disabled,
|
||||||
|
.icon-btn:disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
box-shadow: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-btn:active {
|
.icon-btn:active {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { state, KEYS } from '../state.js';
|
import { state, KEYS } from '../state.js';
|
||||||
import { fileToBase64, fileToText, detectLanguage, debounce, truncate, escapeHtml, formatSize } from '../utils.js';
|
import { fileToBase64, fileToText, detectLanguage, debounce, truncate, escapeHtml, formatSize } from '../utils.js';
|
||||||
import { getSelectedModel, isThinkEnabled } from './model-bar.js';
|
import { getSelectedModel, isThinkEnabled, isVisionAvailable } from './model-bar.js';
|
||||||
import {
|
import {
|
||||||
renderMessages, appendAssistantPlaceholder, appendSystemMessage,
|
renderMessages, appendAssistantPlaceholder, appendSystemMessage,
|
||||||
updateLastAssistantMessage, clearMessages, safeMarkdown
|
updateLastAssistantMessage, clearMessages, safeMarkdown
|
||||||
@@ -98,7 +98,13 @@ export function initInputArea() {
|
|||||||
chatInputEl.addEventListener('input', autoResizeTextarea);
|
chatInputEl.addEventListener('input', autoResizeTextarea);
|
||||||
|
|
||||||
// 图片上传
|
// 图片上传
|
||||||
document.querySelector('#btnAttachImg').addEventListener('click', () => fileInputEl.click());
|
document.querySelector('#btnAttachImg').addEventListener('click', () => {
|
||||||
|
if (!isVisionAvailable()) {
|
||||||
|
showToast('当前模型不支持图片分析', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileInputEl.click();
|
||||||
|
});
|
||||||
fileInputEl.addEventListener('change', (e) => {
|
fileInputEl.addEventListener('change', (e) => {
|
||||||
handleImageSelect(e.target.files);
|
handleImageSelect(e.target.files);
|
||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
@@ -290,6 +296,12 @@ export async function sendMessage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 有图片但模型不支持 vision → 阻止发送
|
||||||
|
if (pendingImages.length > 0 && !isVisionAvailable()) {
|
||||||
|
appendSystemMessage('⚠️ 当前模型不支持图片分析,请移除图片后重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const currentSession = state.get(KEYS.CURRENT_SESSION);
|
const currentSession = state.get(KEYS.CURRENT_SESSION);
|
||||||
if (!currentSession) return;
|
if (!currentSession) return;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import { state, KEYS } from '../state.js';
|
import { state, KEYS } from '../state.js';
|
||||||
import { formatSize } from '../utils.js';
|
import { formatSize } from '../utils.js';
|
||||||
|
|
||||||
let modelSelectEl, badgeThinkEl, badgeVisionEl, thinkCheckbox, thinkWrap;
|
let modelSelectEl, badgeThinkEl, badgeVisionEl, thinkCheckbox, thinkWrap, btnAttachImg;
|
||||||
|
|
||||||
/** 缓存模型能力 { modelName: { think: bool, vision: bool } } */
|
/** 缓存模型能力 { modelName: { think: bool, vision: bool } } */
|
||||||
const modelCapabilityCache = new Map();
|
const modelCapabilityCache = new Map();
|
||||||
@@ -17,6 +17,7 @@ export function initModelBar() {
|
|||||||
badgeVisionEl = document.querySelector('#badgeVision');
|
badgeVisionEl = document.querySelector('#badgeVision');
|
||||||
thinkCheckbox = document.querySelector('#toggleThink');
|
thinkCheckbox = document.querySelector('#toggleThink');
|
||||||
thinkWrap = document.querySelector('#thinkToggleWrap');
|
thinkWrap = document.querySelector('#thinkToggleWrap');
|
||||||
|
btnAttachImg = document.querySelector('#btnAttachImg');
|
||||||
|
|
||||||
// 模型切换 → 检测能力 + 保存设置
|
// 模型切换 → 检测能力 + 保存设置
|
||||||
modelSelectEl.addEventListener('change', async () => {
|
modelSelectEl.addEventListener('change', async () => {
|
||||||
@@ -26,6 +27,7 @@ export function initModelBar() {
|
|||||||
await checkModelCapability(model);
|
await checkModelCapability(model);
|
||||||
} else {
|
} else {
|
||||||
setThinkAvailable(false);
|
setThinkAvailable(false);
|
||||||
|
setVisionAvailable(false);
|
||||||
badgeThinkEl.style.display = 'none';
|
badgeThinkEl.style.display = 'none';
|
||||||
badgeVisionEl.style.display = 'none';
|
badgeVisionEl.style.display = 'none';
|
||||||
}
|
}
|
||||||
@@ -48,6 +50,7 @@ export async function loadModels() {
|
|||||||
if (!data.models || data.models.length === 0) {
|
if (!data.models || data.models.length === 0) {
|
||||||
modelSelectEl.innerHTML = '<option value="">未安装任何模型</option>';
|
modelSelectEl.innerHTML = '<option value="">未安装任何模型</option>';
|
||||||
badgeThinkEl.style.display = 'none'; badgeVisionEl.style.display = 'none';
|
badgeThinkEl.style.display = 'none'; badgeVisionEl.style.display = 'none';
|
||||||
|
setThinkAvailable(false); setVisionAvailable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +80,7 @@ export async function loadModels() {
|
|||||||
console.error('[ModelBar] 加载模型失败:', err);
|
console.error('[ModelBar] 加载模型失败:', err);
|
||||||
modelSelectEl.innerHTML = '<option value="">加载失败 - 检查连接</option>';
|
modelSelectEl.innerHTML = '<option value="">加载失败 - 检查连接</option>';
|
||||||
badgeThinkEl.style.display = 'none'; badgeVisionEl.style.display = 'none';
|
badgeThinkEl.style.display = 'none'; badgeVisionEl.style.display = 'none';
|
||||||
|
setThinkAvailable(false); setVisionAvailable(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +121,7 @@ async function checkModelCapability(modelName) {
|
|||||||
*/
|
*/
|
||||||
function applyCapability(modelName, caps) {
|
function applyCapability(modelName, caps) {
|
||||||
setThinkAvailable(caps.think);
|
setThinkAvailable(caps.think);
|
||||||
|
setVisionAvailable(caps.vision);
|
||||||
|
|
||||||
// 更新能力标签
|
// 更新能力标签
|
||||||
badgeThinkEl.style.display = caps.think ? '' : 'none';
|
badgeThinkEl.style.display = caps.think ? '' : 'none';
|
||||||
@@ -139,6 +144,29 @@ function setThinkAvailable(available) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置图片上传按钮是否可用
|
||||||
|
*/
|
||||||
|
function setVisionAvailable(available) {
|
||||||
|
if (!btnAttachImg) return;
|
||||||
|
if (available) {
|
||||||
|
btnAttachImg.disabled = false;
|
||||||
|
btnAttachImg.title = '上传图片';
|
||||||
|
btnAttachImg.classList.remove('disabled');
|
||||||
|
} else {
|
||||||
|
btnAttachImg.disabled = true;
|
||||||
|
btnAttachImg.title = '当前模型不支持图片分析';
|
||||||
|
btnAttachImg.classList.add('disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前选择的模型是否支持视觉分析
|
||||||
|
*/
|
||||||
|
export function isVisionAvailable() {
|
||||||
|
return btnAttachImg && !btnAttachImg.disabled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前选择的模型
|
* 获取当前选择的模型
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user