319 lines
9.3 KiB
HTML
319 lines
9.3 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>数据同步工具</title>
|
||
<style>
|
||
body {
|
||
font-family: Arial, sans-serif;
|
||
padding: 40px;
|
||
max-width: 800px;
|
||
margin: 0 auto;
|
||
background: #f5f5f5;
|
||
}
|
||
.container {
|
||
background: white;
|
||
padding: 30px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
}
|
||
h1 {
|
||
color: #333;
|
||
margin-bottom: 10px;
|
||
}
|
||
.description {
|
||
color: #666;
|
||
margin-bottom: 30px;
|
||
line-height: 1.6;
|
||
}
|
||
.button-group {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin: 20px 0;
|
||
}
|
||
button {
|
||
padding: 12px 24px;
|
||
font-size: 16px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
.btn-primary {
|
||
background: #18a058;
|
||
color: white;
|
||
}
|
||
.btn-primary:hover {
|
||
background: #16915e;
|
||
}
|
||
.btn-secondary {
|
||
background: #f0f0f0;
|
||
color: #333;
|
||
}
|
||
.btn-secondary:hover {
|
||
background: #e0e0e0;
|
||
}
|
||
.btn-danger {
|
||
background: #d03050;
|
||
color: white;
|
||
}
|
||
.btn-danger:hover {
|
||
background: #b02848;
|
||
}
|
||
.result {
|
||
margin-top: 20px;
|
||
padding: 15px;
|
||
border-radius: 4px;
|
||
display: none;
|
||
}
|
||
.result.show {
|
||
display: block;
|
||
}
|
||
.result.success {
|
||
background: #f0f9ff;
|
||
border: 1px solid #18a058;
|
||
color: #18a058;
|
||
}
|
||
.result.error {
|
||
background: #fff0f0;
|
||
border: 1px solid #d03050;
|
||
color: #d03050;
|
||
}
|
||
.result.info {
|
||
background: #f8f9fa;
|
||
border: 1px solid #909399;
|
||
color: #606266;
|
||
}
|
||
pre {
|
||
background: #f5f5f5;
|
||
padding: 15px;
|
||
border-radius: 4px;
|
||
overflow-x: auto;
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
}
|
||
.step {
|
||
background: #f8f9fa;
|
||
padding: 15px;
|
||
border-radius: 4px;
|
||
margin: 15px 0;
|
||
border-left: 4px solid #18a058;
|
||
}
|
||
.step-title {
|
||
font-weight: bold;
|
||
color: #18a058;
|
||
margin-bottom: 8px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1>🔄 数据同步工具</h1>
|
||
<p class="description">
|
||
由于系统使用了不同的 LocalStorage 键名,需要将数据从 <code>model-services</code> 同步到 <code>model-providers</code>。
|
||
<br>点击下方按钮完成一键同步。
|
||
</p>
|
||
|
||
<div class="button-group">
|
||
<button class="btn-primary" onclick="syncData()">
|
||
✓ 一键同步数据
|
||
</button>
|
||
<button class="btn-secondary" onclick="checkData()">
|
||
🔍 检查当前数据
|
||
</button>
|
||
<button class="btn-danger" onclick="clearOldData()">
|
||
🗑️ 清理旧数据
|
||
</button>
|
||
</div>
|
||
|
||
<div id="result" class="result"></div>
|
||
|
||
<div class="step">
|
||
<div class="step-title">📋 同步步骤说明:</div>
|
||
<ol>
|
||
<li>点击"检查当前数据"查看两个键的数据状态</li>
|
||
<li>点击"一键同步数据"将 model-services 的数据复制到 model-providers</li>
|
||
<li>刷新聊天页面,模型列表将显示正确的数据</li>
|
||
<li>(可选) 同步完成后可以点击"清理旧数据"删除 model-services</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function showResult(message, type = 'info') {
|
||
const resultDiv = document.getElementById('result')
|
||
resultDiv.innerHTML = message
|
||
resultDiv.className = `result ${type} show`
|
||
}
|
||
|
||
function syncData() {
|
||
try {
|
||
// 读取旧数据
|
||
const oldData = localStorage.getItem('model-services')
|
||
const newData = localStorage.getItem('model-providers')
|
||
|
||
if (!oldData) {
|
||
showResult('⚠️ 未找到 model-services 数据,无需同步', 'info')
|
||
return
|
||
}
|
||
|
||
// 解析并验证数据
|
||
let services
|
||
try {
|
||
services = JSON.parse(oldData)
|
||
if (!Array.isArray(services)) {
|
||
throw new Error('数据格式不正确')
|
||
}
|
||
} catch (e) {
|
||
showResult(`❌ 数据解析失败: ${e.message}`, 'error')
|
||
return
|
||
}
|
||
|
||
// 保存到新的键
|
||
localStorage.setItem('model-providers', oldData)
|
||
|
||
// 验证同步结果
|
||
const synced = localStorage.getItem('model-providers')
|
||
const syncedData = JSON.parse(synced)
|
||
|
||
let summary = `
|
||
<div style="font-size: 16px; font-weight: bold; margin-bottom: 10px;">
|
||
✓ 同步成功!
|
||
</div>
|
||
<div style="margin-bottom: 10px;">
|
||
已将 ${services.length} 个服务从 model-services 同步到 model-providers
|
||
</div>
|
||
<div style="margin-top: 15px;">
|
||
<strong>同步的服务:</strong>
|
||
<ul style="margin: 10px 0; padding-left: 20px;">
|
||
`
|
||
|
||
services.forEach(service => {
|
||
const modelCount = service.models?.length || 0
|
||
summary += `<li>${service.name} (${service.type}) - ${modelCount} 个模型</li>`
|
||
})
|
||
|
||
summary += `
|
||
</ul>
|
||
</div>
|
||
<div style="margin-top: 15px; padding: 10px; background: #fff3cd; border-radius: 4px;">
|
||
⚡ <strong>下一步:</strong> 请刷新聊天页面查看效果
|
||
</div>
|
||
`
|
||
|
||
showResult(summary, 'success')
|
||
|
||
// 自动刷新页面提示
|
||
setTimeout(() => {
|
||
if (confirm('数据同步成功!是否立即打开聊天页面查看效果?')) {
|
||
window.open('/', '_blank')
|
||
}
|
||
}, 1000)
|
||
|
||
} catch (error) {
|
||
showResult(`❌ 同步失败: ${error.message}`, 'error')
|
||
console.error('Sync error:', error)
|
||
}
|
||
}
|
||
|
||
function checkData() {
|
||
try {
|
||
const oldData = localStorage.getItem('model-services')
|
||
const newData = localStorage.getItem('model-providers')
|
||
|
||
let report = '<div style="font-size: 16px; font-weight: bold; margin-bottom: 15px;">📊 数据检查报告</div>'
|
||
|
||
// 检查 model-services
|
||
report += '<div style="margin-bottom: 20px;">'
|
||
report += '<strong>model-services (旧键):</strong><br>'
|
||
if (oldData) {
|
||
try {
|
||
const services = JSON.parse(oldData)
|
||
report += `✓ 找到数据 (${services.length} 个服务)<br>`
|
||
report += '<ul style="margin: 5px 0; padding-left: 20px;">'
|
||
services.forEach(s => {
|
||
report += `<li>${s.name} - ${s.models?.length || 0} 个模型</li>`
|
||
})
|
||
report += '</ul>'
|
||
} catch (e) {
|
||
report += `⚠️ 数据格式错误: ${e.message}`
|
||
}
|
||
} else {
|
||
report += '✗ 无数据'
|
||
}
|
||
report += '</div>'
|
||
|
||
// 检查 model-providers
|
||
report += '<div style="margin-bottom: 20px;">'
|
||
report += '<strong>model-providers (新键):</strong><br>'
|
||
if (newData) {
|
||
try {
|
||
const providers = JSON.parse(newData)
|
||
report += `✓ 找到数据 (${providers.length} 个服务)<br>`
|
||
report += '<ul style="margin: 5px 0; padding-left: 20px;">'
|
||
providers.forEach(p => {
|
||
report += `<li>${p.name} - ${p.models?.length || 0} 个模型</li>`
|
||
})
|
||
report += '</ul>'
|
||
} catch (e) {
|
||
report += `⚠️ 数据格式错误: ${e.message}`
|
||
}
|
||
} else {
|
||
report += '✗ 无数据'
|
||
}
|
||
report += '</div>'
|
||
|
||
// 建议
|
||
report += '<div style="padding: 10px; background: #f0f9ff; border-radius: 4px; border-left: 4px solid #18a058;">'
|
||
if (oldData && !newData) {
|
||
report += '💡 建议: 点击"一键同步数据"将旧数据迁移到新键'
|
||
} else if (oldData && newData) {
|
||
const oldServices = JSON.parse(oldData)
|
||
const newProviders = JSON.parse(newData)
|
||
if (oldServices.length > newProviders.length) {
|
||
report += '💡 建议: model-services 的数据更新,建议重新同步'
|
||
} else {
|
||
report += '✓ 数据已同步,可以点击"清理旧数据"删除 model-services'
|
||
}
|
||
} else if (!oldData && newData) {
|
||
report += '✓ 数据正常,新键已有数据'
|
||
} else {
|
||
report += '⚠️ 未找到任何模型服务数据,请先在"模型服务"页面添加服务'
|
||
}
|
||
report += '</div>'
|
||
|
||
showResult(report, 'info')
|
||
} catch (error) {
|
||
showResult(`❌ 检查失败: ${error.message}`, 'error')
|
||
console.error('Check error:', error)
|
||
}
|
||
}
|
||
|
||
function clearOldData() {
|
||
if (!confirm('确定要删除 model-services 的旧数据吗?\n\n请确保已经完成数据同步!')) {
|
||
return
|
||
}
|
||
|
||
try {
|
||
const oldData = localStorage.getItem('model-services')
|
||
if (!oldData) {
|
||
showResult('ℹ️ model-services 中没有数据', 'info')
|
||
return
|
||
}
|
||
|
||
localStorage.removeItem('model-services')
|
||
showResult('✓ 旧数据已清理完成', 'success')
|
||
} catch (error) {
|
||
showResult(`❌ 清理失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 页面加载时自动检查
|
||
window.onload = function() {
|
||
checkData()
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|