Дата: Среда, 07.01.2026, 11:10 | Сообщение # 1 |
|
Написал: Узнаваемый
Автор темы
Мурчанн
не в сети
Сообщений: 162
Да, это финальная версия модуля - скрипта, который выводит подарки на главную страницу.Хочу объяснить, почему я называю его последним. Разместить кнопки редактирования или удалить невозможно для обычных пользователей: эта функция предусмотрена только для администрации, поэтому смысла в ней нет. Я создал версию скрипта с такой возможностью, но я решил её убрать, чтобы модуль стал легче и быстрее. Вывод подарков в стиле «ВКонтакте» тоже не полностью реализован: я пытался привязать последовательно к дате, чтобы отображались только последние подарки, но скрипт постоянно путался и показывал либо чужие аватарки, либо сами подарки некорректно. Я потратил на это много времени, но сделать продвинутый вариант так и не получилось.Поэтому, скорее всего, этот модуль будет последним: он уже успел «подпортить мне нервы неслабо». Передаю эстафету: пробуйте, меняйте дизайн, меняйте логику, добавляйте новые функции, прокачивайте.Если что-то непонятно - смотрите исходный код: Код
<!-- ======= Блок подарков ======= --> <div class="gifts-block"> <div class="gifts-header">Подарки</div> <div class="gifts-footer" id="gifts-count">Загрузка…</div> <div class="gifts-container" id="gifts-container"></div> </div> <style> .gifts-block { background: #fff; border: 1px solid #d1d5da; font-family: Tahoma, Arial, sans-serif; font-size: 13px; color: #000; max-width: 600px; } .gifts-header { background: #f0f2f5; padding: 6px 8px; border-bottom: 1px solid #d1d5da; color: #45688e; font-weight: bold; } .gifts-footer { background: #f7f7f7; border-top: 1px solid #d1d5da; padding: 5px 8px; color: #666; } .gifts-container { display: flex; gap: 10px; padding: 10px 8px; flex-direction: row-reverse; /* последние подарки справа налево */ } .gift-card { width: 140px; text-align: center; position: relative; cursor: pointer; } .gift-card img { width: 90px; height: 90px; border-radius: 0%; object-fit: cover; margin: 0 auto; transition: .25s ease; } .gift-card::after { content: ''; position: absolute; inset: 0; border-radius: 5%; background: rgba(0,0,0,.25); opacity: 0; transition: .25s ease; } .gift-card:hover::after { opacity: 0.1; } .vk-reply a { display: inline-block; text-decoration: none; color: #fff; /* белый текст */ background: linear-gradient( 145deg, #4a76a8, /* светлый синий */ #3f6aa0, #365d8c, /* основной синий */ #2d4b70, #25395a /* темный синий */ ); /* плавный градиент в стиле современного ВК */ border: 1px solid #2e4a6b; padding: 10px 20px; /* чуть больше паддинга */ border-radius: 50px; /* супер скругление */ cursor: pointer; font-weight: 600; font-size: 13px; transition: all 0.3s ease; /* плавная анимация */ box-shadow: 0 2px 6px rgba(0,0,0,0.2); } /* Hover эффект */ .vk-reply a:hover { background: linear-gradient( 145deg, #5a88b5, #4d79a7, #3f6aa0, #365d8c, #2d4b70 ); box-shadow: 0 6px 12px rgba(0,0,0,0.3); transform: translateY(0px); /* эффект поднятия */ } .vk-comment { position: relative; margin-top: 12px; font-size: 13px; color: #333; background: #f5f7fa; padding: 8px 12px; border-radius: 8px; border: 1px solid #dce1e6; max-width: 400px; } /* Хвостик сверху */ .vk-comment::before { content: ""; position: absolute; top: -8px; /* сдвигаем над блоком */ left: 3px; /* позиция от левого края блока */ width: 0px; height: 0px; border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 8px solid #f5f7fa; /* тот же цвет фона */ } </style> <script> (async function(){ const container = document.getElementById('gifts-container'); const counter = document.getElementById('gifts-count'); const userId = <?$JSENCODE$($USER_ID$)?>; const STORAGE_KEY = `giftsData_${userId}`; let allGifts = []; /* ======================= СТИЛИ POPUP (СТАРЫЙ ВК) ======================= */ const style = document.createElement('style'); style.innerHTML = ` .gifts-popup-overlay{position:fixed; inset:0; background:rgba(0,0,0,.45); display:flex; align-items:center; justify-content:center; z-index:9999;} .gifts-popup{background:#fff; width:520px; max-height:75vh; border-radius:6px; overflow:hidden; display:flex; flex-direction:column; box-shadow:0 10px 40px rgba(0,0,0,.35); font-family:Tahoma,Arial,sans-serif;} .gifts-popup-header{background:#f0f2f5; padding:10px 14px; font-weight:bold; color:#2a5885; border-bottom:1px solid #dce1e6; display:flex; justify-content:space-between; align-items:center;} .gifts-popup-header span{cursor:pointer; font-size:18px; color:#666;} .gifts-popup-list{padding:14px; overflow-y:auto;} .vk-gift{padding-bottom:20px; margin-bottom:20px; border-bottom:1px solid #eee; position:relative;} .vk-user{display:flex; gap:10px; align-items:center; margin-bottom:10px;} .vk-user img{width:55px; height:55px; border-radius:50%; margin-top:-18px;} .vk-user .name{font-weight:bold; color:#2a5885; font-size:13px;} .vk-user .date{font-size:11px; color:#777;} .vk-comment{margin-top:6px; font-size:12px; color:#333; background:#f5f7fa; padding:6px 8px; border-radius:6px;} .vk-gift-img{text-align:center; margin:10px 0;} .vk-gift-img img{max-width:240px;} .vk-reply{text-align:center;} .vk-reply a{display:inline-block; text-decoration:none; color:#2a5885; background:#e9eff6; border:1px solid #c5d0db; padding:6px 14px; border-radius:4px; cursor:pointer;} `; document.head.appendChild(style); /* ======================= ЗАГРУЗКА ПОДАРКОВ ======================= */ async function loadGifts(){ allGifts = []; container.innerHTML = ''; try{ const res = await fetch(`/index/54-${userId}-`); const text = await res.text(); const xml = new DOMParser().parseFromString(text,'application/xml'); const cmd = xml.querySelector('cmd[p="content"]'); if(!cmd){ counter.textContent='0 подарков'; return; } const temp = document.createElement('div'); temp.innerHTML = cmd.textContent; const tds = [...temp.querySelectorAll('td[onclick*="_uWnd.reload"]')]; if(!tds.length){ counter.textContent='0 подарков'; return; } let total = 0; for(const td of tds){ const img = td.querySelector('img'); if(!img) continue; const count = td.querySelector('b') ? +td.querySelector('b').textContent || 1 : 1; total += count; const onclick = td.getAttribute('onclick'); for(let i=0;i<count;i++){ allGifts.push({ img: img.src, onclick, nick: 'Загрузка...', avatar: '/.s/src/profile/img/profile_photo_thumbnail.png', date: '', comment: '', profile: '#', recipientId: null }); } } counter.textContent = total + (total===1?' подарок':total<5?' подарка':' подарков'); // Вывод последних 3 подарков const cached = JSON.parse(localStorage.getItem(STORAGE_KEY)||'[]'); allGifts.forEach(g=>{ const saved = cached.find(c=>c.img===g.img && c.profile===g.profile); if(saved){ g.nick = saved.nick; g.avatar = saved.avatar; g.date = saved.date; g.comment = saved.comment; g.profile = saved.profile; g.recipientId = saved.recipientId; } }); allGifts.forEach(g=>{ if(!g.date) g.date = new Date().toISOString(); }); allGifts.sort((a,b)=> new Date(b.date) - new Date(a.date)); allGifts.slice(0,3).forEach(g=>{ const div = document.createElement('div'); div.className='gift-card'; div.title = g.nick; div.innerHTML = `<img src="${g.img}" alt="">`; container.appendChild(div); }); }catch(e){ console.error(e); counter.textContent='Ошибка загрузки'; } } /* ======================= POPUP ======================= */ async function showPopup(){ if(!allGifts.length) return; const overlay = document.createElement('div'); overlay.className='gifts-popup-overlay'; const popup = document.createElement('div'); popup.className='gifts-popup'; popup.innerHTML=` <div class="gifts-popup-header"> Мои подарки <span>×</span> </div> <div class="gifts-popup-list"></div> `; overlay.appendChild(popup); document.body.appendChild(overlay); popup.querySelector('span').onclick = ()=>overlay.remove(); overlay.onclick = e=>{ if(e.target===overlay) overlay.remove(); }; const list = popup.querySelector('.gifts-popup-list'); let cached = JSON.parse(localStorage.getItem(STORAGE_KEY)||'[]'); allGifts.forEach((g, idx)=>{ const saved = cached[idx]; if(saved){ g.nick = saved.nick; g.avatar = saved.avatar; g.date = saved.date; g.comment = saved.comment || ''; g.profile = saved.profile || '#'; g.recipientId = saved.recipientId || null; } if(!g.recipientId && g.onclick){ const match = g.onclick.match(/url:'\/index\/55-(\d+)'/); if(match) g.recipientId = match[1]; cached[idx] = {...g}; localStorage.setItem(STORAGE_KEY, JSON.stringify(cached)); } const div = document.createElement('div'); div.className='vk-gift'; div.dataset.idx=idx; div.innerHTML=` <div class="vk-user"> <img src="${g.avatar}"> <div> <div class="name">${g.nick}</div> <div class="date">${g.date}</div> ${g.comment ? `<div class="vk-comment">${g.comment}</div>` : ''} </div> </div> <div class="vk-gift-img"><img src="${g.img}"></div> <div class="vk-reply"></div> `; // Кнопки редактирования и удаления убраны const replyDiv = div.querySelector('.vk-reply'); replyDiv.innerHTML=''; if(g.recipientId && g.recipientId != userId){ const btn = document.createElement('a'); btn.href="javascript:;"; btn.textContent='🎁 Вручить подарок'; btn.onclick = ()=>new _uWnd('AwD','Вручить награду',380,200, {maxh:300,minh:100,closeonesc:1}, {url:'/index/55-'+g.recipientId}); replyDiv.appendChild(btn); } else { replyDiv.textContent = 'Нельзя вручить себе'; } list.appendChild(div); }); setTimeout(async ()=>{ const newData = []; for(const [idx,g] of allGifts.entries()){ try{ const urlMatch = g.onclick.match(/url:'([^']+)'/); if(!urlMatch) continue; const giftPage = await fetch(urlMatch[1]).then(r=>r.text()); const giftDoc = new DOMParser().parseFromString(giftPage,'text/html'); const legend = giftDoc.querySelector('legend a'); g.nick = legend ? legend.textContent.trim() : g.nick; g.profile = legend ? legend.href : g.profile; g.comment = giftDoc.querySelector('[id^="mtx"]')?.textContent.trim() || g.comment; g.date = giftDoc.querySelector('div[style*="text-align:right"]')?.textContent.trim() || g.date; if(g.profile && g.profile!=='#'){ const prof = await fetch(g.profile).then(r=>r.text()); const pdoc = new DOMParser().parseFromString(prof,'text/html'); const photoDiv = pdoc.querySelector('.profile-photo'); if(photoDiv){ const bg = photoDiv.style.backgroundImage; if(bg && bg!=='none'){ g.avatar = bg.replace(/^url\(["']?|["']?\)$/g,''); } } const match = g.profile.match(/-(\d+)$/); if(match) g.recipientId = match[1]; } const giftDiv = list.children[idx]; giftDiv.querySelector('.name').textContent = g.nick; giftDiv.querySelector('.date').textContent = g.date; giftDiv.querySelector('.vk-user img').src = g.avatar; let commentDiv = giftDiv.querySelector('.vk-comment'); if(!commentDiv && g.comment){ commentDiv = document.createElement('div'); commentDiv.className='vk-comment'; giftDiv.querySelector('.vk-user div').appendChild(commentDiv); } if(commentDiv) commentDiv.textContent = g.comment; const replyDiv = giftDiv.querySelector('.vk-reply'); replyDiv.innerHTML=''; if(g.recipientId && g.recipientId!=userId){ const btn = document.createElement('a'); btn.href="javascript:;"; btn.textContent='🎁 Вручить подарок'; btn.onclick = ()=>new _uWnd('AwD','Вручить награду',380,200, {maxh:300,minh:100,closeonesc:1}, {url:'/index/55-'+g.recipientId}); replyDiv.appendChild(btn); } else { replyDiv.textContent='Нельзя вручить себе'; } newData.push({...g}); }catch(e){console.error(e);} } localStorage.setItem(STORAGE_KEY, JSON.stringify(newData)); },100); } counter.style.cursor='pointer'; counter.onclick=showPopup; loadGifts(); })(); </script>
Признаюсь, не знаю почему, но глядя на звезды мне всегда хочется мечтать.