预览
应用中心
新建 themes\butterfly\layout\includes\apps.pug
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #apps .apps-head span 应用中心 .close-btn(onclick="ctrl.hideAPPs()" href="javascript:void(0);") i.fas.fa-circle-xmark .apps-content .app-box .app-icon(style="background:url(https://xxx.webp) no-repeat 100% / cover;") a.app-link(href='https://gavin-chen.top', title='主线路', target='_blank', one-link-mark='yes') span.app-name 主线路 .app-box .app-icon(style="background:url(https://xxx.webp) no-repeat 100% / cover;") a.app-link(href='https://blog.cansin.top', title='Vercel线路', target='_blank', one-link-mark='yes') span.app-name Vercel线路 .app-box .app-icon(style="background:url(https://xxx.webp) no-repeat 100% / cover;") a.app-link(href='https://netlify.gavin-chen.top', title='Netlify线路', target='_blank', one-link-mark='yes') span.app-name Netlify线路 .app-box .app-icon(style="background:url(https://xxx.png) no-repeat 100% / cover;") a.app-link(href='https://zeabur.cansin.top', title='Zeabur线路', target='_blank', one-link-mark='yes') span.app-name Zeabur线路 .app-box .app-icon(style="background:url(https://xxx.webp) no-repeat 100% / cover;") a.app-link(href='https://blogdrive.gavin-chen.top', title='藏兵谷', target='_blank', rel='noopener nofollow', one-link-mark='yes') span.app-name 藏兵谷 .app-box .app-icon(style="background:url(https://xxx.webp) no-repeat 100% / cover;") a.app-link(href='https://music.cansin.top', title='司音堂', target='_blank', rel='noopener nofollow', one-link-mark='yes') span.app-name 司音堂 .app-box .app-icon(style="background:url(https://xxx.webp) no-repeat 100% / cover;") a.app-link(href='https://music.gavin-chen.top', title='幻音坊', target='_blank', rel='noopener nofollow', one-link-mark='yes') span.app-name 幻音坊 .app-box .app-icon(style="background:url(https://xxx.webp) no-repeat 100% / cover;") a.app-link(onclick='ctrl.toggleWinbox("encryption")', title='加密工具', target='_blank', rel='noopener nofollow', one-link-mark='yes') span.app-name 加密工具 .apps-mask(onclick="ctrl.hideAPPs()" href="javascript:void(0);" rel="external nofollow")
|
在 themes\butterfly\layout\includes\layout.pug 末尾引入
1 2
| include ./console.pug + include ./apps.pug
|
js部分通过合适的按钮点击打开应用中心,这里通过左上角田字形图标打开,手机端入口在侧滑菜单的右上角。CSS 通过 F12 自取。
加密工具
“加密工具”是应用中心第一款应用,窗口部分采用开源框架winbox实现,加密算法采用 Crypto-JS 库实现。
首先在主题配置文件 _config.butterfly.yml 中引入 winbox
1 2 3
| inject: bottom: + - <script src="https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js"></script> # 窗口
|
自定义 xxx.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| var ctrl = { toggleWinbox(app) { if (document.getElementById('winboxForApps')) winbox.toggleClass('hide'); else ctrl.createWinboxForApps(app); }, resizeWinbox() { let box = document.getElementById('winboxForApps'); if (!box || box.classList.contains('min') || box.classList.contains('max')) return var offsetWid = document.documentElement.clientWidth; if (offsetWid <= 768) { winbox.resize(offsetWid * 0.95 + "px", "80%").move("center", "center"); } else { winbox.resize(offsetWid * 0.6 + "px", "70%").move("center", "center"); } }, createWinboxForApps(app) { var title = '', className = '', html = ''; switch (app) { case "encryption": title = '参星阁 - 加密工具'; className = 'encryption'; html=` <div class="select-items"> <select class="select-item type"> <option class="opt" value="MD5">MD5</option> <option class="opt" value="SHA1">SHA1</option> <option class="opt" value="SHA3">SHA3</option> <option class="opt" value="SHA224">SHA224</option> <option class="opt" value="SHA256">SHA256</option> <option class="opt" value="SHA384">SHA384</option> <option class="opt" value="SHA512">SHA512</option> <option class="opt" value="RIPEMD160">RIPEMD160</option> <option class="opt" value="AESEncode">AES加密</option> <option class="opt" value="AESDecode">AES解密</option> </select> <select class="select-item code"> <option class="opt" value="Hex">Hex</option> <option class="opt" value="Base64">Base64</option> </select> <select class="select-item case"> <option class="opt" value="Lower">小写</option> <option class="opt" value="Upper">大写</option> </select> <select class="select-item mode hide"> <option class="opt" value="CBC">CBC</option> <option class="opt" value="CFB">CFB</option> <option class="opt" value="CTR">CTR</option> <option class="opt" value="OFB">OFB</option> <option class="opt" value="ECB">ECB</option> </select> <select class="select-item pad hide"> <option class="opt" value="Pkcs7">Pkcs7</option> <option class="opt" value="Iso97971">Iso97971</option> <option class="opt" value="Iso10126">Iso10126</option> <option class="opt" value="AnsiX923">AnsiX923</option> <option class="opt" value="ZeroPadding">ZeroPadding</option> <option class="opt" value="NoPadding">NoPadding</option> </select> <input type="text" class="select-item key hide" placeholder="密钥"> <input type="text" class="select-item iv hide" placeholder="偏移量"> </div> <textarea autocomplete="off" rows="6" placeholder="请输入或者粘贴需要处理的文本" class="inner" style="min-height: 32.6px;"></textarea> <textarea autocomplete="off" rows="6" placeholder="处理结果" class="outer lock" style="min-height: 32.6px;" disabled="disabled"></textarea> <div class="btns"> <button class="btn blue" type="button" onclick="transcode()">转码</button> <button class="btn green" type="button" onclick="copyTranscode()">复制</button> <button class="btn red" type="button" onclick="clearTranscode()">清空</button> </div> `; document.head.appendChild(Object.assign(document.createElement("script"), { src: "https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/crypto-js/4.1.1/crypto-js.min.js", id: "crypto-js" })); break; default: title = '参星阁 - App'; className = ''; html = '暂无应用程序'; } let div = document.createElement('div'); document.body.appendChild(div); winbox = WinBox({ id: 'winboxForApps', class: className, index: 989, title: title, x: "center", y: "center", minwidth: '300px', height: "60%", background: '#29516C', onmaximize: () => { div.innerHTML = `<style>body::-webkit-scrollbar {display: none;}div#winboxForApps {width: 100% !important;}</style>` }, onrestore: () => { div.innerHTML = '' } }); ctrl.resizeWinbox(); window.addEventListener('resize', ctrl.resizeWinbox); winbox.body.innerHTML = html; document.head.appendChild(Object.assign(document.createElement("script"), { src: "/js/" + className + ".js", id: "appScript" })); document.querySelector('.wb-header .wb-close').addEventListener('click', ()=>{ var script = document.getElementById('appScript'); if (script) document.head.removeChild(script); var script1 = document.getElementById('crypto-js'); if (script1) document.head.removeChild(script1); }) } }
|
自定义 encryption.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| var _type = document.querySelector(".encryption .select-item.type"), _code = document.querySelector(".encryption .select-item.code"), _case = document.querySelector(".encryption .select-item.case"), _mode = document.querySelector(".encryption .select-item.mode"), _pad = document.querySelector(".encryption .select-item.pad"), _key = document.querySelector(".encryption .select-item.key"), _iv = document.querySelector(".encryption .select-item.iv"), textIn = document.querySelector('.encryption .inner'), textOut = document.querySelector('.encryption .outer'); textOut.addEventListener("input", ()=>{ if (textOut.value == '') { textOut.disabled = true; textOut.classList.add('lock'); } else { textOut.disabled = false; textOut.classList.remove('lock'); } }) _type.addEventListener('change', ()=>{ if (_type.value == 'AESEncode') { _case.classList.contains('hide') ? null : _case.classList.add('hide'); _code.classList.contains('hide') ? _code.classList.remove('hide') : null; _mode.classList.contains('hide') ? _mode.classList.remove('hide') : null; _pad.classList.contains('hide') ? _pad.classList.remove('hide') : null; _key.classList.contains('hide') ? _key.classList.remove('hide') : null; _iv.classList.contains('hide') ? _iv.classList.remove('hide') : null; } else if (_type.value == 'AESDecode') { _code.classList.contains('hide') ? null : _code.classList.add('hide'); _case.classList.contains('hide') ? null : _case.classList.add('hide'); _mode.classList.contains('hide') ? _mode.classList.remove('hide') : null; _pad.classList.contains('hide') ? _pad.classList.remove('hide') : null; _key.classList.contains('hide') ? _key.classList.remove('hide') : null; _iv.classList.contains('hide') ? _iv.classList.remove('hide') : null; } else { _code.classList.contains('hide') ? _code.classList.remove('hide') : null; _case.classList.contains('hide') ? _case.classList.remove('hide') : null; _mode.classList.contains('hide') ? null : _mode.classList.add('hide'); _pad.classList.contains('hide') ? null : _pad.classList.add('hide'); _key.classList.contains('hide') ? null : _key.classList.add('hide'); _iv.classList.contains('hide') ? null : _iv.classList.add('hide'); } }) _mode.addEventListener('change', ()=>{ if (_mode.value != 'ECB') { if (_iv.classList.contains('lock')) { _iv.classList.remove('lock'); _iv.disabled = false; }; } else { if (!_iv.classList.contains('lock')) { _iv.classList.add('lock'); _iv.disabled = true; }; } })
function copyTranscode() { textOut.select(); document.execCommand('copy'); }
function clearTranscode() { document.querySelector('.encryption .inner').value = ''; textOut.value = ''; textOut.disabled = true; textOut.classList.add('lock'); }
function transcode() { var a = '', b = '', __mode = '', __pad = '', __text = '', __key = CryptoJS.enc.Utf8.parse(_key.value), __iv = CryptoJS.enc.Utf8.parse(_iv.value); switch (_mode.value) { case "CBC": __mode = CryptoJS.mode.CBC; break; case "CFB": __mode = CryptoJS.mode.CFB; break; case "CTR": __mode = CryptoJS.mode.CTR; break; case "OFB": __mode = CryptoJS.mode.OFB; break; case "ECB": __mode = CryptoJS.mode.ECB; break; default: __mode = CryptoJS.mode.CBC; } switch (_pad.value) { case "Pkcs7": __pad = CryptoJS.pad.Pkcs7; break; case "Iso97971": __pad = CryptoJS.pad.Iso97971; break; case "Iso10126": __pad = CryptoJS.pad.Iso10126; break; case "AnsiX923": __pad = CryptoJS.pad.AnsiX923; break; case "ZeroPadding": __pad = CryptoJS.pad.ZeroPadding; break; case "NoPadding": __pad = CryptoJS.pad.NoPadding; break; default: __pad = CryptoJS.pad.Pkcs7; } switch (_type.value) { case "MD5": a = CryptoJS.MD5(textIn.value); b = _code.value == 'Hex' ? a.toString() : CryptoJS.enc.Base64.stringify(a); textOut.value = _case.value == 'Lower' ? b : b.toUpperCase(); break; case "SHA1": a = CryptoJS.SHA1(textIn.value); b = _code.value == 'Hex' ? a.toString() : CryptoJS.enc.Base64.stringify(a); textOut.value = _case.value == 'Lower' ? b : b.toUpperCase(); break; case "SHA3": a = CryptoJS.SHA3(textIn.value); b = _code.value == 'Hex' ? a.toString() : CryptoJS.enc.Base64.stringify(a); textOut.value = _case.value == 'Lower' ? b : b.toUpperCase(); break; case "SHA224": a = CryptoJS.SHA224(textIn.value); b = _code.value == 'Hex' ? a.toString() : CryptoJS.enc.Base64.stringify(a); textOut.value = _case.value == 'Lower' ? b : b.toUpperCase(); break; case "SHA256": a = CryptoJS.SHA256(textIn.value); b = _code.value == 'Hex' ? a.toString() : CryptoJS.enc.Base64.stringify(a); textOut.value = _case.value == 'Lower' ? b : b.toUpperCase(); break; case "SHA384": a = CryptoJS.SHA384(textIn.value); b = _code.value == 'Hex' ? a.toString() : CryptoJS.enc.Base64.stringify(a); textOut.value = _case.value == 'Lower' ? b : b.toUpperCase(); break; case "SHA512": a = CryptoJS.SHA512(textIn.value); b = _code.value == 'Hex' ? a.toString() : CryptoJS.enc.Base64.stringify(a); textOut.value = _case.value == 'Lower' ? b : b.toUpperCase(); break; case "RIPEMD160": a = CryptoJS.RIPEMD160(textIn.value); b = _code.value == 'Hex' ? a.toString() : CryptoJS.enc.Base64.stringify(a); textOut.value = _case.value == 'Lower' ? b : b.toUpperCase(); break; case "AESEncode": __text = CryptoJS.enc.Utf8.parse(textIn.value); a = CryptoJS.AES.encrypt(__text, __key, { iv: __iv, mode: __mode, padding: __pad }); b = _code.value == 'Hex' ? CryptoJS.enc.Hex.stringify(a.ciphertext) : a.toString(); textOut.value = b; break; case "AESDecode": var base64Regex = /^[A-Za-z0-9+/]*={1,2}$/, hexRegex = /^(?:[0-9a-fA-F]{4})*$/; if (base64Regex.test(textIn.value)) { console.log('base64'); __text = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Base64.parse(textIn.value)); textOut.value = CryptoJS.AES.decrypt(__text, __key, { iv: __iv, mode: __mode, padding: __pad }).toString(CryptoJS.enc.Utf8); break; } else if (hexRegex.test(textIn.value)) { console.log('hex'); __text = CryptoJS.enc.Hex.parse(textIn.value); textOut.value = CryptoJS.AES.decrypt({ ciphertext: __text }, __key, { iv: __iv, mode: __mode, padding: __pad }).toString(CryptoJS.enc.Utf8); break; } else { console.log('数据错误'); alert('数据错误');break; } default: ; } textOut.disabled = false; textOut.classList.remove('lock'); }
|
参考资料
winbox 部分代码参考 Leonus 的文章:
winbox 开源仓库:
CryptoJS 参考文档: