Files
map-client-vue/web/public/sync-data.html
2025-10-14 21:52:11 +08:00

319 lines
9.3 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>