Commit 7a5f3bf6 by Performance System

1

parent 28357a9b
Pipeline #3160 passed with stage
in 28 seconds
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片URL调试工具</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
}
.header {
background: #E6A23C;
color: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
text-align: center;
}
.section {
background: #f5f5f5;
padding: 20px;
margin: 15px 0;
border-radius: 8px;
border-left: 4px solid #E6A23C;
}
.debug-steps {
border-left-color: #409EFF;
background: #f0f9ff;
}
.warning {
border-left-color: #F56C6C;
background: #fef0f0;
}
.code {
background: #f4f4f4;
padding: 15px;
border-radius: 4px;
font-family: monospace;
margin: 10px 0;
overflow-x: auto;
white-space: pre-wrap;
}
.checklist {
list-style: none;
padding: 0;
}
.checklist li {
padding: 8px 0;
position: relative;
padding-left: 30px;
}
.checklist li:before {
content: "🔍";
position: absolute;
left: 0;
font-size: 16px;
}
.highlight {
background: #fff3cd;
padding: 2px 6px;
border-radius: 3px;
font-weight: bold;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="header">
<h1>🔍 图片URL缺失问题调试指南</h1>
<p>排查用户视图导出中 image.url 为空的原因</p>
</div>
<div class="section warning">
<h2>⚠️ 问题现象</h2>
<p>在用户视图导出ZIP功能中,<span class="highlight">image.url</span> 字段可能为空,导致无法生成图片文件。</p>
<h3>🎯 可能的原因</h3>
<ul>
<li><strong>数据存储问题</strong>:图片保存时url字段丢失</li>
<li><strong>数据过滤问题</strong>:加载时被过滤掉了无效图片</li>
<li><strong>权限问题</strong>:获取的机构数据不完整</li>
<li><strong>数据映射问题</strong>:导出时数据转换错误</li>
</ul>
</div>
<div class="section debug-steps">
<h2>🔍 调试步骤</h2>
<h3>第一步:检查控制台日志</h3>
<ol class="checklist">
<li>打开浏览器开发者工具 (F12)</li>
<li>切换到 Console 标签页</li>
<li>清空控制台日志</li>
<li>进入管理员页面 → 用户视图</li>
<li>选择一个用户,观察日志输出</li>
</ol>
<h3>第二步:查看用户机构数据</h3>
<p>在控制台中查找以下日志:</p>
<div class="code">=== 用户机构数据调试 ===
用户ID: [用户ID]
用户名: [用户名]
获取到的机构数量: [数量]
机构 1: {
id: "inst_xxx",
institutionId: "001",
name: "机构名称",
ownerId: "user_xxx",
hasImages: true/false,
imageCount: [数量],
images: [...]
}</div>
<h3>第三步:检查图片数据结构</h3>
<p>重点关注每个图片对象的结构:</p>
<div class="code">图片 1 原始数据: {
id: "img_xxx",
name: "图片名称.jpg",
hasUrl: true/false, ← 关键检查点
urlType: "string",
urlLength: [长度],
size: [文件大小],
uploadTime: "2024-xx-xx"
}</div>
<h3>第四步:尝试导出并观察详细日志</h3>
<p>点击"导出用户数据 → ZIP图片包",观察详细处理日志:</p>
<div class="code">=== 开始处理机构和图片数据 ===
导出数据结构: {...}
--- 处理机构: [机构名] ---
机构详细信息: {...}
处理图片 1/[总数]: [图片名]
✅ 已添加图片: [路径] 或 ❌ 图片缺少URL数据</div>
</div>
<div class="section">
<h2>🔧 常见问题和解决方案</h2>
<div class="grid">
<div>
<h3>❌ 问题1:hasUrl: false</h3>
<p><strong>现象:</strong>图片对象存在但url字段为空</p>
<p><strong>原因:</strong>图片保存时出现问题</p>
<p><strong>解决:</strong>检查图片上传逻辑</p>
</div>
<div>
<h3>❌ 问题2:imageCount: 0</h3>
<p><strong>现象:</strong>机构没有图片数据</p>
<p><strong>原因:</strong>用户确实没有上传图片</p>
<p><strong>解决:</strong>选择有图片的用户测试</p>
</div>
</div>
<div class="grid">
<div>
<h3>❌ 问题3:权限错误</h3>
<p><strong>现象:</strong>获取到的机构数量为0</p>
<p><strong>原因:</strong>用户-机构关联错误</p>
<p><strong>解决:</strong>检查ownerId字段</p>
</div>
<div>
<h3>❌ 问题4:数据过滤</h3>
<p><strong>现象:</strong>原有图片被过滤掉</p>
<p><strong>原因:</strong>数据验证过于严格</p>
<p><strong>解决:</strong>检查过滤条件</p>
</div>
</div>
</div>
<div class="section">
<h2>🛠️ 手动检查方法</h2>
<h3>方法1:直接检查localStorage</h3>
<p>在浏览器控制台中执行:</p>
<div class="code">// 检查存储的机构数据
const institutions = JSON.parse(localStorage.getItem('score_system_institutions') || '[]')
console.log('所有机构:', institutions)
// 检查特定用户的机构
const userId = 'your_user_id' // 替换为实际用户ID
const userInstitutions = institutions.filter(inst => inst.ownerId === userId)
console.log('用户机构:', userInstitutions)
// 检查图片数据
userInstitutions.forEach(inst => {
console.log(`机构 ${inst.name}:`)
if (inst.images) {
inst.images.forEach((img, index) => {
console.log(` 图片 ${index + 1}:`, {
name: img.name,
hasUrl: !!img.url,
urlLength: img.url ? img.url.length : 0
})
})
}
})</div>
<h3>方法2:检查数据存储函数</h3>
<p>在控制台中测试数据存储:</p>
<div class="code">// 获取数据存储实例
const dataStore = window.dataStore || app.config.globalProperties.$dataStore
// 检查用户机构
const userId = 'your_user_id'
const institutions = dataStore.getInstitutionsByUserId(userId)
console.log('通过dataStore获取的机构:', institutions)
// 检查每个机构的图片
institutions.forEach(inst => {
console.log(`机构 ${inst.name} 的图片:`, inst.images)
})</div>
</div>
<div class="section">
<h2>📋 调试检查清单</h2>
<ol class="checklist">
<li>确认选择的用户确实有图片数据</li>
<li>检查localStorage中的原始数据</li>
<li>验证getInstitutionsByUserId返回的数据</li>
<li>确认图片对象包含url字段</li>
<li>检查url字段是否为有效的Base64格式</li>
<li>验证数据映射过程中url字段是否丢失</li>
<li>确认ZIP生成过程中的错误处理</li>
</ol>
</div>
<div class="section warning">
<h2>🚨 如果发现问题</h2>
<h3>情况1:url字段确实为空</h3>
<ul>
<li>检查图片上传功能是否正常保存url</li>
<li>查看addImageToInstitution函数的实现</li>
<li>确认imageData参数包含url字段</li>
</ul>
<h3>情况2:数据被错误过滤</h3>
<ul>
<li>检查loadFromStorage中的过滤逻辑</li>
<li>确认过滤条件:img && img.id && img.url</li>
<li>可能需要放宽过滤条件</li>
</ul>
<h3>情况3:权限或关联问题</h3>
<ul>
<li>检查机构的ownerId是否正确</li>
<li>确认用户-机构关联关系</li>
<li>验证getInstitutionsByUserId的逻辑</li>
</ul>
</div>
<div class="section debug-steps">
<h2>✅ 下一步行动</h2>
<p>现在请按照以上步骤进行调试:</p>
<ol>
<li><strong>打开控制台</strong>,清空日志</li>
<li><strong>进入用户视图</strong>,选择用户</li>
<li><strong>观察详细日志</strong>,找出问题点</li>
<li><strong>尝试导出</strong>,查看错误信息</li>
<li><strong>根据日志结果</strong>,确定具体问题</li>
</ol>
<p>请将控制台中的关键日志信息反馈给我,我会根据具体情况提供针对性的修复方案!</p>
</div>
</body>
</html>
......@@ -1979,6 +1979,28 @@ const loadSelectedUserData = () => {
performanceScore: userScore?.performanceScore || 0
}
selectedUserInstitutions.value = dataStore.getInstitutionsByUserId(user.id)
// 调试:检查获取到的机构数据
console.log('=== 用户机构数据调试 ===')
console.log('用户ID:', user.id)
console.log('用户名:', user.name)
console.log('获取到的机构数量:', selectedUserInstitutions.value.length)
selectedUserInstitutions.value.forEach((inst, index) => {
console.log(`机构 ${index + 1}:`, {
id: inst.id,
institutionId: inst.institutionId,
name: inst.name,
ownerId: inst.ownerId,
hasImages: !!inst.images,
imageCount: inst.images ? inst.images.length : 0,
images: inst.images ? inst.images.map(img => ({
id: img.id,
name: img.name,
hasUrl: !!img.url,
urlLength: img.url ? img.url.length : 0
})) : []
})
})
}
}
......@@ -2035,19 +2057,63 @@ const exportUserData = async (format = 'json') => {
interactionScore: dataStore.calculateInteractionScore(selectedViewUser.value.id),
performanceScore: dataStore.calculatePerformanceScore(selectedViewUser.value.id)
},
institutions: selectedUserInstitutions.value.map(inst => ({
id: inst.id,
institutionId: inst.institutionId,
name: inst.name,
imageCount: inst.images ? inst.images.length : 0,
images: inst.images ? inst.images.map(img => ({
id: img.id,
name: img.name,
uploadTime: img.uploadTime,
size: img.size,
url: img.url // 添加图片的Base64数据,ZIP导出需要此字段
})) : []
}))
institutions: selectedUserInstitutions.value.map(inst => {
console.log(`\n=== 准备导出数据 - 机构: ${inst.name} ===`)
console.log('原始机构数据:', {
id: inst.id,
institutionId: inst.institutionId,
name: inst.name,
ownerId: inst.ownerId,
hasImages: !!inst.images,
imageCount: inst.images ? inst.images.length : 0
})
const mappedImages = inst.images ? inst.images.map((img, imgIndex) => {
console.log(` 图片 ${imgIndex + 1} 原始数据:`, {
id: img.id,
name: img.name,
hasUrl: !!img.url,
urlType: typeof img.url,
urlLength: img.url ? img.url.length : 0,
size: img.size,
uploadTime: img.uploadTime
})
const mappedImg = {
id: img.id,
name: img.name,
uploadTime: img.uploadTime,
size: img.size,
url: img.url // 添加图片的Base64数据,ZIP导出需要此字段
}
console.log(` 图片 ${imgIndex + 1} 映射后:`, {
id: mappedImg.id,
name: mappedImg.name,
hasUrl: !!mappedImg.url,
urlPreserved: img.url === mappedImg.url
})
return mappedImg
}) : []
const result = {
id: inst.id,
institutionId: inst.institutionId,
name: inst.name,
imageCount: inst.images ? inst.images.length : 0,
images: mappedImages
}
console.log('机构导出数据准备完成:', {
name: result.name,
imageCount: result.imageCount,
actualImagesLength: result.images.length,
imagesWithUrl: result.images.filter(img => !!img.url).length
})
return result
})
}
// 根据格式导出
......@@ -2157,18 +2223,78 @@ const exportUserDataAsZIP = async (exportData, userName, currentMonth) => {
zip.file(`${userName}_数据摘要.json`, JSON.stringify(summaryData, null, 2))
// 按机构创建文件夹并添加图片
console.log('=== 开始处理机构和图片数据 ===')
console.log('导出数据结构:', {
institutionsCount: exportData.institutions.length,
institutions: exportData.institutions.map(inst => ({
name: inst.name,
id: inst.id,
institutionId: inst.institutionId,
imageCount: inst.images ? inst.images.length : 0,
hasImages: !!(inst.images && inst.images.length > 0)
}))
})
for (const institution of exportData.institutions) {
console.log(`处理机构: ${institution.name}, 图片数量: ${institution.images ? institution.images.length : 0}`)
console.log(`\n--- 处理机构: ${institution.name} ---`)
console.log('机构详细信息:', {
id: institution.id,
institutionId: institution.institutionId,
name: institution.name,
imageCount: institution.imageCount,
imagesArrayLength: institution.images ? institution.images.length : 0,
imagesExists: !!institution.images
})
if (institution.images && institution.images.length > 0) {
const folderName = `${institution.name}_${institution.institutionId}`
totalImages += institution.images.length
for (const image of institution.images) {
console.log(`机构 ${institution.name} 的图片列表:`)
institution.images.forEach((img, index) => {
console.log(` 图片 ${index + 1}:`, {
id: img.id,
name: img.name,
hasUrl: !!img.url,
urlType: img.url ? typeof img.url : 'undefined',
urlLength: img.url ? img.url.length : 0,
urlPrefix: img.url ? img.url.substring(0, 50) + '...' : 'N/A',
size: img.size,
uploadTime: img.uploadTime
})
})
for (const [index, image] of institution.images.entries()) {
try {
// 检查图片URL是否存在
console.log(`\n处理图片 ${index + 1}/${institution.images.length}: ${image.name}`)
// 详细检查图片URL
if (!image.url) {
console.warn(`图片缺少URL数据: ${image.name}`)
console.error(`❌ 图片缺少URL数据:`, {
imageName: image.name,
imageId: image.id,
imageObject: image,
institutionName: institution.name
})
continue
}
// 检查URL格式
if (typeof image.url !== 'string') {
console.error(`❌ 图片URL不是字符串:`, {
imageName: image.name,
urlType: typeof image.url,
url: image.url
})
continue
}
// 检查是否是Base64格式
if (!image.url.startsWith('data:')) {
console.error(`❌ 图片URL不是Base64格式:`, {
imageName: image.name,
urlPrefix: image.url.substring(0, 100)
})
continue
}
......@@ -2182,14 +2308,28 @@ const exportUserDataAsZIP = async (exportData, userName, currentMonth) => {
zip.file(`${folderName}/${fileName}`, base64Data, { base64: true })
addedImages++
console.log(`已添加图片: ${folderName}/${fileName}`)
console.log(`✅ 已添加图片: ${folderName}/${fileName}`, {
mimeType,
extension,
base64Length: base64Data.length
})
} else {
console.warn(`图片Base64数据无效: ${image.name}`)
console.error(`❌ 图片Base64数据无效:`, {
imageName: image.name,
url: image.url,
splitResult: image.url.split(',')
})
}
} catch (error) {
console.warn(`处理图片失败: ${image.name}`, error)
console.error(`❌ 处理图片失败: ${image.name}`, {
error: error.message,
stack: error.stack,
imageData: image
})
}
}
} else {
console.log(`机构 ${institution.name} 没有图片数据`)
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment