385 lines
13 KiB
Handlebars
385 lines
13 KiB
Handlebars
{{template "base/head" .}}
|
|
<!-- 强制加载 jQuery 和 Semantic UI -->
|
|
<script>
|
|
if (typeof jQuery === 'undefined') {
|
|
document.write('<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js"><\/script>');
|
|
document.write('<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.5.0/dist/semantic.min.js"><\/script>');
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.search-sort-container {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.search-input-container {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.sort-dropdown-container {
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.sort-dropdown-container:hover {
|
|
cursor: pointer;
|
|
background: var(--color-hover) !important;
|
|
}
|
|
|
|
.sort-dropdown-container.active {
|
|
background: var(--color-active) !important;
|
|
}
|
|
|
|
.sort-dropdown-trigger {
|
|
padding: 10px 12px;
|
|
}
|
|
|
|
.ui.dropdown .menu {
|
|
position: absolute;
|
|
top: 110%;
|
|
}
|
|
|
|
.ui.dropdown .text {
|
|
display: inline-block;
|
|
max-width: 120px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
/* 新增样式:选中项高亮 */
|
|
.ui.dropdown .menu .active.item,
|
|
.ui.dropdown .menu .item:hover {
|
|
background: var(--color-hover) !important;
|
|
}
|
|
|
|
/* 当前选中项样式 */
|
|
.ui.dropdown .menu .selected.item {
|
|
color: var(--color-text) !important;
|
|
background: var(--color-active) !important;
|
|
font-weight: var(--font-weight-medium) !important;
|
|
}
|
|
|
|
/* 隐藏元素 */
|
|
.hidden {
|
|
display: none !important;
|
|
}
|
|
</style>
|
|
|
|
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
|
|
<div class="ui middle very relaxed page grid">
|
|
<div class="column">
|
|
<!-- 搜索和排序控制栏 -->
|
|
<div class="search-sort-container">
|
|
<!-- 搜索框 -->
|
|
<div class="ui action input search-input-container">
|
|
<input type="text" id="searchInput" placeholder="搜索模板...">
|
|
<button class="ui small icon button" id="searchButton" aria-label="{{ctx.Locale.Tr " search.search"}}" {{with
|
|
.Tooltip}}data-tooltip-content="{{.}}" {{end}}{{if .Disabled}} disabled{{end}}>{{svg
|
|
"octicon-search"}}</button>
|
|
</div>
|
|
|
|
<!-- 排序下拉框 -->
|
|
<div class="sort-dropdown-container">
|
|
<div class="ui small dropdown type jump item" id="sortDropdown">
|
|
<div class="sort-dropdown-trigger">
|
|
<span class="text">
|
|
{{ctx.Locale.Tr "repo.issues.filter_sort"}}
|
|
</span>
|
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
|
</div>
|
|
<div class="menu">
|
|
<div class="item " data-value="newest">最新创建</div>
|
|
<div class="item" data-value="oldest">最早创建</div>
|
|
<div class="item" data-value="name_asc">按字母顺序排序</div>
|
|
<div class="item" data-value="name_desc">按字母逆序排序</div>
|
|
<div class="item" data-value="recently_updated">最近更新</div>
|
|
<div class="item" data-value="least_recently_updated">最早更新</div>
|
|
<div class="item" data-value="most_likes">点赞由多到少</div>
|
|
<div class="item" data-value="least_likes">点赞由少到多</div>
|
|
<div class="item" data-value="most_forks">派生由多到少</div>
|
|
<div class="item" data-value="least_forks">派生由少到多</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 模板卡片容器 -->
|
|
<div class="ui cards migrate-entries" id="template-cards">
|
|
<div class="ui active inverted">
|
|
<div class="ui text loader">加载模板中...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// 确保 jQuery 已加载
|
|
function ensureJQuery(callback) {
|
|
if (window.jQuery) {
|
|
callback();
|
|
} else {
|
|
setTimeout(() => ensureJQuery(callback), 100);
|
|
}
|
|
}
|
|
|
|
// 模板选择处理函数
|
|
const checkDevStarTemplate = function () {
|
|
if ($devstarTemplate.val() !== '' && $devstarTemplate.val() !== '0') {
|
|
if ($('#repo_name').val() == '') {
|
|
$('#repo_name').val(getRepoNameFromGitUrl($devstarTemplate.val()));
|
|
}
|
|
if ($('#description').val() == '') {
|
|
$('#description').val("init repo from " + $devstarTemplate.val());
|
|
}
|
|
$repoTemplate.val('');
|
|
hideElem($gitURLTemplateArea);
|
|
hideElem($repoTemplateArea);
|
|
hideElem($nonTemplate);
|
|
} else {
|
|
showElem($repoTemplateArea);
|
|
showElem($gitURLTemplateArea);
|
|
hideElem($templateUnits);
|
|
showElem($nonTemplate);
|
|
}
|
|
};
|
|
|
|
// 辅助函数
|
|
function hideElem($elem) {
|
|
$elem.addClass('hidden');
|
|
}
|
|
|
|
function showElem($elem) {
|
|
$elem.removeClass('hidden');
|
|
}
|
|
|
|
function getRepoNameFromGitUrl(url) {
|
|
return url.split('/').pop().replace('.git', '');
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
ensureJQuery(function () {
|
|
// 初始化变量
|
|
const cardsContainer = document.getElementById('template-cards');
|
|
const searchInput = document.getElementById('searchInput');
|
|
const searchButton = document.getElementById('searchButton');
|
|
const sortContainer = document.querySelector('.sort-dropdown-container');
|
|
const sortTrigger = document.querySelector('.sort-dropdown-trigger');
|
|
const sortDropdown = $('#sortDropdown');
|
|
let allRepos = [];
|
|
let currentSearchTerm = '';
|
|
let currentSortValue = 'newest';
|
|
|
|
// 假设这些元素在目标页面中存在
|
|
$devstarTemplate = $('#devstar_template');
|
|
$repoTemplate = $('#repo_template');
|
|
$gitURLTemplateArea = $('#git_url_template_area');
|
|
$repoTemplateArea = $('#repo_template_area');
|
|
$nonTemplate = $('#non_template');
|
|
$templateUnits = $('#template_units');
|
|
|
|
// 全局设置silent模式
|
|
$.fn.dropdown.settings.silent = true;
|
|
$.fn.transition.settings.silent = true;
|
|
|
|
// 初始化下拉框并添加选中状态处理
|
|
sortDropdown.dropdown({
|
|
action: 'hide',
|
|
onShow: function () {
|
|
$(`.ui.dropdown .menu .item[data-value="${currentSortValue}"]`).addClass('selected');
|
|
return document.body.contains(this);
|
|
},
|
|
onHide: function () {
|
|
return document.body.contains(this);
|
|
},
|
|
onChange: function (value) {
|
|
currentSortValue = value;
|
|
$('.ui.dropdown .menu .item').removeClass('selected');
|
|
$(`.ui.dropdown .menu .item[data-value="${value}"]`).addClass('selected');
|
|
sortContainer.classList.remove('active');
|
|
applyFiltersAndRender();
|
|
},
|
|
onInitialize: function () {
|
|
$('.ui.dropdown .menu .item').removeClass('selected');
|
|
$(`.ui.dropdown .menu .item[data-value="newest"]`).addClass('selected');
|
|
}
|
|
});
|
|
|
|
// 点击触发器时切换 active 类
|
|
sortTrigger.addEventListener('click', function () {
|
|
sortContainer.classList.toggle('active');
|
|
$(`.ui.dropdown .menu .item[data-value="${currentSortValue}"]`).addClass('selected');
|
|
});
|
|
|
|
// 点击页面其他位置时移除 active 类
|
|
document.addEventListener('click', function (event) {
|
|
if (!sortContainer.contains(event.target)) {
|
|
sortContainer.classList.remove('active');
|
|
}
|
|
});
|
|
|
|
// 鼠标右键时移除active类
|
|
document.addEventListener('contextmenu', function (event) {
|
|
if (!sortContainer.contains(event.target)) {
|
|
sortContainer.classList.remove('active');
|
|
}
|
|
});
|
|
|
|
// 获取模板数据
|
|
fetch('https://devstar.cn/api/v1/repos/search?template=true')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
allRepos = (data.data || []).map(repo => ({
|
|
...repo,
|
|
createdTimestamp: new Date(repo.created_at).getTime(),
|
|
updatedTimestamp: new Date(repo.updated_at).getTime(),
|
|
lowerName: repo.name.toLowerCase(),
|
|
lowerDescription: (repo.description || '').toLowerCase(),
|
|
starsCount: repo.stars_count || 0,
|
|
forksCount: repo.forks_count || 0
|
|
}));
|
|
|
|
applyFiltersAndRender();
|
|
|
|
// 搜索按钮点击事件
|
|
searchButton.addEventListener('click', function () {
|
|
currentSearchTerm = searchInput.value.trim().toLowerCase();
|
|
applyFiltersAndRender();
|
|
});
|
|
|
|
// 输入框回车事件
|
|
searchInput.addEventListener('keypress', function (e) {
|
|
if (e.key === 'Enter') {
|
|
currentSearchTerm = searchInput.value.trim().toLowerCase();
|
|
applyFiltersAndRender();
|
|
}
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error('获取模板数据失败:', error);
|
|
cardsContainer.innerHTML = '<div class="ui error message">加载模板失败</div>';
|
|
});
|
|
|
|
// 应用搜索和排序筛选
|
|
function applyFiltersAndRender() {
|
|
// 1. 应用搜索筛选
|
|
let filteredRepos = currentSearchTerm ?
|
|
allRepos.filter(repo =>
|
|
repo.lowerName.includes(currentSearchTerm)
|
|
) :
|
|
[...allRepos];
|
|
|
|
// 2. 应用排序
|
|
let sortedRepos = [...filteredRepos];
|
|
switch (currentSortValue) {
|
|
case 'newest':
|
|
sortedRepos.sort((a, b) => b.createdTimestamp - a.createdTimestamp);
|
|
break;
|
|
case 'oldest':
|
|
sortedRepos.sort((a, b) => a.createdTimestamp - b.createdTimestamp);
|
|
break;
|
|
case 'name_asc':
|
|
sortedRepos.sort((a, b) => a.name.localeCompare(b.name));
|
|
break;
|
|
case 'name_desc':
|
|
sortedRepos.sort((a, b) => b.name.localeCompare(a.name));
|
|
break;
|
|
case 'recently_updated':
|
|
sortedRepos.sort((a, b) => b.updatedTimestamp - a.updatedTimestamp);
|
|
break;
|
|
case 'least_recently_updated':
|
|
sortedRepos.sort((a, b) => a.updatedTimestamp - b.updatedTimestamp);
|
|
break;
|
|
case 'most_likes':
|
|
sortedRepos.sort((a, b) => b.starsCount - a.starsCount);
|
|
break;
|
|
case 'least_likes':
|
|
sortedRepos.sort((a, b) => a.starsCount - b.starsCount);
|
|
break;
|
|
case 'most_forks':
|
|
sortedRepos.sort((a, b) => b.forksCount - a.forksCount);
|
|
break;
|
|
case 'least_forks':
|
|
sortedRepos.sort((a, b) => a.forksCount - b.forksCount);
|
|
break;
|
|
}
|
|
|
|
// 3. 渲染结果
|
|
renderTemplates(sortedRepos);
|
|
}
|
|
|
|
// 渲染模板列表
|
|
function renderTemplates(repos) {
|
|
cardsContainer.innerHTML = '';
|
|
|
|
if (repos && repos.length > 0) {
|
|
repos.forEach(repo => {
|
|
const createdDate = new Date(repo.owner?.created || repo.created_at);
|
|
const formattedDate = createdDate.toLocaleDateString('zh-CN', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
|
|
const card = `
|
|
<div class="ui card migrate-entry">
|
|
<a class="ui card migrate-entry tw-flex tw-items-center template-card"
|
|
href="#"
|
|
data-name="${repo.name}"
|
|
data-description="${repo.description || ''}">
|
|
<img src="/assets/img/favicon.svg" width="184" height="184" class="tw-p-4" alt="${repo.name}" />
|
|
<div class="content">
|
|
<div class="header tw-text-center">${repo.name}</div>
|
|
<div class="description tw-text-center tw-text-balance">${repo.description || ''}</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
`;
|
|
cardsContainer.insertAdjacentHTML('beforeend', card);
|
|
});
|
|
|
|
// 添加点击事件处理
|
|
document.querySelectorAll('.template-card').forEach(card => {
|
|
card.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
|
|
const repoName = this.getAttribute('data-name');
|
|
const repoDescription = this.getAttribute('data-description');
|
|
|
|
// 填充表单值
|
|
if ($('#repo_name').length && $('#repo_name').val() === '') {
|
|
$('#repo_name').val(repoName);
|
|
}
|
|
|
|
if ($('#description').length && $('#description').val() === '') {
|
|
$('#description').val(repoDescription || `init repo from ${repoName}`);
|
|
}
|
|
|
|
// 模拟 $devstarTemplate 有值的情况
|
|
if ($devstarTemplate.length) {
|
|
$devstarTemplate.val('1');
|
|
checkDevStarTemplate();
|
|
}
|
|
|
|
// 如果需要跳转,可以在这里添加
|
|
window.location.href = "{{.AppSubUrl}}/repo/create";
|
|
});
|
|
});
|
|
} else {
|
|
cardsContainer.innerHTML = '<div class="ui warning message">未找到匹配的模板</div>';
|
|
}
|
|
|
|
// 确保下拉框状态正确
|
|
sortDropdown.dropdown('refresh');
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
{{template "base/footer" .}}
|