<!DOCTYPE html>

<html lang="zh-CN">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>演示站合集</title>

<script src="https://cdn.tailwindcss.com"></script>

<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">

<script>

tailwind.config = {

  theme:{

    extend:{

      colors:{

        dark:"#111318",

        card:"#1b1f29",

        primary:"#3b82f6",

        ok:"#22c55e",

        down:"#ef4444"

      }

    }

  }

}

</script>

<style>

*{box-sizing:border-box}

.card-img{min-height:90px;object-fit:cover}

.modal-wrap{background:rgba(0,0,0,0.75)}

</style>

</head>

<body class="bg-dark text-gray-200">

<div class="max-w-7xl mx-auto px-4 py-6">

  <div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-6">

    <div>

      <h1 class="text-[22px] font-bold text-white">演示站合集</h1>

      <p class="text-gray-400 text-sm mt-1">总计:<span id="total">0</span> 个演示站</p>

    </div>

    <div class="flex flex-col sm:flex-row gap-3 w-full md:w-auto">

      <input id="search" type="text" placeholder="🔍 搜索名称/简介" class="bg-card border-0 rounded px-3 py-2 w-full sm:w-64 outline-none focus:ring-1 focus:ring-primary">

      <div id="tags" class="flex flex-wrap gap-2"></div>

    </div>

  </div>


  <div id="list" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4"></div>

</div>


<!-- 弹窗 -->

<div id="modal" class="fixed inset-0 modal-wrap hidden items-center justify-center z-50 p-4">

  <div class="bg-card w-full max-w-xl rounded-lg overflow-hidden">

    <div class="relative">

      <img id="modal-img" class="w-full max-h-72 object-contain bg-black">

      <button id="close-modal" class="absolute top-2 right-2 bg-black/50 w-7 h-7 rounded-full"><i class="fa fa-times"></i></button>

    </div>

    <div class="p-5">

      <h2 id="modal-name" class="text-xl font-bold text-white"></h2>

      <div class="flex gap-3 mt-2 text-sm">

        <span id="modal-cate" class="text-primary"></span>

        <span id="modal-status"></span>

      </div>

      <div class="my-4 text-gray-300 text-sm" id="modal-desc"></div>

      <div class="mb-4">

        <div class="text-xs text-gray-400 mb-1">功能列表</div>

        <div id="modal-feature" class="flex flex-wrap gap-2"></div>

      </div>

      <div class="text-sm space-y-1 mb-5">

        <div>演示账号:<span id="modal-user"></span></div>

        <div>演示密码:<span id="modal-pwd"></span></div>

      </div>

      <div class="flex gap-3">

        <a id="modal-link" target="_blank" class="bg-primary px-4 py-2 rounded text-white text-sm"><i class="fa fa-external-link"></i> 访问演示</a>

        <a id="modal-git" target="_blank" class="bg-gray-700 px-4 py-2 rounded text-white text-sm"><i class="fa fa-github"></i> 源码地址</a>

      </div>

    </div>

  </div>

</div>


<script>

let allData = [];

let activeTag = "全部";

const $ = s=>document.querySelector(s);

const $$ = s=>document.querySelectorAll(s);

const listEl = $("#list");

const searchEl = $("#search");

const tagsEl = $("#tags");

const modal = $("#modal");


// 加载数据

fetch("./sites.json")

.then(res=>res.json())

.then(data=>{

  allData = data;

  renderTags();

  render();

});


function renderTags(){

  const cates = new Set(["全部"]);

  allData.forEach(i=>cates.add(i.category));

  tagsEl.innerHTML = "";

  [...cates].forEach(cate=>{

    const b = document.createElement("button");

    b.innerText = cate;

    b.className = `px-2 py-1 rounded text-sm ${cate===activeTag?"bg-primary text-white":"bg-card"}`;

    b.onclick = ()=>{

      activeTag = cate;

      renderTags();

      render();

    }

    tagsEl.appendChild(b);

  })

}


function render(){

  const kw = searchEl.value.trim().toLowerCase();

  const filter = allData.filter(item=>{

    const mkw = item.name.toLowerCase()+item.desc.toLowerCase();

    const matchKw = !kw || mkw.includes(kw);

    const matchCate = activeTag==="全部" || item.category === activeTag;

    return matchKw && matchCate;

  })

  $("#total").innerText = filter.length;

  listEl.innerHTML = "";

  filter.forEach(item=>{

    const statusClass = item.status==="正常"?"text-ok":"text-down";

    const statusIcon = item.status==="正常"?"✅":"❌";

    const div = document.createElement("div");

    div.className = "bg-card rounded overflow-hidden cursor-pointer hover:ring-1 hover:ring-primary transition";

    div.innerHTML = `

      <img class="card-img w-full lazy" data-src="${item.thumb}" src="https://placehold.co/400x220/222/777?text=无截图">

      <div class="p-3">

        <div class="font-medium truncate">${item.name}</div>

        <div class="flex justify-between text-xs mt-1">

          <span class="text-primary">${item.category}</span>

          <span class="${statusClass}">${statusIcon}</span>

        </div>

      </div>

    `

    div.onclick = ()=>openModal(item);

    listEl.appendChild(div);

  })

  lazyLoad();

}


function openModal(item){

  $("#modal-img").src = item.thumb || "https://placehold.co/800/222/777?text=无截图";

  $("#modal-name").innerText = item.name;

  $("#modal-cate").innerText = item.category;

  $("#modal-status").innerText = item.status;

  $("#modal-desc").innerText = item.desc;

  $("#modal-user").innerText = item.user || "-";

  $("#modal-pwd").innerText = item.pwd || "-";

  $("#modal-link").href = item.url;

  $("#modal-git").href = item.github || "#";

  $("#modal-feature").innerHTML = "";

  if(item.feature && item.feature.length){

    item.feature.forEach(f=>{

      const span = document.createElement("span");

      span.className = "bg-dark px-2 py-1 rounded text-xs";

      span.innerText = f;

      $("#modal-feature").appendChild(span);

    })

  }

  modal.classList.remove("hidden");

  modal.classList.add("flex");

}


function closeModal(){

  modal.classList.add("hidden");

  modal.classList.remove("flex");

}

$("#close-modal").onclick = closeModal;

modal.onclick = (e)=>{if(e.target===modal) closeModal()}


searchEl.oninput = render;


// 简易懒加载

function lazyLoad(){

  const imgs = $$(".lazy");

  const io = new IntersectionObserver((entries)=>{

    entries.forEach(en=>{

      if(en.isIntersecting){

        const img = en.target;

        img.src = img.dataset.src;

        io.unobserve(img);

      }

    })

  })

  imgs.forEach(img=>io.observe(img));

}

</script>

</body>

</html>