detail.ejs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <!DOCTYPE html>
  2. <html lang="<%= lang %>">
  3. <head>
  4. <%- include('common-meta') %>
  5. <meta property="og:image" content="<%= imageUrl %>" />
  6. <link rel="stylesheet" href="/stylesheets/styles.css">
  7. <link rel="stylesheet" href="/stylesheets/header.css">
  8. <link rel="stylesheet" href="/stylesheets/detail.css">
  9. <script type="text/javascript"
  10. src="https://platform-api.sharethis.com/js/sharethis.js#property=67e0d66a54a3d000192a4615&product=inline-share-buttons&source=platform"
  11. async="async"></script>
  12. <script type="application/ld+json">
  13. {
  14. "@context": "https://schema.org",
  15. "@type": "CreativeWork",
  16. "name": "<%= translate.printableColoringPage[lang] %>: <%= detail.title %>",
  17. "description": "<%= detail.desc %>",
  18. "url": "https://art.pcoloring.com<%= uri %>",
  19. "image": "<%= detail.thumb %>",
  20. "category": "<%= detail.tags[0] %>",
  21. "keywords": "coloring page, <%= detail.title %>, color by number, paint by number, free, printable, <%= detail.tags.join() %>",
  22. "contentRating": "General Audience",
  23. "mainEntityOfPage": "https://art.pcoloring.com<%= uri %>",
  24. "publisher": {
  25. "@type": "Organization",
  26. "name": "JCCY",
  27. "logo": {
  28. "@type": "ImageObject",
  29. "url": "https://art.pcoloring.com/assets/icon/icon.webp"
  30. }
  31. },
  32. "offers": {
  33. "@type": "Offer",
  34. "priceCurrency": "USD",
  35. "price": "0.00",
  36. "eligibleRegion": {
  37. "@type": "Place",
  38. "name": "Worldwide"
  39. },
  40. "url": "https://art.pcoloring.com<%= uri %>"
  41. },
  42. "author": {
  43. "@type": "Person",
  44. "name": "<%= detail.user.username %>"
  45. },
  46. "datePublished": "<%= detail.publishTime %>"
  47. }
  48. </script>
  49. </head>
  50. <!-- Google tag (gtag.js) -->
  51. <script async src="https://www.googletagmanager.com/gtag/js?id=G-JBGGVGLHTP"></script>
  52. <script>
  53. window.dataLayer = window.dataLayer || [];
  54. function gtag() { dataLayer.push(arguments); }
  55. gtag('js', new Date());
  56. gtag('config', 'G-JBGGVGLHTP');
  57. </script>
  58. <body>
  59. <%- include('header') %>
  60. <div class="details">
  61. <div id="poster" class="poster" data-content-id="<%= detail._id %>"><img src="<%= detail.thumb %>" alt="<%= detail.title %>"></div>
  62. <div class="description">
  63. <div style="display: flex; justify-content: space-between;">
  64. <h1><%= detail.title %></h1>
  65. <div class="sharethis-inline-share-buttons"></div>
  66. </div>
  67. <% if (detail.totalStartCount> 0) { %>
  68. <div style="color:gray;">
  69. <%=detail.totalStartCount%>
  70. <%= translate.interested[lang] %>
  71. </div>
  72. <% } %>
  73. <p>
  74. <%= translate.artist[lang] %>: <a href="/<%= lang %>/artist/<%= detail.user._id %>"
  75. class="tag-button">
  76. <%= detail.user.username %>
  77. </a>
  78. </p>
  79. <p>
  80. <%= translate.publishTime[lang] %>: <%= detail.publishTime %>
  81. </p>
  82. <p>
  83. <%= translate.tag[lang] %>:
  84. <% detail.tags.forEach(tag=> { %>
  85. <a href="/<%= lang %>/tag/<%= tag %>" class="tag-button">
  86. <%= tag %>
  87. </a>
  88. <% }); %>
  89. </p>
  90. <p>
  91. <%= detail.copy ?? detail.desc %>
  92. </p>
  93. <div class="button-wrapper">
  94. <a id="playBtn" href="/play/<%= detail._id %>" class="play-button">
  95. <%= translate.play[lang] %>
  96. </a>
  97. <a id="continueBtn" href="/play/<%= detail._id %>" class="play-button"
  98. style="display: none;">
  99. <%= translate.continuex[lang] %>
  100. </a>
  101. <a id="repaintBtn" onclick="onRepaint('<%= detail._id %>')" class="play-button"
  102. style="display: none;">
  103. <%= translate.repaint[lang] %>
  104. </a>
  105. <a id="reviewBtn" href="/play/<%= detail._id %>" class="play-button"
  106. style="background-color: orange; display: none;">
  107. <%= translate.view[lang] %>
  108. </a>
  109. <a id="appBtn" class="play-button" style="background-color: darkolivegreen;">
  110. <%= translate.playOnApp[lang] %>
  111. </a>
  112. <a href="/download/pdf/page/<%= detail._id %>" class="play-button"
  113. style="background-color: lightseagreen;">
  114. <%= translate.download[lang] %>
  115. </a>
  116. <a id="printBtn" onclick="printImage('<%= detail._id %>')" class="play-button"
  117. style="background-color: black;">
  118. <%= translate.print[lang] %>
  119. </a>
  120. <a id="deleteBtn" onclick="onDelete('<%= detail._id %>')" class="play-button"
  121. style="background-color: grey; display: none;">
  122. <%= translate.deletex[lang] %>
  123. </a>
  124. </div>
  125. </div>
  126. </div>
  127. <p style=" display: flex; justify-content: center; color: #777; font-size: 18px; font-weight: 500;">
  128. <%= translate.mayYouLike[lang] %>:
  129. </p>
  130. <div class="content" style="margin-bottom: 40px;">
  131. <div class="image-grid">
  132. <% relates.forEach(item=> { %>
  133. <div data-content-id="<%= item._id %>" class="image-card">
  134. <a href="<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>"></a>
  135. <div class="card-title">
  136. <%= item.title %>
  137. </div>
  138. </div>
  139. <% }); %>
  140. </div>
  141. </div>
  142. <!-- <%- include('pagination') %> -->
  143. <script>
  144. // 重新进入或返回,强制刷新
  145. window.addEventListener('pageshow', function (event) {
  146. if (event.persisted) {
  147. window.location.reload();
  148. }
  149. });
  150. function isMobileDevice() {
  151. return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  152. }
  153. window.onload = function () {
  154. if (isMobileDevice()) {
  155. const printBtn = document.getElementById('printBtn');
  156. printBtn.style.display = 'none'; // 移动端隐藏打印按钮
  157. }
  158. };
  159. document.getElementById('appBtn').addEventListener('click', function () {
  160. const userAgent = navigator.userAgent || navigator.vendor || window.opera;
  161. // Android 检测
  162. if (/android/i.test(userAgent)) {
  163. window.open('https://play.google.com/store/apps/details?id=com.pcoloring.art.puzzle.color.by.number', '_blank');
  164. }
  165. // iOS 检测
  166. else if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
  167. window.open('https://apps.apple.com/gb/app/art-number-coloring-book/id1575480118', '_blank');
  168. }
  169. // 其他操作系统(例如桌面)
  170. else {
  171. // 可以显示一个提示,或者跳转到通用的下载页面
  172. console.log('无法确定操作系统,或者为桌面操作系统');
  173. window.open('https://pcoloring.com/anc/', '_blank');
  174. }
  175. });
  176. async function printImage(id) {
  177. try {
  178. const response = await fetch(`/download/pdf/page/${id}`);
  179. if (!response.ok) {
  180. throw new Error(`HTTP error! status: ${response.status}`);
  181. }
  182. const pdfBlob = await response.blob();
  183. const pdfUrl = URL.createObjectURL(pdfBlob);
  184. const printWindow = window.open(pdfUrl, '_blank');
  185. printWindow.onload = () => {
  186. printWindow.print();
  187. };
  188. URL.revokeObjectURL(pdfUrl); // 释放 URL 对象
  189. } catch (error) {
  190. console.error('Error printing image:', error);
  191. }
  192. }
  193. function onDelete(id) {
  194. if (confirm('确定要删除游戏进度吗?')) {
  195. // 删除本地存储
  196. localStorage.removeItem(id);
  197. const METADATA_KEY = '__storage_metadata__';
  198. const metadata = JSON.parse(localStorage.getItem(METADATA_KEY) || '{}');
  199. delete metadata[id];
  200. localStorage.setItem(METADATA_KEY, JSON.stringify(metadata));
  201. location.reload(); // 刷新页面恢复初始状态
  202. }
  203. }
  204. function onRepaint(id) {
  205. // 删除本地存储
  206. localStorage.removeItem(id);
  207. const METADATA_KEY = '__storage_metadata__';
  208. const metadata = JSON.parse(localStorage.getItem(METADATA_KEY) || '{}');
  209. delete metadata[id];
  210. localStorage.setItem(METADATA_KEY, JSON.stringify(metadata));
  211. window.open(`/play/${id}`, '_self');
  212. }
  213. // 页面加载完成后执行
  214. document.addEventListener('DOMContentLoaded', () => {
  215. const METADATA_KEY = '__storage_metadata__';
  216. const container = document.getElementById('poster');
  217. const contentId = container.dataset.contentId;
  218. // 获取本地存储数据
  219. const metaData = JSON.parse(localStorage.getItem(METADATA_KEY)) || {};
  220. if (metaData[contentId]) {
  221. const progress = Math.round(metaData[contentId].progress);
  222. // 创建进度角标
  223. const badge = document.createElement('div');
  224. badge.className = 'progress-badge';
  225. badge.innerHTML = `<span>${progress >= 100 ? '✓' : progress + '%'}</span>`;
  226. badge.style.backgroundColor = progress >= 100 ? '#4CAF50' : '#FF5252';
  227. // 添加渐变动画
  228. badge.style.transition = 'background-color 0.3s ease';
  229. // 插入到缩略图容器内
  230. container.appendChild(badge);
  231. // 添加删除按钮
  232. const deleteBtn = document.getElementById('deleteBtn');
  233. deleteBtn.style.display = 'block';
  234. // 开始填色按钮便成继续填色或重新填色
  235. const playBtn = document.getElementById('playBtn');
  236. const continueBtn = document.getElementById('continueBtn');
  237. const repaintBtn = document.getElementById('repaintBtn');
  238. const reviewBtn = document.getElementById('reviewBtn');
  239. playBtn.style.display = 'none';
  240. if (progress < 100) {
  241. continueBtn.style.display = 'block';
  242. } else {
  243. repaintBtn.style.display = 'block';
  244. reviewBtn.style.display = 'block';
  245. const img = document.querySelector('#poster img');
  246. img.src = img.src.replace('/page/', '/work/');
  247. }
  248. }
  249. });
  250. </script>
  251. <script src="/scripts/progress.js"></script>
  252. </body>
  253. </html>