mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-06 20:02:49 +08:00
fix: 修复路由竞态、删除确认、输入同步等交互问题
- router.js 添加竞态防护和页面清理钩子 - logs.js 切换 Tab 时清空搜索框 - models.js 删除 Provider 添加确认提示,输入框改 oninput 实时同步 - mcp.js 删除 Server 添加确认提示 - gateway.js Tailscale 地址为空时保留原配置
This commit is contained in:
@@ -107,7 +107,7 @@ async function saveConfig(page, state) {
|
||||
state.config.gateway = {
|
||||
...state.config.gateway,
|
||||
port, bind, mode, authToken,
|
||||
tailscale: tailscaleAddr ? { address: tailscaleAddr } : undefined,
|
||||
tailscale: tailscaleAddr ? { address: tailscaleAddr } : (state.config.gateway?.tailscale || undefined),
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -42,6 +42,7 @@ export async function render() {
|
||||
page.querySelectorAll('.tab').forEach(t => t.classList.remove('active'))
|
||||
tab.classList.add('active')
|
||||
currentTab = tab.dataset.tab
|
||||
page.querySelector('#log-search').value = ''
|
||||
loadLog(page, currentTab)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -76,6 +76,7 @@ function renderServers(page, state) {
|
||||
const action = btn.dataset.action
|
||||
|
||||
if (action === 'delete') {
|
||||
if (!confirm(`确定删除 MCP Server "${key}"?`)) return
|
||||
if (state.config.mcpServers) delete state.config.mcpServers[key]
|
||||
else delete state.config[key]
|
||||
renderServers(page, state)
|
||||
|
||||
@@ -102,6 +102,7 @@ function renderProviders(page, state) {
|
||||
const models = section.querySelector('.provider-models')
|
||||
models.style.display = models.style.display === 'none' ? 'block' : 'none'
|
||||
} else if (action === 'delete-provider') {
|
||||
if (!confirm(`确定删除 Provider "${providerKey}"?`)) return
|
||||
delete state.config.models.providers[providerKey]
|
||||
renderProviders(page, state)
|
||||
toast(`已删除 ${providerKey}`, 'info')
|
||||
@@ -124,9 +125,9 @@ function renderProviders(page, state) {
|
||||
}
|
||||
})
|
||||
|
||||
// 输入框变更同步到 state
|
||||
// 输入框变更实时同步到 state
|
||||
listEl.querySelectorAll('[data-field]').forEach(input => {
|
||||
input.onchange = () => {
|
||||
input.oninput = () => {
|
||||
const providerKey = input.closest('[data-provider]').dataset.provider
|
||||
state.config.models.providers[providerKey][input.dataset.field] = input.value
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
*/
|
||||
const routes = {}
|
||||
let _contentEl = null
|
||||
let _loadId = 0
|
||||
let _currentCleanup = null
|
||||
|
||||
export function registerRoute(path, loader) {
|
||||
routes[path] = loader
|
||||
@@ -23,16 +25,33 @@ async function loadRoute() {
|
||||
const loader = routes[hash]
|
||||
if (!loader || !_contentEl) return
|
||||
|
||||
// 竞态防护:记录本次加载 ID
|
||||
const thisLoad = ++_loadId
|
||||
|
||||
// 清理上一个页面
|
||||
if (_currentCleanup) {
|
||||
try { _currentCleanup() } catch (_) {}
|
||||
_currentCleanup = null
|
||||
}
|
||||
|
||||
_contentEl.innerHTML = ''
|
||||
const mod = await loader()
|
||||
// 动态 import 返回模块对象,调用 render() 获取页面元素
|
||||
|
||||
// 如果加载期间路由又变了,丢弃本次结果
|
||||
if (thisLoad !== _loadId) return
|
||||
|
||||
const page = mod.render ? await mod.render() : mod.default ? await mod.default() : mod
|
||||
if (thisLoad !== _loadId) return
|
||||
|
||||
if (typeof page === 'string') {
|
||||
_contentEl.innerHTML = page
|
||||
} else if (page instanceof HTMLElement) {
|
||||
_contentEl.appendChild(page)
|
||||
}
|
||||
|
||||
// 保存页面清理函数
|
||||
_currentCleanup = mod.cleanup || null
|
||||
|
||||
// 更新侧边栏激活状态
|
||||
document.querySelectorAll('.nav-item').forEach(item => {
|
||||
item.classList.toggle('active', item.dataset.route === hash)
|
||||
|
||||
Reference in New Issue
Block a user