优化代码
This commit is contained in:
@@ -66,6 +66,7 @@
|
|||||||
color: var(--inputcolor-font);
|
color: var(--inputcolor-font);
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
|
height: 46px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control:focus {
|
.form-control:focus {
|
||||||
@@ -80,6 +81,8 @@
|
|||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
height: 46px;
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-button:hover {
|
.search-button:hover {
|
||||||
@@ -87,6 +90,16 @@
|
|||||||
background-color: #2ea8a0;
|
background-color: #2ea8a0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-append {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.result-card {
|
.result-card {
|
||||||
background-color: var(--card-bg);
|
background-color: var(--card-bg);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -535,6 +548,72 @@
|
|||||||
<div id="toast"></div>
|
<div id="toast"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// 添加统一的格式化工具对象
|
||||||
|
const formatUtils = {
|
||||||
|
formatNumber(num) {
|
||||||
|
if (num >= 1000000000) return (num / 1000000000).toFixed(1) + 'B+';
|
||||||
|
if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M+';
|
||||||
|
if (num >= 1000) return (num / 1000).toFixed(1) + 'K+';
|
||||||
|
return num.toString();
|
||||||
|
},
|
||||||
|
formatTimeAgo(dateString) {
|
||||||
|
if (!dateString) return '未知时间';
|
||||||
|
try {
|
||||||
|
let date;
|
||||||
|
date = new Date(dateString);
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
const formats = [
|
||||||
|
{ regex: /^(\d{4})-(\d{2})-(\d{2})$/, handler: (m) => new Date(m[1], m[2] - 1, m[3]) },
|
||||||
|
{ regex: /^(\d{4})\/(\d{2})\/(\d{2})$/, handler: (m) => new Date(m[1], m[2] - 1, m[3]) },
|
||||||
|
{ regex: /^(\d{2})\/(\d{2})\/(\d{4})$/, handler: (m) => new Date(m[3], m[1] - 1, m[2]) }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const format of formats) {
|
||||||
|
const match = dateString.match(format.regex);
|
||||||
|
if (match) {
|
||||||
|
date = format.handler(match);
|
||||||
|
if (!isNaN(date.getTime())) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
return '未知时间';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const diffTime = Math.abs(now - date);
|
||||||
|
const diffMinutes = Math.floor(diffTime / (1000 * 60));
|
||||||
|
const diffHours = Math.floor(diffTime / (1000 * 60 * 60));
|
||||||
|
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
||||||
|
const diffMonths = Math.floor(diffDays / 30);
|
||||||
|
const diffYears = Math.floor(diffDays / 365);
|
||||||
|
|
||||||
|
if (diffMinutes < 1) return '刚刚';
|
||||||
|
if (diffMinutes < 60) return `${diffMinutes}分钟前`;
|
||||||
|
if (diffHours < 24) return `${diffHours}小时前`;
|
||||||
|
if (diffDays < 7) return `${diffDays}天前`;
|
||||||
|
if (diffDays < 30) return `${Math.floor(diffDays / 7)}周前`;
|
||||||
|
if (diffMonths < 12) return `${diffMonths}个月前`;
|
||||||
|
if (diffYears < 1) return '近1年';
|
||||||
|
return `${diffYears}年前`;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('日期处理错误:', error);
|
||||||
|
return '未知时间';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatSize(bytes) {
|
||||||
|
const units = ['B', 'KB', 'MB', 'GB'];
|
||||||
|
let size = bytes;
|
||||||
|
let unitIndex = 0;
|
||||||
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||||
|
size /= 1024;
|
||||||
|
unitIndex++;
|
||||||
|
}
|
||||||
|
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let currentPage = 1;
|
let currentPage = 1;
|
||||||
let totalPages = 1;
|
let totalPages = 1;
|
||||||
let currentQuery = '';
|
let currentQuery = '';
|
||||||
@@ -715,72 +794,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatNumber(num) {
|
|
||||||
if (num >= 1000000000) {
|
|
||||||
return (num / 1000000000).toFixed(1) + 'B+';
|
|
||||||
}
|
|
||||||
if (num >= 1000000) {
|
|
||||||
return (num / 1000000).toFixed(1) + 'M+';
|
|
||||||
}
|
|
||||||
if (num >= 1000) {
|
|
||||||
return (num / 1000).toFixed(1) + 'K+';
|
|
||||||
}
|
|
||||||
return num.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatTimeAgo(dateString) {
|
|
||||||
if (!dateString) return '未知时间';
|
|
||||||
|
|
||||||
try {
|
|
||||||
let date;
|
|
||||||
// 尝试标准格式解析
|
|
||||||
date = new Date(dateString);
|
|
||||||
|
|
||||||
// 如果解析失败,尝试其他常见格式
|
|
||||||
if (isNaN(date.getTime())) {
|
|
||||||
const formats = [
|
|
||||||
{ regex: /^(\d{4})-(\d{2})-(\d{2})$/, handler: (m) => new Date(m[1], m[2] - 1, m[3]) },
|
|
||||||
{ regex: /^(\d{4})\/(\d{2})\/(\d{2})$/, handler: (m) => new Date(m[1], m[2] - 1, m[3]) },
|
|
||||||
{ regex: /^(\d{2})\/(\d{2})\/(\d{4})$/, handler: (m) => new Date(m[3], m[1] - 1, m[2]) }
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const format of formats) {
|
|
||||||
const match = dateString.match(format.regex);
|
|
||||||
if (match) {
|
|
||||||
date = format.handler(match);
|
|
||||||
if (!isNaN(date.getTime())) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNaN(date.getTime())) {
|
|
||||||
return '未知时间';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = new Date();
|
|
||||||
const diffTime = Math.abs(now - date);
|
|
||||||
const diffMinutes = Math.floor(diffTime / (1000 * 60));
|
|
||||||
const diffHours = Math.floor(diffTime / (1000 * 60 * 60));
|
|
||||||
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
||||||
const diffMonths = Math.floor(diffDays / 30);
|
|
||||||
const diffYears = Math.floor(diffDays / 365);
|
|
||||||
|
|
||||||
// 更精确的时间显示
|
|
||||||
if (diffMinutes < 1) return '刚刚';
|
|
||||||
if (diffMinutes < 60) return `${diffMinutes}分钟前`;
|
|
||||||
if (diffHours < 24) return `${diffHours}小时前`;
|
|
||||||
if (diffDays < 7) return `${diffDays}天前`;
|
|
||||||
if (diffDays < 30) return `${Math.floor(diffDays / 7)}周前`;
|
|
||||||
if (diffMonths < 12) return `${diffMonths}个月前`;
|
|
||||||
if (diffYears < 1) return '近1年';
|
|
||||||
return `${diffYears}年前`;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('日期处理错误:', error);
|
|
||||||
return '未知时间';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayResults(results, targetRepo = '') {
|
function displayResults(results, targetRepo = '') {
|
||||||
const resultsContainer = document.getElementById('searchResults');
|
const resultsContainer = document.getElementById('searchResults');
|
||||||
resultsContainer.innerHTML = '';
|
resultsContainer.innerHTML = '';
|
||||||
@@ -839,8 +852,8 @@
|
|||||||
if (organization) badges.push(`<span class="badge badge-organization">By ${organization}</span>`);
|
if (organization) badges.push(`<span class="badge badge-organization">By ${organization}</span>`);
|
||||||
|
|
||||||
const stats = [];
|
const stats = [];
|
||||||
if (starCount > 0) stats.push(`<span class="meta-item">⭐ ${formatNumber(starCount)}</span>`);
|
if (starCount > 0) stats.push(`<span class="meta-item">⭐ ${formatUtils.formatNumber(starCount)}</span>`);
|
||||||
if (pullCount > 0) stats.push(`<span class="meta-item">⬇️ ${formatNumber(pullCount)}+</span>`);
|
if (pullCount > 0) stats.push(`<span class="meta-item">⬇️ ${formatUtils.formatNumber(pullCount)}+</span>`);
|
||||||
|
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<div class="result-title">
|
<div class="result-title">
|
||||||
@@ -851,12 +864,12 @@
|
|||||||
<div class="result-meta">
|
<div class="result-meta">
|
||||||
<div class="meta-stats">
|
<div class="meta-stats">
|
||||||
${stats.join(' ')}
|
${stats.join(' ')}
|
||||||
${lastUpdated ? `<span class="meta-item">更新于 ${formatTimeAgo(lastUpdated)}</span>` : ''}
|
${lastUpdated ? `<span class="meta-item">更新于 ${formatUtils.formatTimeAgo(lastUpdated)}</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
${pullsLastWeek > 0 ? `
|
${pullsLastWeek > 0 ? `
|
||||||
<div class="meta-pulls">
|
<div class="meta-pulls">
|
||||||
<div>本周拉取次数</div>
|
<div>本周拉取次数</div>
|
||||||
<div class="pulls-count">${formatNumber(pullsLastWeek)}</div>
|
<div class="pulls-count">${formatUtils.formatNumber(pullsLastWeek)}</div>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
</div>
|
</div>
|
||||||
@@ -921,9 +934,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tag-description">${currentRepo.short_description || '暂无描述'}</div>
|
<div class="tag-description">${currentRepo.short_description || '暂无描述'}</div>
|
||||||
<div class="tag-meta">
|
<div class="tag-meta">
|
||||||
${currentRepo.star_count > 0 ? `<span class="meta-item">⭐ ${formatNumber(currentRepo.star_count)}</span>` : ''}
|
${currentRepo.star_count > 0 ? `<span class="meta-item">⭐ ${formatUtils.formatNumber(currentRepo.star_count)}</span>` : ''}
|
||||||
${currentRepo.pull_count > 0 ? `<span class="meta-item">⬇️ ${formatNumber(currentRepo.pull_count)}+</span>` : ''}
|
${currentRepo.pull_count > 0 ? `<span class="meta-item">⬇️ ${formatUtils.formatNumber(currentRepo.pull_count)}+</span>` : ''}
|
||||||
${currentRepo.last_updated ? `<span class="meta-item">更新于 ${formatTimeAgo(currentRepo.last_updated)}</span>` : ''}
|
${currentRepo.last_updated ? `<span class="meta-item">更新于 ${formatUtils.formatTimeAgo(currentRepo.last_updated)}</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="tag-pull-command">
|
<div class="tag-pull-command">
|
||||||
docker pull ${fullRepoName}
|
docker pull ${fullRepoName}
|
||||||
@@ -961,7 +974,7 @@
|
|||||||
const images = tag.images || [];
|
const images = tag.images || [];
|
||||||
const architectures = images.map(img => {
|
const architectures = images.map(img => {
|
||||||
const arch = `${img.os}/${img.architecture}${img.variant ? '/' + img.variant : ''}`;
|
const arch = `${img.os}/${img.architecture}${img.variant ? '/' + img.variant : ''}`;
|
||||||
const size = formatSize(img.size);
|
const size = formatUtils.formatSize(img.size);
|
||||||
return `<div class="arch-item" title="大小: ${size}">${arch}</div>`;
|
return `<div class="arch-item" title="大小: ${size}">${arch}</div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
@@ -972,9 +985,9 @@
|
|||||||
${vulnIndicators ? `<div class="vulnerability-indicator">${vulnIndicators}</div>` : ''}
|
${vulnIndicators ? `<div class="vulnerability-indicator">${vulnIndicators}</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="tag-meta">
|
<div class="tag-meta">
|
||||||
<span>最后更新: ${formatTimeAgo(tag.last_updated)}</span>
|
<span>最后更新: ${formatUtils.formatTimeAgo(tag.last_updated)}</span>
|
||||||
${tag.last_pusher ? `<span>由 ${tag.last_pusher} 推送</span>` : ''}
|
${tag.last_pusher ? `<span>由 ${tag.last_pusher} 推送</span>` : ''}
|
||||||
${tag.full_size ? `<span>大小: ${formatSize(tag.full_size)}</span>` : ''}
|
${tag.full_size ? `<span>大小: ${formatUtils.formatSize(tag.full_size)}</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="tag-pull-command">
|
<div class="tag-pull-command">
|
||||||
docker pull ${fullRepoName}:${tag.name}
|
docker pull ${fullRepoName}:${tag.name}
|
||||||
@@ -1042,19 +1055,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSize(bytes) {
|
|
||||||
const units = ['B', 'KB', 'MB', 'GB'];
|
|
||||||
let size = bytes;
|
|
||||||
let unitIndex = 0;
|
|
||||||
|
|
||||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
||||||
size /= 1024;
|
|
||||||
unitIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyToClipboard(text) {
|
function copyToClipboard(text) {
|
||||||
navigator.clipboard.writeText(text).then(() => {
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
showToast('已复制到剪贴板');
|
showToast('已复制到剪贴板');
|
||||||
|
|||||||
@@ -89,6 +89,18 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 添加全局HTTP客户端配置
|
||||||
|
var defaultHTTPClient = &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
DisableCompression: true,
|
||||||
|
DisableKeepAlives: false,
|
||||||
|
MaxIdleConnsPerHost: 10,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cache) Get(key string) (interface{}, bool) {
|
func (c *Cache) Get(key string) (interface{}, bool) {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
entry, exists := c.data[key]
|
entry, exists := c.data[key]
|
||||||
@@ -255,25 +267,8 @@ func searchDockerHub(ctx context.Context, query string, page, pageSize int) (*Se
|
|||||||
|
|
||||||
fullURL = fullURL + "?" + params.Encode()
|
fullURL = fullURL + "?" + params.Encode()
|
||||||
|
|
||||||
// 发送请求
|
// 使用全局HTTP客户端
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil)
|
client := defaultHTTPClient
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("创建请求失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Accept", "application/json")
|
|
||||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
|
|
||||||
|
|
||||||
client := &http.Client{
|
|
||||||
Timeout: 10 * time.Second,
|
|
||||||
Transport: &http.Transport{
|
|
||||||
MaxIdleConns: 100,
|
|
||||||
IdleConnTimeout: 90 * time.Second,
|
|
||||||
DisableCompression: true,
|
|
||||||
DisableKeepAlives: false,
|
|
||||||
MaxIdleConnsPerHost: 10,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var result *SearchResult
|
var result *SearchResult
|
||||||
var lastErr error
|
var lastErr error
|
||||||
@@ -454,6 +449,9 @@ func getRepositoryTags(ctx context.Context, namespace, name string) ([]TagInfo,
|
|||||||
|
|
||||||
fullURL := baseURL + "?" + params.Encode()
|
fullURL := baseURL + "?" + params.Encode()
|
||||||
|
|
||||||
|
// 使用全局HTTP客户端
|
||||||
|
client := defaultHTTPClient
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("创建请求失败: %v", err)
|
return nil, fmt.Errorf("创建请求失败: %v", err)
|
||||||
@@ -464,7 +462,6 @@ func getRepositoryTags(ctx context.Context, namespace, name string) ([]TagInfo,
|
|||||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("发送请求失败: %v", err)
|
return nil, fmt.Errorf("发送请求失败: %v", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user