哪吒面板终极进化之VPS橱窗

2024-11-12 20:39:00 # 燕坊清作

简介

为了方便,为了好看,继续了哪吒面板的终极进化之路:VPS橱窗。我觉得可以叫做VPS小商城了,貌似该有的都有了,没有的以后可能还会有。。

更新日志

  • 2024年11月06日
    • 更新前台脚本,由于新版主题支持了公开备注,因此简化了前台脚本去除不必要的代码
    • 更新前台脚本,分离了联系信息配置,默认不显示联系信息,如需显示请单独添加联系信息的代码
    • 更新后台脚本,去除不必要的代码,支持在服务器页面公开备注鼠标悬停时的提示信息key显示为中文
  • 2024年09月03日
    • 更新后台脚本修复可能无法获取到所需数据的bug
  • 2024年08月30日
    • 更新后台脚本支持商家信息管理
    • 更新后台脚本商家支持下拉选择
  • 2024年08月28日
    • 修复shop为空时按钮链接的小bug
    • 优化默认主题进度条过短时到期日期显示问题
  • 2024年08月27日
    • 增加默认启用半透明效果代码
  • 2024年08月19日
    • 更新前台脚本过期时间进度条样式,尽量贴合原框架风格
  • 2024年08月17日
    • 支持0.18.14+版本的后台自定义代码,请查看后台脚本
    • 优化支持最新版的serverstatus主题,请查看前台脚本

步骤

  • 哪吒面板设置界面自定义代码(包括 style 和 script)中添加下面前台脚本代码;
  • 刷新即可看到效果
  • 演示,哪吒版本:0.19.20
  • 后台脚本~~~~仅支持哪吒面板~~~~`v0.18.14+`~~~~,低于此版本的需要使用油猴脚本:哪吒VPS橱窗后台脚本食用,油猴脚本不在更新,~~~~`推荐安装哪吒面板最新版~~~~并使用下方~~~~后台脚本`
  • 最新的前台脚本后台脚本仅支持哪吒面板v0.19.13+,低版本不再支持
  • 哪吒面板设置界面Custom Codes for Dashboard中添加下面后台脚本代码;

特性

  • 支持购买同款按钮
  • 分组支持数量显示
  • 支持价格付款周期展示
  • 支持多种联系方式展示
  • 支持到期日期进度条展示
  • 支持剩余价值展示,支持年付半年付季付月付,参考下面特殊说明cycle字段,仅供参考,计算方式取自@sunfei大佬的帖子,感谢分享。
  • 支持ServerStatus主题
  • 支持自动续费配置
  • 默认显示分组,不需要可删除相关代码
  • 默认显示暗黑模式,不需要可删除相关代码
  • 默认启用半透明效果,不需要可删除相关代码
  • 后台脚本仅支持哪吒面板0.18.14+版本,低于此版本的请继续使用油猴版VPS橱窗后台脚本,油猴脚本不再更新
  • 后台脚本支持商家信息管理VPS额外信息管理,将尽可能保证支持界面可视化操作,减少编辑脚本操作

特殊说明

  • 不限定所属分类,需要为每个小鸡指定shop字段,其值对应affLinks中的key
  • affLinks中填写不同商家的邀请链接地址
  • extraData中的数字key要换成自己机器的id
  • extraData中的购买价格price支持单位:$、¥、P、€
  • extraData中的付款周期cycle支持填写:年付、半年付、季付、月付、年、半、季、月、Year、Half、Quarterly、Month、Y、H、Q、M、year、half、quarterly、month
  • extraData中的自动续费autoPay支持填写:是、否
  • contacts中填写你需要的联系方式
  • contactsmain标识需要显示在购买同款按钮旁边的联系方式
  • contactsanimatedType标识剩余价值的动画方式,留空为横向移动,可选值为:verticalfade。参考地址:https://semantic-ui.com/elements/button.html#animated
  • contacts中的icon图标在这里查找:https://semantic-ui.com/elements/icon.html
  • 进度条的颜色progressType可用值参考:https://semantic-ui.com/modules/progress.html
  • ServerStatus主题的修改默认隐藏掉了系统在线时间负载三列
  • 对增加的魔改元素增加css类名,方便有需要的同学添加个性化样式,具体可以使用浏览器工具检查元素查看
  • 其它效果大家可自行发挥

前台脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
// 联系信息
var 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'
},
}
</script>
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
<script>
// 默认分组模式
if(!localStorage.getItem("showGroup")){
localStorage.setItem("showGroup", true);
}
// 默认暗黑模式
if(!localStorage.getItem("theme")){
localStorage.setItem("theme", 'dark');
}
// 默认半透明效果
if(!localStorage.getItem("semiTransparent")){
localStorage.setItem("semiTransparent", 'false');
}
window.onload = function(){
// 必要函数
var nvwDeepCopy = function(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = nvwDeepCopy(obj[key]);
}
}
return copy;
}
var nvwGetCurrentServer = (id)=>{
if(!id){
return null;
}
let arr = servers.filter(e=>{
return e['ID'] == id;
})
if(arr[0]){
return arr[0];
}
return null;
}

// 获取VPS数据
var serversHtmlMatch = document.body.innerHTML.match(/(?<=this\.servers \= JSON.parse\(\')[\s\S]+(?=\'\)\.servers;)/g);
if(!serversHtmlMatch){
console.log('%cVPS橱窗提示:请先升级哪吒面板到v0.19.13+', 'padding:0 8px;background:red;color:#fff;line-height:1.8;');
return false;
}
var servers = JSON.parse(serversHtmlMatch[0].replace(/\\u0022/g, '"').replace(/\\\\r/g, '').replace(/\\\\n/g, '').replace(/\\"/g, '"')).servers;

// 判断版本
var $version = document.body.innerText.match(/(哪吒监控\s+[0-9\.]+)/g);
if($version){
var $versionText = $version[0].split(' ')[1];
$versionArr = $versionText.split('.');
$versionArr.forEach((e,i)=>{
if(e[0]<=0){
if(e[1]<19){
console.log('%cVPS橱窗提示:请先升级哪吒面板到v0.19.13+', 'padding:0 8px;background:red;color:#fff;line-height:1.8;');
return false;
} else {
if(e[1]==19 && e[2]<13){
console.log('%cVPS橱窗提示:请先升级哪吒面板到v0.19.13+', 'padding:0 8px;background:red;color:#fff;line-height:1.8;');
return false;
}
}
}
});
}

// 数据处理
servers.forEach(item=>{
item.PublicNote = item.PublicNote ? JSON.parse(item.PublicNote) : null;
});

const commonContacts = 'none';
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');
let extraData = nvwGetCurrentServer(id);
if(extraData && extraData.PublicNote){
let affLink = extraData.PublicNote.affLink;
let price = extraData.PublicNote.price;
let start = extraData.PublicNote.start;
let expire = extraData.PublicNote.expire;
let cycle = extraData.PublicNote.cycle;
let autoPay = extraData.PublicNote.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', affLink ? '_blank' : '');
$aLinkBuy.innerHTML = '购买同款';
$aLinkBuy.href = affLink ? affLink : 'javascript:void(0);';
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;left:.5em;text-align: right;';
if(expire === '∞'){
progressType = 'success';
textStyle = '';
}
$expireR.setAttribute('class', 'expire-box thirteen wide column');
$expireR.innerHTML = '<div class="ui indicating progress '+ progressType +' active"><div class="bar" style="line-height: 1em !important;transition-duration: 300ms; min-width: unset; width: '+ cTime +'% !important;padding-left: 0.4em;"><div class="progress" style="'+ textStyle +'">'+ expire +'</div></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 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);

try{
let remainingAnimatedType = contacts['animatedType'];
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);
}
}
} catch(error){
console.log('%cVPS橱窗提示:当前未配置 contacts 信息', 'padding:0 8px;background:orange;color:#fff;line-height:1.8;');
let remainingAnimatedType = 'vertical';
// 购买同款按钮右侧主要联系方式显示剩余价值
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="剩余价值">剩余价值</div><div class="visible content" style="padding:0;min-width: 40px;" title="剩余价值">'+ priceUnit + remainingPrice +'</div>';
$aLinkButtons.append($aLinkContactMain);
$aButtonsBox.append($aLinkButtons);
}
$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);

try{
console.log(contacts);
let $contactTh = document.createElement('th');
$contactTh.innerText = '议价';
$contactTh.setAttribute('class', 'node-cell expire center');
$head.append($contactTh);
} catch(error){
console.log('%cVPS橱窗提示:当前未配置 contacts 信息', 'padding:0 8px;background:orange;color:#fff;line-height:1.8;');
}

$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', '');
let extraData = nvwGetCurrentServer(id);
if(extraData && extraData.PublicNote){
let affLink = extraData.PublicNote.affLink;
let price = extraData.PublicNote.price;
let start = extraData.PublicNote.start;
let expire = extraData.PublicNote.expire;
let cycle = extraData.PublicNote.cycle;
let autoPay = extraData.PublicNote.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 = 'danger';
if(expire === '∞'){
progressType = 'warning';
}

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 +' progress-bar-striped active" style="width: '+ cTime +'%;padding-right:5px;background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);"><small class="progress" style="white-space: nowrap;background-color: transparent !important;background-image: unset;">'+ 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', affLink ? '_blank' : '');
$buyTdBtnLabelIcon.addEventListener('click', (e)=>{e.stopPropagation()});
$buyTdBtnLabelIcon.innerHTML = '<i class="shopping cart icon"></i>';
$buyTdBtnLabelIcon.href = affLink ? affLink : 'javascript:void(0);';
$buyTdBtn.append($buyTdBtnLabelIcon);
$buyTd.append($buyTdBtn);
ee.append($buyTd);
// 联系方式列
try {
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);
} catch(error){
};
} 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);
try {
console.log(contacts);
let $contactTd = document.createElement('td');
$contactTd.setAttribute('class', 'node-cell contact');
$contactTd.setAttribute('style', 'text-align:center;');
$contactTd.innerHTML = '-';
ee.append($contactTd);
} catch(error){
}
}
});
});
}
}
</script>

后台脚本

1
2
3
4
5
6
7
8
9
// 公开备注:为避免与官方主题配置冲突,请按下面设置
{
"affLink": "https://get.crunchbits.com/order/lblk-yearly-kvm-ssd-vps/84",
"price": "$27.38",
"cycle": "Year",
"start": "08/13/2023",
"expire": "08/13/2025",
"autoPay": "否"
}
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
<script>
window.onload = function(){
const extraDataKeyName = {
affLink: "推广链接",
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;

if(pathname === '/server'){
const $table = document.querySelector('table.table');
const $tableTr = $table.querySelectorAll('tbody tr');
$tableTr.forEach(e=>{
let $tds = e.querySelectorAll('td');
let $publicNoteTd = $tds[11];
let $publicNoteBtton = $publicNoteTd.querySelector('button');
if(!$publicNoteBtton) return;
for(let key in extraDataKeyName){
$publicNoteBtton.title = $publicNoteBtton.title.replace(key, extraDataKeyName[key]);
}
});
}

function nvwSuiAlert(message, type){
if(!message){
return false;
}
$.suiAlert({
title: '',
description: message,
type: type || 'info',
time: '3',
position: 'top-center',
});
}
}
</script>

前台效果图

QQ20240820-084107.png

QQ20240820-084036.png

WX20240508-233030@2x.png

后台效果图

20241106163209.png

20240817180140.png