系統資訊面板

经验创意 · 162 次浏览
simonlkch 创建于 2026-07-21 14:13



------------------------------------
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>系统信息 2</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
html,body{width:100%;height:100%;margin:0;overflow:hidden;background:transparent;font-family:"Microsoft YaHei",sans-serif}
.root{width:100%;height:100%;display:flex;flex-direction:column;background:rgba(16,16,20,.92);border-radius:10px;overflow:hidden;color:#c8c8ce;font-size:12px;line-height:1.5}
.hd{display:flex;align-items:center;justify-content:space-between;padding:7px 12px;border-bottom:1px solid rgba(255,255,255,.06);flex-shrink:0}
.hd-t{font-size:12px;font-weight:600;color:#e2e2e6;letter-spacing:.3px}
.ib{background:none;border:none;color:#7a7a84;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;transition:color .15s,background .15s;outline:none}
.ib:hover{color:#d4d4d8;background:rgba(255,255,255,.08)}
.ib:active{background:rgba(255,255,255,.13)}
.ib:focus-visible{box-shadow:0 0 0 2px rgba(56,189,176,.5)}
.ib.spn svg{animation:sp .7s linear infinite}
.ib:disabled{opacity:.35;cursor:default}
@keyframes sp{to{transform:rotate(360deg)}}
.bd{flex:1;overflow-y:auto;overflow-x:hidden;padding:4px 0}
.bd::-webkit-scrollbar{width:3px}
.bd::-webkit-scrollbar-track{background:transparent}
.bd::-webkit-scrollbar-thumb{background:rgba(255,255,255,.1);border-radius:2px}
.sc{padding:5px 12px}
.sc+.sc{border-top:1px solid rgba(255,255,255,.04)}
.st{font-size:9.5px;font-weight:700;color:#38bdb0;letter-spacing:1px;margin-bottom:3px;text-transform:uppercase}
.rw{display:flex;justify-content:space-between;align-items:baseline;gap:8px;padding:1.2px 0}
.rw .k{color:#636370;flex-shrink:0;white-space:nowrap}
.rw .v{color:#d4d4d8;text-align:right;word-break:break-all;font-variant-numeric:tabular-nums}
.rw.sub .v{text-align:left;width:100%;font-size:11px;color:#a8a8b2}
.rw.cp{cursor:pointer;border-radius:3px;margin:0 -4px;padding:0 4px;transition:background .12s}
.rw.cp:hover{background:rgba(255,255,255,.05)}
.bw{margin:2px 0 1px}
.bt{height:3px;background:rgba(255,255,255,.06);border-radius:2px;overflow:hidden}
.bf{height:100%;border-radius:2px;transition:width .4s ease}
.bf.ok{background:#38bdb0}
.bf.wr{background:#e5a320}
.bf.dg{background:#e04848}
.bl{display:flex;justify-content:space-between;font-size:10px;color:#5a5a66;margin-top:1px}
.di{margin-bottom:5px}
.di:last-child{margin-bottom:0}
.dn{font-size:12px;font-weight:600;color:#a0a0aa;margin-bottom:1px}
.ft{padding:4px 12px;border-top:1px solid rgba(255,255,255,.04);font-size:10px;color:#44444e;text-align:right;flex-shrink:0;min-height:22px}
.ft-e{color:#c04040;cursor:help}
.ld,.er{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:36px 16px;color:#636370;font-size:12px;text-align:center;height:100%}
.sk{width:18px;height:18px;border:2px solid rgba(255,255,255,.08);border-top-color:#38bdb0;border-radius:50%;animation:sp .7s linear infinite}
.em{color:#c04040;max-width:220px;word-break:break-all;font-size:11px}
.rb{background:rgba(255,255,255,.06);border:none;color:#a0a0aa;padding:4px 16px;border-radius:4px;cursor:pointer;font-size:11px;font-family:inherit;transition:background .15s,color .15s;outline:none}
.rb:hover{background:rgba(255,255,255,.1);color:#d4d4d8}
.rb:focus-visible{box-shadow:0 0 0 2px rgba(56,189,176,.5)}
.ei{width:24px;height:24px;border-radius:50%;background:rgba(224,72,72,.12);color:#e04848;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}
</style>
</head>
<body>
<div class="root">
  <div class="hd">
    <span class="hd-t">系统信息</span>
    <button class="ib" id="rbtn" title="刷新" aria-label="刷新">
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2v6h-6"/><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M3 22v-6h6"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/></svg>
    </button>
  </div>
  <div class="bd" id="bd"></div>
  <div class="ft" id="ft"></div>
</div>
<script>
(function(){
  /* ── 宿主适配层:优先 widgetHost,回退 window.external(Quicker) ── */
  var host;
  (function initHost(){
    if(window.widgetHost){host=window.widgetHost;return}
    var e=window.external;
    if(!e){host=null;return}
    /* 检测 Quicker 可用的方法名 */
    var gsv=e.GetSharedVar||e.GetVariable;
    var ssv=e.SetSharedVar||e.SetVariable;
    var rsv=e.RemoveSharedVar||e.RemoveVariable;
    var csv=e.ClearSharedVars||e.ClearVariables;
    host={
      state:{
        read:function(k,d){try{var v=gsv(k);return(v===null||v===undefined||v==='')?d:v}catch(x){return d}},
        write:function(k,v){try{ssv(k,v)}catch(x){}},
        remove:function(k){try{rsv(k)}catch(x){}},
        clear:function(){try{csv()}catch(x){}},
        flush:function(){}
      },
      clipboard:{
        read:function(){try{return e.GetClipboardText()||''}catch(x){return''}},
        write:function(t){try{e.SetClipboardText(t)}catch(x){}}
      },
      url:{
        open:function(o){try{e.OpenUrl(o.url)}catch(x){}if(o.hideAfterOpen)try{e.HideWidget()}catch(x){}}
      },
      path:{
        open:function(o){try{e.OpenFile(o.path)}catch(x){}if(o.hideAfterOpen)try{e.HideWidget()}catch(x){}}
      },
      window:{hide:function(){try{e.HideWidget()}catch(x){}}},
      process:{
        start:function(o){
          var sm={normal:1,hidden:0,minimized:2,maximized:3};
          try{e.RunProgram(o.file,(o.args||[]).join(' '),sm[o.windowStyle]!==undefined?sm[o.windowStyle]:1,false);return{started:true,processId:0}}
          catch(x){throw x}
        },
        run:function(o){
          var j;
          if(e.RunProgramEx){
            j=e.RunProgramEx(o.file,(o.args||[]).join(' '),o.workingDirectory||'',0,true,o.timeoutMs||15000,o.maxOutputBytes||1048576,o.input||'');
          }else if(e.RunProgram){
            j=e.RunProgram(o.file,(o.args||[]).join(' '),0,true);
          }else{throw new Error('宿主不支持进程调用')}
          try{return JSON.parse(j)}catch(x){throw new Error('进程输出解析失败: '+x.message)}
        }
      },
      http:{
        get:function(o){
          var h=o.headers?JSON.stringify(o.headers):'';
          var j=e.HttpGet?e.HttpGet(o.url,h,o.timeoutMs||15000):null;
          if(!j)throw new Error('宿主不支持 HTTP GET');
          try{return JSON.parse(j)}catch(x){throw new Error('HTTP 响应解析失败')}
        },
        post:function(o){
          var h=o.headers?JSON.stringify(o.headers):'';
          var j=e.HttpPost?e.HttpPost(o.url,o.body||'',o.contentType||'',h,o.timeoutMs||15000):null;
          if(!j)throw new Error('宿主不支持 HTTP POST');
          try{return JSON.parse(j)}catch(x){throw new Error('HTTP 响应解析失败')}
        },
        request:function(o){
          var h=o.headers?JSON.stringify(o.headers):'';
          var j=e.HttpRequest?e.HttpRequest(o.method||'GET',o.url,h,o.body||'',o.contentType||'',o.timeoutMs||15000):null;
          if(!j)throw new Error('宿主不支持 HTTP 请求');
          try{return JSON.parse(j)}catch(x){throw new Error('HTTP 响应解析失败')}
        }
      },
      fs:{
        getKnownFolders:function(){try{return JSON.parse(e.GetKnownFolders())}catch(x){throw x}},
        exists:function(o){try{return JSON.parse(e.FileExists(o.path))}catch(x){throw x}},
        getInfo:function(o){try{return JSON.parse(e.GetFileInfo(o.path))}catch(x){throw x}},
        readText:function(o){try{return JSON.parse(e.ReadFileText(o.path,o.encoding||'utf-8',o.maxBytes||0))}catch(x){throw x}},
        readBase64:function(o){try{return JSON.parse(e.ReadFileBase64(o.path,o.maxBytes||0))}catch(x){throw x}},
        list:function(o){try{return JSON.parse(e.ListFiles(o.path,o.pattern||'',!!o.recursive,o.includeFiles!==false,o.includeFolders!==false,o.maxItems||0))}catch(x){throw x}},
        selectFile:function(o){try{var r=e.SelectFile(o.filter||'',o.title||'',o.defaultFileName||'');return r?JSON.parse(r):null}catch(x){throw x}},
        selectFolder:function(o){try{var r=e.SelectFolder(o.title||'');return r?JSON.parse(r):null}catch(x){throw x}}
      }
    };
  })();

  /* ── DOM ── */
  var bd=document.getElementById('bd'),ft=document.getElementById('ft'),rbtn=document.getElementById('rbtn');
  var busy=false,SK='sysinfo';

  function esc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;')}
  function bc(p){return p>=85?'dg':p>=65?'wr':'ok'}
  function hasC(){return bd.querySelector('.sc')!==null}
  function showLd(){bd.innerHTML='<div class="ld"><div class="sk"></div>获取中...</div>'}
  function showEr(msg){bd.innerHTML='<div class="er"><div class="ei">!</div><div class="em" title="'+esc(msg)+'">'+esc(msg)+'</div><button class="rb" id="rtb">重试</button></div>';document.getElementById('rtb').addEventListener('click',function(){doFetch(false)})}
  function ftTm(){var d=new Date();ft.textContent='更新于 '+String(d.getHours()).padStart(2,'0')+':'+String(d.getMinutes()).padStart(2,'0')+':'+String(d.getSeconds()).padStart(2,'0')}
  function ftEr(msg){ft.innerHTML='<span class="ft-e" title="'+esc(msg)+'">刷新失败</span>'}
  function r(k,v){return'<div class="rw cp" title="点击复制"><span class="k">'+esc(k)+'</span><span class="v">'+esc(v)+'</span></div>'}
  function bar(l,p){return'<div class="bw"><div class="bt"><div class="bf '+bc(p)+'" style="width:'+p+'%"></div></div><div class="bl"><span>'+esc(l)+'</span><span>'+p+'%</span></div></div>'}

  function render(d){
    var h='';
    h+='<div class="sc"><div class="st">系统</div>';
    h+=r('主机',d.cn)+r('用户',d.un)+r('系统',d.os)+r('版本',d.ver)+r('架构',d.arch);
    if(d.upD!=null)h+=r('运行',d.upD+'天 '+d.upH+'小时');
    h+='</div>';
    if(d.cpu){
      h+='<div class="sc"><div class="st">处理器</div>';
      h+='<div class="rw sub"><span class="v">'+esc(d.cpu)+'</span></div>';
      h+=r('规格',d.cores+'核 \u00B7 '+d.threads+'线程');
      h+='</div>';
    }
    if(d.memT!=null){
      var mp=d.memT>0?Math.round(d.memU/d.memT*1000)/10:0;
      h+='<div class="sc"><div class="st">内存</div>'+bar(d.memU+' / '+d.memT+' GB',mp)+'</div>';
    }
    if(d.disks&&d.disks.length){
      h+='<div class="sc"><div class="st">磁盘</div>';
      d.disks.forEach(function(dk){
        var used=Math.round((dk.t-dk.f)*10)/10,dp=dk.t>0?Math.round(used/dk.t*1000)/10:0;
        h+='<div class="di"><div class="dn">'+esc(dk.l)+'</div>'+bar(used+' / '+dk.t+' GB',dp)+'</div>';
      });
      h+='</div>';
    }
    if(d.nets&&d.nets.length){
      h+='<div class="sc"><div class="st">网络</div>';
      d.nets.forEach(function(n){h+=r(n.i,n.p)});
      h+='</div>';
    }
    bd.innerHTML=h;
    bd.querySelectorAll('.rw.cp').forEach(function(row){
      var v=row.querySelector('.v');
      if(v)row.addEventListener('click',function(){try{host.clipboard.write(v.textContent)}catch(e){}});
    });
  }

  var PS='[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;$o=Get-CimInstance Win32_OperatingSystem;$c=Get-CimInstance Win32_Processor;$m=Get-CimInstance Win32_ComputerSystem;$up=(Get-Date)-$o.LastBootUpTime;ConvertTo-Json -Depth 3 @{cn=$env:COMPUTERNAME;un=$env:USERNAME;os=$o.Caption;ver=$o.Version;arch=$o.OSArchitecture;upD=$up.Days;upH=$up.Hours;cpu=($c.Name -replace \'\\s+\',\' \').Trim();cores=$c.NumberOfCores;threads=$c.NumberOfLogicalProcessors;memU=[math]::Round(($m.TotalPhysicalMemory-$o.FreePhysicalMemory)/1GB,1);memT=[math]::Round($m.TotalPhysicalMemory/1GB,1);disks=(Get-CimInstance Win32_LogicalDisk -Filter \'DriveType=3\'|ForEach-Object{@{l=$_.DeviceID;t=[math]::Round($_.Size/1GB,1);f=[math]::Round($_.FreeSpace/1GB,1)}});nets=(Get-NetIPAddress -AddressFamily IPv4|Where-Object{$_.PrefixOrigin -ne \'WellKnown\'}|Select-Object -First 6|ForEach-Object{@{i=$_.InterfaceAlias;p=$_.IPAddress}})}';

  async function doFetch(silent){
    if(busy)return;
    if(!host){showEr('未检测到宿主接口');return}
    busy=true;rbtn.disabled=true;rbtn.classList.add('spn');
    if(!silent)showLd();
    try{
      var res=await host.process.run({file:'powershell',args:['-NoProfile','-Command',PS],timeoutMs:15000,maxOutputBytes:65536});
      if(res.timedOut)throw new Error('获取超时,请重试');
      if(res.truncated)throw new Error('输出被截断');
      var data;try{data=JSON.parse(res.stdout)}catch(e){throw new Error('数据解析失败')}
      try{host.state.write(SK,data)}catch(e){}
      render(data);ftTm();
    }catch(e){
      var msg=String(e?.message||e);
      if(silent){ftEr(msg)}else{showEr(msg)}
    }finally{busy=false;rbtn.disabled=false;rbtn.classList.remove('spn')}
  }

  try{
    var cached=host?host.state.read(SK,null):null;
    if(cached){render(cached);ftTm();doFetch(true)}
    else{showLd();doFetch(false)}
  }catch(e){showEr('初始化失败: '+(e?.message||e))}

  rbtn.addEventListener('click',function(){doFetch(hasC())});
})();
</script>
</body>
</html>

simonlkch 最后更新于 2026/7/21

回复内容
暂无回复
回复主贴