You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openrank/front/frontend/index.html

306 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenRank Dashboard - 开源贡献分析平台</title>
<link rel="stylesheet" href="styles.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<!-- Inline tiny favicon to avoid browser requesting /favicon.ico and causing 404 in dev console -->
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=">
</head>
<body>
<div class="container">
<!-- 头部导航 -->
<header class="header">
<div class="logo">
<i class="fas fa-chart-network"></i>
<span>OpenRank Analytics</span>
</div>
<div class="mode-switch" id="mode-switch">
<button class="mode-btn active" data-mode="global">全局模式</button>
<button class="mode-btn" data-mode="project">项目模式</button>
</div>
<nav class="nav">
<button class="nav-btn active" data-tab="overview">总览</button>
<button class="nav-btn" data-tab="repositories">仓库分析</button>
<button class="nav-btn" data-tab="developers">开发者分析</button>
<button class="nav-btn" data-tab="network">网络图谱</button>
<button class="nav-btn" data-tab="settings">配置</button>
</nav>
<div class="header-actions">
<button class="btn-primary" id="calculate-btn">
<i class="fas fa-calculator"></i>
计算 OpenRank
</button>
</div>
</header>
<!-- 主要内容区域 -->
<main class="main-content" id="global-root">
<!-- 总览面板 -->
<div class="tab-content active" id="overview">
<div class="stats-grid">
<!-- 关键指标卡片 -->
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-users"></i>
</div>
<div class="stat-info">
<h3>活跃开发者</h3>
<span class="stat-value" id="active-developers">0</span>
<span class="stat-change positive">+12%</span>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-code-branch"></i>
</div>
<div class="stat-info">
<h3>活跃仓库</h3>
<span class="stat-value" id="active-repos">0</span>
<span class="stat-change positive">+8%</span>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-star"></i>
</div>
<div class="stat-info">
<h3>总 OpenRank</h3>
<span class="stat-value" id="total-openrank">0</span>
<span class="stat-change positive">+15%</span>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-trending-up"></i>
</div>
<div class="stat-info">
<h3>平均贡献度</h3>
<span class="stat-value" id="avg-contribution">0</span>
<span class="stat-change positive">+5%</span>
</div>
</div>
</div>
<!-- 图表区域 -->
<div class="charts-grid">
<div class="chart-card">
<h3>OpenRank 分布</h3>
<div class="chart-container" id="distribution-chart">
<canvas id="distribution-canvas"></canvas>
</div>
</div>
<div class="chart-card">
<h3>活动类型分析</h3>
<div class="chart-container" id="activity-chart">
<canvas id="activity-canvas"></canvas>
</div>
</div>
<div class="chart-card full-width">
<h3>Top 10 开发者排名</h3>
<div class="ranking-table" id="developer-ranking">
<div class="table-header">
<span>排名</span>
<span>开发者</span>
<span>OpenRank</span>
<span>变化</span>
</div>
<div class="table-body" id="ranking-body">
<!-- 排名数据将通过JS动态生成 -->
</div>
</div>
</div>
</div>
</div>
<!-- 仓库分析面板 -->
<div class="tab-content" id="repositories">
<div class="panel-header">
<h2>仓库分析</h2>
<div class="filter-controls">
<select id="repo-filter">
<option value="all">所有仓库</option>
<option value="top10">Top 10</option>
<option value="rising">上升最快</option>
</select>
</div>
</div>
<div class="repo-grid" id="repo-grid">
<!-- 仓库卡片将通过JS动态生成 -->
</div>
</div>
<!-- 开发者分析面板 -->
<div class="tab-content" id="developers">
<div class="panel-header">
<h2>开发者分析</h2>
<div class="search-box">
<input type="text" placeholder="搜索开发者..." id="developer-search">
<i class="fas fa-search"></i>
</div>
</div>
<div class="developer-stats">
<!-- 开发者分析内容 -->
</div>
</div>
<!-- 网络图谱面板 -->
<div class="tab-content" id="network">
<div class="panel-header">
<h2>协作网络图谱</h2>
<div class="graph-controls">
<button class="btn-secondary" id="zoom-in">
<i class="fas fa-plus"></i>
</button>
<button class="btn-secondary" id="zoom-out">
<i class="fas fa-minus"></i>
</button>
<button class="btn-secondary" id="reset-view">
<i class="fas fa-sync"></i>
</button>
</div>
</div>
<div class="graph-container" id="network-graph">
<canvas id="graph-canvas"></canvas>
</div>
</div>
<!-- 配置面板 -->
<div class="tab-content" id="settings">
<div class="panel-header">
<h2>算法配置</h2>
</div>
<div class="settings-grid">
<div class="setting-card">
<h3>全局参数</h3>
<div class="setting-item">
<label>衰减因子</label>
<input type="number" id="attenuation-factor" value="0.85" step="0.01" min="0" max="1">
</div>
<div class="setting-item">
<label>开发者继承比例</label>
<input type="number" id="developer-retention" value="0.5" step="0.01" min="0" max="1">
</div>
<div class="setting-item">
<label>仓库继承比例</label>
<input type="number" id="repo-retention" value="0.3" step="0.01" min="0" max="1">
</div>
</div>
<div class="setting-card">
<h3>活动权重</h3>
<div class="setting-item">
<label>Issue 评论</label>
<input type="number" id="issue-comment-weight" value="0.5252" step="0.0001">
</div>
<div class="setting-item">
<label>创建 Issue</label>
<input type="number" id="open-issue-weight" value="2.2235" step="0.0001">
</div>
<div class="setting-item">
<label>创建 PR</label>
<input type="number" id="open-pull-weight" value="4.0679" step="0.0001">
</div>
</div>
</div>
</div>
</main>
<!-- 项目模式根容器(初始隐藏,通过模式切换显示) -->
<div id="project-mode-root" class="project-mode hidden">
<div class="project-layout">
<aside class="project-sidebar">
<div class="project-sidebar-header">
<h3>项目列表</h3>
<input type="text" id="project-search" placeholder="搜索项目..." />
</div>
<ul id="project-list" class="project-list"></ul>
</aside>
<section class="project-content" id="project-content">
<div class="project-placeholder">
<h3>选择一个项目查看项目级 OpenRank</h3>
<p>左侧选择仓库,右侧展示项目级贡献者、活动分布和时间序列。</p>
</div>
</section>
</div>
</div>
<!-- 状态栏 -->
<footer class="footer">
<div class="status-info">
<span class="status-dot"></span>
<span>系统就绪</span>
</div>
<div class="footer-info">
<span>OpenRank v1.0.0</span>
<span>最后更新: <span id="last-update">-</span></span>
</div>
</footer>
</div>
<!-- 加载动画 -->
<div class="loading-overlay" id="loading-overlay">
<div class="loading-spinner">
<i class="fas fa-atom fa-spin"></i>
<p>计算中...</p>
</div>
</div>
<!-- 计算 OpenRank 输入对话框 -->
<div class="modal" id="calc-modal">
<div class="modal-content" style="max-width:480px;position:relative;">
<button class="modal-close" data-close-calc>×</button>
<h3 style="margin-top:0;font-size:1.15rem;">计算 OpenRank</h3>
<p style="font-size:.8rem;opacity:.7;margin:.25rem 0 1rem;">请输入仓库 owner 与 repo并指定起止日期任意跨度。如不填则默认最近 30 天。可使用快捷按钮快速填充最近 30/90/180/365 天。</p>
<form id="calc-form" style="display:flex;flex-direction:column;gap:.9rem;">
<div style="display:flex;flex-direction:column;gap:.4rem;">
<label style="font-size:.7rem;letter-spacing:.5px;opacity:.75;">Owner (组织 / 用户)</label>
<input id="calc-owner" type="text" placeholder="例如: demo-org" autocomplete="off" required style="background:rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.2);color:#fff;padding:.55rem .7rem;border-radius:8px;outline:none;" />
</div>
<div style="display:flex;flex-direction:column;gap:.4rem;">
<label style="font-size:.7rem;letter-spacing:.5px;opacity:.75;">Repo (仓库名)</label>
<input id="calc-repo" type="text" placeholder="例如: awesome-project" autocomplete="off" required style="background:rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.2);color:#fff;padding:.55rem .7rem;border-radius:8px;outline:none;" />
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.75rem;align-items:end;">
<div style="display:flex;flex-direction:column;gap:.4rem;">
<label style="font-size:.7rem;letter-spacing:.5px;opacity:.75;">开始日期 (start)</label>
<input id="calc-start" type="date" style="background:rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.2);color:#fff;padding:.55rem .6rem;border-radius:8px;outline:none;font-size:.75rem;" />
</div>
<div style="display:flex;flex-direction:column;gap:.4rem;">
<label style="font-size:.7rem;letter-spacing:.5px;opacity:.75;">结束日期 (end)</label>
<input id="calc-end" type="date" style="background:rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.2);color:#fff;padding:.55rem .6rem;border-radius:8px;outline:none;font-size:.75rem;" />
</div>
<div style="display:flex;flex-direction:column;gap:.4rem;">
<label style="font-size:.7rem;letter-spacing:.5px;opacity:.75;">快捷范围</label>
<div style="display:flex;flex-wrap:wrap;gap:.35rem;">
<button type="button" class="quick-range-btn" data-range="30">30天</button>
<button type="button" class="quick-range-btn" data-range="90">90天</button>
<button type="button" class="quick-range-btn" data-range="180">180天</button>
<button type="button" class="quick-range-btn" data-range="365">365天</button>
</div>
</div>
</div>
<div style="display:flex;gap:.6rem;justify-content:flex-end;margin-top:.5rem;">
<button type="button" class="btn-secondary" data-close-calc style="min-width:90px;">取消</button>
<button type="submit" class="btn-primary" style="min-width:140px;">开始计算</button>
</div>
</form>
<div id="calc-hint" style="margin-top:.75rem;font-size:.65rem;line-height:1.2;opacity:.55;">
说明:首次计算会写入合成仓库记录,项目模式刷新后即可选择;正式项目级重算需在项目面板触发。
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="app.js"></script>
</body>
</html>