feat: 生成/审查页面的 ref 选择器添加标签和最近提交

This commit is contained in:
2026-06-19 22:23:54 +08:00
parent aee8f90148
commit d320c3a847
2 changed files with 134 additions and 37 deletions
+60 -18
View File
@@ -13,13 +13,13 @@
<div class="bg-white rounded-lg shadow-md p-6 mb-6"> <div class="bg-white rounded-lg shadow-md p-6 mb-6">
<div class="grid grid-cols-2 gap-4 mb-4"> <div class="grid grid-cols-2 gap-4 mb-4">
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">Base 分支</label> <label class="block text-sm font-medium text-gray-700 mb-1">Base</label>
<select id="base-ref" class="w-full border rounded-md px-3 py-2" required> <select id="base-ref" class="w-full border rounded-md px-3 py-2" required>
<option value="">加载中...</option> <option value="">加载中...</option>
</select> </select>
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">Head 分支</label> <label class="block text-sm font-medium text-gray-700 mb-1">Head</label>
<select id="head-ref" class="w-full border rounded-md px-3 py-2" required> <select id="head-ref" class="w-full border rounded-md px-3 py-2" required>
<option value="">加载中...</option> <option value="">加载中...</option>
</select> </select>
@@ -94,40 +94,82 @@
if (!resp.ok) throw new Error('Failed to load refs'); if (!resp.ok) throw new Error('Failed to load refs');
const refs = await resp.json(); const refs = await resp.json();
const baseSelect = document.getElementById('base-ref'); // Fetch recent commits for the "Recent Commits" group
const headSelect = document.getElementById('head-ref'); let commits = [];
baseSelect.innerHTML = '<option value="">选择分支</option>'; try {
headSelect.innerHTML = '<option value="">选择分支</option>'; const graphResp = await fetch(`/api/repos/${repoId}/graph?max_commits=20`);
if (graphResp.ok) {
commits = await graphResp.json();
}
} catch (e) {
console.warn('Failed to load recent commits:', e);
}
const selects = ['base-ref', 'head-ref'];
const branches = refs.filter(r => !r.is_tag); const branches = refs.filter(r => !r.is_tag);
const tags = refs.filter(r => r.is_tag);
branches.forEach(ref => { selects.forEach(selectId => {
const opt1 = document.createElement('option'); const select = document.getElementById(selectId);
opt1.value = ref.name; select.innerHTML = '<option value="">选择分支/标签</option>';
opt1.textContent = ref.name + (ref.is_head ? ' (HEAD)' : '');
baseSelect.appendChild(opt1);
const opt2 = document.createElement('option'); if (branches.length > 0) {
opt2.value = ref.name; const group = document.createElement('optgroup');
opt2.textContent = ref.name + (ref.is_head ? ' (HEAD)' : ''); group.label = '分支';
headSelect.appendChild(opt2); branches.forEach(ref => {
const opt = document.createElement('option');
opt.value = ref.name;
opt.textContent = ref.name + (ref.is_head ? ' (HEAD)' : '');
group.appendChild(opt);
});
select.appendChild(group);
}
if (tags.length > 0) {
const group = document.createElement('optgroup');
group.label = '标签';
tags.forEach(ref => {
const opt = document.createElement('option');
opt.value = ref.name;
opt.textContent = ref.name;
group.appendChild(opt);
});
select.appendChild(group);
}
if (commits.length > 0) {
const group = document.createElement('optgroup');
group.label = '最近提交';
commits.forEach(commit => {
const opt = document.createElement('option');
opt.value = commit.hash;
opt.textContent = `${commit.short_hash} ${commit.message.substring(0, 40)}`;
group.appendChild(opt);
});
select.appendChild(group);
}
}); });
// Auto-select from URL params, or fall back to main/master and HEAD // Auto-select from URL params, or fall back to main/master and HEAD
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const urlBase = params.get('base'); const urlBase = params.get('base');
const urlHead = params.get('head'); const urlHead = params.get('head');
const baseSelect = document.getElementById('base-ref');
const headSelect = document.getElementById('head-ref');
const mainBranch = branches.find(b => b.name === 'main' || b.name === 'master'); const mainBranch = branches.find(b => b.name === 'main' || b.name === 'master');
const headBranch = branches.find(r => r.is_head); const headBranch = refs.find(r => r.is_head);
if (urlBase && branches.some(b => b.name === urlBase)) { // Check if value exists in select options
const hasOption = (select, val) => Array.from(select.options).some(o => o.value === val);
if (urlBase && hasOption(baseSelect, urlBase)) {
baseSelect.value = urlBase; baseSelect.value = urlBase;
} else if (mainBranch) { } else if (mainBranch) {
baseSelect.value = mainBranch.name; baseSelect.value = mainBranch.name;
} }
if (urlHead && branches.some(b => b.name === urlHead)) { if (urlHead && hasOption(headSelect, urlHead)) {
headSelect.value = urlHead; headSelect.value = urlHead;
} else if (headBranch) { } else if (headBranch) {
headSelect.value = headBranch.name; headSelect.value = headBranch.name;
+74 -19
View File
@@ -19,13 +19,13 @@
<div class="bg-white rounded-lg shadow-md p-6 mb-6"> <div class="bg-white rounded-lg shadow-md p-6 mb-6">
<div class="grid grid-cols-3 gap-4 mb-4"> <div class="grid grid-cols-3 gap-4 mb-4">
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">Base 分支</label> <label class="block text-sm font-medium text-gray-700 mb-1">Base</label>
<select id="base-ref" class="w-full border rounded-md px-3 py-2" required> <select id="base-ref" class="w-full border rounded-md px-3 py-2" required>
<option value="">加载中...</option> <option value="">加载中...</option>
</select> </select>
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">Head 分支</label> <label class="block text-sm font-medium text-gray-700 mb-1">Head</label>
<select id="head-ref" class="w-full border rounded-md px-3 py-2" required> <select id="head-ref" class="w-full border rounded-md px-3 py-2" required>
<option value="">加载中...</option> <option value="">加载中...</option>
</select> </select>
@@ -115,30 +115,85 @@
if (!resp.ok) throw new Error('Failed to load refs'); if (!resp.ok) throw new Error('Failed to load refs');
const refs = await resp.json(); const refs = await resp.json();
const baseSelect = document.getElementById('base-ref'); // Fetch recent commits for the "Recent Commits" group
const headSelect = document.getElementById('head-ref'); let commits = [];
baseSelect.innerHTML = '<option value="">选择分支</option>'; try {
headSelect.innerHTML = '<option value="">选择分支</option>'; const graphResp = await fetch(`/api/repos/${repoId}/graph?max_commits=20`);
if (graphResp.ok) {
commits = await graphResp.json();
}
} catch (e) {
console.warn('Failed to load recent commits:', e);
}
const selects = ['base-ref', 'head-ref'];
const branches = refs.filter(r => !r.is_tag); const branches = refs.filter(r => !r.is_tag);
const tags = refs.filter(r => r.is_tag);
branches.forEach(ref => { selects.forEach(selectId => {
const opt1 = document.createElement('option'); const select = document.getElementById(selectId);
opt1.value = ref.name; select.innerHTML = '<option value="">选择分支/标签</option>';
opt1.textContent = ref.name + (ref.is_head ? ' (HEAD)' : '');
baseSelect.appendChild(opt1);
const opt2 = document.createElement('option'); if (branches.length > 0) {
opt2.value = ref.name; const group = document.createElement('optgroup');
opt2.textContent = ref.name + (ref.is_head ? ' (HEAD)' : ''); group.label = '分支';
headSelect.appendChild(opt2); branches.forEach(ref => {
const opt = document.createElement('option');
opt.value = ref.name;
opt.textContent = ref.name + (ref.is_head ? ' (HEAD)' : '');
group.appendChild(opt);
});
select.appendChild(group);
}
if (tags.length > 0) {
const group = document.createElement('optgroup');
group.label = '标签';
tags.forEach(ref => {
const opt = document.createElement('option');
opt.value = ref.name;
opt.textContent = ref.name;
group.appendChild(opt);
});
select.appendChild(group);
}
if (commits.length > 0) {
const group = document.createElement('optgroup');
group.label = '最近提交';
commits.forEach(commit => {
const opt = document.createElement('option');
opt.value = commit.hash;
opt.textContent = `${commit.short_hash} ${commit.message.substring(0, 40)}`;
group.appendChild(opt);
});
select.appendChild(group);
}
}); });
const mainBranch = branches.find(b => b.name === 'main' || b.name === 'master'); // Auto-select from URL params, or fall back to main/master and HEAD
const headBranch = branches.find(r => r.is_head); const params = new URLSearchParams(window.location.search);
const urlBase = params.get('base');
const urlHead = params.get('head');
const baseSelect = document.getElementById('base-ref');
const headSelect = document.getElementById('head-ref');
if (mainBranch) baseSelect.value = mainBranch.name; const mainBranch = branches.find(b => b.name === 'main' || b.name === 'master');
if (headBranch) headSelect.value = headBranch.name; const headBranch = refs.find(r => r.is_head);
const hasOption = (select, val) => Array.from(select.options).some(o => o.value === val);
if (urlBase && hasOption(baseSelect, urlBase)) {
baseSelect.value = urlBase;
} else if (mainBranch) {
baseSelect.value = mainBranch.name;
}
if (urlHead && hasOption(headSelect, urlHead)) {
headSelect.value = urlHead;
} else if (headBranch) {
headSelect.value = headBranch.name;
}
} catch (err) { } catch (err) {
console.error('Load refs error:', err); console.error('Load refs error:', err);
} }