要用
前台脚本
<script>
// 默认分组模式
if(!localStorage.getItem("showGroup")){
localStorage.setItem("showGroup", true);
}
// 默认暗黑模式
if(!localStorage.getItem("theme")){
localStorage.setItem("theme", 'dark');
}
window.onload = function(){
const affLinks = {
crunchbits: 'https://get.crunchbits.com/order/lblk-yearly-kvm-ssd-vps/84',
rainyun: 'https://www.rainyun.com/MjYzMTk=_',
colocrossing: 'https://cloud.colocrossing.com/aff.php?aff=316',
racknerd: 'https://my.racknerd.com/aff.php?aff=9727',
}
const contacts = {
main: 'telegram',
animatedType: 'vertical',
telegram: {
label: 'TG',
icon: 'telegram plane',
url: 'https://t.me/bmqyChatBot'
},
qq: {
label: 'QQ',
icon: 'qq',
url: 'https://wpa.qq.com/msgrd?V=3&Uin=88268459&Site=nezha.887776.xyz&menu=yes'
},
email: {
label: 'Email',
icon: 'mail',
url: 'mailto:notice@bmqy.net'
},
}
const extraData = {
2: {
pid: 0,
shop: 'crunchbits',
price: '$27.38',
cycle: 'Year',
start: '08/13/2023',
expire: '08/13/2024',
autoPay: '否'
},
15: {
pid: 0,
shop: 'crunchbits',
price: '$22.69',
cycle: 'Year',
start: '04/08/2024',
expire: '04/08/2025',
autoPay: '否'
},
10: {
pid: 358,
shop: 'racknerd',
price: '$10.98',
cycle: 'Year',
start: '11/14/2023',
expire: '11/14/2024',
autoPay: '否'
},
12: {
pid: 23,
shop: 'colocrossing',
price: '$10.00',
cycle: 'Year',
start: '02/13/2024',
expire: '02/13/2025',
autoPay: '否'
},
13: {
pid: 0,
shop: 'rainyun',
price: '¥245',
cycle: 'Year',
start: '07/03/2023',
expire: '08/09/2024',
autoPay: '否'
},
14: {
pid: 0,
shop: 'rainyun',
price: 'P5500',
cycle: 'Month',
start: '12/27/2023',
expire: '∞',
autoPay: '是'
},
}
const cycleNames = {
'年付': 'year',
'半年付': 'half',
'季付': 'quarterly',
'月付': 'month',
'年': 'year',
'半': 'half',
'季': 'quarterly',
'月': 'month',
'Year': 'year',
'Half': 'half',
'Quarterly': 'quarterly',
'Month': 'month',
'Y': 'year',
'H': 'half',
'Q': 'quarterly',
'M': 'month',
'year': 'year',
'half': 'half',
'quarterly': 'quarterly',
'month': 'month',
}
const cycleValues = {
year: 365,
half: 180,
quarterly: 90,
month: 30,
}
// 判断当前主题
const cookie = document.cookie;
let preferredTheme = document.body.innerHTML.match(/(?<=defaultTemplate: ")(default|server-status)(?=")/g) ? document.body.innerHTML.match(/(?<=defaultTemplate: ")(default|server-status)(?=")/g)[0] : 'default';
preferredTheme = document.cookie.match(/(server-status|default)/g) ? document.cookie.match(/(server-status|default)/g)[0] : preferredTheme;
// 默认主题
if(preferredTheme === 'default'){
const cats = document.querySelectorAll('.ui.accordion');
cats.forEach((e, i)=>{
let $catsTitle = e.querySelector('.title');
let ct = $catsTitle.innerText;
ct = ct.trim();
let $itemCard = e.querySelectorAll('.ui.card');
let uiCardCount = $itemCard.length;
$catsTitle.innerHTML = $catsTitle.innerHTML.replace(ct, ct+ ' ('+ uiCardCount +')');
$itemCard.forEach((ee, ii)=>{
let $content = ee.querySelector('.content');
let $descriptionGrid = ee.querySelector('.description .ui.grid');
let $itemTitle = $content.querySelector('.header');
let id = ee.getAttribute('id');
if(extraData[id]){
let pid = extraData[id].pid;
pid = parseInt(pid);
let shop = extraData[id].shop;
let price = extraData[id].price;
let start = extraData[id].start;
let expire = extraData[id].expire;
let cycle = extraData[id].cycle;
let autoPay = extraData[id].autoPay;
let cycleName = cycleNames[cycle];
let cycleValue = cycleValues[cycleName];
let nowTime = parseInt(new Date().getTime() / 1000);
let beginTime = parseInt(new Date(start).getTime() / 1000);
if(autoPay && autoPay=='是'){
let beginDate = new Date(start);
let nowDate = new Date();
let mSteps = {
year: 12,
half: 6,
quarterly: 3,
month: 1,
}
expire = beginDate.setMonth(beginDate.getMonth() + mSteps[cycleName]);
expire = new Date(expire);
while(expire.getTime() < nowDate.getTime()){
expire = expire.setMonth(expire.getMonth() + mSteps[cycleName]);
expire = new Date(expire);
}
expire = expire.toLocaleDateString();
}
let endTime = parseInt(new Date(expire).getTime() / 1000);
// 购买同款按钮
let $aButtonsBox = document.createElement('div');
$aButtonsBox.setAttribute('style', 'margin-bottom: .5em;text-align:right;');
let $aLinkButtons = document.createElement('div');
$aLinkButtons.setAttribute('class', 'btn-buy-box ui buttons mini');
let $aLinkBuy = document.createElement('a');
$aLinkBuy.setAttribute('class', 'ui btn-buy button positive');
$aLinkBuy.setAttribute('target', '_blank');
$aLinkBuy.innerHTML = '购买同款';
$aLinkBuy.href = affLinks[shop];
if(pid){
$aLinkBuy.href += '&pid='+ pid;
}
if(price){
// 购买价格行
let $priceL = document.createElement('div');
$priceL.setAttribute('class', 'three wide column');
$priceL.innerHTML = '价格';
$descriptionGrid.insertBefore($priceL, $descriptionGrid.childNodes[$descriptionGrid.childNodes.length-3]);
let $priceR = document.createElement('div');
$priceR.setAttribute('class', 'price-box thirteen wide column');
$priceR.innerHTML = '<div class="ui red label"><i class="money bill alternate yellow icon"></i>'+ price +'<a class="detail">'+ cycle +'</a></div>';
$descriptionGrid.insertBefore($priceR, $descriptionGrid.childNodes[$descriptionGrid.childNodes.length-3])
}
if(expire){
// 到期日期行
let $expireL = document.createElement('div');
$expireL.setAttribute('class', 'three wide column');
$expireL.innerHTML = '到期';
$descriptionGrid.insertBefore($expireL, $descriptionGrid.childNodes[$descriptionGrid.childNodes.length-3])
// 到期日期行右侧进度条
let $expireR = document.createElement('div');
let aTime = (nowTime-beginTime);
let bTime = (endTime-beginTime);
let cTime = parseInt(aTime / bTime * 100);
let progressType = 'red';
let textStyle = 'text-shadow: 0px 0px 5px #db2828;';
if(expire === '∞'){
progressType = 'success';
textStyle = '';
}
$expireR.setAttribute('class', 'expire-box thirteen wide column');
$expireR.innerHTML = '<div class="ui progress '+ progressType +'"><div class="bar" style="transition-duration: 300ms; min-width: unset; width: '+ cTime +'% !important;padding-left: 0.4em;"><small style="'+ textStyle +'">'+ expire +'</small></div></div>';
$descriptionGrid.insertBefore($expireR, $descriptionGrid.childNodes[$descriptionGrid.childNodes.length-3])
}
$aLinkButtons.append($aLinkBuy);
let $aLinkOr = document.createElement('div');
$aLinkOr.innerHTML = '<div class="or" data-text="or"></div>';
$aLinkButtons.append($aLinkOr);
// 购买同款按钮右侧联系方式
let remainingAnimatedType = contacts['animatedType'];
let priceValue = price.replace(/[$¥P€]/g, '');
let priceUnit = price.match(/[$¥P€]/g)[0];
let remainingDays = Math.floor((endTime - nowTime) / (24 * 60 * 60));
let remainingPrice = parseFloat(priceValue) / cycleValue * remainingDays;
if(!remainingPrice){
remainingPrice = 0;
}
remainingPrice = remainingPrice.toFixed(2);
let mainContact = contacts['main'];
// 购买同款按钮右侧主要联系方式显示剩余价值
let $aLinkContactMain = document.createElement('a');
$aLinkContactMain.setAttribute('class', 'contact-main ui button '+ remainingAnimatedType +' animated blue');
$aLinkContactMain.setAttribute('target', '_blank');
$aLinkContactMain.innerHTML = '<div class="hidden content" style="padding:0;" title="剩余价值">'+ contacts[mainContact].label +'议价</div><div class="visible content" style="padding:0;" title="剩余价值"><i class="'+ contacts[mainContact].icon +' icon" style="color:white;"></i>'+ priceUnit + remainingPrice +'</div>';
$aLinkContactMain.href = contacts[mainContact].url;
$aLinkButtons.append($aLinkContactMain);
$aButtonsBox.append($aLinkButtons);
// 购买同款按钮右侧其它联系方式
for(let key in contacts){
if(key!='main' && key!='animatedType' && key!=contacts['main']){
let icon = contacts[key].icon;
let $aLinkContact = document.createElement('a');
$aLinkContact.setAttribute('class', 'contact-btn ui circular '+ icon +' mini icon button');
$aLinkContact.setAttribute('target', '_blank');
$aLinkContact.setAttribute('style', 'margin-left:.5em;');
$aLinkContact.innerHTML = '<i class="'+ icon +' icon"></i>';
$aLinkContact.href = contacts[key].url;
$aButtonsBox.append($aLinkContact);
}
}
$content.append($aButtonsBox);
}
});
});
}
// ServerStatus主题
function bindToggleViewClick(){
let $toggleView = document.querySelector('aside.toolbox .toggleView')
$toggleView && $toggleView.addEventListener('click', ()=>{
setTimeout(function(){
location.reload();
}, 0);
});
}
if(preferredTheme === 'server-status'){
bindToggleViewClick();
let $biThreeDots = document.querySelector('aside.toolbox .bi-three-dots');
$biThreeDots && $biThreeDots.addEventListener('click', ()=>{
bindToggleViewClick();
});
let $tables = document.querySelectorAll('.table.table-condensed');
$tables.forEach(e=>{
$tableTh = e.querySelector('.node-group-tag th');
$list = e.querySelectorAll('tr.accordion-toggle');
$tableTh && ($tableTh.innerText += '('+ $list.length +')');
let $head = e.querySelector('table.table-condensed thead').lastChild;
let $servers = e.querySelector('#servers');
// 隐藏三列:系统、在线天数、负载
$head.querySelector('th.os').style.display = 'none';
$head.querySelector('th.uptime').style.display = 'none';
$head.querySelector('th.load').style.display = 'none';
// 添加三列
let $expireTh = document.createElement('th');
$expireTh.innerText = '到期 / 剩余价值';
$expireTh.setAttribute('class', 'node-cell expire center');
$head.insertBefore($expireTh, $head.childNodes[3]);
let $buyTh = document.createElement('th');
$buyTh.innerText = '购买同款';
$buyTh.setAttribute('class', 'node-cell expire center');
$head.append($buyTh);
let $contactTh = document.createElement('th');
$contactTh.innerText = '议价';
$contactTh.setAttribute('class', 'node-cell expire center');
$head.append($contactTh);
$servers.querySelectorAll('tr.accordion-toggle').forEach(ee=>{
ee.querySelector('td.os').style.display = 'none';
ee.querySelector('td.uptime').style.display = 'none';
ee.querySelector('td.load').style.display = 'none';
let id = ee.getAttribute('id');
id = id.replace('r', '');
if(extraData[id]){
let pid = extraData[id].pid;
pid = parseInt(pid);
let shop = extraData[id].shop;
let price = extraData[id].price;
let start = extraData[id].start;
let expire = extraData[id].expire;
let cycle = extraData[id].cycle;
let autoPay = extraData[id].autoPay;
let cycleName = cycleNames[cycle];
let cycleValue = cycleValues[cycleName];
let nowTime = parseInt(new Date().getTime() / 1000);
let beginTime = parseInt(new Date(start).getTime() / 1000);
if(autoPay && autoPay=='是'){
let beginDate = new Date(start);
let nowDate = new Date();
let mSteps = {
year: 12,
half: 6,
quarterly: 3,
month: 1,
}
expire = beginDate.setMonth(beginDate.getMonth() + mSteps[cycleName]);
expire = new Date(expire);
while(expire.getTime() < nowDate.getTime()){
expire = expire.setMonth(expire.getMonth() + mSteps[cycleName]);
expire = new Date(expire);
}
expire = expire.toLocaleDateString();
}
let endTime = parseInt(new Date(expire).getTime() / 1000);
if(expire || price){
// 到期时间、剩余价值列
let $expireTd = document.createElement('td');
$expireTd.setAttribute('class', 'node-cell expire');
let aTime = (nowTime-beginTime);
let bTime = (endTime-beginTime);
let cTime = parseInt(aTime / bTime * 100);
let progressType = 'warning';
if(expire === '∞'){
progressType = 'success';
}
let priceValue = price.replace(/[$¥P€]/g, '');
let priceUnit = price.match(/[$¥P€]/g)[0];
let remainingDays = Math.floor((endTime - nowTime) / (24 * 60 * 60));
let remainingPrice = parseFloat(priceValue) / cycleValue * remainingDays;
if(!remainingPrice){
remainingPrice = 0;
}
remainingPrice = remainingPrice.toFixed(2);
$expireTd.innerHTML = '<div class="progress progress-expire"><div class="progress-bar progress-bar-'+ progressType +'" style="width: '+ cTime +'%;padding-right:5px;"><small style="white-space: nowrap;">'+ expire +' / '+ priceUnit + remainingPrice +'</small></div></div>';
ee.insertBefore($expireTd, ee.childNodes[3]);
}
// 购买同款列
let $buyTd = document.createElement('td');
$buyTd.setAttribute('class', 'node-cell buy');
$buyTd.setAttribute('style', 'text-align:center;');
let $buyTdBtn = document.createElement('div');
$buyTdBtn.setAttribute('class', 'ui left labeled button');
let $buyTdBtnLabel = document.createElement('div');
$buyTdBtnLabel.setAttribute('class', 'ui basic label mini');
$buyTdBtnLabel.setAttribute('style', 'min-height: 20px;padding:0 .5em;height: 20px;font-weight:normal;line-height: 20px;font-size:.78571429rem;');
$buyTdBtnLabel.innerHTML = price +' / '+ cycle;
$buyTdBtn.append($buyTdBtnLabel);
let $buyTdBtnLabelIcon = document.createElement('a');
$buyTdBtnLabelIcon.setAttribute('class', 'ui icon button mini green');
$buyTdBtnLabelIcon.setAttribute('style', 'min-height: 20px;padding:0 .5em;height: 20px;line-height: 20px;');
$buyTdBtnLabelIcon.setAttribute('target', '_blank');
$buyTdBtnLabelIcon.addEventListener('click', (e)=>{e.stopPropagation()});
$buyTdBtnLabelIcon.innerHTML = '<i class="shopping cart icon"></i>';
$buyTdBtnLabelIcon.href = affLinks[shop];
if(pid){
$buyTdBtnLabelIcon.href += '&pid='+ pid;
}
$buyTdBtn.append($buyTdBtnLabelIcon);
$buyTd.append($buyTdBtn);
ee.append($buyTd);
// 联系方式列
let $contactTd = document.createElement('td');
$contactTd.setAttribute('class', 'node-cell contact');
$contactTd.setAttribute('style', 'text-align:center;white-space:nowrap;');
for(let key in contacts){
if(key!='main' && key!='animatedType'){
let $contactTdContactBtn = document.createElement('a');
let contactIcon = contacts[key].icon;
$contactTdContactBtn.setAttribute('class', 'ui circular '+ contactIcon +' icon button mini blue');
$contactTdContactBtn.setAttribute('style', 'min-height: 20px;padding:0 .5em;height: 20px;line-height: 20px;');
$contactTdContactBtn.setAttribute('target', '_blank');
$contactTdContactBtn.addEventListener('click', (e)=>{e.stopPropagation()});
$contactTdContactBtn.innerHTML = '<i class="'+ contactIcon +' icon"></i>';
$contactTdContactBtn.href = contacts[key].url;
$contactTd.append($contactTdContactBtn);
}
}
ee.append($contactTd);
} else {
// 无附加信息时显示占位符
let $expireTd = document.createElement('td');
$expireTd.setAttribute('class', 'node-cell expire');
$expireTd.setAttribute('style', 'text-align:center;');
$expireTd.innerHTML = '-';
ee.insertBefore($expireTd, ee.childNodes[3]);
let $buyTd = document.createElement('td');
$buyTd.setAttribute('class', 'node-cell buy');
$buyTd.setAttribute('style', 'text-align:center;');
$buyTd.innerHTML = '-';
ee.append($buyTd);
let $contactTd = document.createElement('td');
$contactTd.setAttribute('class', 'node-cell contact');
$contactTd.setAttribute('style', 'text-align:center;');
$contactTd.innerHTML = '-';
ee.append($contactTd);
}
});
});
}
}
</script>
后台脚本
<script>
window.onload = function(){
const storagePreKey = 'nzVpsChuChuangData.';
const storageSet = function(key, value){
key = `${storagePreKey}${key}`;
localStorage.setItem(key, JSON.stringify(value));
}
const storageGet = function(key, defaultValue){
key = `${storagePreKey}${key}`;
return JSON.parse(localStorage.getItem(key)) || defaultValue;
}
const extraDataKeyName = {
shop:"商家名称",
pid: '产品ID',
price:"购买价格",
cycle: "付款周期",
start: "购买日期",
expire: "过期时间",
autoPay: '自动续费',
}
const cycleOptions = [
'年付',
'半年付',
'季付',
'月付',
'年',
'半',
'季',
'月',
'Year',
'Half',
'Quarterly',
'Month',
'Y',
'H',
'Q',
'M',
'year',
'half',
'quarterly',
'month',
];
const autoPayOptions = [
'否',
'是'
];
let timmer = null;
let changer = false;
const pathname = location.pathname;
const $footer = document.querySelector('.footer');
if(!$footer || $footer.innerText.indexOf('Powered by 哪吒监控')==-1) return false;
let raw = storageGet('raw', '');
let extra = storageGet('extra', null);
if(pathname === '/setting'){
const $settingForm = document.forms.settingForm;
let settingData = new FormData($settingForm);
settingData = new URLSearchParams(settingData).toString();
storageSet('raw', settingData);
const CustomCode = document.querySelector('textarea[name=CustomCode]').value;
let CustomCodeValue =CustomCode.match(/(?<=extraData = )[\s\S]+(?=\n([\s\t]+)?const cycleNames)/g);
if(!CustomCodeValue){
$.suiAlert({
title: '',
description: '请检查是否已经添加《哪吒面板VPS橱窗脚本》,脚本可去:https://www.bmqy.net/2665.html,获取',
type: 'error',
time: '3',
position: 'top-center',
});
return false;
}
CustomCodeValue = CustomCodeValue ? CustomCodeValue[0] : '{}';
CustomCodeValue = CustomCodeValue.replace(/([0-9a-zA-Z]+):/g, '"$1":').replace(/},\n}/g, '}\n}').replace(/'/g, '"');
extra = JSON.parse(CustomCodeValue);
storageSet('extra', extra);
if(extra == null){
$.suiAlert({
title: '',
description: 'VPS橱窗后台脚本数据获取成功,可以正常使用了。',
type: 'success',
time: '3',
position: 'top-center',
});
} else {
$.suiAlert({
title: '',
description: 'VPS橱窗后台脚本已重新获取数据。',
type: 'success',
time: '3',
position: 'top-center',
});
}
} else {
if(pathname !='/' && pathname !='/login' && extra == null){
$.suiAlert({
title: '',
description: '请先进入【设置】页面获取脚本所需数据!!!',
type: 'warning',
time: '3',
position: 'top-center',
});
return false;
}
if(pathname === '/server'){
const $table = document.querySelector('table.table');
const $tableTr = $table.querySelectorAll('tbody tr');
$tableTr.forEach(e=>{
let $tds = e.querySelectorAll('td');
let id = $tds[1].innerText;
id = id.replace(/\(\d+\)/g, '');
let $nameTd = $tds[2];
let $extraDataBox = document.createElement('div');
$extraDataBox.id = id;
$extraDataBox.setAttribute('class', 'extra-box');
$extraDataBox.setAttribute('style', 'margin-top:10px;');
for(let key in extraDataKeyName){
let extraData = extra[id];
let $inputLabel = document.createElement('div');
$inputLabel.setAttribute('style', 'white-space: nowrap;padding-bottom:3px;');
let requiredTag = '';
if(['price', 'cycle', 'start'].indexOf(key) > -1){
requiredTag = '*';
}
$inputLabel.innerHTML = '<span style="display:inline-block;width:70px;">'+ requiredTag + extraDataKeyName[key] +':</span>';
let $input = document.createElement('input');
if(['cycle', 'autoPay'].indexOf(key) > -1){
$input = document.createElement('select');
if(key === 'cycle'){
for(let key in cycleOptions){
$input.options.add(new Option(cycleOptions[key], cycleOptions[key]));
}
}
if(key === 'autoPay'){
for(let key in autoPayOptions){
$input.options.add(new Option(autoPayOptions[key], autoPayOptions[key]));
}
}
}
$input.name = key;
if(extraData){
$input.value = extraData[key] ? extraData[key] : '';
}
$input.addEventListener('change', ()=>{
changer = true;
if(timmer) return false;
console.log('1s 后提交');
timmer = setTimeout(function(){
updateExtraData();
}, 1500);
});
$input.addEventListener('focus', ()=>{
if(timmer){
console.log('终断提交');
clearTimeout(timmer);
timmer = null;
}
});
$input.addEventListener('blur', ()=>{
if(timmer) return false;
if(changer){
console.log('1s 后提交');
timmer = setTimeout(function(){
updateExtraData();
}, 1500);
}
});
$inputLabel.append($input);
$extraDataBox.append($inputLabel);
}
$nameTd.append($extraDataBox);
})
}
}
const updateExtraData = function(){
let paramsRaw = new URLSearchParams(raw);
let customCodeOld = paramsRaw.get('CustomCode');
let $extraBox = document.querySelectorAll('table.table .extra-box');
let extraNew = {};
$extraBox.forEach(e=>{
let shop = e.querySelector('input[name=shop]').value,
pid = e.querySelector('input[name=pid]').value,
price = e.querySelector('input[name=price]').value,
cycle = e.querySelector('select[name=cycle]').value,
start = e.querySelector('input[name=start]').value,
expire = e.querySelector('input[name=expire]').value,
autoPay = e.querySelector('select[name=autoPay]').value;
if(price && cycle && start){
console.log(price)
extraNew[e.id] = {
shop: shop,
pid: pid,
price: price,
cycle: cycle,
start: start,
expire: expire,
autoPay: autoPay,
}
}
});
storageSet('extra', extraNew);
let customCodeNew = customCodeOld.replace(/(?<=extraData = )[\s\S]+(?=\n[\s\t]*const cycleNames)/g, JSON.stringify(extraNew));
paramsRaw.set('CustomCode', customCodeNew);
$.post('/api/setting', paramsRaw.toString()).then(res=>{
if(res.code == 200){
$.suiAlert({
title: '',
description: 'VPS橱窗前台脚本更新成功。',
type: 'success',
time: '3',
position: 'top-center',
});
} else {
$.suiAlert({
title: '',
description: responses.responseText,
type: 'error',
time: '3',
position: 'top-center',
});
}
}).catch(err=>{
$.suiAlert({
title: '',
description: JSON.stringify(err),
type: 'error',
time: '3',
position: 'top-center',
});
}).always(()=>{
clearTimeout(timmer);
timmer = null;
changer = false;
})
}
}
</script>