MadHat's Test

    Информация о пользователе

    Привет, Гость! Войдите или зарегистрируйтесь.


    Вы здесь » MadHat's Test » Новый форум » магазин


    магазин

    Сообщений 1 страница 2 из 2

    1

    тест

    0

    2

    [html]<div id="shop" class="shop">
      <div class="shop__top">
        <div class="shop__filters">
          <input id="shopSearch" type="search" placeholder="Найти предмет..." />
          <select id="shopCategory">
            <option value="all">Все категории</option>
          </select>
        </div>

        <details class="shop__stats" open>
          <summary>Скиллы персонажа (для проверки требований)</summary>

          <div class="shop__statsGrid">
            <label>Сила <input id="st_sila" type="number" min="0" value="0"></label>
            <label>Скорость <input id="st_skor" type="number" min="0" value="0"></label>
            <label>Здоровье <input id="st_zdr" type="number" min="0" value="0"></label>

            <label>Тайдзюцу <input id="st_tai" type="number" min="0" value="0"></label>
            <label>Ниндзюцу <input id="st_nin" type="number" min="0" value="0"></label>
            <label>Фуиндзюцу <input id="st_fuin" type="number" min="0" value="0"></label>

            <label>Тактика <input id="st_takt" type="number" min="0" value="0"></label>
            <label>Знания <input id="st_znan" type="number" min="0" value="0"></label>
            <label>Метательные навыки <input id="st_met" type="number" min="0" value="0"></label>
          </div>
        </details>
      </div>

      <div class="shop__layout">
        <section class="shop__list">
          <div id="shopItems" class="shop__items"></div>
        </section>

        <aside class="shop__cart">
          <div class="shop__cartHeader">
            <strong>Корзина</strong>
            <button id="cartClear" type="button">Очистить</button>
          </div>

          <div id="cartEmpty" class="shop__muted">Пока пусто. Как твой холодильник, когда “просто заглянула”.</div>

          <div id="cartWrap" hidden>
            <table class="shop__cartTable">
              <thead>
                <tr>
                  <th>Предмет</th>
                  <th>Кол-во</th>
                  <th>Цена</th>
                  <th></th>
                </tr>
              </thead>
              <tbody id="cartRows"></tbody>
            </table>

            <div class="shop__cartTotal">
              <span>Итого:</span>
              <strong><span id="cartTotal">0</span> рё</strong>
            </div>

            <div class="shop__postTools">
              <input id="episodeLine" type="text" placeholder="Строка эпизода (необязательно), напр.: В эпизоде 24.01.999 - Волчий след!" />
              <button id="copyPost" type="button">Скопировать текст покупки</button>
              <textarea id="postOut" rows="10" readonly></textarea>
            </div>
          </div>
        </aside>
      </div>
    </div>

    <script>
    (() => {
      // ====== ДАННЫЕ МАГАЗИНА (добавляй свои позиции по образцу) ======
      // Взято из твоего списка "Магазин предметов" (оружие/броня/щиты/снаряжение). :contentReference[oaicite:1]{index=1}
      const ITEMS = [
        {
          id: "sai",
          name: "Саи",
          category: "Колющее",
          classRank: "D-ранг",
          price: 3500,
          image: "https://upforme.ru/uploads/001a/12/f3/6/t744066.jpg",
          stats: [
            "Урон: 30 ед. + Скорость*2",
            "Крит: 50 ед. (25%)",
            "Бонус: +1 к блокированию клинков"
          ],
          req: { tai: 4, skor: 4 }
        },
        {
          id: "pika",
          name: "Пика",
          category: "Колющее",
          classRank: "D-ранг",
          price: 3500,
          image: "https://upforme.ru/uploads/001a/12/f3/6/t191823.webp",
          stats: [
            "Урон: 30 ед. + Скорость*2",
            "Крит: 60 ед. (15%)"
          ],
          req: { skor: 5, tai: 4 }
        },
        {
          id: "trident",
          name: "Трезубец",
          category: "Колющее",
          classRank: "D-ранг",
          price: 4000,
          image: "",
          stats: [
            "Урон: 35 ед. + Скорость*2",
            "Крит: 70 ед. (20%)",
            "Бонус: возможность метания"
          ],
          req: { tai: 5, skor: 4 }
        },
        {
          id: "kunai_pack_10",
          name: "Комплект кунаев (10 шт.)",
          category: "Метательное",
          classRank: "E-ранг",
          price: 1200,
          image: "",
          stats: [
            "Урон: 15 ед. + Скорость*2",
            "Крит: 30 ед. (15%)",
            "Бонус: возможность метания"
          ],
          req: { tai: 1, skor: 1 }
        },
        {
          id: "smoke_bomb",
          name: "Дымовая шашка",
          category: "Вспомогательное снаряжение",
          classRank: "E-ранг",
          price: 600,
          image: "",
          stats: [
            "Эффект: туман на 2 хода",
            "Если добавить яд — урон как у яда"
          ],
          req: { tai: 4, skor: 4 }
        },
        {
          id: "flash_bomb",
          name: "Световая бомба",
          category: "Вспомогательное снаряжение",
          classRank: "E-ранг",
          price: 800,
          image: "",
          stats: [
            "Урон: 0",
            "Бонус: ослепление на 2 хода"
          ],
          req: { tai: 3 }
        },
        {
          id: "blank_scroll",
          name: "Чистый свиток",
          category: "Вспомогательное снаряжение",
          classRank: "E-ранг",
          price: 100,
          image: "",
          stats: [
            "Бонус: запечатывание техник/оружия/предметов"
          ],
          req: { fuin: 3, nin: 3 }
        },
        {
          id: "light_armor",
          name: "Лёгкая броня",
          category: "Защита",
          classRank: "Уровень 4",
          price: 3500,
          image: "",
          stats: [
            "Защита от физ. урона: 10",
            "Комплект: жилет, щитки на голени и предплечьях"
          ],
          req: { sila: 4, zdr: 4 }
        },
        {
          id: "medium_armor",
          name: "Средняя броня",
          category: "Защита",
          classRank: "Уровень 6",
          price: 6000,
          image: "",
          stats: [
            "Защита от физ. урона: 20",
            "Комплект: жилет, щитки на голени и предплечьях"
          ],
          req: { sila: 6, zdr: 6 }
        },
        {
          id: "heavy_armor",
          name: "Тяжёлая броня",
          category: "Защита",
          classRank: "Уровень 8",
          price: 7500,
          image: "",
          stats: [
            "Защита от физ. урона: 30"
          ],
          req: { sila: 7, zdr: 7 }
        },
        {
          id: "buckler",
          name: "Баклер",
          category: "Защита",
          classRank: "Уровень 3",
          price: 2500,
          image: "",
          stats: [
            "Защита: 25",
            "Урон щитом: 5 + Сила*2",
            "Проверка блока: Тайдзюцу+Сила"
          ],
          req: { sila: 3 }
        },
        {
          id: "mokibishi",
          name: "Мокибиши",
          category: "Вспомогательное снаряжение",
          classRank: "E-ранг",
          price: 500,
          image: "https://upforme.ru/uploads/001a/12/f3/6/t514347.webp",
          stats: [
            "Урон при наступлении: 10",
            "Крит: 20 (10%)"
          ],
          req: { met: 2 }
        },
        {
          id: "paper_bombs_3",
          name: "Комплект взрывных печатей (3 шт.)",
          category: "Вспомогательное снаряжение",
          classRank: "E-ранг",
          price: 900,
          image: "",
          stats: [
            "Урон: 40 (прямое) / 20 (близкая дистанция)"
          ],
          req: {} // требований в твоём списке рядом не видно, оставила пусто
        }
      ];

      const CATEGORIES_ORDER = [
        "Колющее",
        "Режущее",
        "Рубящее",
        "Колюще-режущее",
        "Рубяще-колющее",
        "Ударно-раздробляющее",
        "Дробяще-колющее",
        "Метательное",
        "Защита",
        "Вспомогательное снаряжение"
      ];

      // ====== DOM ======
      const elItems = document.getElementById("shopItems");
      const elSearch = document.getElementById("shopSearch");
      const elCat = document.getElementById("shopCategory");

      const elCartEmpty = document.getElementById("cartEmpty");
      const elCartWrap = document.getElementById("cartWrap");
      const elCartRows = document.getElementById("cartRows");
      const elCartTotal = document.getElementById("cartTotal");
      const elClear = document.getElementById("cartClear");

      const elEpisode = document.getElementById("episodeLine");
      const elCopy = document.getElementById("copyPost");
      const elOut = document.getElementById("postOut");

      const statIds = ["sila","skor","zdr","tai","nin","fuin","takt","znan","met"];
      const statInputs = Object.fromEntries(statIds.map(k => [k, document.getElementById("st_" + k)]));

      // ====== STATE ======
      const cart = new Map(); // id -> {qty}

      // ====== HELPERS ======
      const fmt = (n) => (Number(n)||0).toLocaleString("ru-RU");
      const getStats = () => Object.fromEntries(statIds.map(k => [k, Number(statInputs[k].value||0)]));

      function unmetReq(item, stats) {
        const req = item.req || {};
        const miss = [];
        for (const [k, v] of Object.entries(req)) {
          if ((stats[k] || 0) < v) miss.push(`${labelStat(k)} ≥ ${v}`);
        }
        return miss;
      }

      function labelStat(k) {
        return ({
          sila: "Сила",
          skor: "Скорость",
          zdr: "Здоровье",
          tai: "Тайдзюцу",
          nin: "Ниндзюцу",
          fuin: "Фуиндзюцу",
          takt: "Тактика",
          znan: "Знания",
          met: "Метательные навыки"
        })[k] || k;
      }

      function categories() {
        const set = new Set(ITEMS.map(i => i.category));
        const sorted = [...set].sort((a,b) => {
          const ia = CATEGORIES_ORDER.indexOf(a);
          const ib = CATEGORIES_ORDER.indexOf(b);
          if (ia === -1 && ib === -1) return a.localeCompare(b, "ru");
          if (ia === -1) return 1;
          if (ib === -1) return -1;
          return ia - ib;
        });
        return sorted;
      }

      // ====== RENDER ======
      function renderCategoryOptions() {
        const cats = categories();
        for (const c of cats) {
          const opt = document.createElement("option");
          opt.value = c;
          opt.textContent = c;
          elCat.appendChild(opt);
        }
      }

      function renderItems() {
        const q = (elSearch.value || "").trim().toLowerCase();
        const cat = elCat.value;
        const stats = getStats();

        const filtered = ITEMS.filter(i => {
          const okCat = (cat === "all") || (i.category === cat);
          const okQ = !q || (i.name.toLowerCase().includes(q));
          return okCat && okQ;
        });

        elItems.innerHTML = "";

        for (const item of filtered) {
          const miss = unmetReq(item, stats);
          const canBuy = miss.length === 0;

          const card = document.createElement("article");
          card.className = "shop__card";
          card.dataset.id = item.id;

          const imgHtml = item.image
            ? `<div class="shop__img"><img src="${item.image}" alt=""></div>`
            : `<div class="shop__img shop__img--ph">нет картинки</div>`;

          card.innerHTML = `
            ${imgHtml}
            <div class="shop__body">
              <div class="shop__titleRow">
                <div class="shop__title">
                  <strong>${item.name}</strong>
                  <div class="shop__meta">${item.classRank} • ${item.category}</div>
                </div>
                <div class="shop__price">${fmt(item.price)} рё</div>
              </div>

              <ul class="shop__statsList">
                ${(item.stats || []).map(s => `<li>${s}</li>`).join("")}
              </ul>

              <div class="shop__req">
                ${
                  Object.keys(item.req || {}).length
                    ? `<div class="shop__reqLine"><span class="shop__muted">Требования:</span> ${
                        Object.entries(item.req).map(([k,v]) => `${labelStat(k)} ≥ ${v}`).join(", ")
                      }</div>`
                    : `<div class="shop__reqLine shop__muted">Требования: —</div>`
                }
                ${
                  canBuy
                    ? `<div class="shop__ok">Можно купить</div>`
                    : `<div class="shop__bad">Не хватает: ${miss.join(", ")}</div>`
                }
              </div>

              <div class="shop__actions">
                <button type="button" class="shop__btnAdd" ${canBuy ? "" : "disabled"}>
                  Добавить
                </button>
              </div>
            </div>
          `;

          card.querySelector(".shop__btnAdd").addEventListener("click", () => addToCart(item.id, 1));
          elItems.appendChild(card);
        }
      }

      function renderCart() {
        if (cart.size === 0) {
          elCartEmpty.hidden = false;
          elCartWrap.hidden = true;
          elOut.value = "";
          return;
        }

        elCartEmpty.hidden = true;
        elCartWrap.hidden = false;

        elCartRows.innerHTML = "";
        let total = 0;

        for (const [id, row] of cart.entries()) {
          const item = ITEMS.find(i => i.id === id);
          if (!item) continue;

          const line = item.price * row.qty;
          total += line;

          const tr = document.createElement("tr");
          tr.innerHTML = `
            <td>
              <div><strong>${item.name}</strong></div>
              <div class="shop__muted">${item.classRank} • ${item.category}</div>
            </td>
            <td>
              <div class="shop__qty">
                <button type="button" data-act="dec">−</button>
                <input type="number" min="1" value="${row.qty}">
                <button type="button" data-act="inc">+</button>
              </div>
            </td>
            <td>${fmt(line)} рё</td>
            <td><button type="button" data-act="del">✕</button></td>
          `;

          const qtyInput = tr.querySelector('input[type="number"]');
          tr.querySelector('[data-act="dec"]').addEventListener("click", () => setQty(id, row.qty - 1));
          tr.querySelector('[data-act="inc"]').addEventListener("click", () => setQty(id, row.qty + 1));
          qtyInput.addEventListener("change", () => setQty(id, Number(qtyInput.value || 1)));
          tr.querySelector('[data-act="del"]').addEventListener("click", () => removeFromCart(id));

          elCartRows.appendChild(tr);
        }

        elCartTotal.textContent = fmt(total);
        elOut.value = buildPostText(total);
      }

      function buildPostText(total) {
        const lines = [];
        const ep = (elEpisode.value || "").trim();
        if (ep) lines.push(ep);

        lines.push("Покупка:");
        for (const [id, row] of cart.entries()) {
          const item = ITEMS.find(i => i.id === id);
          if (!item) continue;
          const sum = item.price * row.qty;
          // если в названии уже есть комплект/шт — не выдумываем единицы
          lines.push(`${row.qty} x ${item.name} = ${fmt(sum)} рё`);
        }
        lines.push(`Итог - ${fmt(total)}`);
        return lines.join("\n");
      }

      // ====== CART OPS ======
      function addToCart(id, qty) {
        const cur = cart.get(id)?.qty || 0;
        cart.set(id, { qty: cur + qty });
        renderCart();
      }
      function setQty(id, qty) {
        if (qty <= 0) cart.delete(id);
        else cart.set(id, { qty: Math.max(1, Math.floor(qty)) });
        renderCart();
      }
      function removeFromCart(id) {
        cart.delete(id);
        renderCart();
      }

      // ====== EVENTS ======
      function onFilterChange() { renderItems(); }
      elSearch.addEventListener("input", onFilterChange);
      elCat.addEventListener("change", onFilterChange);

      for (const k of statIds) statInputs[k].addEventListener("input", () => { renderItems(); });

      elClear.addEventListener("click", () => {
        cart.clear();
        renderCart();
      });

      elEpisode.addEventListener("input", () => {
        // обновлять текст внизу, если корзина не пуста
        if (cart.size) elOut.value = buildPostText(Number(elCartTotal.textContent.replace(/\s/g,"")) || 0);
      });

      elCopy.addEventListener("click", async () => {
        const txt = elOut.value || "";
        try {
          await navigator.clipboard.writeText(txt);
          elCopy.textContent = "Скопировано";
          setTimeout(() => (elCopy.textContent = "Скопировать текст покупки"), 900);
        } catch (e) {
          // fallback
          elOut.focus();
          elOut.select();
          document.execCommand("copy");
        }
      });

      // ====== INIT ======
      renderCategoryOptions();
      renderItems();
      renderCart();
    })();
    </script>
    [/html]

    0


    Вы здесь » MadHat's Test » Новый форум » магазин


    Рейтинг форумов | Создать форум бесплатно