优化: 对话交互修复与界面完善

- 修输入法组词回车直发/分离窗误关/出错后按钮死锁
- 新建任务失败弹窗内联提示,会话切换乐观高亮
- 状态多选联动刷新不再冲空可见行
- 测试连接保留已配开关与权重,防抖按提供商分键
- 代码块复制/长回复折叠带量/队列回填/排队时钟
- 子代理完成卡渲染,死链卡片与引用清理
- 截图渲染换 pro 版依赖,样式 token 化收尾
This commit is contained in:
lxy
2026-10-03 15:35:18 +08:00
parent b4fafa0e9a
commit 2d43f90e50
20 changed files with 609 additions and 255 deletions
+12 -3
View File
@@ -61,8 +61,9 @@
<span class="setting-desc">{{ $t('settings.descApprovalTimeout') }}</span>
</div>
<!-- 文件上传服务端点:配置后聊天图片上传到用户自己的文件服务转公开 URL;留空则图片本地 base64 内嵌。
与 fileUpload.ts 的读取约定同 key(df-file-upload-base),blur/enter 提交即落库生效 -->
<SettingRow ref="rowUploadBase" :label="$t('settings.labelFileUploadBase')" :desc="$t('settings.descFileUploadBase')">
与 fileUpload.ts 的读取约定同 key(df-file-upload-base),blur/enter 提交即落库生效。
upload-row 整行堆叠(对齐 prompt-row 先例):原横排 280px 最小宽在窄列强挤 label 致逐字竖排 -->
<SettingRow ref="rowUploadBase" class="upload-row" :label="$t('settings.labelFileUploadBase')" :desc="$t('settings.descFileUploadBase')">
<input
v-model="fileUploadBase"
class="setting-input upload-base-input"
@@ -264,9 +265,17 @@ function onApprovalTimeoutChange() {
word-break: break-all;
line-height: 1.4;
}
/* 文件上传端点行:整行堆叠——label/desc 在上,输入独立一行拉满(对齐 prompt-row 先例)。
窄列不再 280px 强制最小宽挤压 label 逐字竖排 */
.upload-row {
flex-direction: column;
align-items: stretch;
}
/* 端点输入走等宽字体(URL 逐字符核对更易读),对齐 AllowedDirsPanel 路径输入风格 */
.upload-base-input {
min-width: 280px;
width: 100%;
min-width: 0;
box-sizing: border-box;
font-family: var(--df-font-mono, monospace);
}
/* 自定义提示词行:整行堆叠,textarea 独立一行从左到右拉满。
+14 -5
View File
@@ -481,6 +481,8 @@ async function fetchModels() {
})
// 手动优先:探测结果合并已存在的 UserSet 标注——用户手动改过的能力不被「测试连接」覆盖
// (对齐后端 ai_fetch_models 合并逻辑:probe_source==UserSet 保留手动能力维度)。
// enabled/weight 同理保留旧值:重拉只是刷新能力维度,用户已关掉的模型/调过的权重
// 属用户配置意图,被探测默认值复位等于静默丢配置(走查 P1-2)。
providerForm.models = models.map(m => {
const old = providerForm.models.find(o => o.model_id === m.model_id)
const isManual = old?.probe_source === 'user_set'
@@ -489,6 +491,8 @@ async function fetchModels() {
modalities: isManual ? [...old.modalities] : [...m.modalities],
capabilities: isManual ? [...old.capabilities] : [...m.capabilities],
probe_source: isManual ? 'user_set' : m.probe_source,
enabled: old ? old.enabled : m.enabled,
weight: old ? old.weight : m.weight,
}
})
emit('toast', t('settings.toastFetchOk', { count: models.length }), 'info')
@@ -600,7 +604,9 @@ async function setDefaultProvider(id: string) {
// 重建 per_provider caps,下条消息即按新配置 acquire。
// 不走 saveProvider(全量 INSERT OR REPLACE 会触发 R-PD-1 密钥迁移分支,与 c 轻量 UPDATE 路径冲突)。
// ============================================================
let _poolTimer: ReturnType<typeof setTimeout> | null = null
// 计时器按 provider id 分键:单例计时器下 300ms 内先后改两家权重,B 的变更会清掉
// A 尚未落库的保存,先改的一方静默丢失(走查 P2-8)。
const _poolTimers = new Map<string, ReturnType<typeof setTimeout>>()
/**
* toggle 即时生效(开关语义=立即入/出池,无 debounce 必要;但失败需回滚视图)。
@@ -628,8 +634,11 @@ function onPoolWeightChange(p: AiProviderConfig, raw: number) {
const clamped = Math.min(100, Math.max(0, Number.isFinite(raw) ? Math.trunc(raw) : 50))
const prev = p.weight
p.weight = clamped
if (_poolTimer) clearTimeout(_poolTimer)
_poolTimer = setTimeout(async () => {
// 仅重置同一 provider 的待发计时;跨 provider 互不干扰
const prevTimer = _poolTimers.get(p.id)
if (prevTimer) clearTimeout(prevTimer)
_poolTimers.set(p.id, setTimeout(async () => {
_poolTimers.delete(p.id)
try {
await aiApi.updateProviderPool(p.id, p.enabled !== false, clamped)
emit('toast', t('settings.toastPoolUpdateOk'), 'info')
@@ -638,7 +647,7 @@ function onPoolWeightChange(p: AiProviderConfig, raw: number) {
console.error(t('settings.toastPoolUpdateFail', { msg: errMsg(e) }), e)
emit('toast', t('settings.toastPoolUpdateFail', { msg: errMsg(e) }), 'error')
}
}, 300)
}, 300))
}
function maskKey(key: string): string {
@@ -654,7 +663,7 @@ defineExpose({
loadProviders,
isFormDirty,
closeProviderForm,
clearPoolTimer() { if (_poolTimer) clearTimeout(_poolTimer) },
clearPoolTimer() { for (const t of _poolTimers.values()) clearTimeout(t); _poolTimers.clear() },
})
// 阶段3 UX 重构:本面板改为 ai-model 类懒挂载,不再由 Settings.vue onMounted 调用,
+5 -2
View File
@@ -20,18 +20,21 @@
/* ===== 设置行容器(表单型 Panel 共用) ===== */
.general-settings { display: flex; flex-direction: column; }
/* 窄列(设置抽屉/面板窄态)三防线:gap 隔开 info 与 control;info min-width:0 允许收缩、
label/desc 文本按常规断行(多词换行,不逐字竖排);control 不被压缩挤扁 */
.setting-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
padding: 14px 0;
border-bottom: 0.5px solid var(--df-border);
}
.setting-row:last-child { border-bottom: none; }
.setting-info { display: flex; flex-direction: column; gap: 2px; }
.setting-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.setting-label { font-size: 14px; font-weight: 500; color: var(--df-text); }
.setting-desc { font-size: 12px; color: var(--df-text-dim); }
.setting-control { display: flex; align-items: center; }
.setting-control { display: flex; align-items: center; flex-shrink: 0; }
/* ===== 控件(5/5 Panel 重复) ===== */
/* 控件统一基准(padding/border/radius/font 对齐),各 type 仅差异化必要属性 */