index.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?php
  2. $blogs = DB::selectAll("select blogs.id, title, poster, post_time from important_blogs, blogs where is_hidden = 0 and important_blogs.blog_id = blogs.id order by level desc, important_blogs.blog_id desc limit 8");
  3. $activateusers = DB::selectAll("select submitter, count(*) as cnt from submissions where datediff(now(), submit_time) <= 30 group by submitter order by cnt desc limit 12;");
  4. $topusers = DB::selectAll("select u.username, u.rating, u.rating_rd, u.rating_vol, u.motto, u.qq, count(*) cnt from user_info u inner join contests_registrants c on u.username = c.username inner join contests s on s.id = c.contest_id where datediff(now(), s.start_time) <= 180 group by c.username having cnt >= 3 order by rating desc, username asc limit 12;");
  5. $hotusers = DB::selectAll("select username, rating, rating_rd, rating_vol, qq, motto from user_info order by rating_vol desc, username asc limit 12;");
  6. $topblogs = DB::selectAll("select id from blogs order by zan desc limit 20;");
  7. $songs_data = htmlspecialchars(json_encode(DB::selectAll("select * from songs;")), ENT_QUOTES, 'UTF-8');
  8. // 最近比赛:未结束的按开始时间升序取,不足 5 场时补最近结束的
  9. $recent_contests = array();
  10. $result = DB::selectAll("select * from contests where status != 'finished' order by start_time asc, id asc limit 5");
  11. foreach ($result as $contest) {
  12. genMoreContestInfo($contest);
  13. $recent_contests[] = $contest;
  14. }
  15. if (count($recent_contests) < 5) {
  16. $need = 5 - count($recent_contests);
  17. $result = DB::selectAll("select * from contests where status = 'finished' order by start_time desc, id desc limit $need");
  18. foreach ($result as $contest) {
  19. genMoreContestInfo($contest);
  20. $recent_contests[] = $contest;
  21. }
  22. }
  23. ?>
  24. <?php echoUOJPageHeader(UOJConfig::$data['profile']['oj-name-short']) ?>
  25. <div id="songs-data" data-songs='<?php echo $songs_data; ?>'></div>
  26. <div class="row my-4 wyoj-topcard">
  27. <div class="card-body">
  28. <div class="row">
  29. <div class="col-sm-12 col-md-9">
  30. <table class="table table-sm">
  31. <tbody>
  32. <?php $now_cnt = 0; ?>
  33. <?php foreach ($blogs as $blog): ?>
  34. <?php
  35. $now_cnt++;
  36. $new_tag = '';
  37. if ((time() - strtotime($blog['post_time'])) / 3600 / 24 <= 7) {
  38. $new_tag = '<sup style="color:red">&nbsp;new</sup>';
  39. }
  40. ?>
  41. <tr>
  42. <td><a href="/blogs/<?= $blog['id'] ?>"><?= $blog['title'] ?></a><?= $new_tag ?></td>
  43. <td>by <?= getUserLink($blog['poster']) ?></td>
  44. <td><small><?= $blog['post_time'] ?></small></td>
  45. </tr>
  46. <?php endforeach ?>
  47. <?php for ($i = $now_cnt + 1; $i <= 8; $i++): ?>
  48. <tr><td colspan="233">&nbsp;</td></tr>
  49. <?php endfor ?>
  50. <tr><td class="text-right" colspan="233">
  51. <span>本歌曲由 </span><span id="recommender"></span><span> 推荐</span>
  52. </td></tr>
  53. <tr><td class="text-right" colspan="233">
  54. <a href="https://git.ryp.org.cn/wyoj/" class="text-info font-weight-bold">WyOJ 以及所有相关项目均为自由软件。</a>
  55. <a href="/announcements"><?= UOJLocale::get('all the announcements') ?></a>
  56. </td></tr>
  57. <tr>
  58. <td class="text-right" colspan="233">
  59. <span class="text-danger font-weight-bold">声明:本网站属个人非经营性网站,仅用于学习交流,无任何盈利目的,与任何组织无关</span>
  60. <a href="/certs-manage/songs">我要推歌</a>
  61. </td>
  62. </tr>
  63. </tbody>
  64. </table>
  65. </div>
  66. <div class="col-xs-6 col-sm-4 col-md-3">
  67. <div class="card" style="max-width:300px; width:100%; border-radius:0; ">
  68. <div class="image-frame" style="aspect-ratio:1/1; position:relative;">
  69. <img id="photo" src="" alt="" style="width:100%; height:100%; object-fit:cover; display:block; cursor:pointer; -webkit-tap-highlight-color:transparent;">
  70. </div>
  71. <div class="intro" style="padding:1.6rem 1.8rem 1.2rem; text-align:center; background:#ffffff;">
  72. <p id="name" style="font-size:1.0rem; font-weight:800; letter-spacing:1px; margin-bottom:0.5rem; line-height:1.2; margin-block:0;"></p>
  73. <p id="amb" style="font-size:0.7rem; font-weight:400; color:#4f5d69; margin-bottom:0.25rem; line-height:1.5; border-top:1px dashed #cdd9e3; padding-top:0.8rem; white-space:pre-wrap; margin-block:0;"></p>
  74. <p id="writer" style="font-size:0.7rem; font-weight:400; color:#4f5d69; margin-bottom:0.25rem; line-height:1.5; white-space:pre-wrap; margin-block:0;"></p>
  75. </div>
  76. <div class="button-wrapper" style="padding:0.2rem 1.8rem 2rem 1.8rem; background:#ffffff;">
  77. <style>
  78. #randomBtn{
  79. display: inline-flex;
  80. align-items: center;
  81. justify-content: center;
  82. gap: 0.6rem;
  83. width: 100%;
  84. background: #1f2f3b;
  85. color: white;
  86. padding: 0.7rem 0;
  87. cursor: pointer;
  88. transition: all 0.2s ease;
  89. letter-spacing: 0.5px;
  90. border: 1px solid rgba(255, 255, 255, 0.2);
  91. backdrop-filter: blur(4px);
  92. font-family: inherit;
  93. line-height: 1;
  94. }
  95. </style>
  96. <button id="randomBtn">换一首</button>
  97. </div>
  98. </div>
  99. <script>
  100. (function() {
  101. let dataDiv = document.getElementById('songs-data');
  102. let library = JSON.parse(dataDiv.dataset.songs);
  103. const photoImg = document.getElementById('photo');
  104. const nameEl = document.getElementById('name');
  105. const ambEl = document.getElementById('amb');
  106. const writerEl = document.getElementById('writer');
  107. const randomBtn = document.getElementById('randomBtn');
  108. const recommenderEl = document.getElementById('recommender');
  109. let lastIndex = -1;
  110. let currentMusicLink = library[0].musiclink;
  111. photoImg.addEventListener('mousedown', function(e) {
  112. this.style.opacity = '0.8';
  113. });
  114. photoImg.addEventListener('mouseup', function(e) {
  115. this.style.opacity = '';
  116. });
  117. photoImg.addEventListener('mouseleave', function(e) {
  118. this.style.opacity = '';
  119. });
  120. photoImg.addEventListener('touchstart', function(e) {
  121. this.style.opacity = '0.8';
  122. });
  123. photoImg.addEventListener('touchend', function(e) {
  124. this.style.opacity = '';
  125. });
  126. photoImg.addEventListener('touchcancel', function(e) {
  127. this.style.opacity = '';
  128. });
  129. function setBtnActive() {
  130. randomBtn.style.background = '#17242e';
  131. randomBtn.style.transform = 'scale(0.98)';
  132. randomBtn.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)';
  133. }
  134. function setBtnNormal() {
  135. randomBtn.style.background = '#1f2f3b';
  136. randomBtn.style.transform = 'scale(1)';
  137. randomBtn.style.boxShadow = 'none';
  138. }
  139. randomBtn.addEventListener('mousedown', setBtnActive);
  140. randomBtn.addEventListener('mouseup', setBtnNormal);
  141. randomBtn.addEventListener('mouseleave', setBtnNormal);
  142. randomBtn.addEventListener('touchstart', setBtnActive);
  143. randomBtn.addEventListener('touchend', setBtnNormal);
  144. randomBtn.addEventListener('touchcancel', setBtnNormal);
  145. photoImg.addEventListener('click', function() {
  146. if (currentMusicLink) {
  147. window.open(currentMusicLink, '_blank');
  148. }
  149. });
  150. function randmusic() {
  151. let randid = lastIndex;
  152. while (randid === lastIndex) {
  153. randid = Math.floor(Math.random() * library.length);
  154. }
  155. if (lastIndex == -1) randid = 3;
  156. lastIndex = randid;
  157. const ys = library[randid];
  158. photoImg.src = ys.cover;
  159. photoImg.alt = ys.name;
  160. nameEl.textContent = ys.name;
  161. ambEl.textContent = ys.album;
  162. writerEl.textContent = ys.writer;
  163. recommenderEl.textContent = ys.user;
  164. currentMusicLink = ys.click;
  165. }
  166. randmusic();
  167. randomBtn.addEventListener('click', function() {
  168. randmusic();
  169. });
  170. })();
  171. </script>
  172. </div>
  173. </div>
  174. </div>
  175. </div>
  176. <?php
  177. // ---- 日期 ----
  178. $todayTs = time();
  179. $dayNum = (int)date('j', $todayTs);
  180. $monthNum = (int)date('n', $todayTs);
  181. $wday = (int)date('w', $todayTs);
  182. $monthCN = ['一','二','三','四','五','六','七','八','九','十','十一','十二'];
  183. $weekCN = ['日','一','二','三','四','五','六'];
  184. $monthText = $monthCN[$monthNum - 1] . '月';
  185. $weekText = '星期' . $weekCN[$wday];
  186. // ---- NOIP 倒计时 ----
  187. $noipTs = strtotime('2026-11-28');
  188. $todayMidnight = strtotime(date('Y-m-d', $todayTs));
  189. $daysLeft = (int)floor(($noipTs - $todayMidnight) / 86400);
  190. ?>
  191. <?php
  192. $todayTs = time();
  193. $dayNum = (int)date('j', $todayTs);
  194. $monthNum = (int)date('n', $todayTs);
  195. $wday = (int)date('w', $todayTs);
  196. $monthCN = ['一','二','三','四','五','六','七','八','九','十','十一','十二'];
  197. $weekCN = ['日','一','二','三','四','五','六'];
  198. $bigMonths = [1,3,5,7,8,10,12];
  199. if ($monthNum == 2) {
  200. $sizeChar = '平'; // 闰年可改成 '闰'
  201. } elseif (in_array($monthNum, $bigMonths)) {
  202. $sizeChar = '大';
  203. } else {
  204. $sizeChar = '小';
  205. }
  206. $monthText = $monthCN[$monthNum - 1] . '月' . $sizeChar;
  207. $weekText = '星期' . $weekCN[$wday];
  208. $todayMidnight = strtotime(date('Y-m-d', $todayTs));
  209. $noipTs = strtotime('2026-11-28');
  210. $daysLeft = max(0, (int)floor(($noipTs - $todayMidnight) / 86400));
  211. $csj1Ts = strtotime('2026-09-19');
  212. $csp1daysLeft = max(0, (int)floor(($csp1Ts - $todayMidnight) / 86400));
  213. $csp2Ts = strtotime('2026-10-31');
  214. $csp2daysLeft = max(0, (int)floor(($csp2Ts - $todayMidnight) / 86400));
  215. ?>
  216. <div class="row my-4">
  217. <!-- ============ 左列:日程 + 博客 ============ -->
  218. <div class="col-12 col-lg-9">
  219. <!-- 黑色日期卡片 -->
  220. <div class="mb-4"
  221. style="background:#000;color:#fff;padding:2rem 1.5rem;border-radius:0;">
  222. <div class="d-flex justify-content-center align-items-center">
  223. <div style="writing-mode:vertical-rl;font-size:1.5rem;font-weight:bold;letter-spacing:0.3em;line-height:1;">
  224. <?= $monthText ?>
  225. </div>
  226. <div class="mx-4" style="font-size:5rem;font-weight:600;line-height:1;">
  227. <?= $dayNum ?>
  228. </div>
  229. <div style="writing-mode:vertical-rl;font-size:1.5rem;font-weight:bold;letter-spacing:0.3em;line-height:1;">
  230. <?= $weekText ?>
  231. </div>
  232. </div>
  233. <div class="text-center mt-4" style="font-size:1rem;letter-spacing:0.05em;">
  234. <?php if ($daysLeft > 0): ?>
  235. 距 <strong>NOIP2026</strong> 还有 <strong><?= $daysLeft ?></strong> 天
  236. <?php elseif ($daysLeft === 0): ?>
  237. <strong>NOIP2026</strong> 就是今天
  238. <?php else: ?>
  239. <strong>NOIP2026</strong> 已结束
  240. <?php endif; ?>
  241. <br/>
  242. <?php if ($csp1daysLeft > 0): ?>
  243. 距 <strong>CSP-J/S 2026 第一轮</strong> 还有 <strong><?= $csp1daysLeft ?></strong> 天
  244. <?php elseif ($csp1daysLeft === 0): ?>
  245. <strong>CSP-J/S 2026 第一轮</strong> 就是今天
  246. <?php else: ?>
  247. <strong>CSP-J/S 2026 第一轮</strong> 已结束
  248. <?php endif; ?>
  249. <br/>
  250. <?php if ($csp2daysLeft > 0): ?>
  251. 距 <strong>CSP-J/S 2026 第二轮</strong> 还有 <strong><?= $csp2daysLeft ?></strong> 天
  252. <?php elseif ($csp2daysLeft === 0): ?>
  253. <strong>CSP-J/S 2026 第二轮</strong> 就是今天!
  254. <?php else: ?>
  255. <strong>CSP-J/S 2026 第二轮</strong> 已结束
  256. <?php endif; ?>
  257. </div>
  258. </div>
  259. <!-- 博客列表 -->
  260. <div class="row">
  261. <?php foreach ($topblogs as $blog): ?>
  262. <?= echoBlogCard(queryBlog($blog['id'])) ?>
  263. <?php endforeach ?>
  264. </div>
  265. </div>
  266. <!-- ============ 右列:排行榜 ============ -->
  267. <div class="col-12 col-lg-3">
  268. <!-- 活跃 -->
  269. <div class="card shadow-sm border-0 mb-3">
  270. <div class="card-body p-3">
  271. <h6 class="font-weight-bold mb-3">活跃排行榜</h6>
  272. <?php foreach ($activateusers as $username): ?>
  273. <?php $user = queryUser($username['submitter']); ?>
  274. <div class="d-flex align-items-start border-bottom pb-2 mb-2">
  275. <img class="uoj-avatar rounded-circle flex-shrink-0 mr-2"
  276. style="width:36px;height:36px;object-fit:cover;"
  277. src="https://q.qlogo.cn/g?b=qq&nk=<?= $user['qq'] ?>&s=100" alt="...">
  278. <div class="flex-grow-1" style="min-width:0;">
  279. <div class="text-truncate font-weight-bold" style="font-size:0.9rem;">
  280. <?= getUserLink($user['username']) ?>
  281. </div>
  282. <div class="text-truncate" style="font-size:0.8rem;font-family:'SimSun','宋体','Songti SC','STSong',serif;">
  283. <?= $user['motto'] ?>
  284. </div>
  285. </div>
  286. </div>
  287. <?php endforeach ?>
  288. </div>
  289. </div>
  290. <!-- 比赛 -->
  291. <div class="card shadow-sm border-0 mb-3">
  292. <div class="card-body p-3">
  293. <h6 class="font-weight-bold mb-1">比赛排行榜</h6>
  294. <div class="mb-3" style="font-size:0.75rem;">
  295. <span class="text-muted">180 天内三场及以上</span>
  296. <a href="/ranklist" class="ml-1">全部排名</a>
  297. </div>
  298. <?php foreach ($topusers as $user): ?>
  299. <div class="d-flex align-items-start border-bottom pb-2 mb-2">
  300. <img class="uoj-avatar rounded-circle flex-shrink-0 mr-2"
  301. style="width:36px;height:36px;object-fit:cover;"
  302. src="https://q.qlogo.cn/g?b=qq&nk=<?= $user['qq'] ?>&s=100" alt="...">
  303. <div class="flex-grow-1" style="min-width:0;">
  304. <div class="d-flex justify-content-between align-items-baseline">
  305. <span class="text-truncate font-weight-bold" style="font-size:0.9rem;">
  306. <?= getUserLink($user['username']) ?>
  307. </span>
  308. <strong class="text-danger ml-1 flex-shrink-0" style="font-size:0.75rem;">
  309. <?= $user['rating'] ?> <small>(±<?= round($user['rating_rd']) * 2 ?>)</small>
  310. </strong>
  311. </div>
  312. <div class="text-truncate" style="font-size:0.8rem;font-family:'SimSun','宋体','Songti SC','STSong',serif;">
  313. <?= $user['motto'] ?>
  314. </div>
  315. </div>
  316. </div>
  317. <?php endforeach ?>
  318. </div>
  319. </div>
  320. <!-- 黑马 -->
  321. <div class="card shadow-sm border-0 mb-3">
  322. <div class="card-body p-3">
  323. <h6 class="font-weight-bold mb-3">黑马 / 爆冷榜</h6>
  324. <?php foreach ($hotusers as $user): ?>
  325. <div class="d-flex align-items-start border-bottom pb-2 mb-2">
  326. <img class="uoj-avatar rounded-circle flex-shrink-0 mr-2"
  327. style="width:36px;height:36px;object-fit:cover;"
  328. src="https://q.qlogo.cn/g?b=qq&nk=<?= $user['qq'] ?>&s=100" alt="...">
  329. <div class="flex-grow-1" style="min-width:0;">
  330. <div class="d-flex justify-content-between align-items-baseline">
  331. <span class="text-truncate font-weight-bold" style="font-size:0.9rem;">
  332. <?= getUserLink($user['username']) ?>
  333. </span>
  334. <strong class="text-danger ml-1 flex-shrink-0" style="font-size:0.75rem;">
  335. Vol <?= round(1000 * $user['rating_vol']) / 1000 ?>
  336. </strong>
  337. </div>
  338. <div class="text-truncate" style="font-size:0.8rem;font-family:'SimSun','宋体','Songti SC','STSong',serif;">
  339. <?= $user['motto'] ?>
  340. </div>
  341. </div>
  342. </div>
  343. <?php endforeach ?>
  344. </div>
  345. </div>
  346. </div>
  347. </div>
  348. <!-- 博客卡片悬停效果 -->
  349. <style>
  350. .wyoj-blog-card {
  351. transition: box-shadow .2s ease;
  352. }
  353. .wyoj-blog-card:hover {
  354. box-shadow: 0 4px 14px rgba(0,0,0,.10) !important;
  355. }
  356. .wyoj-blog-card .wyoj-blog-title {
  357. font-size: 1.05rem;
  358. line-height: 1.5;
  359. }
  360. </style>
  361. <?php echoUOJPageFooter() ?>