refactor: 合并为单 crate,前端改为内嵌 React 控制台
合并 stash-cli / stash-core 为根目录单 crate(lib + bin),新增 React + Vite + TS 控制台替换 Askama 模板。 build.rs 用 walkdir 把 stash-web/dist 同步到 $OUT_DIR/web,由 include_dir! 编译期内嵌,产出单文件二进制。 路由改为 / 与 /assets/** 走内嵌前端,其余交给 ServeDir,缺失路径返回内置 404。同步更新依赖、.gitignore 与 README。
This commit is contained in:
@@ -0,0 +1,745 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Git 规范标准化与工作流生成器</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #f8fafc;
|
||||
--card-bg: #ffffff;
|
||||
--text-main: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border-color: #e2e8f0;
|
||||
--primary: #2563eb;
|
||||
--primary-hover: #1d4ed8;
|
||||
--primary-light: #eff6ff;
|
||||
--success: #10b981;
|
||||
--radius: 8px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-main);
|
||||
line-height: 1.5;
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1040px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-main);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
header p {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.nav-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
margin-bottom: 20px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 10px 20px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
background: none;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -2px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
color: var(--primary);
|
||||
border-bottom-color: var(--primary);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 完美的图片展示容器 */
|
||||
.img-viewport {
|
||||
background: #0f172a;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
overflow-x: auto;
|
||||
box-shadow: inset 0 0 10px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.img-viewport img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Grid layout for types */
|
||||
.type-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.type-card {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius);
|
||||
padding: 10px 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.type-card:hover {
|
||||
border-color: var(--primary);
|
||||
background: var(--primary-light);
|
||||
}
|
||||
|
||||
.type-card.selected {
|
||||
border-color: var(--primary);
|
||||
background: var(--primary-light);
|
||||
box-shadow: 0 0 0 2px var(--primary);
|
||||
}
|
||||
|
||||
.type-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.type-tag {
|
||||
font-family: monospace;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.type-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.form-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.form-row .form-group {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6px;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
label span.opt {
|
||||
color: var(--text-muted);
|
||||
font-weight: normal;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius);
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-main);
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
textarea:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* Output Box */
|
||||
.output-card {
|
||||
background: #1e293b;
|
||||
color: #f8fafc;
|
||||
border-radius: var(--radius);
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.output-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #334155;
|
||||
}
|
||||
|
||||
.output-title {
|
||||
font-size: 0.85rem;
|
||||
color: #94a3b8;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 6px 14px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.copy-btn.copied {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 0.9rem;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Template Cards for Branch */
|
||||
.template-selector {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tpl-btn {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--border-color);
|
||||
background: #fff;
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tpl-btn.active {
|
||||
border-color: var(--primary);
|
||||
background: var(--primary-light);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.rules-box {
|
||||
background: #f1f5f9;
|
||||
border-left: 4px solid var(--primary);
|
||||
padding: 12px;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
margin-top: 16px;
|
||||
border-radius: 0 var(--radius) var(--radius) 0;
|
||||
}
|
||||
|
||||
.rules-box ul {
|
||||
padding-left: 18px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
.type-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>Git 规范标准化生成器</h1>
|
||||
<p>企业级 Git 分支管理与 Commit 标准化生成工具</p>
|
||||
</header>
|
||||
|
||||
<!-- Nav Tabs -->
|
||||
<div class="nav-tabs">
|
||||
<button class="tab-btn active" onclick="switchTab('init-tab')">项目 Init 提交</button>
|
||||
<button class="tab-btn" onclick="switchTab('commit-tab')">常规 Commit 提交</button>
|
||||
<button class="tab-btn" onclick="switchTab('branch-tab')">分支命名与流转图</button>
|
||||
</div>
|
||||
|
||||
<!-- TAB 1: 项目 Init 提交 -->
|
||||
<div id="init-tab" class="tab-content active">
|
||||
<div class="card">
|
||||
<div class="section-title">初始化项目 Commit 模版</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="init-proj-name">项目名称</label>
|
||||
<input type="text" id="init-proj-name" placeholder="例: git_helper" oninput="updateInitOutput()">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="init-proj-desc">项目描述</label>
|
||||
<input type="text" id="init-proj-desc" placeholder="例: 基于 Spring Boot 的企业级后台" oninput="updateInitOutput()">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="init-env">开发环境 / 技术栈</label>
|
||||
<input type="text" id="init-env" placeholder="例: JDK 17, Spring Boot 3.x, MySQL" oninput="updateInitOutput()">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="init-items">初始化完成工作 <span class="opt">(按行分隔)</span></label>
|
||||
<textarea id="init-items" rows="5" oninput="updateInitOutput()">创建了基本的项目结构
|
||||
添加了初始的 README 文件
|
||||
添加了 .gitignore 文件
|
||||
配置了基本的开发环境(如 ESLint, Prettier)
|
||||
设置了基础文件夹结构(src, public, config)</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="init-next">下一步计划 <span class="opt">(按行分隔)</span></label>
|
||||
<textarea id="init-next" rows="3" oninput="updateInitOutput()">填充 README 文件
|
||||
添加项目基本设计文档
|
||||
配置 CI/CD 自动化部署流水线</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="output-card">
|
||||
<div class="output-header">
|
||||
<span class="output-title">生成的 Init Commit Message</span>
|
||||
<button class="copy-btn" onclick="copyText('init-result', this)">复制初始化信息</button>
|
||||
</div>
|
||||
<pre id="init-result"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB 2: 常规 Commit -->
|
||||
<div id="commit-tab" class="tab-content">
|
||||
<div class="card">
|
||||
<div class="section-title">1. 选择提交类型 (Type)</div>
|
||||
<div class="type-grid" id="commit-type-grid"></div>
|
||||
|
||||
<div class="section-title" style="margin-top:20px;">2. 填写提交内容</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="commit-scope">影响范围 Scope <span class="opt">(可选,如: auth, db)</span></label>
|
||||
<input type="text" id="commit-scope" placeholder="例: auth" oninput="updateCommitOutput()">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="commit-subject">简明描述 Description <span class="opt">(必填,建议主动语态)</span></label>
|
||||
<input type="text" id="commit-subject" placeholder="例: add user login functionality" oninput="updateCommitOutput()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="commit-body">详细说明 Body <span class="opt">(可选,补充变动原因及细节)</span></label>
|
||||
<textarea id="commit-body" placeholder="- Implemented login endpoint with JWT authentication - Added login form to frontend" oninput="updateCommitOutput()"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="section-title" style="margin-top:20px;">3. 脚注选项 (Footer)</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="commit-footer">关联 Issue / 脚注 <span class="opt">(可选,例: Closes #45)</span></label>
|
||||
<input type="text" id="commit-footer" placeholder="例: Closes #45" oninput="updateCommitOutput()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="commit-wip">WIP 状态 <span class="opt">(可选,正在进行中的工作)</span></label>
|
||||
<input type="text" id="commit-wip" placeholder="例: ing 登录模块UI正在联调" oninput="updateCommitOutput()">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="commit-fixup">FIXUP 目标 <span class="opt">(可选,修复先前提交的错误)</span></label>
|
||||
<input type="text" id="commit-fixup" placeholder="例: 修正上一个提交的拼写错误" oninput="updateCommitOutput()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="checkbox-group" style="display:flex; align-items:center; gap:8px; cursor:pointer;">
|
||||
<input type="checkbox" id="commit-breaking" onchange="updateCommitOutput()">
|
||||
<span>包含破坏性变动 (BREAKING CHANGE)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="output-card">
|
||||
<div class="output-header">
|
||||
<span class="output-title">生成的 Commit Message</span>
|
||||
<button class="copy-btn" onclick="copyText('commit-result', this)">复制提交信息</button>
|
||||
</div>
|
||||
<pre id="commit-result"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB 3: 分支命名与高清图片流转图 -->
|
||||
<div id="branch-tab" class="tab-content">
|
||||
<div class="card">
|
||||
<div class="section-title">企业敏捷 Git 演进架构图 (原生高清预览)</div>
|
||||
|
||||
<!-- 使用图片完美还原 Obsidian 效果 -->
|
||||
<div class="img-viewport">
|
||||
<img src="git-flow.png" alt="企业敏捷 Git 拓扑流转图">
|
||||
</div>
|
||||
|
||||
<div class="section-title">选择分支类型生成 Git 命令</div>
|
||||
<div class="template-selector">
|
||||
<button class="tpl-btn active" onclick="selectBranchType('develop', this)">创建 develop 分支</button>
|
||||
<button class="tpl-btn" onclick="selectBranchType('feature', this)">迭代功能分支 (SNAPSHOT)</button>
|
||||
<button class="tpl-btn" onclick="selectBranchType('personal', this)">组员个人开发分支</button>
|
||||
<button class="tpl-btn" onclick="selectBranchType('staging', this)">预生产分支 (Release)</button>
|
||||
<button class="tpl-btn" onclick="selectBranchType('hotfix', this)">Hotfix 修复分支</button>
|
||||
</div>
|
||||
|
||||
<div class="section-title" style="margin-top:16px;">填写项目与人员配置</div>
|
||||
<div class="form-row" id="proj-row">
|
||||
<div class="form-group">
|
||||
<label for="branch-proj">项目名称</label>
|
||||
<input type="text" id="branch-proj" value="git_helper" oninput="updateBranchOutput()">
|
||||
</div>
|
||||
<div class="form-group" id="version-group">
|
||||
<label for="branch-version">版本号</label>
|
||||
<input type="text" id="branch-version" value="v2.1.1" oninput="updateBranchOutput()">
|
||||
</div>
|
||||
<div class="form-group" id="date-group">
|
||||
<label for="branch-date">日期</label>
|
||||
<input type="text" id="branch-date" oninput="updateBranchOutput()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row" id="personal-fields" style="display:none;">
|
||||
<div class="form-group">
|
||||
<label for="branch-author">组员姓名/拼音</label>
|
||||
<input type="text" id="branch-author" value="zhangsan" oninput="updateBranchOutput()">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="branch-seq">同名序号 <span class="opt">(可选,如: 2)</span></label>
|
||||
<input type="text" id="branch-seq" placeholder="例: 2" oninput="updateBranchOutput()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rules-box">
|
||||
<strong>当前分支说明与规则:</strong>
|
||||
<ul id="branch-rules-list">
|
||||
<li>从 <code>main</code> 拉取并创建 <code>develop</code> 分支作为团队日常开发基线。</li>
|
||||
<li>执行命令:<code>git checkout -b develop main</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="output-card">
|
||||
<div class="output-header">
|
||||
<span class="output-title">生成的 Git 创建/切换分支命令</span>
|
||||
<button class="copy-btn" onclick="copyText('branch-result', this)">复制 Git 命令</button>
|
||||
</div>
|
||||
<pre id="branch-result"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 1. Commit Types Definition
|
||||
const commitTypes = [
|
||||
{ type: 'feat', label: '✨ feat', desc: '新功能 / 新特性' },
|
||||
{ type: 'fix', label: '🐛 fix', desc: '修复 bug' },
|
||||
{ type: 'docs', label: '📚 docs', desc: '文档更新' },
|
||||
{ type: 'style', label: '💄 style', desc: '代码格式(不影响功能)' },
|
||||
{ type: 'refactor', label: '💎 refactor', desc: '重构(既不修复bug也不加功能)' },
|
||||
{ type: 'perf', label: '⚡ perf', desc: '性能提升' },
|
||||
{ type: 'test', label: '🚨 test', desc: '添加或修改测试' },
|
||||
{ type: 'chore', label: '🔧 chore', desc: '构建/工具等杂项变动' },
|
||||
{ type: 'build', label: '📦 build', desc: '影响构建系统或依赖的变动' },
|
||||
{ type: 'security', label: '🛡️ security', desc: '安全性修复或增强' }
|
||||
];
|
||||
|
||||
let currentCommitType = 'feat';
|
||||
let currentBranchType = 'develop';
|
||||
|
||||
// Initialize Page
|
||||
window.onload = () => {
|
||||
renderCommitTypes();
|
||||
const today = new Date();
|
||||
const yyyy = today.getFullYear();
|
||||
const mm = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(today.getDate()).padStart(2, '0');
|
||||
document.getElementById('branch-date').value = `${yyyy}${mm}${dd}`;
|
||||
|
||||
updateCommitOutput();
|
||||
updateInitOutput();
|
||||
updateBranchOutput();
|
||||
};
|
||||
|
||||
// Tab Switching
|
||||
function switchTab(tabId) {
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
||||
|
||||
event.target.classList.add('active');
|
||||
document.getElementById(tabId).classList.add('active');
|
||||
}
|
||||
|
||||
// Render Commit Type Cards
|
||||
function renderCommitTypes() {
|
||||
const grid = document.getElementById('commit-type-grid');
|
||||
grid.innerHTML = commitTypes.map(t => `
|
||||
<div class="type-card ${t.type === currentCommitType ? 'selected' : ''}" onclick="selectCommitType('${t.type}', this)">
|
||||
<div class="type-header">
|
||||
<span class="type-tag">${t.label}</span>
|
||||
</div>
|
||||
<div class="type-desc">${t.desc}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function selectCommitType(type, el) {
|
||||
currentCommitType = type;
|
||||
document.querySelectorAll('#commit-type-grid .type-card').forEach(c => c.classList.remove('selected'));
|
||||
el.classList.add('selected');
|
||||
updateCommitOutput();
|
||||
}
|
||||
|
||||
// Update Commit Output
|
||||
function updateCommitOutput() {
|
||||
const scope = document.getElementById('commit-scope').value.trim();
|
||||
const subject = document.getElementById('commit-subject').value.trim();
|
||||
const body = document.getElementById('commit-body').value.trim();
|
||||
const footer = document.getElementById('commit-footer').value.trim();
|
||||
const wip = document.getElementById('commit-wip').value.trim();
|
||||
const fixup = document.getElementById('commit-fixup').value.trim();
|
||||
const isBreaking = document.getElementById('commit-breaking').checked;
|
||||
|
||||
let header = currentCommitType;
|
||||
if (scope) {
|
||||
header += `(${scope})`;
|
||||
}
|
||||
header += `: ${subject || '<描述>'}`;
|
||||
|
||||
let result = header;
|
||||
|
||||
if (body) {
|
||||
result += `\n\n${body}`;
|
||||
}
|
||||
|
||||
let footers = [];
|
||||
if (isBreaking) {
|
||||
footers.push(`BREAKING CHANGE: 包含破坏性变更,可能不向下兼容。`);
|
||||
}
|
||||
if (wip) {
|
||||
footers.push(`WIP: ${wip}`);
|
||||
}
|
||||
if (fixup) {
|
||||
footers.push(`FIXUP: ${fixup}`);
|
||||
}
|
||||
if (footer) {
|
||||
footers.push(footer);
|
||||
}
|
||||
|
||||
if (footers.length > 0) {
|
||||
result += `\n\n${footers.join('\n')}`;
|
||||
}
|
||||
|
||||
document.getElementById('commit-result').textContent = result;
|
||||
}
|
||||
|
||||
// Update Init Output
|
||||
function updateInitOutput() {
|
||||
const projName = document.getElementById('init-proj-name').value.trim() || '[项目名称]';
|
||||
const projDesc = document.getElementById('init-proj-desc').value.trim() || '[项目描述]';
|
||||
const env = document.getElementById('init-env').value.trim() || '[开发环境]';
|
||||
|
||||
const items = document.getElementById('init-items').value.trim().split('\n').filter(i => i).map(i => `- ${i}`).join('\n');
|
||||
const nexts = document.getElementById('init-next').value.trim().split('\n').filter(i => i).map(i => `- ${i}`).join('\n');
|
||||
|
||||
const result = `init: 初始化项目\n\n${items}\n\n项目说明:\n- 项目名称:${projName}\n- 项目描述:${projDesc}\n- 开发环境:${env}\n\n下一步:\n${nexts}\n\n感谢您的贡献!`;
|
||||
|
||||
document.getElementById('init-result').textContent = result;
|
||||
}
|
||||
|
||||
// Branch Type Selection
|
||||
function selectBranchType(type, btn) {
|
||||
currentBranchType = type;
|
||||
document.querySelectorAll('.template-selector .tpl-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
|
||||
const personalFields = document.getElementById('personal-fields');
|
||||
const versionGroup = document.getElementById('version-group');
|
||||
const dateGroup = document.getElementById('date-group');
|
||||
const rulesList = document.getElementById('branch-rules-list');
|
||||
|
||||
if (type === 'develop') {
|
||||
versionGroup.style.display = 'none';
|
||||
dateGroup.style.display = 'none';
|
||||
personalFields.style.display = 'none';
|
||||
rulesList.innerHTML = `
|
||||
<li>从 <code>main</code> 拉取并创建 <code>develop</code> 分支作为团队日常开发基线。</li>
|
||||
<li>执行命令:<code>git checkout -b develop main</code></li>
|
||||
`;
|
||||
} else {
|
||||
versionGroup.style.display = 'block';
|
||||
dateGroup.style.display = 'block';
|
||||
}
|
||||
|
||||
if (type === 'feature') {
|
||||
personalFields.style.display = 'none';
|
||||
rulesList.innerHTML = `
|
||||
<li>开发前从 <code>develop</code> 分支拉取,保持起始点一致。用于自动化部署测试环境。</li>
|
||||
<li>规范格式:<code>feature-{项目名}-{版本}-SNAPSHOT-{日期}</code></li>
|
||||
`;
|
||||
} else if (type === 'personal') {
|
||||
personalFields.style.display = 'flex';
|
||||
rulesList.innerHTML = `
|
||||
<li>开发中组员基于<b>迭代功能分支</b>拉取个人临时分支,提测时合并回功能分支。</li>
|
||||
<li>规范格式:<code>feature-{项目名}-{版本}-SNAPSHOT-{日期}-{姓名}</code></li>
|
||||
`;
|
||||
} else if (type === 'staging') {
|
||||
personalFields.style.display = 'none';
|
||||
rulesList.innerHTML = `
|
||||
<li>开发环境测试结束后,从功能分支拉出去掉 <code>SNAPSHOT</code> 标识的预生产分支。</li>
|
||||
<li>上线验证通过后,将预生产分支分别合并回 <code>main</code> 和 <code>develop</code>。</li>
|
||||
<li>规范格式:<code>feature-{项目名}-{版本}-{日期}</code></li>
|
||||
`;
|
||||
} else if (type === 'hotfix') {
|
||||
personalFields.style.display = 'none';
|
||||
rulesList.innerHTML = `
|
||||
<li>线上紧急修复,从 <code>main</code> 拉取,修复验证后同时合并回 <code>main</code> 与 <code>develop</code>。</li>
|
||||
<li>规范格式:<code>hotfix-{项目名}-{版本}-{日期}</code></li>
|
||||
`;
|
||||
}
|
||||
|
||||
updateBranchOutput();
|
||||
}
|
||||
|
||||
// Update Branch Output
|
||||
function updateBranchOutput() {
|
||||
const proj = document.getElementById('branch-proj').value.trim() || 'git_helper';
|
||||
const version = document.getElementById('branch-version').value.trim() || 'v2.1.1';
|
||||
const date = document.getElementById('branch-date').value.trim() || '20240703';
|
||||
const author = document.getElementById('branch-author').value.trim() || 'zhangsan';
|
||||
const seq = document.getElementById('branch-seq').value.trim();
|
||||
|
||||
let command = '';
|
||||
|
||||
if (currentBranchType === 'develop') {
|
||||
command = `git checkout -b develop main`;
|
||||
} else if (currentBranchType === 'feature') {
|
||||
const branchName = `feature-${proj}-${version}-SNAPSHOT-${date}`;
|
||||
command = `git checkout -b ${branchName} develop`;
|
||||
} else if (currentBranchType === 'personal') {
|
||||
const seqStr = seq ? `-${seq}` : '';
|
||||
const baseBranch = `feature-${proj}-${version}-SNAPSHOT-${date}`;
|
||||
const branchName = `${baseBranch}-${author}${seqStr}`;
|
||||
command = `git checkout -b ${branchName} ${baseBranch}`;
|
||||
} else if (currentBranchType === 'staging') {
|
||||
const baseBranch = `feature-${proj}-${version}-SNAPSHOT-${date}`;
|
||||
const branchName = `feature-${proj}-${version}-${date}`;
|
||||
command = `git checkout -b ${branchName} ${baseBranch}`;
|
||||
} else if (currentBranchType === 'hotfix') {
|
||||
const branchName = `hotfix-${proj}-${version}-${date}`;
|
||||
command = `git checkout -b ${branchName} main`;
|
||||
}
|
||||
|
||||
document.getElementById('branch-result').textContent = command;
|
||||
}
|
||||
|
||||
// Copy helper
|
||||
function copyText(elementId, btn) {
|
||||
const text = document.getElementById(elementId).textContent;
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
const originalText = btn.textContent;
|
||||
btn.textContent = '已复制!';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(() => {
|
||||
btn.textContent = originalText;
|
||||
btn.classList.remove('copied');
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user