您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在Git仓库页面添加跳转到HPX打包页面的按钮
// ==UserScript== // @name Git仓库一键跳转HPX // @namespace http://tampermonkey.net/ // @version 1.0 // @description 在Git仓库页面添加跳转到HPX打包页面的按钮 // @author Dean // @match https://dev.sankuai.com/code/repo-detail/* // @grant GM_xmlhttpRequest // @require https://code.jquery.com/jquery-3.6.0.min.js // ==/UserScript== (function() { 'use strict'; // 添加样式 const style = document.createElement('style'); style.textContent = ` #zy_hpx_button { margin-right: 8px; position: relative; overflow: hidden; } .mtd-button-content { display: flex; align-items: center; justify-content: center; position: relative; z-index: 2; } .mtdicon-fast-forward { margin-right: 4px; } /* 节日装饰样式 */ .festival-icon { position: absolute; pointer-events: none; font-size: 12px; z-index: 1; } /* 春节样式 */ .spring-festival .festival-icon { animation: springFestival 2s infinite; } /* 圣诞节样式 */ .christmas .festival-icon { animation: snowfall 3s infinite; } /* 万圣节样式 */ .halloween .festival-icon { animation: spooky 3s infinite; } /* 元宵节样式 */ .lantern-festival .festival-icon { animation: floating 3s infinite; } /* 动画效果 */ @keyframes springFestival { 0% { transform: scale(1) rotate(0deg); opacity: 1; } 50% { transform: scale(1.2) rotate(180deg); opacity: 0.8; } 100% { transform: scale(1) rotate(360deg); opacity: 1; } } @keyframes snowfall { 0% { transform: translateY(-100%) rotate(0deg); opacity: 1; } 100% { transform: translateY(100%) rotate(360deg); opacity: 0; } } @keyframes spooky { 0% { transform: translateX(-20px) translateY(0); opacity: 1; } 50% { transform: translateX(20px) translateY(-10px); opacity: 0.7; } 100% { transform: translateX(-20px) translateY(0); opacity: 1; } } @keyframes floating { 0% { transform: translateY(0) rotate(-5deg); } 50% { transform: translateY(-10px) rotate(5deg); } 100% { transform: translateY(0) rotate(-5deg); } } /* 光效装饰 */ .festival-sparkle { position: absolute; width: 100%; height: 100%; top: 0; left: 0; pointer-events: none; z-index: 1; } .festival-sparkle::before, .festival-sparkle::after { content: ''; position: absolute; width: 2px; height: 2px; border-radius: 50%; background: rgba(255,255,255,0.6); animation: sparkle 2s infinite; } .festival-sparkle::after { animation-delay: 1s; } @keyframes sparkle { 0%, 100% { transform: translate(0, 0) scale(0); opacity: 0; } 50% { transform: translate(20px, -20px) scale(1); opacity: 1; } } `; document.head.appendChild(style); // 页面加载完成后执行 if (window.location.toString().indexOf('dev.sankuai.com/code/repo-detail') >= 0) { // 使用 MutationObserver 监听DOM变化 const observer = new MutationObserver((mutations, observer) => { if ($(".btn-box").length > 0 && $("#zy_hpx_button").length === 0) { logger('检测到按钮容器'); observer.disconnect(); // 停止观察 inject(() => {}); } }); // 立即检查是否已存在按钮容器 if ($(".btn-box").length > 0) { logger('按钮容器已存在'); inject(() => {}); } else { logger('等待按钮容器'); // 开始观察 observer.observe(document.body, { childList: true, subtree: true }); } // 添加页面 URL 变化监听 let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; logger('URL 发生变化'); if (url.indexOf('dev.sankuai.com/code/repo-detail') >= 0) { inject(() => {}); } } }).observe(document, {subtree: true, childList: true}); } // 缓存键名 const CACHE_KEY = 'HPX_PROJECT_CACHE'; const CACHE_EXPIRE = 24 * 60 * 60 * 1000; // 24小时缓存 // 获取缓存的项目数据 function getCachedProject(git) { try { const cache = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}'); const data = cache[git]; if (data && (Date.now() - data.timestamp) < CACHE_EXPIRE) { return data.project; } } catch (e) { logger('读取缓存失败', e); } return null; } // 设置项目缓存 function setCachedProject(git, project) { try { const cache = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}'); cache[git] = { project: project, timestamp: Date.now() }; localStorage.setItem(CACHE_KEY, JSON.stringify(cache)); } catch (e) { logger('设置缓存失败', e); } } // 入侵 function inject(callback) { if ($(".btn-box").length <= 0) { logger('没有查到元素'); return false; } logger('查到元素'); // 先渲染一个加载中的按钮 renderLoadingButton(); // 查询git地址 getGitAddress(function(git) { if (git.length <= 0) { removeButton(); callback(true); return; } // 先检查缓存 const cachedProject = getCachedProject(git); if (cachedProject) { logger('使用缓存数据'); renderHPXButton(cachedProject); callback(true); // 异步更新缓存 updateProjectCache(git); return; } // 无缓存时请求新数据 requestProjectData(git, callback); }); } // 异步更新缓存 function updateProjectCache(git) { GM_xmlhttpRequest({ method: 'GET', url: 'https://hpx.sankuai.com/api/open/getProjectUrlList?repoUrl=' + git, onload: function(response) { try { const data = JSON.parse(response.responseText); if (data.data && data.data.length > 0) { const project = data.data[data.data.length - 1]; setCachedProject(git, project); logger('缓存已更新'); } } catch (e) { logger('更新缓存失败', e); } } }); } // 请求项目数据 function requestProjectData(git, callback) { GM_xmlhttpRequest({ method: 'GET', url: 'https://hpx.sankuai.com/api/open/getProjectUrlList?repoUrl=' + git, onload: function(response) { try { const data = JSON.parse(response.responseText); if (data.data && data.data.length > 0) { const project = data.data[data.data.length - 1]; if (project.length > 0) { logger('获取新数据'); setCachedProject(git, project); renderHPXButton(project); callback(true); return; } } // 如果没有获取到有效数据,移除loading按钮 removeButton(); callback(true); } catch (e) { logger('请求数据失败', e); removeButton(); callback(true); } }, onerror: function() { logger('网络请求失败'); removeButton(); callback(true); } }); } // 渲染加载中按钮 function renderLoadingButton() { removeButton(); // 先移除已存在的按钮 $(".btn-box").prepend(` <button id="zy_hpx_button" type="button" class="mtd-btn mtd-btn-primary"> <span> <div class="mtd-button-content"> <span class="mtdicon mtdicon-fast-forward"></span> <span>Loading...</span> </div> </span> </button> `); } // 渲染按钮 function renderHPXButton(project) { removeButton(); // 先移除已存在的按钮 const festival = getFestival(); const festivalConfig = { 'spring-festival': { icon: '🏮', text: '新年快乐', icons: ['🏮', '💰', '🧨', '🎊', '🐲', '福'] }, 'lantern-festival': { icon: '🏮', text: '元宵节快乐', icons: ['🏮', '👻', '🌕', '⭐'] }, 'halloween': { icon: '🎃', text: 'Happy Halloween', icons: ['🎃', '👻', '🦇', '🕷️', '🕸️'] }, 'christmas': { icon: '🎄', text: 'Merry Xmas', icons: ['❄️', '🎄', '🎅', '🎁', '⛄', '🦌'] } }; // 在首部插入Button $(".btn-box").prepend(` <button id="zy_hpx_button" type="button" class="mtd-btn mtd-btn-primary ${festival}"> ${festival ? '<div class="festival-sparkle"></div>' : ''} <span> <div class="mtd-button-content"> <span class="mtdicon mtdicon-fast-forward"></span> <span>Go to HyperloopX</span> ${festival ? `<span style="margin-left: 4px">${festivalConfig[festival].icon}</span>` : ''} </div> </span> </button> `); $("#zy_hpx_button").click(function(){ // 点击效果 if (festival) { const config = festivalConfig[festival]; const icon = config.icons[Math.floor(Math.random() * config.icons.length)]; const $icon = $(`<span class="festival-icon">${icon}</span>`); $icon.css({ left: '50%', top: '50%', transform: 'translate(-50%, -50%) scale(3)', opacity: 0 }); $(this).append($icon); setTimeout(() => $icon.remove(), 500); } // 打开窗口 window.open(project); }); } // 统一的按钮移除函数 function removeButton() { $("#zy_hpx_button").remove(); } // 查询git地址 function getGitAddress(callback) { var str = 'dev.sankuai.com/code/repo-detail'; var index = window.location.toString().indexOf(str); var reset = window.location.toString().substring(index + str.length); var components = reset.split('/'); if (components.length >= 3) { var url = 'https://dev.sankuai.com/rest/api/2.0/projects/' + components[1] + '/repos/' + components[2]; $.get(url, {}, function(data){ var git = ''; for (let i = 0; i < data.links.clone.length; i++) { let item = data.links.clone[i]; if (item.name === 'ssh') { git = item.href; break; } } callback(git); }); } return ''; } // 获取当前节日 function getFestival() { const date = new Date(); const month = date.getMonth() + 1; const day = date.getDate(); // 农历新年判断(这里使用简化判断,实际应该使用农历计算) if (month === 1 && day >= 20 || month === 2 && day <= 20) { return 'spring-festival'; } // 元宵节 if (month === 2 && day >= 24 && day <= 26) { return 'lantern-festival'; } // 万圣节 if (month === 10 && day >= 29 || month === 11 && day <= 2) { return 'halloween'; } // 圣诞节 if (month === 12 && day >= 20 && day <= 26) { return 'christmas'; } return ''; } // log function logger(log) { console.log("[go to HPX]", log); } })();