哪吒面板新版VPS橱窗前台脚本及后台自定义代码整合帖

简介

新版本哪吒面板0.18.14+已支持后台的自定义脚本,以及由于新版本serverstatus主题代码更新导致原前台脚本bug,所以新开一个前后台脚本的整合帖。

更新日志

  • 2024年09月03日
    • 更新后台脚本修复可能无法获取到所需数据的bug
  • 2024年08月30日
    • 更新后台脚本支持商家信息管理
    • 更新后台脚本商家支持下拉选择
  • 2024年08月28日
    • 修复shop为空时按钮链接的小bug
    • 优化默认主题进度条过短时到期日期显示问题
    • 增加默认启用半透明效果代码
  • 2024年08月20日
    • 更新前台脚本过期时间进度条样式,尽量贴合原框架风格

其它说明

后台脚本特性

支持商家信息管理VPS额外信息管理,将尽可能保证支持界面可视化操作,减少编辑脚本操作

前台脚本

<script>
// 默认分组模式
if(!localStorage.getItem("showGroup")){
    localStorage.setItem("showGroup", true);
}
// 默认暗黑模式
if(!localStorage.getItem("theme")){
    localStorage.setItem("theme", 'dark');
}
// 默认半透明效果
if(!localStorage.getItem("semiTransparent")){
    localStorage.setItem("semiTransparent", 'true');
}
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', shop ? '_blank' : '');
          $aLinkBuy.innerHTML = '购买同款';
          $aLinkBuy.href = shop ? affLinks[shop] : 'javascript:void(0);';
          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;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 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 = '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', shop ? '_blank' : '');
          $buyTdBtnLabelIcon.addEventListener('click', (e)=>{e.stopPropagation()});
          $buyTdBtnLabelIcon.innerHTML = '<i class="shopping cart icon"></i>';
          $buyTdBtnLabelIcon.href = shop ? affLinks[shop] : 'javascript:void(0);';
          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 affLinks = storageGet('affLinks', null);
  let extra = storageGet('extra', null);
  let isFirst = false;
  if(affLinks == null && extra== null){
    isFirst = true;
  }
  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 CustomCodeValueAffLinks =CustomCode.match(/(?<=affLinks = )[\s\S]+(?=\n([\s\t]+)?const contacts)/g);
    let CustomCodeValueExtra =CustomCode.match(/(?<=extraData = )[\s\S]+(?=\n([\s\t]+)?const cycleNames)/g);
    if(!CustomCodeValueAffLinks || !CustomCodeValueExtra){
      $.suiAlert({
          title: '',
          description: '请检查是否已经添加《哪吒面板VPS橱窗脚本》,脚本可去:https://www.bmqy.net/2665.html,获取',
          type: 'error',
          time: '3',
          position: 'top-center',
      });
      return false;
    }
    CustomCodeValueAffLinks = CustomCodeValueAffLinks ? CustomCodeValueAffLinks[0] : '{}';
    CustomCodeValueAffLinks = CustomCodeValueAffLinks.replace(/([0-9a-zA-Z]+):\s?'/g, '"$1":"').replace(/'/g, '"').replace(/[\r\n]+/g, '').replace(/,}/g, '}');
    affLinks = JSON.parse(CustomCodeValueAffLinks);
    storageSet('affLinks', affLinks);
    CustomCodeValueExtra = CustomCodeValueExtra ? CustomCodeValueExtra[0] : '{}';
    CustomCodeValueExtra = CustomCodeValueExtra.replace(/([0-9a-zA-Z]+):/g, '"$1":').replace(/},\n}/g, '}\n}').replace(/'/g, '"').replace(/[\r\n]+/g, '').replace(/,}/g, '}');
    extra = JSON.parse(CustomCodeValueExtra);
    storageSet('extra', extra);
    if(isFirst){
      $.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' && (affLinks == null || extra == null)){
      $.suiAlert({
          title: '',
          description: '请先进入【设置】页面获取脚本所需数据!!!',
          type: 'warning',
          time: '3',
          position: 'top-center',
      });
      return false;
    }
    if(pathname === '/server'){
      createShopFormModal(affLinks);
      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(key === 'pid'){
            $input.placeholder = '商家所售产品ID';
          } else if(['start', 'expire'].indexOf(key) > -1){
            $input.placeholder = '月/日/年';
          } else if(key === 'price'){
            $input.placeholder = '支持:$、¥、€、P';
          }
          if(['shop', 'cycle', 'autoPay'].indexOf(key) > -1){
            $input = document.createElement('select');
            if(key === 'shop'){
              $input.options.add(new Option('请选择', ''));
              for(let key in affLinks){
                $input.options.add(new Option(key, key));
              }
            }
            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);
          if(key === 'shop'){
            let $addShopBtn = document.createElement('button');
            $addShopBtn.title = '管理商家信息';
            $addShopBtn.setAttribute('class', 'ui icon button mini');
            $addShopBtn.setAttribute('style', 'margin-left:5px;');
            $addShopBtn.innerHTML = '<i class="icon setting"></i>';
            $addShopBtn.addEventListener('click', managerShoopFormModal);
            $inputLabel.append($addShopBtn);
          }
          $extraDataBox.append($inputLabel);
        }
        $nameTd.append($extraDataBox);
      })
    }
  }
  
  function updateExtraData(){
    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('select[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){
            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;
    })
  }
  
  function managerShoopFormModal(){
    showOnSubmitFormModal(".shopForm.modal", "#shopForm", "/api/setting", getShopData);
  }
  
  function getShopData(){
    let paramsRaw = new URLSearchParams(raw);
    let customCodeOld = paramsRaw.get('CustomCode');
    let affLinks = storageGet('affLinks', null);
    let customCodeNew = customCodeOld.replace(/(?<=affLinks = )[\s\S]+(?=\n[\s\t]*const contacts)/g, JSON.stringify(affLinks));
    paramsRaw.set('CustomCode', customCodeNew);
    return paramsRaw.toString();
  }
  
  function updateShopData(){
    let $shopForm = document.querySelector('#shopForm');
    let data = {};
    let emptyCount = 0;
    for(let i=0; i<$shopForm.elements.length; i+=2){
      let $name = $shopForm.elements[i];
      let $url = $shopForm.elements[i+1];
      if($name.value && $url.value){
        data[$name.value] = $url.value;
        storageSet('affLinks', data);
        if(i == $shopForm.elements.length-2){
          createShopFormModal(data);
        }
      } else {
        emptyCount += 1;
      }
    }
    if(emptyCount > 1){
      createShopFormModal(data);
    }
  }
  
  function createInput(name, value){
    let $input = document.createElement('input');
    $input.type = 'text';
    $input.name = name;
    $input.setAttribute('value', value || '');
    $input.placeholder = name==='name' ? '名称' : '邀请链接';
    $input.addEventListener('blur', updateShopData);
    return $input;
  }
  function createShopFormModal(affLinks){
    let $shopFormModal = document.querySelector('#shopFormModal');
    let isFirst = true;
    if(!$shopFormModal){
      $shopFormModal = document.createElement('div');
      $shopFormModal.id = 'shopFormModal';
      $shopFormModal.setAttribute('class', 'ui tiny shopForm modal transition hidden');
    } else {
      isFirst = false;
      $shopFormModal.innerHTML = '';
    }
    let $shopForm = document.createElement('form');
    $shopForm.id = 'shopForm';
    $shopForm.setAttribute('class', 'ui form');
    for(let key in affLinks){
      let $shopFormField = document.createElement('div');
      let $shopFormGrid = document.createElement('div');
      $shopFormGrid.setAttribute('class', 'ui grid');
      let $shopFormGridItem = document.createElement('div');
      $shopFormGridItem.setAttribute('class', 'four wide column');
      $shopFormGridItem.append(createInput('name', key));
      $shopFormGrid.append($shopFormGridItem);
      $shopFormGridItem = document.createElement('div');
      $shopFormGridItem.setAttribute('class', 'twelve wide column');
      $shopFormGridItem.append(createInput('url', affLinks[key]));
      $shopFormGrid.append($shopFormGridItem);
      $shopFormField.append($shopFormGrid);
      $shopForm.append($shopFormField);
    }
    let $shopFormField = document.createElement('div');
    let $shopFormGrid = document.createElement('div');
    $shopFormGrid.setAttribute('class', 'ui grid');
    let $shopFormGridItem = document.createElement('div');
    $shopFormGridItem = document.createElement('div');
    $shopFormGridItem.setAttribute('class', 'four wide column');
    $shopFormGridItem.append(createInput('name', ''));
    $shopFormGrid.append($shopFormGridItem);
    $shopFormGridItem = document.createElement('div');
    $shopFormGridItem.setAttribute('class', 'twelve wide column');
    $shopFormGridItem.append(createInput('url', ''));
    $shopFormGrid.append($shopFormGridItem);
    $shopFormField.append($shopFormGrid);
    $shopForm.append($shopFormField);
    
    let $shopFormModalHead = document.createElement('div');
    $shopFormModalHead.setAttribute('class', 'header');
    $shopFormModalHead.innerHTML = 'VPS橱窗商家信息管理';
    $shopFormModal.append($shopFormModalHead);
    let $shopFormModalContent = document.createElement('div');
    $shopFormModalContent.setAttribute('class', 'content');
    $shopFormModalContent.append($shopForm);
    $shopFormModal.append($shopFormModalContent);
    let $shopFormModalActions = document.createElement('div');
    $shopFormModalActions.setAttribute('class', 'actions');
    let $shopFormModalActionsCancelBtn = document.createElement('div');
    $shopFormModalActionsCancelBtn.setAttribute('class', 'ui negative button');
    $shopFormModalActionsCancelBtn.innerHTML = '取消';
    $shopFormModalActions.append($shopFormModalActionsCancelBtn);
    let $shopFormModalActionsConfirmBtn = document.createElement('div');
    $shopFormModalActionsConfirmBtn.setAttribute('class', 'ui positive nezha-primary-btn right labeled icon button vps-window-btn');
    $shopFormModalActionsConfirmBtn.innerHTML = '确认<i class="checkmark icon"></i>';
    $shopFormModalActions.append($shopFormModalActionsConfirmBtn);
    $shopFormModal.append($shopFormModalActions);
    isFirst && document.body.append($shopFormModal);
  }
  
  function showOnSubmitFormModal(modelSelector, formID, URL, getData) {
    $(modelSelector)
    .modal({
      closable: true,
      onApprove: function () {
        let success = false;
        const btn = $(modelSelector + " .vps-window-btn.button");
        const form = $(modelSelector + " form");
        if (btn.hasClass("loading")) {
          return success;
        }
        form.children(".message").remove();
        btn.toggleClass("loading");
        const data = getData
          ? getData()
          : $(formID)
            .serializeArray()
            .reduce(function (obj, item) {
              // ID 类的数据
              if (item.name === "pid") {
                obj[item.name] = parseInt(item.value);
              } else {
                obj[item.name] = item.value;
              }
              return obj;
            }, {});
        $.post(URL, data)
          .done(function (resp) {
            if (resp.code == 200) {
              window.location.reload()
            } else {
              form.append(
                `<div class="ui negative message"><div class="header">操作失败</div><p>` +
                resp.message +
                `</p></div>`
              );
            }
          })
          .fail(function (err) {
            form.append(
              `<div class="ui negative message"><div class="header">网络错误</div><p>` +
              err.responseText +
              `</p></div>`
            );
          })
          .always(function () {
            btn.toggleClass("loading");
          });
        return success;
      },
    })
    .modal("show");
  }
}
</script>

效果图






点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注