```html
VersionLog - 版本迭代记录工具
// 增强的文件分析功能
function analyzeCodeFiles(files) {
analysisContent.innerHTML = '
';
setTimeout(() => {
let analysisHTML = '';
let allChanges = [];
// 分析HTML文件
const htmlFiles = files.filter(f => f.name.endsWith('.html'));
if (htmlFiles.length > 0) {
const htmlChanges = [];
htmlFiles.forEach(file => {
const content = file.content;
// 提取重要HTML结构
const divCount = (content.match(/
${file.name}
结构分析:
包含 ${divCount} 个 div 元素
识别到 ${componentCount} 个组件
${newElements.length > 0 ? `
新增元素:
${newElements.slice(0, 5).join('\n')}
` : ''}
`;
});
allChanges.push(...htmlChanges.map(c => `HTML: 新增 ${c}`));
}
// 分析CSS文件
const cssFiles = files.filter(f => f.name.endsWith('.css'));
if (cssFiles.length > 0) {
const cssChanges = [];
cssFiles.forEach(file => {
const content = file.content;
const rules = extractCSSRules(content);
const newRules = extractNewRules(content);
cssChanges.push(...newRules);
analysisHTML += `
${file.name}
样式分析:
包含 ${rules.length} 条样式规则
${newRules.length > 0 ? `
新增规则:
${newRules.slice(0, 5).join('\n')}
` : ''}
`;
});
allChanges.push(...cssChanges.map(c => `CSS: 新增 ${c.split('{')[0].trim()}`));
}
// 分析JS文件
const jsFiles = files.filter(f => f.name.endsWith('.js'));
if (jsFiles.length > 0) {
const jsChanges = [];
jsFiles.forEach(file => {
const content = file.content;
const functions = extractFunctions(content);
const newFunctions = extractNewFunctions(content);
jsChanges.push(...newFunctions);
analysisHTML += `
${file.name}
功能分析:
包含 ${functions.length} 个函数/方法
${newFunctions.length > 0 ? `
新增功能:
${newFunctions.slice(0, 5).join('\n')}
` : ''}
`;
});
allChanges.push(...jsChanges.map(c => `JS: 新增 ${c}`));
}
// 如果没有分析结果
if (!analysisHTML) {
analysisHTML = '
未识别到可分析的代码文件
';
} else {
// 添加自动生成的变更建议
if (allChanges.length > 0) {
analysisHTML += `
自动生成的变更建议
根据代码分析,建议将此版本的变更内容包含以下条目:
${allChanges.slice(0, 8).map(c => `- ${c}
`).join('')}
`;
// 添加应用到变更的事件
setTimeout(() => {
const applyBtn = document.getElementById('applyChangesBtn');
if (applyBtn) {
applyBtn.addEventListener('click', () => {
const version = state.versions.find(v => v.id === state.currentVersion);
if (version) {
version.changes = [...new Set([...version.changes, ...allChanges.slice(0, 8)])];
selectVersion(version.id);
}
});
}
}, 100);
}
}
analysisContent.innerHTML = analysisHTML;
}, 1000);
}
// 提取HTML新增元素
function extractNewElements(htmlContent) {
const elementRegex = /<([a-z][a-z0-9]*)(?![^>]*\/>)[^>]*>/gi;
const elements = [];
let match;
while ((match = elementRegex.exec(htmlContent)) !== null) {
const tag = match[1];
const fullTag = match[0];
// 忽略常见元素
if (!['div', 'span', 'p', 'a', 'img', 'ul', 'li'].includes(tag)) {
elements.push(fullTag);
}
}
return elements.slice(0, 10); // 只返回前10个特殊元素
}
// 提取CSS新增规则
function extractNewRules(cssContent) {
const ruleRegex = /[^{]+\{[^}]+\}/g;
const matches = cssContent.match(ruleRegex) || [];
// 过滤掉常见规则
return matches.filter(rule => {
return !rule.includes('margin:') &&
!rule.includes('padding:') &&
!rule.includes('color:') &&
!rule.includes('font-size:');
}).slice(0, 10); // 只返回前10条特殊规则
}
// 提取JS新增函数
function extractNewFunctions(jsContent) {
const functions = [];
const functionRegex = /(function\s+([^\s(]+)\s*\([^)]*\)\s*\{|const\s+([^\s=]+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>\s*\{|class\s+([^\s{]+)\s*\{)/g;
let match;
while ((match = functionRegex.exec(jsContent)) !== null) {
if (match[2]) {
functions.push(`function ${match[2]}()`);
} else if (match[3]) {
functions.push(`const ${match[3]} = () => {}`);
} else if (match[5]) {
functions.push(`class ${match[5]} {}`);
}
}
return functions.slice(0, 10); // 只返回前10个函数
}
// 版本对比功能
function compareVersions(versionId1, versionId2) {
const version1 = state.versions.find(v => v.id === versionId1);
const version2 = state.versions.find(v => v.id === versionId2);
if (!version1 || !version2) {
alert('请选择两个有效的版本进行比较');
return;
}
// 创建比较界面
const compareHTML = `
版本比较: ${version1.id} ↔ ${version2.id}
与
变更内容对比
${compareChanges(version1.changes, version2.changes).map(c => `- ${c}
`).join('')}
代码文件对比
${compareCodeFiles(version1.files, version2.files)}
`;
analysisContent.innerHTML = compareHTML;
// 添加比较事件
document.getElementById('doCompareBtn').addEventListener('click', () => {
const v1 = document.getElementById('compareVersion1').value;
const v2 = document.getElementById('compareVersion2').value;
compareVersions(v1, v2);
});
}
// 比较变更内容
function compareChanges(changes1, changes2) {
const added = changes2.filter(c => !changes1.includes(c));
const removed = changes1.filter(c => !changes2.includes(c));
const results = [];
if (added.length > 0) {
results.push('
新增内容:');
results.push(...added.map(c => `+ ${c}`));
}
if (removed.length > 0) {
results.push('
移除内容:');
results.push(...removed.map(c => `- ${c}`));
}
if (results.length === 0) {
results.push('无显著变更内容');
}
return results;
}
// 比较代码文件
function compareCodeFiles(files1 = [], files2 = []) {
const fileNames1 = files1.map(f => f.name);
const fileNames2 = files2.map(f => f.name);
const addedFiles = files2.filter(f => !fileNames1.includes(f.name));
const removedFiles = files1.filter(f => !fileNames2.includes(f.name));
const commonFiles = files1.filter(f1 => fileNames2.includes(f1.name))
.map(f1 => {
const f2 = files2.find(f => f.name === f1.name);
return { name: f1.name, content1: f1.content, content2: f2.content };
});
let resultHTML = '';
if (addedFiles.length > 0) {
resultHTML += `
新增文件:
${addedFiles.map(f => `- ${f.name}
`).join('')}
`;
}
if (removedFiles.length > 0) {
resultHTML += `
移除文件:
${removedFiles.map(f => `- ${f.name}
`).join('')}
`;
}
if (commonFiles.length > 0) {
resultHTML += `
变更文件:
${commonFiles.map(file => `
${file.name}
${generateDiff(file.content1, file.content2)}
`).join('')}
`;
}
if (!resultHTML) {
resultHTML = '
无文件变更
';
}
return resultHTML;
}
// 生成简单的文本差异
function generateDiff(text1, text2) {
const lines1 = text1.split('\n');
const lines2 = text2.split('\n');
const diff = [];
const maxLines = Math.max(lines1.length, lines2.length);
for (let i = 0; i < maxLines; i++) {
const line1 = lines1[i] || '';
const line2 = lines2[i] || '';
if (line1 !== line2) {
if (line1 && line2) {
diff.push(`
- ${line1}
`);
diff.push(`
+ ${line2}
`);
} else if (line1) {
diff.push(`
- ${line1}
`);
} else if (line2) {
diff.push(`
+ ${line2}
`);
}
}
}
return diff.length > 0 ? diff.join('') : '
文件内容无变化
';
}
// 在初始化函数中添加比较按钮
function initApp() {
renderVersionList();
if (state.versions.length > 0) {
selectVersion(state.versions[0].id);
}
setupEventListeners();
// 添加比较按钮到详情区域
const detailHeader = document.getElementById('versionDetailHeader');
const compareBtn = document.createElement('button');
compareBtn.innerHTML = '
比较版本';
compareBtn.style.marginLeft = 'auto';
compareBtn.style.padding = '0.5rem 1rem';
compareBtn.style.background = 'var(--primary-light)';
compareBtn.style.color = 'var(--primary)';
compareBtn.style.border = 'none';
compareBtn.style.borderRadius = '8px';
compareBtn.style.cursor = 'pointer';
compareBtn.addEventListener('click', () => {
if (state.versions.length < 2) {
alert('至少需要两个版本才能进行比较');
return;
}
// 默认比较当前版本和前一个版本
const currentIndex = state.versions.findIndex(v => v.id === state.currentVersion);
const prevVersion = currentIndex < state.versions.length - 1 ? state.versions[currentIndex + 1].id : state.versions[0].id;
compareVersions(prevVersion, state.currentVersion);
});
const subtitle = document.getElementById('versionSubtitle');
subtitle.parentNode.insertBefore(compareBtn, subtitle.nextSibling);
}
// 更新文件上传处理,支持多文件分析
function handleFileSelect(event) {
const files = event.target.files;
if (!files || files.length === 0) return;
// 清空文件列表
fileList.innerHTML = '';
// 读取所有文件
const fileReaders = [];
Array.from(files).forEach(file => {
if (file.type.startsWith('text/') ||
file.name.endsWith('.js') ||
file.name.endsWith('.css') ||
file.name.endsWith('.html')) {
// 创建文件项
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.dataset.name = file.name;
const fileInfo = `
${file.name}
${formatFileSize(file.size)}
`;
fileItem.innerHTML = fileInfo;
fileList.appendChild(fileItem);
// 读取文件内容
const reader = new FileReader();
reader.onload = (e) => {
const content = e.target.result;
// 更新文件项状态
const icon = fileItem.querySelector('.fa-spinner');
if (icon) {
icon.className = 'fas fa-check-circle';
icon.style.color = '#4CAF50';
}
// 添加到上传文件列表
state.uploadedFiles.push({
name: file.name,
size: file.size,
type: file.type,
content: content,
lastModified: file.lastModified
});
// 如果所有文件都读取完成,进行分析
if (state.uploadedFiles.length === Array.from(files).filter(f =>
f.type.startsWith('text/') ||
f.name.endsWith('.js') ||
f.name.endsWith('.css') ||
f.name.endsWith('.html')
).length) {
// 自动创建新版本或添加到当前版本
if (confirm('是否要为这些文件创建一个新版本?')) {
showVersionModal();
} else if (state.currentVersion) {
const version = state.versions.find(v => v.id === state.currentVersion);
if (version) {
version.files = [...(version.files || []), ...state.uploadedFiles];
analyzeCodeFiles(version.files);
state.uploadedFiles = [];
}
}
}
};
reader.readAsText(file);
fileReaders.push(reader);
}
});
}
// 更新保存版本函数,包含上传的文件
function saveVersion() {
const versionNumber = document.getElementById('versionNumber').value.trim();
const versionName = document.getElementById('versionName').value.trim();
const versionDate = document.getElementById('versionDate').value;
const versionOwner = document.getElementById('versionOwner').value.trim();
const changesText = document.getElementById('versionChangesInput').value.trim();
if (!versionNumber || !versionName || !versionDate || !versionOwner || !changesText) {
alert('请填写所有必填字段');
return;
}
const changes = changesText.split('\n').filter(line => line.trim().length > 0);
const tagCheckboxes = document.querySelectorAll('input[name="versionTag"]:checked');
const tags = Array.from(tagCheckboxes).map(cb => cb.value);
const newVersion = {
id: versionNumber,
title: `${versionNumber} - ${versionName}`,
date: versionDate,
owner: versionOwner,
tags: tags,
changes: changes,
files: [...state.uploadedFiles] // 包含上传的文件
};
state.versions.unshift(newVersion);
state.uploadedFiles = [];
selectVersion(newVersion.id);
hideVersionModal();
// 重置表单
document.getElementById('versionNumber').value = '';
document.getElementById('versionName').value = '';
document.getElementById('versionOwner').value = '';
document.getElementById('versionChangesInput').value = '';
document.querySelectorAll('input[name="versionTag"]').forEach(cb => cb.checked = false);
// 清空文件列表
fileList.innerHTML = '';
}
// 初始化应用
initApp();