```
feat(api): 添加万川平台模型配置获取和同步功能 - 新增 getWanchuanModelConfig 函数,按模型编码获取平台模型配置 - 新增 syncWanchuanModelToSettings 函数,从万川平台拉取模型配置并写入后端 AI 设置 - 支持按用途分多个模型编码(generic/vision/voice)分别同步配置 - 配置失败时跳过对应字段,不影响其他模型同步 feat(settings): 重构AI模型配置界面支持多模块分组 - 将AI配置按话题分析、报告生成、视觉、语音四个模块分组展示 - 每个模块独立配置接口地址、密钥和模型名称 - 添加从万川平台获取配置的按钮和同步功能 - 优化配置状态指示和错误提示信息 refactor(config): 扩展AI配置支持独立的语音视觉报告网关 - 新增 voice_base_url/voice_api_key 配置项 - 新增 vision_base_url/vision_api_key 配置项 - 新增 summary_base_url/summary_api_key 配置项 - 留空时回退到 ai_base_url/ai_api_key 兼容单网关场景 refactor(http): 统一使用共享HTTP客户端减少连接开销 - 替换各处 httpx.AsyncClient 为 shared_client - 在 lifespan 中正确关闭共享客户端资源 - 优化 get_current_wxid 和 health 检查中的HTTP请求 refactor(ai): 按用途缓存AI客户端支持不同网关配置 - 重构 get_openai_client 支持按(base_url, api_key)缓存 - 新增 get_client_for 函数按用途获取对应客户端 - 支持语音、视觉、报告等不同用途使用独立网关和密钥 ```
This commit is contained in:
@@ -159,7 +159,8 @@
|
||||
height: 660px;
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: none;
|
||||
filter: drop-shadow(0 48px 55px rgba(0,0,0,0.78));
|
||||
/* 原本的 drop-shadow 滤镜会随手臂无限动画每帧重算整块阴影,极耗 GPU。
|
||||
SVG 内部已有一个静态阴影椭圆,这里移除滤镜即可。 */
|
||||
}
|
||||
|
||||
.robot {
|
||||
@@ -203,9 +204,9 @@
|
||||
padding: 26px;
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: 24px;
|
||||
background: linear-gradient(180deg, rgba(27,29,34,0.94), rgba(15,16,19,0.9));
|
||||
/* 面板背景已接近不透明,去掉 backdrop-filter:blur 避免 GPU 每帧重模糊(动画背景下尤其卡) */
|
||||
background: linear-gradient(180deg, rgba(29,31,37,0.97), rgba(16,17,21,0.96));
|
||||
box-shadow: var(--shadow);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
@@ -467,8 +468,8 @@
|
||||
z-index: 100;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.72);
|
||||
backdrop-filter: blur(8px);
|
||||
/* 去掉 backdrop-filter:blur,改用更深的纯遮罩,避免弹窗出现时整屏每帧重模糊 */
|
||||
background: rgba(0, 0, 0, 0.82);
|
||||
}
|
||||
|
||||
.modal-overlay.show {
|
||||
@@ -751,13 +752,26 @@
|
||||
let startingAll = false;
|
||||
let refreshingAccount = false;
|
||||
|
||||
let robotRafPending = false
|
||||
let robotNextX = 0
|
||||
let robotNextY = 0
|
||||
|
||||
launcher.addEventListener('mousemove', (event) => {
|
||||
const rect = launcher.getBoundingClientRect();
|
||||
const x = ((event.clientX - rect.left) / rect.width - 0.5) * 2;
|
||||
const y = ((event.clientY - rect.top) / rect.height - 0.5) * 2;
|
||||
const clampedX = Math.max(-1, Math.min(1, x));
|
||||
const clampedY = Math.max(-1, Math.min(1, y));
|
||||
robotHead.style.transform = `translate(${clampedX * 9}px, ${clampedY * 6}px) rotate(${clampedX * 7}deg)`;
|
||||
// 缓存目标坐标,用 rAF 合并到每帧最多一次写入,避免每个 mousemove 事件
|
||||
// 都触发 getBoundingClientRect()(强制同步布局)和 robotHead 滤镜重算。
|
||||
robotNextX = event.clientX
|
||||
robotNextY = event.clientY
|
||||
if (robotRafPending) return
|
||||
robotRafPending = true
|
||||
requestAnimationFrame(() => {
|
||||
robotRafPending = false
|
||||
const rect = launcher.getBoundingClientRect()
|
||||
const x = ((robotNextX - rect.left) / rect.width - 0.5) * 2
|
||||
const y = ((robotNextY - rect.top) / rect.height - 0.5) * 2
|
||||
const clampedX = Math.max(-1, Math.min(1, x))
|
||||
const clampedY = Math.max(-1, Math.min(1, y))
|
||||
robotHead.style.transform = `translate(${clampedX * 9}px, ${clampedY * 6}px) rotate(${clampedX * 7}deg)`
|
||||
})
|
||||
});
|
||||
|
||||
launcher.addEventListener('mouseleave', () => {
|
||||
|
||||
Reference in New Issue
Block a user