Procházet zdrojové kódy

1.meta title and description; add designer page;

guoziyun před 1 rokem
rodič
revize
7d561afbc3
89 změnil soubory, kde provedl 5545 přidání a 740 odebrání
  1. 25 0
      app.js
  2. 9 0
      config/app/index.js
  3. 207 0
      config/meta.js
  4. 158 0
      config/translate.js
  5. binární
      dist/assets/avatar/5b965144028d9b3606010b13.jpeg
  6. binární
      dist/assets/avatar/5bcec649be52850bc0ae031a.jpeg
  7. binární
      dist/assets/avatar/5cb6c118a0a47d351b3f9fec.jpeg
  8. binární
      dist/assets/avatar/5cf9e8e9df14570f1c6597e7.jpeg
  9. binární
      dist/assets/avatar/5d03a2306f3b9225b94d5786.jpeg
  10. binární
      dist/assets/avatar/5d2049416f3b9225b94d5812.jpeg
  11. binární
      dist/assets/avatar/5d204a0d6f3b9225b94d5814.jpeg
  12. binární
      dist/assets/avatar/5d818c269766014dc4f76a16.jpeg
  13. binární
      dist/assets/avatar/5d819afd3bf0981b15e4d60c.jpeg
  14. binární
      dist/assets/avatar/5d8fff98ba6a711b14916101.jpeg
  15. binární
      dist/assets/avatar/5e630b82e2fc0b5d6e8aeb7d.jpeg
  16. binární
      dist/assets/avatar/5f156d5daf9f457afafa237e.jpeg
  17. binární
      dist/assets/avatar/5f17a7fcaf9f457afafa238b.jpeg
  18. binární
      dist/assets/avatar/5f17dc03af9f457afafa238e.jpeg
  19. binární
      dist/assets/avatar/60a4b39339d1c8517c7160dc.jpeg
  20. binární
      dist/assets/avatar/60c87d0739d1c8517c716199.jpeg
  21. binární
      dist/assets/avatar/60d9440f39d1c8517c716222.jpeg
  22. binární
      dist/assets/avatar/60e513ce39d1c8517c71629b.jpeg
  23. binární
      dist/assets/avatar/6103d7f539d1c8517c7163f9.jpeg
  24. binární
      dist/assets/avatar/620b8a4139d1c8517c7167d8.jpeg
  25. binární
      dist/assets/avatar/62219e5758988209ee91aa0b.jpeg
  26. binární
      dist/assets/avatar/6233048688754c598848b9f8.jpeg
  27. binární
      dist/assets/avatar/623e9be59165993a4de7d133.jpeg
  28. binární
      dist/assets/avatar/623edd649165993a4de7d141.jpeg
  29. binární
      dist/assets/avatar/623f0c429165993a4de7d145.jpeg
  30. binární
      dist/assets/avatar/626a4b5413d2c95a09ff26d4.jpeg
  31. binární
      dist/assets/avatar/62d8eb8f632fcc7c892b6424.jpeg
  32. binární
      dist/assets/avatar/62d8efdc632fcc7c892b6426.jpeg
  33. binární
      dist/assets/avatar/62de38944fd0de78ba6d7a23.jpeg
  34. binární
      dist/assets/avatar/6311ff71b2d0e52232e2875a.jpeg
  35. binární
      dist/assets/avatar/643e6957c0d7b434fbb6f180.jpeg
  36. binární
      dist/assets/avatar/64893e9c3d5b2b63bd5d6102.jpeg
  37. binární
      dist/assets/avatar/66278056aae27d6dda1649c9.jpeg
  38. binární
      dist/assets/avatar/default.jpeg
  39. binární
      dist/assets/icon/404.png
  40. binární
      dist/assets/icon/art-puzzle.webp
  41. binární
      dist/assets/icon/color-by-number.webp
  42. binární
      dist/assets/icon/find-difference.webp
  43. 25 0
      dist/assets/icon/icon-app-store.svg
  44. 8 0
      dist/assets/icon/icon-google-play.svg
  45. binární
      dist/assets/icon/icon.png
  46. binární
      dist/assets/icon/jigsaw-puzzle.webp
  47. binární
      dist/assets/icon/logo_1024x1024.png
  48. binární
      dist/assets/icon/logo_640x640.webp
  49. binární
      dist/assets/icon/soduko.webp
  50. 4 0
      dist/assets/svg/email.svg
  51. 17 0
      dist/assets/svg/location.svg
  52. 277 0
      dist/assets/svg/texture.svg
  53. 5 5
      dist/stylesheets/category.css
  54. 32 0
      dist/stylesheets/designer.css
  55. 1 0
      dist/stylesheets/header.css
  56. 167 0
      dist/stylesheets/info.css
  57. 1 1
      dist/stylesheets/styles.css
  58. 32 0
      libs/image.js
  59. 13 1
      libs/utils.js
  60. 450 1
      package-lock.json
  61. 2 1
      package.json
  62. 545 283
      routes/index.js
  63. 47 0
      routes/res/thumbs.js
  64. 2377 0
      test/meta.html
  65. 421 0
      test/meta2.html
  66. 13 13
      test/script.js
  67. 10 57
      test/styles.css
  68. 13 175
      test/tags.html
  69. 106 21
      test/test.html
  70. 91 0
      views/404.ejs
  71. 23 19
      views/album.ejs
  72. 26 14
      views/albums.ejs
  73. 29 18
      views/category.ejs
  74. 2 2
      views/common-meta.ejs
  75. 37 0
      views/designer.ejs
  76. 38 0
      views/designers.ejs
  77. 29 18
      views/detail.ejs
  78. 6 29
      views/footer.ejs
  79. 35 20
      views/header.ejs
  80. 1 1
      views/hot-section.ejs
  81. 13 2
      views/index.ejs
  82. 167 0
      views/info.ejs
  83. 9 6
      views/latest-section.ejs
  84. 3 7
      views/pagination.ejs
  85. 2 2
      views/play.ejs
  86. 20 12
      views/search.ejs
  87. 1 1
      views/special-section.ejs
  88. 24 14
      views/special.ejs
  89. 24 17
      views/tag.ejs

+ 25 - 0
app.js

@@ -2,6 +2,7 @@ const express = require('express');
 const path = require('path');
 const app = express();
 const { getLocale } = require('./libs/utils');
+const config = require('./config/app')
 
 // 设置视图引擎为EJS
 app.set('view engine', 'ejs');
@@ -10,13 +11,37 @@ app.set('view engine', 'ejs');
 app.set('views', path.join(__dirname, 'views'));
 
 
+app.use(express.static(config.STATIC_DIR));
 app.use(express.static(path.join(__dirname, 'dist')));
 
 
+app.use('/thumbs/v1', require('./routes/res/thumbs'));
+
 app.use('/', require('./routes/index'));
 
 app.use('/proxy', require('./routes/proxy'));
 
+
+// catch 404 and forward to error handler
+app.use(function (req, res) {
+  // 设置状态码为404
+  res.status(404);
+  // res.sendFile(path.join(__dirname, '404.html'));
+  res.render('404', { title: '404 Error', description: 'PAGE NOT FOUND' });
+});
+
+
+// error handler
+app.use(function (err, req, res, next) {
+  // set locals, only providing error in development
+  res.locals.message = err.message;
+  res.locals.error = req.app.get('env') === 'development' ? err : {};
+  console.log("error:" + err);
+  // render the error page
+  res.status(err.status || 500);
+  res.render('404', { title: '404 Error', description: 'PAGE NOT FOUND' });
+});
+
 // 启动服务器,监听3000端口
 const PORT = process.env.PORT || 3000;
 app.listen(PORT, () => {

+ 9 - 0
config/app/index.js

@@ -1,3 +1,7 @@
+const path = require('path');
+const os = require('os');
+const fs = require('fs');
+
 let configs = {
   type: 'production',
   cookie: {
@@ -11,13 +15,18 @@ let configs = {
   mongodbUrl: 'mongodb://coloring:coloring123.@localhost:62701/artsite?authSource=admin',
   host: 'http://art.pcoloring.com',
   resHost: 'http://pcoloring.com',
+  STATIC_DIR: path.resolve(os.homedir(), 'www/artsite'),
 }
 
+
 let node_env = require('process').env.NODE_ENV || 'production';
 try {
   console.log(`Trying to load env spcified configs: ./${node_env}.js`)
   let envConfigs = require(`./${node_env}`);
   configs = Object.assign(configs, envConfigs);
+
+  fs.mkdirSync(configs.STATIC_DIR, { recursive: true });
+
 } catch (err) {
   console.warn(`Load env specific configs failed: ${err}`);
 }

+ 207 - 0
config/meta.js

@@ -0,0 +1,207 @@
+let homePageTile = {
+  zh: '数字填色书',
+  en: 'Art Coloring Pages',
+  es: 'Páginas de coloreo artístico',
+  pt: 'Páginas de colorir artísticas',
+  ja: 'アート塗り絵用紙',
+}
+
+let homePageDescription = {
+  zh: '免费的、海量的线条底图,等着您来给它们涂上颜色。不需要复杂的操作,只需要根据数字的指引,即可完成一幅幅精美绝伦的作品,让你感受关于构图和色彩的艺术气息...',
+  en: `Free and a vast number of line drawings are waiting for you to color them. There's no need for complicated operations. You just need to follow the number guides, and then you can complete one exquisite work after another, allowing you to feel the artistic charm of composition and colors...`,
+  es: 'Hay una gran colección gratuita de dibujos a lápiz esperando a que les des color. No es necesario realizar operaciones complicadas. Simplemente sigue las indicaciones de los números, colorea por números y podrás completar una y otra obra impresionante, lo que te permitirá sentir la esencia artística de la composición y los colores...',
+  pt: 'Uma vasta coleção gratuita de desenhos a lápis está esperando por você para dar cor a eles. Não é necessário realizar operações complicadas. Basta seguir as indicações dos números, colorir por números e você poderá completar uma obra maravilhosa após outra, permitindo que você sinta a essência artística da composição e das cores...',
+  ja: '無料でたくさんの線画が、あなたが色を塗るのを待っています。複雑な操作は必要ありません。ただ数字の案内に従って、数字に対応した色で塗るだけで、見事な作品を次々に完成させることができ、構図と色彩の芸術的な本質を感じることができます...',
+}
+
+let categoryTitle = {
+  zh: '涂色页分类',
+  en: 'Coloring Pages Categories',
+  es: 'Categorías de páginas para colorear',
+  pt: 'Categorias de páginas para colorir',
+  ja: '塗り絵用紙のカテゴリー',
+}
+
+let categoryDescription = {
+  zh: '为您精选组织的热门填色主题分类,包括动物、植物、人物、风景、曼陀罗、建筑、食物、宠物、花鸟鱼虫等等,帮助您快速找到感兴趣的填色页。',
+  en: `We've carefully selected and organized popular coloring themes for you, including animals, plants, people, landscapes, mandalas, architecture, food, pets, flowers, birds, fish, and insects, etc. This helps you quickly find the coloring pages that interest you.`,
+  es: 'Hemos seleccionado y organizado cuidadosamente categorías de temas de coloreo populares para usted, que incluyen animales, plantas, personas, paisajes, mandalas, arquitectura, alimentos, mascotas, flores, pájaros, peces e insectos, etc. Esto le ayudará a encontrar rápidamente las páginas de coloreo que le interesen.',
+  pt: 'Selecionamos e organizamos cuidadosamente categorias de temas de coloração populares para você, incluindo animais, plantas, pessoas, paisagens, mandalas, arquitetura, alimentos, animais de estimação, flores, pássaros, peixes e insetos, etc. Isso ajuda você a encontrar rapidamente as páginas de colorir que lhe interessam.',
+  ja: '当社では、動物、植物、人物、風景、マンダラ、建築、食べ物、ペット、花鳥魚虫などの人気の塗り絵テーマを厳選して分類しています。これにより、あなたが興味のある塗り絵用紙を素早く見つけることができます。',
+}
+
+let tagTitle = {
+  zh: '涂色页标签检索',
+  en: 'Coloring Pages Tags',
+  es: 'Etiquetas de páginas para colorear',
+  pt: 'Marcadores de páginas para colorir',
+  ja: '塗り絵用紙のタグ',
+}
+
+let tagDescription = {
+  zh: '超乎您想象,多达上百个热门标签:阳光、沙滩、大海、老虎、孔雀、蝴蝶,父母、女孩、婴儿、花园、花鸟鱼虫等等等等,满足您对涂色页面的更细分的检索需求。',
+  en: 'Beyond your imagination, there are up to hundreds of popular tags: sunshine, beach, sea, tiger, peacock, butterfly, parents, girl, baby, garden, flowers, birds, fish, insects and so on. They can meet your more specific search needs for coloring pages.',
+  es: 'Más allá de lo que puedes imaginar, hay hasta cientos de etiquetas populares: sol, playa, mar, tigre, pavo real, mariposa, padres, niña, bebé, jardín, flores, pájaros, peces, insectos, etc. Estas etiquetas pueden satisfacer tus necesidades de búsqueda más detalladas para las páginas de colorear.',
+  pt: 'Além do que você pode imaginar, há até centenas de marcadores populares: sol, praia, mar, tigre, pavão, borboleta, pais, menina, bebê, jardim, flores, pássaros, peixes, insetos, etc. Eles atendem às suas necessidades de busca mais específicas para páginas de colorir.',
+  ja: '想像を超える、太陽、砂浜、海、トラ、孔雀、蝶、両親、少女、赤ちゃん、庭園、花鳥魚虫など、何百もの人気タグがあります。これらはあなたの塗り絵ページに対するより細分化された検索ニーズを満たします。',
+}
+
+let searchTitle = {
+  zh: '按关键词搜索涂色页',
+  en: 'Coloring Pages Search By Key Word',
+  es: 'Buscar páginas de colorear por palabra clave',
+  pt: 'Pesquisar páginas de colorir por palavra-chave',
+  ja: 'キーワードで塗り絵用紙を検索する',
+}
+
+let searchDescription = {
+  zh: '我们提供的热门分类、标签、专辑 还找不到您感兴趣的内容? 那就随心所欲地用关键词搜索你想要的涂色页吧,相信我们的海量图片内容和AI智能检索,定能找到你想要的... ',
+  en: `Can't find what you're interested in from our popular categories, tags, and albums? Then feel free to search for the coloring pages you want using keywords. We're confident that our vast collection of images and AI-powered intelligent search will help you find exactly what you're looking for...`,
+  es: '¿No encuentras el contenido que te interesa entre nuestras categorías populares, etiquetas y álbumes? Entonces, busca las páginas de colorear que quieres usando palabras clave a tu antojo. Estamos seguros de que nuestra inmensa colección de imágenes y la búsqueda inteligente impulsada por IA te ayudarán a encontrar exactamente lo que estás buscando...',
+  pt: 'Não consegue encontrar o que está interessado nas nossas categorias populares, marcadores e álbuns? Então, sinta-se à vontade para pesquisar as páginas de colorir que deseja usando palavras-chave. Temos certeza de que nossa vasta coleção de imagens e a pesquisa inteligente baseada em IA irão ajudá-lo a encontrar exatamente o que está procurando...',
+  ja: '当社が提供する人気のカテゴリ、タグ、アルバムの中から、あなたが興味を持つ内容が見つからないですか?それなら、キーワードを使って思いのままに探したい塗り絵ページを検索してください。私たちの膨大な画像コンテンツと AI による高度な検索機能で、きっとあなたが求めるものを見つけることができると信じています...',
+}
+
+let specialTitle = {
+  zh: '彩绘 - 特殊涂画作品集',
+  en: 'Special Coloring Page Collection',
+  es: 'Colección especial de páginas para colorear',
+  pt: 'Coleção especial de páginas para colorir',
+  ja: '特別な塗り絵用紙コレクション',
+}
+
+
+let specialDescription = {
+  zh: '不同于普通的涂色页一个区块只能一个颜色, 彩绘涂色作品拥有更多的颜色空间和细节,可以呈现出类似照片的细腻感和真实感,相信会给你不一样的“惊喜”, 快来试试吧。',
+  en: `Unlike ordinary coloring pages where each section can only have one color, painted coloring works offer more room for colors and details, capable of presenting a delicate and realistic feel similar to that of a photo. We're sure it'll bring you a unique "surprise". Come and give it a try!`,
+  es: 'A diferencia de las páginas de colorear normales en las que cada sección solo puede tener un color, las obras de coloreado tienen más espacio para los colores y detalles, y pueden ofrecer una sensación de suavidad y realismo similar a la de una foto. Estamos seguros de que te dará una “sorpresa” diferente. ¡Ven y pruébalo ahora!',
+  pt: 'Diferentemente das páginas de colorir comuns, nas quais cada seção pode ter apenas uma cor, as obras de colorir pintadas oferecem mais espaço para cores e detalhes, sendo capazes de apresentar uma sensação de delicadeza e realismo semelhante à de uma foto. Temos certeza de que trará uma “surpresa” única para você. Venha e dê uma chance!',
+  ja: '一般的な塗り絵用紙では一つの区画に一色しか塗れませんが、彩り豊かな塗り絵作品は、より多くの色を使った表現と細部描写が可能で、写真のような繊細さとリアリティを演出することができます。きっとあなたに新鮮な「驚き」をもたらしてくれるはずです。ぜひ試してみてください!',
+}
+
+let albumsTitle = {
+  zh: '涂色页专辑',
+  en: 'Coloring Page Albums',
+  es: 'Álbumes de páginas para colorear',
+  pt: 'Álbuns de páginas para colorir',
+  ja: '塗り絵用紙のアルバム',
+}
+
+let albumsDescription = {
+  zh: '我们将持续推出涂色页系列专辑,如圣诞主题、星座主题、可爱宝贝主题等等,根据不同的主题绘制一系列精美的图片,作为精品呈现给您...',
+  en: `We will continuously launch a series of coloring page albums, such as Christmas-themed, constellation-themed, cute baby-themed ones, etc. We'll draw a series of beautiful pictures based on different themes and present them to you as high-quality works...`,
+  es: 'Seguiremos lanzando una serie de álbumes de páginas para colorear, como los de tema navideño, los de tema de constelaciones, los de tema de bebés adorables, etc. Dibujaremos una serie de hermosas imágenes basadas en diferentes temas y se las presentaremos a usted como obras de alta calidad...',
+  pt: 'Vamos continuar lançando uma série de álbuns de páginas para colorir, como os com tema de Natal, os com tema de constelações, os com tema de bebês fofos, etc. Vamos desenhar uma série de belas imagens com base em diferentes temas e apresentá-las a você como obras de alta qualidade...',
+  ja: '当社は塗り絵用紙のシリーズアルバムを継続的にリリースします。例えばクリスマステーマ、星座テーマ、可愛いベビーテーマなど様々なテーマのアルバムを用意します。それぞれのテーマに基づいて、美しい絵を一連に描き、逸品として皆様にお届けします...',
+}
+
+let albumDetailTitle = {
+  zh: '涂色专辑详情页',
+  en: 'Coloring Page Album Details',
+  es: 'Detalles del álbum de páginas para colorear',
+  pt: 'Detalhes do álbum de páginas para colorir',
+  ja: '塗り絵用紙アルバムの詳細',
+}
+
+let albumDetailDescription = {
+  zh: '涂色页专辑详情信息,查看专辑精品内容,选择您感兴趣的图画作品,开始按照数字涂色吧',
+  en: 'Check the detailed information of the coloring page album, view the high - quality content of the album, select the picture works that interest you, and start coloring according to the numbers...',
+  es: 'Consulta la información detallada del álbum de páginas para colorear, mira el contenido de alta calidad del álbum, selecciona las obras de arte que te interesen y empieza a colorear según los números...',
+  pt: 'Verifique as informações detalhadas do álbum de páginas para colorir, veja o conteúdo de qualidade do álbum, selecione as obras de arte que lhe interessam e comece a colorir de acordo com os números...',
+  ja: '塗り絵用紙のアルバムの詳細情報を確認し、アルバムの逸品コンテンツを見て、あなたが興味を持つ絵画作品を選び、数字に従って塗り始めましょう...',
+}
+
+let designerTitle = {
+  zh: '艺术涂色页设计师专栏',
+  en: 'Art Coloring Page Designer Column',
+  es: 'Columna del diseñador de páginas de arte para colorear',
+  pt: 'Coluna do designer de páginas de arte para colorir',
+  ja: 'アート塗り絵用紙デザイナーコラム',
+}
+
+let designerDescription = {
+  zh: '你是否曾经对某张艺术涂画作品印象深刻?或许是这这张作品的设计师的审美引起了你的共鸣!那么请移步设计师专栏,查看您感兴趣的设计师的作品合集吧。',
+  en: `Have you ever been deeply impressed by a certain piece of art coloring work? Maybe it's the aesthetic sense of the designer of this work that resonates with you! Then please go to the designer column and check out the collections of works by the designers you're interested in.`,
+  es: '¿Alguna vez te has quedado impresionado por una determinada obra de arte de colorear? Quizás es el sentido estético del diseñador de esta obra el que te ha hecho sentir una conexión! Entonces, ve a la columna de los diseñadores y consulta las colecciones de obras de los diseñadores que te interesen.',
+  pt: 'Você já se impressionou com uma determinada obra de arte de colorir? Talvez seja o senso estético do designer desta obra que ressoa com você! Então, vá para a coluna dos designers e confira as coleções de obras dos designers que lhe interessam.',
+  ja: 'あなたは曾て、あるアート塗り絵作品に深く感銘を受けたことはありませんか?おそらく、その作品のデザイナーの審美眼があなたの共感を呼んでいるのではないでしょうか!では、デザイナーコラムに移動して、あなたが興味を持つデザイナーの作品コレクションをチェックしてみてください。',
+}
+
+let infoTitle = {
+  zh: '关于我们, APP下载, 联系我们',
+  en: 'ABOUT US, APP DOWNLOAD, CONTACT US',
+  es: 'SOBRE NOSOTROS, DESCARGA DE LA APP, CONTÁCTENOS',
+  pt: 'SOBRE NÓS, BAIXE O APP, ENTRE EM CONTATO CONOSCO',
+  ja: '私たちについて, アプリをダウンロード, お問い合わせ',
+}
+
+let infoDescription = {
+  zh: 'JCCY 是一家成立于2015年专业从事休闲娱乐游戏开发的公司。我们推出的几款游戏,都深受用户喜爱。其中的《数字填色书》是全球最受欢迎的数字填色应用之一,也是最成功的广告驱动式应用程序,获得广泛认可。我们非常期待并乐于听到您的反馈!',
+  en: `JCCY is a company founded in 2015 that specializes in the development of casual entertainment games. Several of our games have been well received by users. Among them, "Coloring Book by Numbers" is one of the most popular coloring by number apps in the world and the most successful ad-driven app, gaining wide recognition. We'd love to hear from you!`,
+  es: 'JCCY es una empresa fundada en 2015 especializada en el desarrollo de juegos de entretenimiento casual. Varios juegos que lanzamos son muy populares entre los usuarios. Entre ellos, "Coloring Book by Numbers" es una de las aplicaciones para colorear digitales más populares del mundo y también es la aplicación impulsada por publicidad de mayor éxito, obteniendo un amplio reconocimiento.¡Nos encantaría saber de usted!',
+  pt: 'A JCCY é uma empresa fundada em 2015 especializada no desenvolvimento de jogos de entretenimento casual. Vários jogos que lançamos são muito populares entre os usuários. Entre eles, o "Coloring Book by Numbers" é um dos aplicativos de colorir digitais mais populares do mundo e também o aplicativo de publicidade de maior sucesso, ganhando amplo reconhecimento.Gostaríamos muito de ouvir de você!',
+  ja: 'JCCYはカジュアルエンターテイメントゲームの開発を専門に2015年に設立された会社です。当社がリリースしたゲームのいくつかは、ユーザーの間で非常に人気があります。その中でも、「Coloring Book by Numbers」は世界で最も人気のあるデジタル塗り絵アプリの一つであり、最も成功した広告主導型アプリケーションでもあり、幅広い認知度を獲得しています。ご意見をお待ちしております!',
+}
+
+let detailTitle = {
+  zh: '涂色作品详情',
+  en: 'Coloring Works Details',
+  es: 'Detalles de las obras de colorear',
+  pt: 'Detalhes das obras de colorir',
+  ja: '塗り絵作品の詳細',
+}
+
+let detailDescription = {
+  zh: '涂色作品详情',
+  en: 'Coloring Works Details',
+  es: 'Detalles de las obras de colorear',
+  pt: 'Detalhes das obras de colorir',
+  ja: '塗り絵作品の詳細',
+}
+
+let playTitle = {
+  zh: '数字填色游戏',
+  en: 'PLAY COLOR BY NUMBER',
+  es: 'Jugar al juego de colorear por números',
+  pt: 'Jogar o jogo de colorir por números',
+  ja: '数字に従った塗り絵ゲームを遊ぶ',
+}
+
+let playDescription = {
+  zh: '欢迎来到填色游戏,你会看到一张待上色的线条底图,请根据游戏面板下方的数字指示为图片涂上颜色,引爆它们,完成它,你将收获一张“得意之作”,并可自由保存、收藏、或者分享给好友。',
+  en: `Welcome to the coloring game! You'll see a line drawing waiting to be colored. Please color the picture according to the number instructions below the game panel. Bring it to life and complete it. You'll get a "masterpiece" and can freely save, collect, or share it with your friends.`,
+  es: '¡Bienvenido al juego de colorear! Verás un dibujo en línea esperando a ser coloreado. Por favor, colorea la imagen de acuerdo con las indicaciones de los números debajo del panel del juego. Dales vida y complétalo. Obtendrás una "obra maestra" y podrás guardarla, recogerla o compartirla con tus amigos libremente.',
+  pt: 'Bem-vindo ao jogo de colorir! Você verá um desenho a lápis esperando para ser colorido. Por favor, pinte a imagem de acordo com as indicações dos números abaixo do painel do jogo. Dê vida a eles e complete-o. Você terá uma "obra-prima" e poderá salvá-la, colecioná-la ou compartilhá-la com seus amigos livremente.',
+  ja: '塗り絵ゲームへようこそ!あなたは色を塗るために待機している線画を見るでしょう。ゲームパネルの下にある数字の指示に従って、画像に色を塗ってください。それを完成させ、あなたは「自慢の作品」を手に入れることができ、自由に保存、収集、または友人と共有することができます。',
+}
+
+
+
+
+let meta = {
+  homePageTile,
+  homePageDescription,
+  categoryTitle,
+  categoryDescription,
+  tagTitle,
+  tagDescription,
+  searchTitle,
+  searchDescription,
+  specialTitle,
+  specialDescription,
+  albumsTitle,
+  albumsDescription,
+  albumDetailTitle,
+  albumDetailDescription,
+  designerTitle,
+  designerDescription,
+  infoTitle,
+  infoDescription,
+  detailTitle,
+  detailDescription,
+  playTitle,
+  playDescription,
+}
+
+
+module.exports = meta;

+ 158 - 0
config/translate.js

@@ -70,6 +70,14 @@ let designer = {
   ja: 'デザイナー',
 }
 
+let designerColumn = {
+  zh: '设计师专栏',
+  en: 'Designer Column',
+  es: 'Columna de diseño',
+  pt: 'Coluna do Designer',
+  ja: 'デザイナーコラム',
+}
+
 let publishTime = {
   zh: '发布时间',
   en: 'Publish Time',
@@ -263,6 +271,137 @@ let wrongPage = {
   ja: '有効なページ番号を入力してください',
 }
 
+let appIntroduction = {
+  zh: '这是一本完全免费的数字填色书!拥有海量的高清图片资源,涵盖您能想象的方方面面,并且每日更新。只需按照数字提示,即可轻松为图案着色,分分钟做出一幅幅精美的填色画作:释放压力的秘密武器,放松身心的最佳游戏!当你感到疲惫、焦虑的时候,打开这本数字涂色书,用精美漂亮的填色图案,为你带来放松身心、平静心灵的绝妙乐趣!',
+  en: 'This is a completely free digital coloring book! It has a huge amount of high-definition picture resources, covering every aspect you can imagine, and is updated daily. Just follow the digital prompts to easily color the patterns and make beautiful coloring paintings in minutes: the secret weapon to release stress, the best game to relax your body and mind! When you feel tired and anxious, open this digital coloring book, with beautiful and beautiful coloring patterns, it will bring you the wonderful fun of relaxing your body and mind and calming your mind!',
+  es: '¡Este es un libro para colorear por números completamente gratuito! Tiene una enorme cantidad de recursos de imágenes de alta definición, que cubren todos los aspectos que puedas imaginar y se actualiza diariamente. Solo tienes que seguir las pistas digitales para colorear fácilmente los patrones y crear hermosos dibujos para colorear en minutos: ¡el arma secreta para liberar el estrés y el mejor juego para relajar el cuerpo y la mente! Cuando te sientas cansado y ansioso, abre este libro para colorear digital y usa hermosos patrones de coloración para disfrutar de la maravillosa diversión de relajar tu cuerpo y tu mente y calmar tu alma.',
+  pt: 'Este é um livro de colorir por números totalmente gratuito! Ele tem uma enorme quantidade de recursos de imagens em alta definição, cobrindo todos os aspectos que você possa imaginar, e é atualizado diariamente. Basta seguir as dicas digitais para colorir facilmente os padrões e criar lindos desenhos para colorir em minutos: a arma secreta para liberar o estresse e o melhor jogo para relaxar o corpo e a mente! Quando você se sentir cansado e ansioso, abra este livro de colorir digital e use lindos padrões de colorir para lhe proporcionar a maravilhosa diversão de relaxar seu corpo e mente e acalmar sua alma!',
+  ja: 'これは完全に無料の数字塗り絵の本です!想像できるあらゆる側面を網羅した膨大な量の高解像度画像リソースがあり、毎日更新されます。デジタルのヒントに従うだけで、簡単にパターンに色を塗って、数分で美しい塗り絵を作ることができます。これは、ストレスを解消する秘密兵器であり、心身をリラックスさせる最高のゲームです。疲れて不安な気持ちになったら、このデジタル塗り絵を開いて、美しい塗り絵のパターンを使って、心身をリラックスさせ、魂を落ち着かせる素晴らしい楽しみを味わってください。',
+}
+
+let goodExperience = {
+  zh: '绝佳的数字填色游戏体验',
+  en: 'Great coloring by number game experience',
+  es: 'Gran experiencia de juego de colorear por números',
+  pt: 'Gran experiencia de juego de colorear por números',
+  ja: '数字で色を塗る素晴らしいゲーム体験',
+}
+
+let massivePictures = {
+  zh: '海量高清图片',
+  en: 'Massive HD pictures',
+  es: 'Toneladas de imágenes de alta definición',
+  pt: 'Toneladas de imagens em alta definição',
+  ja: '高画質写真が満載',
+}
+
+let dailyUpdate = {
+  zh: '图库每日更新',
+  en: 'Gallery updated daily',
+  es: 'Galería actualizada diariamente',
+  pt: 'Galeria atualizada diariamente',
+  ja: 'ギャラリーは毎日更新されます',
+}
+
+let easyShare = {
+  zh: '轻松分享作品',
+  en: 'Share your work easily',
+  es: 'Comparte tu trabajo fácilmente',
+  pt: 'Compartilhe seu trabalho facilmente',
+  ja: '作品を簡単に共有',
+}
+
+let offlineColoring = {
+  zh: '支持离线涂色',
+  en: 'Support offline coloring',
+  es: 'Admite coloración sin conexión',
+  pt: 'Suporte para colorir offline',
+  ja: 'オフラインカラーリングをサポート',
+}
+
+let companyIntroduction = {
+  zh: 'JCCY 是一家成立于2015年专业从事休闲娱乐游戏开发的公司。我们推出的几款游戏,都深受用户喜爱。其中的《数字填色书》是全球最受欢迎的数字填色应用之一,也是最成功的广告驱动式应用程序,获得广泛认可。',
+  en: 'JCCY is a company founded in 2015 that specializes in the development of casual entertainment games. Several of our games have been well received by users. Among them, "Coloring Book by Numbers" is one of the most popular coloring by number apps in the world and the most successful ad-driven app, gaining wide recognition.',
+  es: 'JCCY es una empresa fundada en 2015 especializada en el desarrollo de juegos de entretenimiento casual. Varios juegos que lanzamos son muy populares entre los usuarios. Entre ellos, "Coloring Book by Numbers" es una de las aplicaciones para colorear digitales más populares del mundo y también es la aplicación impulsada por publicidad de mayor éxito, obteniendo un amplio reconocimiento.',
+  pt: 'A JCCY é uma empresa fundada em 2015 especializada no desenvolvimento de jogos de entretenimento casual. Vários jogos que lançamos são muito populares entre os usuários. Entre eles, o "Coloring Book by Numbers" é um dos aplicativos de colorir digitais mais populares do mundo e também o aplicativo de publicidade de maior sucesso, ganhando amplo reconhecimento.',
+  ja: 'JCCYはカジュアルエンターテイメントゲームの開発を専門に2015年に設立された会社です。当社がリリースしたゲームのいくつかは、ユーザーの間で非常に人気があります。その中でも、「Coloring Book by Numbers」は世界で最も人気のあるデジタル塗り絵アプリの一つであり、最も成功した広告主導型アプリケーションでもあり、幅広い認知度を獲得しています。',
+}
+
+let contactX = {
+  zh: '联系我们',
+  en: 'CONTACT US',
+  es: 'CONTÁCTENOS',
+  pt: 'CONTATE-NOS',
+  ja: 'お問い合わせ',
+}
+
+let aboutX = {
+  zh: '关于我们',
+  en: 'ABOUT US',
+  es: 'ABOUT US',
+  pt: 'ABOUT US',
+  ja: '私たちについて',
+}
+
+let questionAndPartnership = {
+  zh: '问题?合作?',
+  en: 'Questions? Partnership?',
+  es: '¿Preguntas? ¿Asociación?',
+  pt: 'Dúvidas? Parceria?',
+  ja: 'ご質問は?パートナーシップについて?',
+}
+
+let happyToHear = {
+  zh: '我们非常期待并乐于听到您的反馈!',
+  en: `We'd love to hear from you!`,
+  es: '¡Nos encantaría saber de usted!',
+  pt: 'Gostaríamos muito de ouvir de você!',
+  ja: 'ご意見をお待ちしております!',
+}
+
+let beijing = {
+  zh: '中国 北京',
+  en: `BEIJING, CHINA`,
+  es: 'Pekín, China',
+  pt: 'Pequim, China',
+  ja: '中国 北京',
+}
+
+let addr = {
+  zh: '北京市海淀区学院路35号世宁大厦17层',
+  en: '17th Floor, Shining Building, No. 35 Xueyuan Road, Haidian District, Beijing',
+  es: 'Piso 17, Edificio Shining, Número 35, Calle Xueyuan, Distrito de Haidian, Beijing',
+  pt: '17º andar, Edifício Shining, Número 35, Rua Xueyuan, Distrito de Haidian, Beijing',
+  ja: '北京市海淀区学院路 35 号 シャイニングビル 17 階',
+}
+
+let supportOrFeedback = {
+  zh: '支持或反馈',
+  en: `Support Or Feedback`,
+  es: 'Soporte o Retroalimentación',
+  pt: 'Suporte ou Feedback',
+  ja: 'サポートまたはフィードバック',
+}
+
+let mayYouLike = {
+  zh: '猜您喜欢',
+  en: 'May you like',
+  es: 'Supongo que te gustará',
+  pt: 'Acho que você vai gostar',
+  ja: 'あなたが気に入るかもしれません',
+}
+
+let worksCount = {
+  zh: '作品数',
+  en: 'Works Count',
+  es: 'Cantidad de obras',
+  pt: 'Quantidade de obras',
+  ja: '作品の数',
+}
+
+
+
+
 let translate = {
   homePage,
   categoryPage,
@@ -271,6 +410,7 @@ let translate = {
   feedback,
   contactUs,
   designer,
+  designerColumn,
   publishTime,
   descTest,
   titleTest,
@@ -297,6 +437,24 @@ let translate = {
   page,
   jumpTo,
   wrongPage,
+  appIntroduction,
+  goodExperience,
+  massivePictures,
+  dailyUpdate,
+  easyShare,
+  offlineColoring,
+  companyIntroduction,
+  contactX,
+  aboutX,
+  questionAndPartnership,
+  happyToHear,
+  beijing,
+  addr,
+  supportOrFeedback,
+  mayYouLike,
+  worksCount,
+
 }
 
+
 module.exports = translate;

binární
dist/assets/avatar/5b965144028d9b3606010b13.jpeg


binární
dist/assets/avatar/5bcec649be52850bc0ae031a.jpeg


binární
dist/assets/avatar/5cb6c118a0a47d351b3f9fec.jpeg


binární
dist/assets/avatar/5cf9e8e9df14570f1c6597e7.jpeg


binární
dist/assets/avatar/5d03a2306f3b9225b94d5786.jpeg


binární
dist/assets/avatar/5d2049416f3b9225b94d5812.jpeg


binární
dist/assets/avatar/5d204a0d6f3b9225b94d5814.jpeg


binární
dist/assets/avatar/5d818c269766014dc4f76a16.jpeg


binární
dist/assets/avatar/5d819afd3bf0981b15e4d60c.jpeg


binární
dist/assets/avatar/5d8fff98ba6a711b14916101.jpeg


binární
dist/assets/avatar/5e630b82e2fc0b5d6e8aeb7d.jpeg


binární
dist/assets/avatar/5f156d5daf9f457afafa237e.jpeg


binární
dist/assets/avatar/5f17a7fcaf9f457afafa238b.jpeg


binární
dist/assets/avatar/5f17dc03af9f457afafa238e.jpeg


binární
dist/assets/avatar/60a4b39339d1c8517c7160dc.jpeg


binární
dist/assets/avatar/60c87d0739d1c8517c716199.jpeg


binární
dist/assets/avatar/60d9440f39d1c8517c716222.jpeg


binární
dist/assets/avatar/60e513ce39d1c8517c71629b.jpeg


binární
dist/assets/avatar/6103d7f539d1c8517c7163f9.jpeg


binární
dist/assets/avatar/620b8a4139d1c8517c7167d8.jpeg


binární
dist/assets/avatar/62219e5758988209ee91aa0b.jpeg


binární
dist/assets/avatar/6233048688754c598848b9f8.jpeg


binární
dist/assets/avatar/623e9be59165993a4de7d133.jpeg


binární
dist/assets/avatar/623edd649165993a4de7d141.jpeg


binární
dist/assets/avatar/623f0c429165993a4de7d145.jpeg


binární
dist/assets/avatar/626a4b5413d2c95a09ff26d4.jpeg


binární
dist/assets/avatar/62d8eb8f632fcc7c892b6424.jpeg


binární
dist/assets/avatar/62d8efdc632fcc7c892b6426.jpeg


binární
dist/assets/avatar/62de38944fd0de78ba6d7a23.jpeg


binární
dist/assets/avatar/6311ff71b2d0e52232e2875a.jpeg


binární
dist/assets/avatar/643e6957c0d7b434fbb6f180.jpeg


binární
dist/assets/avatar/64893e9c3d5b2b63bd5d6102.jpeg


binární
dist/assets/avatar/66278056aae27d6dda1649c9.jpeg


binární
dist/assets/avatar/default.jpeg


binární
dist/assets/icon/404.png


binární
dist/assets/icon/art-puzzle.webp


binární
dist/assets/icon/color-by-number.webp


binární
dist/assets/icon/find-difference.webp


+ 25 - 0
dist/assets/icon/icon-app-store.svg

@@ -0,0 +1,25 @@
+<svg width="287" height="96" viewBox="0 0 287 96" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M59.9147 48.9353C59.9408 46.9652 60.4789 45.0336 61.4789 43.3201C62.4789 41.6066 63.9084 40.167 65.6343 39.1351C64.5379 37.6123 63.0915 36.3592 61.4099 35.4751C59.7283 34.591 57.858 34.1005 55.9473 34.0423C51.8716 33.6263 47.9204 36.414 45.8435 36.414C43.7264 36.414 40.5287 34.0836 37.0853 34.1525C34.858 34.2225 32.6873 34.8524 30.7848 35.9807C28.8822 37.1091 27.3127 38.6975 26.229 40.5912C21.535 48.4945 25.0363 60.1097 29.5328 66.4981C31.7825 69.6264 34.4117 73.1207 37.852 72.9968C41.2186 72.861 42.4759 70.9091 46.5396 70.9091C50.5656 70.9091 51.7453 72.9968 55.2554 72.918C58.8679 72.861 61.1439 69.7759 63.3146 66.618C64.931 64.389 66.1748 61.9255 67 59.3187C64.9013 58.4555 63.1103 57.0105 61.8503 55.1641C60.5903 53.3176 59.9171 51.1513 59.9147 48.9353V48.9353Z" fill="white"/>
+<path d="M53.1622 29.3421C55.2025 27.0139 56.2076 24.0213 55.9642 21C52.8471 21.3112 49.9678 22.7273 47.9 24.9662C46.889 26.06 46.1146 27.3324 45.6213 28.7108C45.1279 30.0892 44.9251 31.5466 45.0246 32.9995C46.5837 33.0147 48.1261 32.6935 49.5357 32.06C50.9452 31.4265 52.1852 30.4972 53.1622 29.3421Z" fill="white"/>
+<path d="M101.53 64.1616H90.3921L87.7175 72H83L93.5494 43H98.4506L109 72H104.202L101.53 64.1616ZM91.5456 60.5446H100.374L96.0219 47.8235H95.9001L91.5456 60.5446Z" fill="white"/>
+<path d="M132 62.0405C132 68.7584 128.469 73.0746 123.139 73.0746C121.789 73.1465 120.447 72.8298 119.265 72.1609C118.084 71.492 117.111 70.4979 116.459 69.2923H116.358V80H112V51.2302H116.219V54.8258H116.299C116.981 53.6261 117.97 52.6371 119.16 51.9641C120.351 51.2911 121.698 50.9593 123.059 51.004C128.448 51.004 132 55.3412 132 62.0405ZM127.52 62.0405C127.52 57.6637 125.299 54.7862 121.91 54.7862C118.58 54.7862 116.34 57.7243 116.34 62.0405C116.34 66.3963 118.58 69.3134 121.91 69.3134C125.299 69.3134 127.52 66.4569 127.52 62.0405Z" fill="white"/>
+<path d="M156 62.0404C156 68.7584 152.469 73.0746 147.139 73.0746C145.789 73.1465 144.447 72.8298 143.265 72.1609C142.084 71.492 141.111 70.4979 140.459 69.2923H140.358V80H136V51.2302H140.218V54.8258H140.299C140.981 53.626 141.97 52.637 143.16 51.9641C144.351 51.2911 145.698 50.9593 147.059 51.004C152.448 51.004 156 55.3411 156 62.0404ZM151.52 62.0404C151.52 57.6637 149.299 54.7862 145.91 54.7862C142.58 54.7862 140.34 57.7243 140.34 62.0404C140.34 66.3963 142.58 69.3134 145.91 69.3134C149.299 69.3134 151.52 66.4569 151.52 62.0404H151.52Z" fill="white"/>
+<path d="M172.593 64.4271C172.925 67.3061 175.813 69.1964 179.759 69.1964C183.541 69.1964 186.261 67.306 186.261 64.7101C186.261 62.4567 184.621 61.1074 180.735 60.1827L176.85 59.2763C171.345 57.9886 168.79 55.4955 168.79 51.4498C168.79 46.4406 173.297 43 179.698 43C186.033 43 190.375 46.4406 190.521 51.4498H185.993C185.722 48.5525 183.248 46.8037 179.634 46.8037C176.02 46.8037 173.547 48.5731 173.547 51.1484C173.547 53.201 175.127 54.4087 178.991 55.3333L182.294 56.1187C188.444 57.5274 191 59.9201 191 64.1666C191 69.5982 186.532 73 179.427 73C172.779 73 168.29 69.6781 168 64.4269L172.593 64.4271Z" fill="white"/>
+<path d="M200.788 46V51.1427H205V54.6751H200.788V66.6551C200.788 68.5163 201.632 69.3835 203.483 69.3835C203.983 69.375 204.482 69.3405 204.978 69.2803V72.7916C204.146 72.9442 203.3 73.0133 202.453 72.9979C197.969 72.9979 196.22 71.3454 196.22 67.1309V54.6751H193V51.1427H196.22V46H200.788Z" fill="white"/>
+<path d="M207 62C207 55.2448 211.102 51 217.499 51C223.917 51 228 55.2448 228 62C228 68.7737 223.938 73 217.499 73C211.061 73 207 68.7737 207 62ZM223.37 62C223.37 57.3661 221.181 54.6312 217.499 54.6312C213.817 54.6312 211.63 57.387 211.63 62C211.63 66.6524 213.817 69.3665 217.499 69.3665C221.181 69.3665 223.37 66.6524 223.37 62H223.37Z" fill="white"/>
+<path d="M231 51.2316H235.211V54.9367H235.313C235.598 53.7795 236.268 52.7569 237.21 52.0406C238.153 51.3244 239.31 50.9583 240.487 51.0038C240.996 51.002 241.503 51.0579 242 51.1705V55.35C241.358 55.1513 240.688 55.0601 240.016 55.08C239.375 55.0536 238.735 55.1681 238.142 55.4155C237.548 55.6628 237.014 56.0373 236.577 56.5131C236.14 56.9889 235.81 57.5548 235.609 58.1719C235.409 58.789 235.343 59.4428 235.415 60.0883V73H231L231 51.2316Z" fill="white"/>
+<path d="M262.75 66.4278C262.145 70.3253 258.275 73 253.322 73C246.953 73 243 68.8154 243 62.1019C243 55.3676 246.974 51 253.133 51C259.191 51 263 55.0804 263 61.5901V63.1H247.537V63.3663C247.465 64.1565 247.566 64.9524 247.832 65.7014C248.099 66.4504 248.525 67.1352 249.082 67.7104C249.639 68.2855 250.314 68.7379 251.063 69.0374C251.812 69.3369 252.618 69.4767 253.426 69.4476C254.488 69.5451 255.554 69.304 256.465 68.7599C257.376 68.2158 258.083 67.3979 258.482 66.4278L262.75 66.4278ZM247.558 60.02H258.504C258.544 59.3095 258.434 58.5986 258.181 57.9317C257.928 57.2649 257.537 56.6565 257.033 56.1449C256.528 55.6332 255.922 55.2293 255.251 54.9584C254.58 54.6876 253.859 54.5557 253.133 54.5709C252.402 54.5667 251.676 54.7046 250.999 54.9766C250.322 55.2487 249.706 55.6497 249.188 56.1563C248.669 56.6629 248.259 57.2651 247.979 57.9283C247.699 58.5914 247.556 59.3024 247.558 60.02V60.02Z" fill="white"/>
+<path d="M91.1894 21.0159C92.1275 20.9503 93.0689 21.0883 93.9456 21.4198C94.8223 21.7512 95.6125 22.268 96.2592 22.9327C96.9059 23.5974 97.3929 24.3935 97.6852 25.2636C97.9776 26.1338 98.0678 27.0562 97.9495 27.9644C97.9495 32.432 95.4689 35 91.1894 35H86V21.0159H91.1894ZM88.2314 33.0223H90.9402C91.6105 33.0613 92.2813 32.9544 92.9038 32.7092C93.5263 32.464 94.0849 32.0868 94.5387 31.605C94.9926 31.1233 95.3303 30.549 95.5274 29.9242C95.7245 29.2993 95.776 28.6396 95.6781 27.9929C95.7689 27.3488 95.7122 26.6931 95.5121 26.0728C95.312 25.4525 94.9733 24.883 94.5205 24.4051C94.0677 23.9271 93.5119 23.5526 92.8929 23.3083C92.2739 23.064 91.6071 22.956 90.9402 22.9921H88.2314V33.0223Z" fill="white"/>
+<path d="M100.024 29.4953C99.9572 28.7993 100.037 28.097 100.256 27.4336C100.476 26.7701 100.832 26.1602 101.301 25.6427C101.769 25.1253 102.34 24.7119 102.977 24.429C103.614 24.1461 104.303 24 105 24C105.697 24 106.386 24.1461 107.023 24.429C107.66 24.7119 108.231 25.1253 108.699 25.6427C109.168 26.1602 109.524 26.7701 109.744 27.4336C109.963 28.097 110.043 28.7993 109.976 29.4953C110.044 30.192 109.966 30.8953 109.746 31.5598C109.527 32.2243 109.172 32.8354 108.703 33.3538C108.234 33.8723 107.663 34.2865 107.025 34.57C106.387 34.8535 105.697 35 105 35C104.303 35 103.613 34.8535 102.975 34.57C102.337 34.2865 101.766 33.8723 101.297 33.3538C100.828 32.8354 100.473 32.2243 100.254 31.5598C100.034 30.8953 99.9559 30.192 100.024 29.4953V29.4953ZM107.834 29.4953C107.834 27.1995 106.807 25.857 105.003 25.857C103.193 25.857 102.175 27.1995 102.175 29.4953C102.175 31.8095 103.193 33.1417 105.003 33.1417C106.807 33.1417 107.834 31.8003 107.834 29.4953H107.834Z" fill="white"/>
+<path d="M124.028 35H121.812L119.575 26.8985H119.406L117.178 35H114.984L112 24H114.167L116.106 32.3936H116.265L118.491 24H120.54L122.765 32.3936H122.934L124.864 24H127L124.028 35Z" fill="white"/>
+<path d="M130 24.2187H132.005V25.9314H132.161C132.425 25.3163 132.87 24.8006 133.435 24.4563C133.999 24.112 134.655 23.9562 135.31 24.0106C135.824 23.9712 136.339 24.0503 136.819 24.242C137.298 24.4338 137.729 24.7333 138.08 25.1185C138.431 25.5036 138.693 25.9644 138.845 26.4667C138.998 26.9691 139.038 27.5002 138.963 28.0206V34.9999H136.88V28.5549C136.88 26.8223 136.143 25.9607 134.603 25.9607C134.254 25.9441 133.906 26.0047 133.583 26.1384C133.259 26.272 132.968 26.4756 132.728 26.7351C132.489 26.9946 132.307 27.3039 132.196 27.6418C132.084 27.9797 132.046 28.3382 132.083 28.6928V35H130L130 24.2187Z" fill="white"/>
+<path d="M142 20H144V35H142V20Z" fill="white"/>
+<path d="M147.023 29.4954C146.957 28.7994 147.037 28.0971 147.257 27.4336C147.476 26.7702 147.832 26.1602 148.301 25.6428C148.769 25.1253 149.34 24.7119 149.977 24.429C150.614 24.1461 151.303 24 152 24C152.697 24 153.386 24.1461 154.023 24.429C154.66 24.7119 155.231 25.1253 155.699 25.6428C156.168 26.1602 156.524 26.7702 156.743 27.4336C156.963 28.0971 157.043 28.7994 156.977 29.4954C157.044 30.1922 156.966 30.8954 156.746 31.5599C156.527 32.2244 156.171 32.8355 155.703 33.3539C155.234 33.8723 154.662 34.2866 154.025 34.5701C153.387 34.8536 152.697 35 152 35C151.303 35 150.613 34.8536 149.975 34.5701C149.338 34.2866 148.766 33.8723 148.297 33.3539C147.829 32.8355 147.473 32.2244 147.254 31.5599C147.034 30.8954 146.956 30.1922 147.023 29.4954V29.4954ZM154.833 29.4954C154.833 27.1997 153.806 25.8571 152.003 25.8571C150.193 25.8571 149.175 27.1997 149.175 29.4955C149.175 31.8096 150.193 33.1418 152.003 33.1418C153.806 33.1418 154.834 31.8004 154.834 29.4954H154.833Z" fill="white"/>
+<path d="M160 31.8094C160 29.9008 161.437 28.8004 163.988 28.644L166.893 28.4785V27.5632C166.893 26.4433 166.144 25.8109 164.698 25.8109C163.516 25.8109 162.698 26.2398 162.463 26.9895H160.414C160.63 25.1682 162.363 24 164.795 24C167.484 24 169 25.3234 169 27.5632V34.8081H166.963V33.318H166.795C166.455 33.8525 165.978 34.2882 165.412 34.5807C164.846 34.8732 164.212 35.012 163.574 34.9828C163.124 35.0291 162.669 34.9816 162.239 34.8434C161.808 34.7052 161.412 34.4793 161.075 34.1803C160.738 33.8813 160.469 33.5159 160.284 33.1075C160.098 32.6992 160.002 32.257 160 31.8094V31.8094ZM166.893 30.9034V30.0169L164.274 30.1825C162.798 30.2802 162.128 30.7769 162.128 31.7117C162.128 32.666 162.965 33.2214 164.116 33.2214C164.454 33.2551 164.794 33.2214 165.118 33.1223C165.442 33.0232 165.743 32.8607 166.002 32.6444C166.261 32.4282 166.473 32.1626 166.626 31.8635C166.779 31.5644 166.87 31.2379 166.893 30.9034V30.9034Z" fill="white"/>
+<path d="M172 29.4871C172 26.1186 173.739 23.9847 176.445 23.9847C177.114 23.954 177.778 24.1135 178.36 24.4447C178.941 24.7759 179.416 25.265 179.729 25.8549H179.887V20H182V34.8218H179.975V33.1375H179.808C179.471 33.7235 178.98 34.2065 178.387 34.5343C177.794 34.8622 177.123 35.0224 176.445 34.9975C173.721 34.9976 172 32.8636 172 29.4871ZM174.183 29.4871C174.183 31.7481 175.254 33.1087 177.044 33.1087C178.825 33.1087 179.926 31.7285 179.926 29.4963C179.926 27.2745 178.814 25.8747 177.044 25.8747C175.265 25.8747 174.183 27.2445 174.183 29.4871H174.183Z" fill="white"/>
+<path d="M191.024 29.4953C190.957 28.7993 191.037 28.097 191.256 27.4336C191.476 26.7701 191.832 26.1602 192.301 25.6427C192.769 25.1253 193.34 24.7119 193.977 24.429C194.614 24.1461 195.303 24 196 24C196.697 24 197.386 24.1461 198.023 24.429C198.66 24.7119 199.231 25.1253 199.699 25.6427C200.168 26.1602 200.524 26.7701 200.744 27.4336C200.963 28.097 201.043 28.7993 200.976 29.4953C201.044 30.192 200.966 30.8953 200.746 31.5598C200.527 32.2243 200.172 32.8354 199.703 33.3538C199.234 33.8723 198.663 34.2865 198.025 34.57C197.387 34.8535 196.697 35 196 35C195.303 35 194.613 34.8535 193.975 34.57C193.337 34.2865 192.766 33.8723 192.297 33.3538C191.828 32.8354 191.473 32.2243 191.254 31.5598C191.034 30.8953 190.956 30.192 191.024 29.4953V29.4953ZM198.834 29.4953C198.834 27.1995 197.807 25.857 196.003 25.857C194.193 25.857 193.175 27.1995 193.175 29.4953C193.175 31.8095 194.193 33.1417 196.003 33.1417C197.807 33.1417 198.834 31.8003 198.834 29.4953Z" fill="white"/>
+<path d="M204 24.2187H206.005V25.9314H206.161C206.425 25.3163 206.87 24.8006 207.435 24.4563C207.999 24.112 208.655 23.9562 209.31 24.0106C209.824 23.9712 210.339 24.0503 210.819 24.242C211.298 24.4338 211.729 24.7333 212.08 25.1185C212.431 25.5036 212.693 25.9644 212.845 26.4667C212.998 26.9691 213.038 27.5002 212.963 28.0206V34.9999H210.88V28.5549C210.88 26.8223 210.143 25.9607 208.603 25.9607C208.254 25.9441 207.906 26.0047 207.583 26.1384C207.259 26.272 206.968 26.4756 206.728 26.7351C206.489 26.9946 206.307 27.3039 206.196 27.6418C206.084 27.9797 206.046 28.3382 206.083 28.6928V35H204V24.2187Z" fill="white"/>
+<path d="M224.731 22V24.6272H227V26.3498H224.731V31.6784C224.731 32.7639 225.183 33.2392 226.212 33.2392C226.475 33.2384 226.738 33.2226 227 33.192V34.8955C226.629 34.9612 226.253 34.9962 225.876 35C223.578 35 222.662 34.1999 222.662 32.202V26.3497H221V24.6271H222.662V22H224.731Z" fill="white"/>
+<path d="M230 20H232.039V25.9453H232.202C232.475 25.324 232.927 24.805 233.496 24.4592C234.064 24.1133 234.722 23.9574 235.38 24.0126C235.884 23.9842 236.389 24.0714 236.857 24.268C237.325 24.4645 237.746 24.7656 238.089 25.1498C238.431 25.5339 238.688 25.9917 238.84 26.4903C238.993 26.989 239.037 27.5163 238.969 28.0346V35H236.91V28.5599C236.91 26.8367 236.135 25.9639 234.682 25.9639C234.328 25.9339 233.972 25.9842 233.64 26.1113C233.307 26.2385 233.005 26.4393 232.755 26.6998C232.505 26.9603 232.313 27.2743 232.192 27.6196C232.072 27.9649 232.026 28.3333 232.057 28.6991V35H230L230 20Z" fill="white"/>
+<path d="M251.869 31.9179C251.575 32.8836 250.938 33.719 250.068 34.2779C249.199 34.8367 248.154 35.0835 247.115 34.975C246.392 34.9934 245.674 34.86 245.01 34.5842C244.346 34.3084 243.752 33.8968 243.27 33.3778C242.788 32.8589 242.429 32.2451 242.217 31.5791C242.006 30.913 241.948 30.2107 242.047 29.5208C241.951 28.8288 242.01 28.125 242.22 27.4569C242.431 26.7889 242.789 26.1723 243.268 25.6488C243.748 25.1253 244.339 24.7072 245.001 24.4228C245.663 24.1383 246.381 23.9942 247.105 24.0002C250.158 24.0002 252 26.0089 252 29.3271V30.0548H244.253V30.1717C244.219 30.5594 244.269 30.9498 244.401 31.3176C244.533 31.6855 244.743 32.0227 245.018 32.3077C245.293 32.5927 245.627 32.8192 245.999 32.9727C246.37 33.1262 246.77 33.2032 247.174 33.1989C247.692 33.2588 248.217 33.1689 248.682 32.9408C249.147 32.7127 249.531 32.3566 249.785 31.9178L251.869 31.9179ZM244.252 28.5125H249.794C249.821 28.1578 249.771 27.8016 249.647 27.4667C249.523 27.1318 249.328 26.8256 249.073 26.5678C248.819 26.31 248.511 26.1063 248.17 25.9698C247.829 25.8332 247.463 25.7669 247.094 25.7751C246.719 25.7706 246.347 25.8383 246 25.9742C245.654 26.1102 245.338 26.3117 245.074 26.5668C244.809 26.8219 244.6 27.1255 244.459 27.4597C244.318 27.7938 244.248 28.1518 244.253 28.5125H244.252Z" fill="white"/>
+</svg>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 8 - 0
dist/assets/icon/icon-google-play.svg


binární
dist/assets/icon/icon.png


binární
dist/assets/icon/jigsaw-puzzle.webp


binární
dist/assets/icon/logo_1024x1024.png


binární
dist/assets/icon/logo_640x640.webp


binární
dist/assets/icon/soduko.webp


+ 4 - 0
dist/assets/svg/email.svg

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 5.25L3 6V18L3.75 18.75H20.25L21 18V6L20.25 5.25H3.75ZM4.5 7.6955V17.25H19.5V7.69525L11.9999 14.5136L4.5 7.6955ZM18.3099 6.75H5.68986L11.9999 12.4864L18.3099 6.75Z" fill="#e864ff"/>
+</svg>

+ 17 - 0
dist/assets/svg/location.svg

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="-4 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+    
+    <title>location</title>
+    <desc>Created with Sketch Beta.</desc>
+    <defs>
+
+</defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="Icon-Set-Filled" sketch:type="MSLayerGroup" transform="translate(-106.000000, -413.000000)" fill="#e864ff">
+            <path d="M118,422 C116.343,422 115,423.343 115,425 C115,426.657 116.343,428 118,428 C119.657,428 121,426.657 121,425 C121,423.343 119.657,422 118,422 L118,422 Z M118,430 C115.239,430 113,427.762 113,425 C113,422.238 115.239,420 118,420 C120.761,420 123,422.238 123,425 C123,427.762 120.761,430 118,430 L118,430 Z M118,413 C111.373,413 106,418.373 106,425 C106,430.018 116.005,445.011 118,445 C119.964,445.011 130,429.95 130,425 C130,418.373 124.627,413 118,413 L118,413 Z" id="location" sketch:type="MSShapeGroup">
+
+</path>
+        </g>
+    </g>
+</svg>

+ 277 - 0
dist/assets/svg/texture.svg

@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 26.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="1168.2px" height="1168.2px" viewBox="0 0 1168.2 1168.2" style="enable-background:new 0 0 1168.2 1168.2;"
+	 xml:space="preserve">
+<style type="text/css">
+	.st0{display:none;}
+	.st1{display:inline;fill:url(#SVGID_1_);}
+	.st2{display:inline;}
+	.st3{display:inline;opacity:0.2;}
+	.st4{fill:url(#SVGID_00000013876625521378572630000009051402063682515858_);}
+	.st5{fill:url(#SVGID_00000039118223212837949370000012024280464305296304_);}
+	.st6{fill:url(#SVGID_00000183927177412733518610000008967357782393352834_);}
+	.st7{fill:url(#SVGID_00000095300302064861391960000011219852939156737175_);}
+	.st8{fill:url(#SVGID_00000158720300682388720100000016226028251708523451_);}
+	.st9{fill:url(#SVGID_00000139270344152033582590000010546892582329577609_);}
+	.st10{fill:url(#SVGID_00000113314426626039982960000012910754779593404062_);}
+	.st11{fill:url(#SVGID_00000078013155287542697320000012008027321580264600_);}
+	.st12{fill:url(#SVGID_00000099638646901217861690000001510839386397433755_);}
+	.st13{fill:url(#SVGID_00000010303253420293972500000008159622301727577741_);}
+	.st14{fill:url(#SVGID_00000114792211747641231170000001591297360075317418_);}
+	.st15{display:inline;fill:#F2CBC1;}
+	.st16{display:inline;fill:#F9E2D0;}
+	.st17{display:inline;fill:url(#SVGID_00000166647538051529697860000017863581180714283671_);}
+	.st18{opacity:0.2;}
+	.st19{fill:url(#SVGID_00000106860056406895301610000013913208819197626261_);}
+	.st20{fill:url(#SVGID_00000131364137968149240540000004515045657738768312_);}
+	.st21{fill:url(#SVGID_00000144307065933018253710000016635079631029210302_);}
+	.st22{fill:url(#SVGID_00000071536998161421731720000015903826852672965008_);}
+	.st23{fill:url(#SVGID_00000145040306334952910350000011164132950240551615_);}
+	.st24{fill:url(#SVGID_00000173873186455674038930000013001535246302907039_);}
+	.st25{fill:url(#SVGID_00000141449930206712463700000001843019603015368631_);}
+	.st26{fill:url(#SVGID_00000045615493426419426700000001862415832791775665_);}
+	.st27{fill:url(#SVGID_00000067922273273569647190000000308464318071643279_);}
+	.st28{fill:url(#SVGID_00000039820148634623344830000014476944639444888198_);}
+	.st29{fill:url(#SVGID_00000018938433173893923810000015200042971366111141_);}
+	.st30{fill:url(#SVGID_00000060751948487493325090000008588815525491021456_);}
+	.st31{fill:url(#SVGID_00000070825287066066906850000013632751707933118871_);}
+	.st32{fill:url(#SVGID_00000111166603511641038080000011227696324561133964_);}
+	.st33{fill:url(#SVGID_00000134221833650935909540000013009268934693735101_);}
+	.st34{fill:url(#SVGID_00000060724179367336276680000018277870686916204697_);}
+	.st35{fill:url(#SVGID_00000082326371432427869090000008499075865256121239_);}
+	.st36{fill:url(#SVGID_00000161591802338813092500000003341749176800070295_);}
+	.st37{fill:url(#SVGID_00000080169175231210442760000000746089521363082685_);}
+	.st38{fill:url(#SVGID_00000157300896655554106400000002291424433445937855_);}
+	.st39{fill:url(#SVGID_00000054236833068815251660000017401695736854771091_);}
+	.st40{fill:url(#SVGID_00000045588560673944390640000009309385599389670033_);}
+</style>
+<g id="图层_1" class="st0">
+</g>
+<g id="图层_2">
+</g>
+<g id="图层_3">
+	<g class="st18">
+		<g>
+			
+				<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(5.567581e-02 -3.214445e-02 3.214445e-02 5.567581e-02 570.116 737.1)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_1_);" d="M603.1,726.1C603.1,726.1,603.1,726.1,603.1,726.1c5.7-3.2,9.3-2.8,12.8-2.3
+				c1.6,0.2,3.2,0.4,4.9,0.3c1.9-0.2,3.8-0.7,5.8-1.8c4-2.2,7.5-3.5,10.4-4c2.6-0.4,4.7-0.2,6.4,0.7c2,1.1,3.4,3,4.3,6
+				c0.8,2.6,1.1,5.5,1.3,8c0,0.3,0.1,0.6,0.1,1c0.3,3,1.2,5.6,2.8,7.8c1.4,1.9,3.1,3.4,4.6,4.6c1.5,1.2,2.8,2.4,3.6,3.8
+				c0.9,1.5,1,3.2,0.3,5.3c-0.6,1.8-1.3,3.3-2.1,4.4c-0.8,1-1.7,1.9-2.7,2.5c-1.9,1.1-4,1.4-6.1,1.7c-2.1,0.3-4.3,0.7-6.3,1.8
+				c-1.3,0.8-2.4,1.8-3.4,3.2c-2.3,3.5-4.8,6.6-7.6,9.3c-2.5,2.5-5.1,4.5-7.6,6c-2.1,1.2-4.1,2-5.9,2.3c-1.8,0.3-3.3,0.2-4.4-0.4
+				c-0.7-0.4-1.3-0.9-1.8-1.7c-0.4-0.8-0.7-1.7-0.8-2.8c-0.2-1.9-0.6-3.4-1.4-4.6c-0.7-1.1-1.7-2-2.9-2.7c-2.3-1.3-5.2-1.7-8-2.1
+				c-4.1-0.6-8-1.1-9.7-4.2c-1.5-2.8-1.6-5.2-0.4-7.6c1.1-2.1,3-3.8,4.7-5.3c1.1-0.9,2.1-1.8,2.8-2.7c0.8-1,1.2-1.9,1.2-2.8
+				c-0.1-1.3-1-2.6-2.9-4.1c-0.8-0.6-1.4-1.4-1.8-2.3c-0.3-0.8-0.5-1.8-0.4-2.9c0.1-2.4,1.3-5.2,3.2-7.9
+				C598,730,600.5,727.6,603.1,726.1 M603.1,726.1c-7.7,4.5-13.7,15.5-8.1,19.7c10.7,7.9-12.1,10-5.4,22.4c4,7.4,20.8,0.3,22,13.6
+				c0.5,6,6.1,6.6,13,2.6c4.9-2.8,10.4-8,15.2-15.3c1-1.5,2.1-2.5,3.3-3.2c3.9-2.2,8.6-1.4,12.4-3.5c2-1.2,3.7-3.2,4.9-6.9
+				c2.9-9.2-10-8.2-11.3-21.5c-0.8-8.5-1.4-23.3-22.6-11.7C617.4,727.1,613.8,719.9,603.1,726.1C603.1,726,603.1,726.1,603.1,726.1
+				L603.1,726.1z"/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000139273189893873779670000012525713012056904086_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(7.537082e-02 -3.840335e-02 3.840335e-02 7.537082e-02 552.9653 726.0894)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000139273189893873779670000012525713012056904086_);" d="M597,713.9c0,0,0.1,0,0.1-0.1
+				c7.6-3.8,12.3-3,16.9-2.1c2.1,0.4,4.1,0.7,6.4,0.7c2.6-0.1,5-0.7,7.7-2c5.4-2.6,10.1-4.1,13.9-4.6c3.4-0.4,6.2,0.1,8.4,1.4
+				c2.6,1.5,4.3,4.2,5.2,8.2c0.8,3.5,1,7.3,1.1,10.6c0,0.4,0,0.9,0.1,1.3c0.2,4,1.2,7.4,3.1,10.4c1.7,2.6,3.8,4.7,5.7,6.4
+				c1.8,1.7,3.5,3.3,4.5,5.2c1,2.1,1.1,4.3,0.1,6.9c-0.9,2.3-1.9,4.2-3.1,5.7c-1.1,1.3-2.3,2.3-3.8,3.1c-2.5,1.3-5.4,1.6-8.1,1.8
+				c-2.8,0.3-5.8,0.6-8.4,1.9c-1.8,0.9-3.3,2.2-4.6,4c-3.3,4.4-6.8,8.3-10.6,11.7c-3.5,3.1-7,5.6-10.5,7.3c-2.8,1.4-5.5,2.3-7.9,2.6
+				c-2.4,0.3-4.3,0-5.8-0.9c-0.9-0.6-1.7-1.3-2.2-2.3c-0.5-1-0.8-2.3-0.9-3.7c-0.1-2.5-0.6-4.5-1.6-6.2c-0.9-1.5-2-2.7-3.6-3.7
+				c-2.9-1.8-6.7-2.6-10.4-3.3c-5.4-1-10.4-2-12.4-6.2c-1.8-3.7-1.8-7,0-10c1.5-2.6,4.2-4.7,6.6-6.6c1.5-1.1,2.8-2.2,3.9-3.3
+				c1.2-1.3,1.7-2.4,1.7-3.6c0-1.7-1.1-3.5-3.6-5.5c-1-0.8-1.7-1.9-2.2-3.1c-0.4-1.1-0.5-2.4-0.4-3.8c0.3-3.1,2-6.7,4.7-10.2
+				C590,718.6,593.5,715.8,597,713.9 M597,713.9c-10.5,5.3-19.1,19.4-12,25.3c13.5,11.2-16.5,12.4-8.6,29c4.8,10,27.4,1.8,28,19.4
+				c0.3,7.9,7.6,9.1,16.9,4.4c6.6-3.4,14.3-9.8,21.1-19c1.4-1.9,3-3.2,4.6-4c5.3-2.7,11.4-1.2,16.5-3.8c2.7-1.4,5.1-3.9,6.9-8.8
+				c4.4-11.9-12.6-11.4-13.4-29c-0.5-11.3-0.2-30.8-28.9-17c-12.3,5.9-16.5-3.8-31,3.5C597.1,713.8,597,713.9,597,713.9L597,713.9z"
+				/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000141414040449487629290000012783908685379608211_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(0.1017 -4.527120e-02 4.527120e-02 0.1017 531.1877 710.4405)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000141414040449487629290000012783908685379608211_);" d="M589.9,697.5c0,0,0.1,0,0.1-0.1
+				c10.2-4.5,16.4-3,22.4-1.6c2.8,0.7,5.4,1.3,8.4,1.3c3.4,0.1,6.6-0.6,10.2-2.1c7.3-3,13.6-4.7,18.6-5.1c4.5-0.3,8.2,0.5,10.9,2.4
+				c3.2,2.2,5.3,5.8,6.3,11.2c0.9,4.6,0.8,9.6,0.8,14.1c0,0.6,0,1.1,0,1.7c0,5.3,1.1,9.8,3.4,13.9c2,3.6,4.7,6.4,7,8.8
+				c2.3,2.4,4.4,4.6,5.6,7.1c1.2,2.8,1.1,5.7-0.4,9.1c-1.3,3-2.8,5.4-4.5,7.3c-1.5,1.7-3.2,2.9-5.2,3.8c-3.4,1.5-7.2,1.7-10.8,1.9
+				c-3.7,0.2-7.6,0.4-11.2,2c-2.4,1.1-4.5,2.7-6.4,5c-4.6,5.6-9.5,10.5-14.7,14.6c-4.8,3.8-9.6,6.8-14.2,8.9
+				c-3.8,1.7-7.4,2.7-10.5,2.9c-3.1,0.2-5.7-0.3-7.6-1.5c-1.2-0.8-2.1-1.9-2.7-3.2c-0.6-1.4-0.9-3.1-0.9-5c0-3.2-0.5-5.9-1.6-8.2
+				c-1-2-2.5-3.7-4.5-5.2c-3.7-2.6-8.7-3.9-13.5-5.1c-7-1.7-13.6-3.4-15.9-9c-2.1-5-1.9-9.3,0.6-13.2c2.2-3.4,5.9-5.9,9.1-8.2
+				c2-1.4,3.9-2.7,5.3-4.1c1.6-1.6,2.4-3,2.5-4.6c0.1-2.3-1.3-4.7-4.3-7.5c-1.3-1.2-2.2-2.6-2.6-4.2c-0.4-1.5-0.5-3.2-0.2-5.1
+				c0.7-4.1,3.1-8.7,6.9-13C580.4,703.2,585.1,699.7,589.9,697.5 M589.9,697.4c-14.2,6.3-26.4,24.2-17.5,32.5
+				c17,15.6-22.6,15.1-13.3,37.5c5.6,13.5,35.8,4.3,35.5,27.4c-0.2,10.4,9.4,12.4,21.9,6.9c9-4,19.4-11.9,29-23.5
+				c2-2.4,4.1-3.9,6.3-4.9c7.1-3.2,15.1-0.8,22-3.8c3.6-1.6,7-4.8,9.7-11.1c6.6-15.3-15.8-15.9-15.6-39.1
+				c0.1-14.8,1.9-40.4-36.8-24.3c-16.6,6.9-21.5-6.2-41,2.4C590,697.4,589.9,697.4,589.9,697.4L589.9,697.4z"/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000031184187440755313260000013744260082570726791_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(0.1367 -5.248369e-02 5.248369e-02 0.1367 503.65 688.3785)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000031184187440755313260000013744260082570726791_);" d="M581.7,675.4c0.1,0,0.1,0,0.2-0.1
+				c13.7-5.2,21.8-2.9,29.5-0.6c3.6,1,7,2,10.9,2.3c4.4,0.3,8.8-0.3,13.6-2.1c9.8-3.5,18.2-5.3,24.8-5.4c5.9-0.1,10.7,1.2,14.2,3.9
+				c4.1,3.1,6.6,8.1,7.5,15.1c0.8,6.1,0.4,12.7,0,18.5c0,0.7-0.1,1.5-0.1,2.2c-0.4,6.9,0.7,13,3.5,18.6c2.4,4.9,5.7,8.7,8.6,12.1
+				c2.8,3.3,5.5,6.4,6.8,9.8c1.4,3.7,1.1,7.5-1.1,11.9c-1.9,3.9-4,6.9-6.4,9.2c-2.1,2.1-4.4,3.6-7,4.6c-4.6,1.8-9.5,1.7-14.3,1.7
+				c-4.9,0-10,0-14.8,1.8c-3.3,1.3-6.1,3.2-8.7,6.1c-6.4,7-13.2,13.1-20.3,18.2c-6.6,4.7-13.1,8.3-19.3,10.7c-5.2,2-9.9,3-14,3.1
+				c-4.1,0.1-7.4-0.7-9.8-2.5c-1.5-1.1-2.6-2.6-3.4-4.4c-0.7-1.9-1-4.1-0.9-6.6c0.3-4.2-0.2-7.8-1.6-10.9c-1.2-2.8-3-5.1-5.6-7.1
+				c-4.7-3.7-11.1-5.7-17.4-7.6c-9-2.8-17.6-5.4-20.3-12.9c-2.4-6.7-1.8-12.4,1.7-17.3c3.1-4.3,8.1-7.4,12.6-10.2
+				c2.7-1.7,5.3-3.3,7.3-5.1c2.2-2,3.4-3.8,3.6-5.8c0.4-2.9-1.3-6.3-5.1-10.2c-1.6-1.6-2.7-3.5-3.2-5.7c-0.5-2-0.4-4.3,0.1-6.7
+				c1.2-5.3,4.7-11.2,10-16.6C568.8,682.3,575.3,677.9,581.7,675.4 M581.7,675.3c-19,7.3-36.4,30-25.2,41.4
+				c21.2,21.6-30.7,18.3-20.1,48.4c6.4,18.1,46.8,8.1,44.7,38.4c-0.9,13.6,11.4,17,28.2,10.6c12-4.6,26.4-14.3,39.7-28.9
+				c2.8-3,5.7-4.9,8.7-6c9.5-3.7,19.9,0,29.2-3.5c4.9-1.9,9.5-5.8,13.5-13.9c9.7-19.7-19.6-21.9-17.8-52.4
+				c1.2-19.5,5.2-53-46.7-34.5c-22.2,7.9-27.8-9.6-54,0.3C581.8,675.3,581.8,675.3,581.7,675.3L581.7,675.3z"/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000016758950611356348120000016248425938088094904_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(0.1833 -5.954755e-02 5.954755e-02 0.1833 468.985 657.4929)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000016758950611356348120000016248425938088094904_);" d="M572.5,645.9c0.1,0,0.2-0.1,0.3-0.1
+				c18.4-5.9,28.8-2.3,38.8,1.3c4.7,1.6,9.1,3.2,14.2,3.8c5.8,0.7,11.5,0.2,18-1.8c13.1-3.9,24.2-5.7,33-5.4
+				c7.8,0.3,14,2.4,18.4,6.1c5.2,4.4,8.1,11,8.9,20.4c0.7,8.1-0.3,16.7-1.2,24.4c-0.1,1-0.2,1.9-0.3,2.9c-1,9,0.1,17.1,3.3,24.6
+				c2.8,6.5,6.9,11.8,10.5,16.5c3.5,4.5,6.8,8.8,8.3,13.3c1.6,5,0.9,10-2.3,15.6c-2.8,5-5.7,8.8-9,11.7c-2.9,2.6-6,4.4-9.6,5.5
+				c-6.1,2-12.6,1.6-18.9,1.3c-6.5-0.4-13.2-0.7-19.6,1.4c-4.4,1.4-8.3,3.8-11.9,7.4c-8.9,8.8-18.3,16.3-28,22.5
+				c-9,5.7-17.7,10-26.1,12.7c-6.9,2.2-13.2,3.3-18.6,3.2c-5.4-0.1-9.7-1.5-12.8-4c-1.9-1.6-3.3-3.6-4.1-6c-0.8-2.5-1.1-5.5-0.7-8.7
+				c0.7-5.6,0.2-10.3-1.3-14.5c-1.4-3.7-3.6-6.9-6.8-9.7c-5.9-5.2-14.2-8.2-22.3-11.1c-11.7-4.3-22.7-8.3-25.7-18.4
+				c-2.7-9-1.5-16.4,3.5-22.6c4.4-5.4,11.2-9.2,17.2-12.5c3.7-2.1,7.2-4,9.9-6.1c3.1-2.4,4.7-4.8,5.1-7.4c0.7-3.8-1.3-8.3-6.1-13.7
+				c-2-2.2-3.2-4.8-3.8-7.7c-0.5-2.7-0.3-5.7,0.6-8.8c1.9-6.9,7-14.4,14.3-21.2C555,653.9,563.8,648.7,572.5,645.9 M572.4,645.7
+				c-25.5,8.3-49.9,36.9-36,52.7c26.4,29.9-41.6,21.9-29.7,62.2c7.2,24.2,60.9,13.9,56.1,53.6c-2.1,17.8,13.9,23.1,36.4,15.8
+				c16.1-5.2,35.6-17,54.2-35.3c3.8-3.8,7.8-6,11.8-7.3c12.8-4.2,26.1,1.4,38.6-2.6c6.6-2.1,12.9-7,18.7-17.3
+				c14.1-25.2-24.3-30.2-19.8-70.1c2.9-25.5,10.5-69.3-59-48.5c-29.8,8.9-35.9-14.5-71-3.3C572.6,645.6,572.5,645.7,572.4,645.7
+				L572.4,645.7z"/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000142163085233467063680000001841460463426513817_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(0.2449 -6.562422e-02 6.562422e-02 0.2449 425.5626 614.5226)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000142163085233467063680000001841460463426513817_);" d="M562.4,606.4c0.1,0,0.2-0.1,0.3-0.1
+				c24.6-6.5,38-1,51,4.3c6,2.5,11.7,4.8,18.4,6c7.6,1.4,15.1,1,23.8-1.1c17.5-4.2,32.2-5.8,43.7-4.8c10.2,1,18.2,4.1,23.8,9.2
+				c6.5,6.1,9.9,15.1,10.3,27.4c0.3,10.7-1.6,22-3.3,31.9c-0.2,1.3-0.4,2.5-0.6,3.7c-2,11.8-1.1,22.5,2.6,32.6
+				c3.3,8.8,8.3,16,12.7,22.4c4.3,6.2,8.3,12,9.9,18.1c1.8,6.7,0.5,13.2-4.1,20.4c-4,6.3-8.1,11.1-12.7,14.7
+				c-4,3.2-8.2,5.4-12.9,6.6c-8.2,2.2-16.7,1.3-24.9,0.4c-8.5-0.9-17.2-1.9-25.9,0.4c-5.9,1.6-11.1,4.5-16.1,8.9
+				c-12.3,10.9-25.2,20.2-38.3,27.6c-12.2,6.9-24,11.9-35.2,14.9c-9.2,2.5-17.5,3.4-24.7,2.9c-7.1-0.6-12.7-2.6-16.5-6.1
+				c-2.4-2.2-4.1-5-5-8.2c-0.9-3.4-1-7.2-0.3-11.5c1.3-7.3,1-13.5-0.7-19.1c-1.6-5-4.3-9.3-8.3-13.2c-7.4-7.2-18.2-11.8-28.5-16.2
+				c-15.1-6.4-29.3-12.5-32.5-25.9c-2.9-12.1-0.9-21.7,6.1-29.4c6.1-6.8,15.3-11.3,23.5-15.2c5-2.4,9.7-4.8,13.4-7.4
+				c4.2-3,6.5-6,7.3-9.4c1.1-5-1.2-11-7-18.4c-2.5-3.1-3.9-6.6-4.4-10.4c-0.4-3.6,0-7.5,1.3-11.5c3-8.9,10.1-18.5,20.2-26.8
+				C538.9,615.8,550.8,609.5,562.4,606.4 M562.3,606.1c-34.1,9.1-68.1,45.1-50.9,66.8c32.6,41.1-56.2,25.9-43.3,79.7
+				c7.7,32.3,79.1,22.5,70.1,74.3c-4,23.2,16.6,31.3,46.7,23.3c21.6-5.8,48-19.8,73.6-42.6c5.3-4.7,10.6-7.4,16-8.8
+				c17.1-4.6,34.2,3.7,50.8-0.8c8.8-2.4,17.4-8.2,25.8-21.5c20.3-32.1-29.9-41.3-21.2-93.5c5.5-33.3,18.6-90.3-74.1-67.8
+				c-39.7,9.6-46.1-21.5-93.1-9.2C562.5,606.1,562.4,606.1,562.3,606.1L562.3,606.1z"/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000022534453224654761280000010420672009190804880_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(0.3263 -6.936386e-02 6.936386e-02 0.3263 371.4652 555.07)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000022534453224654761280000010420672009190804880_);" d="M551.8,553.8c0.2,0,0.3-0.1,0.5-0.1
+				c32.7-6.8,50,1.3,66.7,9.2c7.7,3.6,15,7.1,23.7,9.1c9.9,2.3,19.8,2.4,31.3,0.2c23.3-4.4,42.8-5.5,57.8-3.3
+				c13.4,2,23.7,6.6,30.6,13.8c8.2,8.5,12,20.5,11.6,36.7c-0.3,14-3.6,28.7-6.5,41.7c-0.4,1.7-0.7,3.3-1.1,4.9
+				c-3.4,15.4-3,29.5,1.2,43c3.7,11.8,9.8,21.6,15.1,30.3c5.2,8.4,10.1,16.3,11.8,24.4c1.9,8.9-0.3,17.3-6.8,26.5
+				c-5.7,8-11.5,14.1-17.7,18.5c-5.5,3.9-11.2,6.5-17.5,7.8c-10.9,2.3-22,0.5-32.8-1.2c-11.1-1.8-22.5-3.7-34.1-1.2
+				c-7.8,1.7-14.9,5.1-21.8,10.6c-16.9,13.5-34.5,24.8-52.2,33.7c-16.5,8.2-32.4,14-47.3,17.2c-12.3,2.6-23.3,3.3-32.6,2.1
+				c-9.3-1.2-16.5-4.3-21.2-9.2c-3-3.1-5-6.8-6-11.1c-1-4.5-0.8-9.6,0.4-15.2c2.2-9.4,2.3-17.7,0.4-25.1c-1.7-6.6-5-12.5-10-17.9
+				c-9.3-10-23-16.7-36.4-23.2c-19.4-9.5-37.7-18.4-41-36.3c-3-16,0.3-28.5,10-38.2c8.5-8.5,20.9-13.8,31.9-18.4
+				c6.7-2.9,13.1-5.6,18.2-8.8c5.7-3.6,9-7.4,10.2-11.8c1.8-6.5-0.8-14.6-7.9-24.7c-3-4.2-4.7-8.9-5.1-14c-0.3-4.7,0.5-9.8,2.6-15
+				c4.5-11.6,14.6-23.6,28.4-33.8C520.3,564.5,536.4,557.1,551.8,553.8 M551.7,553.5c-45.4,9.7-92.6,54.6-71.4,84.3
+				c40,56.3-75.6,30.2-62.4,101.8c7.9,43,102.4,35,87,102.4c-6.9,30.3,19.7,42.3,59.8,33.8c28.7-6.1,64.4-22.8,99.6-50.9
+				c7.3-5.8,14.5-9,21.6-10.5c22.8-4.8,44.8,7.2,66.9,2.5c11.7-2.5,23.4-9.6,35.3-26.4c28.9-40.8-36.4-56.3-21.4-124.3
+				c9.6-43.4,30.7-117.3-92.8-94.2c-52.9,9.9-59.1-31.5-121.7-18.5C552,553.4,551.9,553.4,551.7,553.5L551.7,553.5z"/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000012449139060791388480000017832418360500245945_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(0.4336 -6.867097e-02 6.867097e-02 0.4336 304.4761 473.2248)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000012449139060791388480000017832418360500245945_);" d="M541.5,484c0.2,0,0.4-0.1,0.6-0.1
+				c43.5-6.7,65.6,5.2,87,16.7c9.9,5.3,19.2,10.3,30.6,13.6c12.8,3.7,25.9,4.5,41.1,2.4c30.9-4.1,56.6-4.2,76.2-0.3
+				c17.4,3.5,30.6,10.3,39.2,20.2c10.1,11.7,14.3,27.7,12.7,49c-1.4,18.4-6.7,37.5-11.4,54.4c-0.6,2.2-1.2,4.3-1.8,6.3
+				c-5.5,20-6,38.5-1.3,56.6c4,15.7,11.4,29.1,17.8,40.8c6.3,11.4,12.2,22.1,13.9,32.9c1.8,11.8-1.6,22.8-10.7,34.3
+				c-8,10.2-16,17.7-24.5,23.1c-7.5,4.8-15.2,7.7-23.5,9c-14.5,2.3-29-0.9-43-3.9c-14.4-3.1-29.3-6.4-44.7-3.9
+				c-10.4,1.6-20,5.7-29.3,12.4c-23.1,16.5-47,30.2-70.9,40.6c-22.2,9.7-43.5,16.2-63.3,19.3c-16.3,2.6-30.8,2.7-43,0.5
+				c-12.1-2.3-21.3-6.8-27.3-13.6c-3.7-4.2-6.1-9.3-7.1-15c-1-5.9-0.5-12.6,1.6-19.9c3.5-12.3,4.2-23.1,2.2-33
+				c-1.8-8.8-5.7-16.8-11.9-24.2c-11.5-13.8-29.1-23.6-46.2-33c-24.8-13.8-48.2-26.8-51.3-50.5c-2.8-21.3,2.4-37.5,15.8-49.5
+				c11.8-10.6,28.4-16.6,43.2-22c9.1-3.3,17.6-6.4,24.5-10.3c7.7-4.4,12.3-9.1,14.2-14.8c2.9-8.4,0-19.2-8.7-33
+				c-3.7-5.8-5.6-12.1-5.7-18.7c-0.1-6.3,1.4-12.8,4.4-19.5c6.7-14.9,20.8-30,39.7-42.5C499.4,495.9,521,487.2,541.5,484
+				 M541.4,483.5c-60.3,9.6-125.4,65.3-99.7,105.8c48.7,76.7-101.4,34.5-89,129.4c7.5,57.1,132.1,53,107.2,140.6
+				c-11.2,39.3,23,57,76.2,48.5c38.2-6,86.2-25.5,134.4-60c10-7.2,19.7-10.8,29.1-12.3c30.3-4.8,58.3,12.5,87.7,7.8
+				c15.5-2.5,31.4-11.1,48.2-32.3c40.8-51.6-43.9-76.5-19.5-164.8c15.6-56.4,48.4-152.1-115.4-130.2c-70.2,9.4-75.5-45.4-158.7-32.7
+				C541.8,483.5,541.6,483.5,541.4,483.5L541.4,483.5z"/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000065036085076795729610000002759891915430365101_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(0.5744 -6.037564e-02 6.037564e-02 0.5744 222.0894 361.0683)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000065036085076795729610000002759891915430365101_);" d="M532.8,391.5c0.3,0,0.5-0.1,0.8-0.1
+				c57.6-5.8,85.8,11.3,113.1,27.9c12.6,7.7,24.6,14.9,39.2,20c16.6,5.7,33.7,7.7,53.9,6c40.9-3.3,74.6-1.7,100.1,4.9
+				c22.7,5.8,39.5,15.6,50.2,29.3c12.5,16,16.9,37.4,13.3,65.3c-3.1,24.1-11.4,48.8-18.8,70.6c-0.9,2.8-1.9,5.5-2.8,8.2
+				c-8.6,25.9-10.5,50.2-5.7,74.2c4.2,21,12.9,39,20.6,54.9c7.4,15.4,14.5,29.9,15.9,44.2c1.6,15.7-3.6,29.8-16.4,44.4
+				c-11.3,12.8-22.3,22.2-33.8,28.7c-10.2,5.7-20.4,9.1-31.5,10.3c-19.2,2-38-3.1-56.3-8.1c-18.7-5.1-38.1-10.4-58.4-8.3
+				c-13.8,1.4-26.6,6.1-39.4,14.3c-31.5,20.1-63.8,36.5-96,48.5c-29.8,11.2-58.3,18.2-84.5,21c-21.7,2.3-40.7,1.5-56.5-2.3
+				c-15.8-3.8-27.5-10.4-34.9-19.7c-4.6-5.8-7.4-12.6-8.3-20.2c-0.9-7.9,0.3-16.6,3.5-26.1c5.4-15.9,7.1-30,5.2-43.2
+				c-1.7-11.7-6.3-22.4-14-32.6c-14.2-18.9-36.7-33-58.4-46.6c-31.6-19.8-61.5-38.5-64-69.9c-2.2-28.1,5.7-49.1,24.2-64
+				c16.2-13.1,38.5-19.9,58.2-25.9c12.1-3.7,23.6-7.2,32.9-11.8c10.5-5.2,16.7-11.1,19.7-18.5c4.3-10.9,1.3-25.2-9.2-43.9
+				c-4.4-7.8-6.5-16.2-6.2-25c0.3-8.2,2.7-16.8,7.1-25.4c9.8-19.1,29.4-37.9,55.1-53.1C476.6,404.3,505.7,394.4,532.8,391.5
+				 M532.7,390.9c-79.9,8.4-169.2,77.2-138.3,132.2c58.7,104.1-135.7,38.4-125.9,164c5.9,75.5,169.9,78.7,131.2,192.1
+				c-17.4,50.9,26.2,76.5,96.8,69c50.6-5.3,115-27.6,180.8-69.6c13.6-8.7,26.6-12.8,39.1-14.2c40.1-4.2,75.8,20.4,114.7,16.3
+				c20.6-2.2,42.1-12.4,65.6-39.1c57.2-65-52.5-103.5-14.3-217.8c24.4-73,74.1-196.5-142.7-179c-92.8,7.5-96.1-64.9-206.2-53.9
+				C533.3,390.9,533,390.9,532.7,390.9L532.7,390.9z"/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000158732644941484975630000014238637754254681985_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912" gradientTransform="matrix(0.759 -3.977533e-02 3.977533e-02 0.759 121.5579 208.0227)">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000158732644941484975630000014238637754254681985_);" d="M527.7,269.4c0.4,0,0.7,0,1.1-0.1
+				c76.1-3.6,112,20.8,146.7,44.4c16.1,10.9,31.3,21.3,50.2,28.9c21.4,8.7,43.8,12.4,70.4,11.6c54-1.5,98.2,3,131.2,13.3
+				c29.4,9.2,50.9,23.3,63.9,41.9c15.3,21.9,19.6,50.3,13,86.7c-5.7,31.5-18.4,63.4-29.5,91.5c-1.4,3.6-2.8,7.1-4.2,10.6
+				c-13.1,33.4-17.2,65.2-12.6,97.1c4.1,27.8,14.3,52.1,23.3,73.5c8.7,20.7,17,40.3,17.9,59.1c1,20.7-6.8,38.9-24.7,57.2
+				c-15.7,16-30.8,27.6-46.3,35.3c-13.7,6.9-27.5,10.6-42.1,11.3c-25.4,1.3-49.8-6.7-73.4-14.5c-24.3-8-49.4-16.3-76.2-14.9
+				c-18.2,1-35.4,6.2-52.7,16c-42.8,24.3-86.4,43.5-129.5,57.2c-40,12.6-77.8,20-112.5,21.8c-28.6,1.5-53.6-0.8-74.1-7
+				c-20.5-6.1-35.5-15.6-44.5-28.3c-5.7-8-8.9-17.1-9.5-27.1c-0.6-10.4,1.5-21.9,6.4-34c8.2-20.5,11.4-39,9.8-56.4
+				c-1.5-15.5-6.7-29.9-16.1-43.9c-17.3-25.8-45.9-45.8-73.5-65.2C200,707.6,162,681,160.9,639.5c-0.9-37.1,10.9-64.1,36.2-82.4
+				c22.2-16,52-23.5,78.3-30.1c16.2-4.1,31.5-7.9,44-13.3c14.1-6.1,22.8-13.4,27.2-23c6.5-14,3.5-33.1-9.1-58.4
+				c-5.3-10.6-7.4-21.8-6.4-33.2c0.9-10.8,4.7-21.8,11.1-32.9c14.3-24.4,41.3-47.8,76.1-66C453,282.3,491.9,271.3,527.7,269.4
+				 M527.7,268.7c-105.6,5.5-227.7,89.8-190.8,164.2c69.9,140.8-180.9,41.1-176.7,206.8C162.7,739.2,378,754.7,319.3,901
+				c-26.3,65.6,29.2,102.3,122.5,97.4c66.8-3.5,153-28.3,242.3-79c18.5-10.5,35.8-15,52.4-15.9c53-2.8,98.1,32.1,149.5,29.4
+				c27.2-1.4,56.1-13.3,88.9-46.9c79.6-81.5-61.8-139.7-3.8-287.2c37-94.3,110.8-253.1-175.1-245.1c-122.5,3.4-121.8-91.9-267.3-85
+				C528.4,268.6,528,268.6,527.7,268.7L527.7,268.7z"/>
+		</g>
+		<g>
+			
+				<linearGradient id="SVGID_00000068671212480548264240000001700600385874458510_" gradientUnits="userSpaceOnUse" x1="1147.1908" y1="584.0912" x2="20.9918" y2="584.0912">
+				<stop  offset="0" style="stop-color:#FF5500"/>
+				<stop  offset="1" style="stop-color:#F28E25"/>
+			</linearGradient>
+			<path style="fill:url(#SVGID_00000068671212480548264240000001700600385874458510_);" d="M529.5,108.6c0.5,0,1,0,1.4,0
+				c100.2,0.5,145.7,35.1,189.7,68.5c20.4,15.5,39.6,30.1,63.9,41.5c27.5,12.9,56.6,19.3,91.7,20.1c71.1,1.7,128.8,10.6,171.5,26.5
+				c38,14.1,65.2,34.1,81.1,59.5c18.6,29.9,22.3,67.4,11.1,114.9c-9.6,41-28.5,82-45.1,118.2c-2.1,4.6-4.2,9.2-6.2,13.6
+				c-19.6,43-27.1,84.5-23.2,126.8c3.4,36.9,15.2,69.5,25.5,98.2c10,27.8,19.5,54.1,19.4,78.9c-0.1,27.3-11.6,50.6-36.3,73.4
+				c-21.7,20-42.4,34.1-63.3,43.2c-18.5,8.1-36.8,12-56,12c-33.4,0-64.9-12.3-95.4-24.1c-31.3-12.2-63.7-24.8-99.1-24.8
+				c-23.9,0-47,5.7-70.4,17.4c-57.9,29-116.5,51.3-174.1,66.2c-53.4,13.8-103.6,20.9-149.4,20.9c-37.7,0-70.3-4.8-96.9-14.3
+				c-26.5-9.4-45.5-23-56.6-40.2c-6.9-10.8-10.5-23-10.6-36.2c-0.1-13.7,3.5-28.6,10.7-44.3c12.2-26.4,17.7-50.4,16.7-73.5
+				c-0.8-20.5-6.8-39.7-18.1-58.7c-21-35.1-57.1-63.4-92.1-90.7c-50.9-39.8-99-77.4-97.5-131.9c1.3-48.9,18.7-83.5,53.3-105.8
+				c30.2-19.6,69.9-27.3,105-34.1c21.6-4.2,41.9-8.2,58.8-14.4c19-7.1,30.8-16,37.3-28.3c9.4-17.9,6.9-43.2-7.9-77.3
+				c-6.2-14.3-8.2-29.1-6.1-44.1c2-14.1,7.7-28.4,16.9-42.4c20.4-31.1,57.5-60,104.5-81.5C430.4,120.4,482.2,108.6,529.5,108.6
+				 M529.5,107.6c-139.2,0-305.4,102.3-262,202.6C349.7,500.1,26.9,351.7,21,569.7C17.5,700.8,299.3,736,212.1,924.2
+				c-39.1,84.4,31.3,136.4,154.3,136.4c88,0,203-26.7,323.9-87.2c25-12.5,48.1-17.3,69.9-17.3c69.8,0,126.7,48.9,194.5,48.9
+				c35.8,0,74.7-13.7,120-55.5c110.2-101.6-71.6-187.8,14.8-377.7c55.2-121.3,163.1-324.9-213.2-334.1
+				C715,233.8,722.5,108.6,530.9,107.6C530.4,107.6,529.9,107.6,529.5,107.6L529.5,107.6z"/>
+		</g>
+	</g>
+</g>
+</svg>

+ 5 - 5
dist/stylesheets/category.css

@@ -1,9 +1,8 @@
 .category {
-  display: flex;
-  height: 50px;
-  justify-content: center;
-  align-items: center;
-  margin-top: 20px;
+  width: 92%;
+  padding-top: 20px;
+  text-align: center;
+  margin: 0 auto;
   overflow-x: auto; /* 启用水平滚动 */
   white-space: nowrap; /* 防止内容换行 */
   -ms-overflow-style: none;  /* IE和Edge浏览器隐藏滚动条 */
@@ -16,6 +15,7 @@
 }
 
 .category a {
+  display: inline-block;
   margin-right: 10px;
   text-decoration: none;
   border: none; 

+ 32 - 0
dist/stylesheets/designer.css

@@ -0,0 +1,32 @@
+.container {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+  gap: 16px;
+  width: 90%;
+  margin: 20px auto;
+}
+
+.card {
+
+  border: 1px solid #ccc;
+  padding: 20px;
+  max-width: 200px;
+  text-align: center;
+  border-radius: 10px;
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.card img {
+  border-radius: 50%;
+  width: 100px;
+  height: 100px;
+  object-fit: cover;
+}
+
+.card .info {
+  margin-top: 15px;
+}
+
+.card .info p {
+  margin: 5px 0;
+}

+ 1 - 0
dist/stylesheets/header.css

@@ -34,6 +34,7 @@
 .dropdown-content {
   display: none;
   position: absolute;
+  right: -30px;
   background-color: #f9f9f9;
   min-width: 160px;
   box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);

+ 167 - 0
dist/stylesheets/info.css

@@ -0,0 +1,167 @@
+.full-screen {
+  height: 100vh;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.about-container {
+  background: linear-gradient(to top left, #5c907e, #2e4638);
+}
+
+.app-container {
+  background: linear-gradient(to bottom right, pink, #ff4081);
+  padding: 10px;
+  display: flex;
+
+}
+
+.contact-container {
+  background: linear-gradient(to top left, #054879, #14161a);
+  padding-left: 60px;
+  display: flex;
+  justify-content: start;
+  align-items: center;
+}
+
+
+.section-title {
+  padding: 20px;
+  font-size: 56px;
+  font-weight: 900;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
+}
+
+.company-info {
+  padding-bottom: 10px;
+  width: 80%;
+}
+
+.about-content {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  max-width: 90%;
+}
+
+.contact-content {
+  display: flex;
+  color: white;
+  flex-direction: column;
+  justify-content: start;
+  align-items: start;
+  max-width: 90%;
+  
+}
+
+.contact-content a {
+  text-decoration: none;
+  font-size: 20px;
+  color: #e864ff;
+  padding: 20px;
+}
+
+.contact-content a:hover {
+  color: white;
+}
+
+.left {
+  padding: 40px;
+  max-width: 800px;
+}
+
+.right {
+  padding: 10px;
+  display: flex;
+  justify-content: center;
+}
+
+.app-name {
+  font-size: 56px;
+  font-weight: 900;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+  margin-top: 20px;
+  margin-bottom: 20px;
+}
+
+.app-description {
+  font-size: 18px;
+  margin-bottom: 40px;
+}
+
+.app-icon {
+  width: 150px;
+  border: 3px solid #eeeeee;
+  border-radius: 8px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.app-download-btn {
+  background-color: black;
+  display: inline-block;
+  border-radius: 8px;
+  margin-right: 20px;
+  margin-bottom: 10px;
+}
+
+.app-download-btn img {
+  width: 150px;
+  height: 40px;
+  cursor: pointer;
+}
+
+.app-download-btn:hover {
+  border: 1px solid #ff4081;
+}
+
+
+/* 媒体查询:当屏幕宽度小于768px时,改为上下排布 */
+@media (max-width: 768px) {
+
+  .left {
+    padding: 10px;
+  }
+
+  .contact-container {
+    padding-left: 20px;
+  }
+
+  .right {
+    display: none;
+  }
+
+  .company-info {
+    width: 100%;
+  }
+
+  .app-name {
+    font-size: 48px;
+  }
+
+  .section-title {
+    font-size: 48px;
+  }
+}
+
+
+
+.slideshow-container {
+  position: relative;
+  max-width: 1000px;
+  margin: auto;
+}
+
+.mySlides {
+  display: none;
+}
+
+.fade {
+  animation-name: fade;
+  animation-duration: 2.5s;
+}
+
+@keyframes fade {
+  from {opacity: .1} 
+  to {opacity: 1}
+}

+ 1 - 1
dist/stylesheets/styles.css

@@ -24,7 +24,7 @@ body {
 
 .content-title a {
   text-decoration: none;
-  color: green;
+  color: #ff4081;
   font-weight: bold;
 }
 

+ 32 - 0
libs/image.js

@@ -0,0 +1,32 @@
+const sharp = require('sharp');
+
+
+function makeThumb(src, dest, size) {
+  size = parseInt(size);
+  return new Promise(function (done, reject) {
+    sharp(src)
+      .resize(size)
+      .flatten({ background: { r: 255, g: 255, b: 255 } })
+      .toFile(dest, err => {
+        if (err) reject(err);
+        else done(dest);
+      })
+  });
+
+}
+
+
+module.exports = {
+  makeThumb,
+}
+
+
+async function testMakeThumb() {
+  let src = '/Users/chengen/www/coloring/thumbs/v2/page/original/5c88af4a52b5b2c9a4dc4ff9.png'
+  let dest = './out.png'
+  await makeThumb(src, dest, 200)
+}
+
+if (require.main == module) {
+  testMakeThumb().catch(console.error);
+}

+ 13 - 1
libs/utils.js

@@ -1,4 +1,6 @@
 const languages = require('../config/language');
+const createError = require('http-errors');
+const ObjectId = require('mongoose').Types.ObjectId;
 
 /**
  * 根据http的Accept-Language返回locale
@@ -21,6 +23,16 @@ function ensureLanguage(lang) {
   return 'en';
 }
 
+/**
+ * 验证ID
+ */
+function validateId(id, errMsg) {
+  errMsg = errMsg || 'ID错误!';
+  if (!ObjectId.isValid(id)) {
+    throw createError(400, errMsg);
+  }
+}
+
 module.exports = {
-  getLocale, ensureLanguage
+  getLocale, ensureLanguage, validateId
 }

+ 450 - 1
package-lock.json

@@ -15,7 +15,359 @@
         "ejs": "^3.1.10",
         "express": "^4.21.2",
         "mongoose": "^8.9.5",
-        "node-fetch": "^2.7.0"
+        "node-fetch": "^2.7.0",
+        "sharp": "^0.33.5"
+      }
+    },
+    "node_modules/@emnapi/runtime": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
+      "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@img/sharp-darwin-arm64": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
+      "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-darwin-arm64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-darwin-x64": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
+      "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-darwin-x64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-libvips-darwin-arm64": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
+      "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-darwin-x64": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
+      "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-arm": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
+      "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-arm64": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
+      "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-s390x": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
+      "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
+      "cpu": [
+        "s390x"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-x64": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
+      "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
+      "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
+      "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-linux-arm": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
+      "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-arm": "1.0.5"
+      }
+    },
+    "node_modules/@img/sharp-linux-arm64": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
+      "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-arm64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-linux-s390x": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
+      "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
+      "cpu": [
+        "s390x"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-s390x": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-linux-x64": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
+      "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-x64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-linuxmusl-arm64": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
+      "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-linuxmusl-x64": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
+      "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-wasm32": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
+      "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
+      "cpu": [
+        "wasm32"
+      ],
+      "optional": true,
+      "dependencies": {
+        "@emnapi/runtime": "^1.2.0"
+      },
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-win32-ia32": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
+      "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      }
+    },
+    "node_modules/@img/sharp-win32-x64": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
+      "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
       }
     },
     "node_modules/@mongodb-js/saslprep": {
@@ -175,6 +527,18 @@
         "url": "https://github.com/chalk/chalk?sponsor=1"
       }
     },
+    "node_modules/color": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+      "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+      "dependencies": {
+        "color-convert": "^2.0.1",
+        "color-string": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=12.5.0"
+      }
+    },
     "node_modules/color-convert": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -191,6 +555,15 @@
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
     },
+    "node_modules/color-string": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+      "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+      "dependencies": {
+        "color-name": "^1.0.0",
+        "simple-swizzle": "^0.2.2"
+      }
+    },
     "node_modules/concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -295,6 +668,14 @@
         "npm": "1.2.8000 || >= 1.4.16"
       }
     },
+    "node_modules/detect-libc": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
+      "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/double-ended-queue": {
       "version": "2.1.0-0",
       "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
@@ -608,6 +989,11 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/is-arrayish": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+      "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+    },
     "node_modules/jake": {
       "version": "10.9.2",
       "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
@@ -1023,6 +1409,17 @@
       "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
+    "node_modules/semver": {
+      "version": "7.6.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/send": {
       "version": "0.19.0",
       "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
@@ -1078,6 +1475,44 @@
       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
       "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
     },
+    "node_modules/sharp": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
+      "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "color": "^4.2.3",
+        "detect-libc": "^2.0.3",
+        "semver": "^7.6.3"
+      },
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips"
+      },
+      "optionalDependencies": {
+        "@img/sharp-darwin-arm64": "0.33.5",
+        "@img/sharp-darwin-x64": "0.33.5",
+        "@img/sharp-libvips-darwin-arm64": "1.0.4",
+        "@img/sharp-libvips-darwin-x64": "1.0.4",
+        "@img/sharp-libvips-linux-arm": "1.0.5",
+        "@img/sharp-libvips-linux-arm64": "1.0.4",
+        "@img/sharp-libvips-linux-s390x": "1.0.4",
+        "@img/sharp-libvips-linux-x64": "1.0.4",
+        "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
+        "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
+        "@img/sharp-linux-arm": "0.33.5",
+        "@img/sharp-linux-arm64": "0.33.5",
+        "@img/sharp-linux-s390x": "0.33.5",
+        "@img/sharp-linux-x64": "0.33.5",
+        "@img/sharp-linuxmusl-arm64": "0.33.5",
+        "@img/sharp-linuxmusl-x64": "0.33.5",
+        "@img/sharp-wasm32": "0.33.5",
+        "@img/sharp-win32-ia32": "0.33.5",
+        "@img/sharp-win32-x64": "0.33.5"
+      }
+    },
     "node_modules/side-channel": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
@@ -1151,6 +1586,14 @@
       "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz",
       "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ=="
     },
+    "node_modules/simple-swizzle": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+      "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+      "dependencies": {
+        "is-arrayish": "^0.3.1"
+      }
+    },
     "node_modules/sparse-bitfield": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
@@ -1197,6 +1640,12 @@
         "node": ">=18"
       }
     },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "optional": true
+    },
     "node_modules/type-is": {
       "version": "1.6.18",
       "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",

+ 2 - 1
package.json

@@ -16,6 +16,7 @@
     "ejs": "^3.1.10",
     "express": "^4.21.2",
     "mongoose": "^8.9.5",
-    "node-fetch": "^2.7.0"
+    "node-fetch": "^2.7.0",
+    "sharp": "^0.33.5"
   }
 }

+ 545 - 283
routes/index.js

@@ -7,13 +7,16 @@ const categories = require('../config/category');
 const tags = require('../config/tag');
 const languages = require('../config/language');
 const translate = require('../config/translate');
-const { getLocale, ensureLanguage } = require('../libs/utils');
+const meta = require('../config/meta');
+const { getLocale, ensureLanguage, validateId } = require('../libs/utils');
 const { format } = require('date-fns');
 const { getListBuilder } = require('../libs/pager');
 
 const CACHE_PREFIX = "art_v1";
 const CACHE_EXPIRES = 60; // 60s刷新一次
 
+const artSelect = 'name title desc width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId desc name';
+
 
 router.get('/', (req, res, next) => {
   let locale = getLocale(req.acceptsLanguages());
@@ -22,359 +25,610 @@ router.get('/', (req, res, next) => {
 });
 
 
-router.get('/:lang/', async (req, res, next) => {
-  let lang = ensureLanguage(req.params.lang);
-
-  let baseSort = { publishTime: 'desc' };
-
-  // 最新上线
-  let latest = await models.Art
-    .find({ status: 9000 })
-    .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId')
-    .sort(baseSort)
-    .limit(12)
-    .lean()
-    .exec();
-  organizeData(latest);
-
-  // 热门精选
-  let recommend = await models.Art
-    .find({ tags: 'data_good', status: 9000 })
-    .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId')
-    .sort(baseSort)
-    .limit(12)
-    .lean()
-    .exec();
-  organizeData(recommend);
-
-  // special 专区
-  let special = await models.Art
-    .find({ hasSpecial: true, status: 9000 })
-    .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId')
-    .sort(baseSort)
-    .limit(12)
-    .lean()
-    .exec();
-  organizeData(special);
-
-  // 专辑
-  let albums = await models.ArtAlbum
-    .find({ pid: 'art', enabled: true })
-    .sort({ order: 'asc' })
-    .populate('title')
-    .populate('slogon')
-    .select('tag title slogon contents')
-    .limit(6)
-    .lean()
-    .exec();
-
-
-  for (let doc of albums) {
-    doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`;
-    doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`;
-    doc.title = doc.title ? doc.title[lang] : '';
-    doc.slogon = doc.slogon ? doc.slogon[lang] : '';
-    doc.size = doc.contents.length;
-  }
+router.get('/:lang/', function (req, res, next) {
+  (async function () {
+    let lang = ensureLanguage(req.params.lang);
+
+    let baseSort = { publishTime: 'desc' };
+
+    // 最新上线
+    let latest = await models.Art
+      .find({ status: 9000 })
+      .select(artSelect)
+      .sort(baseSort)
+      .limit(12)
+      .lean()
+      .exec();
+    organizeData(latest, lang);
+
+    // 热门精选
+    let recommend = await models.Art
+      .find({ tags: 'data_good', status: 9000 })
+      .select(artSelect)
+      .sort(baseSort)
+      .limit(12)
+      .lean()
+      .exec();
+    organizeData(recommend, lang);
+
+    // special 专区
+    let special = await models.Art
+      .find({ hasSpecial: true, status: 9000 })
+      .select(artSelect)
+      .sort(baseSort)
+      .limit(12)
+      .lean()
+      .exec();
+    organizeData(special, lang);
+
+    // 专辑
+    let albums = await models.ArtAlbum
+      .find({ pid: 'art', enabled: true })
+      .sort({ order: 'asc' })
+      .populate('title')
+      .populate('slogon')
+      .select('tag title slogon contents')
+      .limit(6)
+      .lean()
+      .exec();
+
+
+    for (let doc of albums) {
+      doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`;
+      doc.cover = `${config.resHost}/res/coloring/album_cover/320/${doc._id}.jpeg`;
+      doc.title = doc.title ? doc.title[lang] : '';
+      doc.slogon = doc.slogon ? doc.slogon[lang] : '';
+      doc.size = doc.contents.length;
+    }
 
 
-  let data = {
-    title: 'Art Number Coloring',
-    latest,
-    recommend,
-    special,
-    albums,
-    translate,
-    categories,
-    languages,
-    lang,
-    uri: `/${lang}`,
-  };
+    let data = {
+      title: meta.homePageTile[lang],
+      description: meta.homePageDescription[lang],
+      latest,
+      recommend,
+      special,
+      albums,
+      translate,
+      categories,
+      languages,
+      lang,
+      tags,
+      uri: `/${lang}`,
+    };
+
+    res.render('index', data);
+  })().catch(next);
+});
 
-  res.render('index', data);
 
-});
 
+router.get('/:lang/category/:tag?', function (req, res, next) {
+  (async function () {
+    let lang = ensureLanguage(req.params.lang);
+    let tag = req.params.tag;
+    if (!tag) tag = 'latest';
 
+    let query = {
+      page: req.query.page,
+      length: req.query.length,
+      orderBy: 'publishTime',
+      order: 'desc',
+      base: { status: 9000 },
+      filters: tag == 'latest' ? {} : { tags: tag },
+    }
 
-router.get('/:lang/category/:tag?', async (req, res, next) => {
-  let lang = ensureLanguage(req.params.lang);
-  let tag = req.params.tag;
-  if (!tag) tag = 'latest';
+    let result = await getListBuilder(query, models.Art);
+    organizeData(result.data, lang);
+
+    let data = {
+      title: meta.categoryTitle[lang],
+      description: meta.categoryDescription[lang],
+      data: result.data,
+      page: result.page,
+      search: req.query.search,
+      length: result.length,
+      recordsFiltered: result.recordsFiltered,
+      recordsTotal: result.recordsTotal,
+      translate,
+      categories,
+      languages,
+      lang,
+      tag,
+      uri: `/${lang}/category/${tag}`,
+    };
+
+    res.render('category', data);
+  })().catch(next);
 
-  let query = {
-    page: req.query.page,
-    length: req.query.length,
-    orderBy: 'publishTime',
-    order: 'desc',
-    base: { status: 9000 },
-    filters: tag == 'latest' ? {} : { tags: tag },
-  }
+});
+
+router.get('/:lang/tag/:tag?', function (req, res, next) {
+  (async function () {
+    let lang = ensureLanguage(req.params.lang);
+    let tag = req.params.tag;
+    if (!tag) tag = 'latest';
+
+    let query = {
+      page: req.query.page,
+      length: req.query.length,
+      search: req.query.search,
+      orderBy: 'publishTime',
+      order: 'desc',
+      base: { status: 9000 },
+      filters: tag == 'latest' ? {} : { tags: tag },
+    }
 
-  let result = await getListBuilder(query, models.Art);
-  organizeData(result.data);
-
-  let data = {
-    title: 'Coloring Page Categories',
-    data: result.data,
-    page: result.page,
-    search: req.query.search,
-    length: result.length,
-    recordsFiltered: result.recordsFiltered,
-    recordsTotal: result.recordsTotal,
-    translate,
-    categories,
-    languages,
-    lang,
-    tag,
-    uri: `/${lang}/category/${tag}`,
-  };
-
-  res.render('category', data);
+    let result = await getListBuilder(query, models.Art);
+    organizeData(result.data, lang);
+
+    let data = {
+      title: meta.tagTitle[lang],
+      description: meta.tagDescription[lang],
+      data: result.data,
+      page: result.page,
+      length: result.length,
+      recordsFiltered: result.recordsFiltered,
+      recordsTotal: result.recordsTotal,
+      translate,
+      categories,
+      languages,
+      lang,
+      tag,
+      tags,
+      uri: `/${lang}/tag/${tag}`,
+    };
+
+    res.render('tag', data);
+
+  })().catch(next);
 
 });
 
-router.get('/:lang/tag/:tag?', async (req, res, next) => {
-  let lang = ensureLanguage(req.params.lang);
-  let tag = req.params.tag;
-  if (!tag) tag = 'latest';
-
-  let query = {
-    page: req.query.page,
-    length: req.query.length,
-    search: req.query.search,
-    orderBy: 'publishTime',
-    order: 'desc',
-    base: { status: 9000 },
-    filters: tag == 'latest' ? {} : { tags: tag },
-  }
 
-  let result = await getListBuilder(query, models.Art);
-  organizeData(result.data);
-
-  let data = {
-    title: 'Coloring Page Tags',
-    data: result.data,
-    page: result.page,
-    length: result.length,
-    recordsFiltered: result.recordsFiltered,
-    recordsTotal: result.recordsTotal,
-    translate,
-    categories,
-    languages,
-    lang,
-    tag,
-    tags,
-    uri: `/${lang}/tag/${tag}`,
-  };
-
-  res.render('tag', data);
+router.get('/:lang/search', function (req, res, next) {
+  (async function () {
+    let lang = ensureLanguage(req.params.lang);
+    let search = req.query.search;
+
+
+    let query = {
+      page: req.query.page,
+      length: req.query.length,
+      search: req.query.search,
+      orderBy: 'publishTime',
+      order: 'desc',
+      base: { status: 9000 },
+      filters: {},
+    }
+
+    let result = await getListBuilder(query, models.Art);
+    organizeData(result.data, lang);
+
+    let data = {
+      title: meta.searchTitle[lang],
+      description: meta.searchDescription[lang],
+      data: result.data,
+      page: result.page,
+      length: result.length,
+      recordsFiltered: result.recordsFiltered,
+      recordsTotal: result.recordsTotal,
+      translate,
+      categories,
+      languages,
+      lang,
+      uri: `/${lang}/search?search=${search}`,
+    };
+
+    res.render('search', data);
+  })().catch(next);
 
 });
 
 
-router.get('/:lang/search', async (req, res, next) => {
-  let lang = ensureLanguage(req.params.lang);
-  let search = req.query.search;
 
+router.get('/:lang/special', function (req, res, next) {
+  (async function () {
+    let lang = ensureLanguage(req.params.lang);
 
-  let query = {
-    page: req.query.page,
-    length: req.query.length,
-    search: req.query.search,
-    orderBy: 'publishTime',
-    order: 'desc',
-    base: { status: 9000 },
-    filters: {},
-  }
+    let query = {
+      title: meta.specialTitle[lang],
+      description: meta.specialDescription[lang],
+      lang,
+      page: req.query.page,
+      length: req.query.length,
+      orderBy: 'publishTime',
+      order: 'desc',
+      base: { status: 9000 },
+      filters: { hasSpecial: true },
+    }
+
+    let result = await getListBuilder(query, models.Art);
+    organizeData(result.data, lang);
+
+    let data = {
+      title: meta.specialTitle[lang],
+      description: meta.specialDescription[lang],
+      data: result.data,
+      page: result.page,
+      length: result.length,
+      recordsFiltered: result.recordsFiltered,
+      recordsTotal: result.recordsTotal,
+      translate,
+      languages,
+      lang,
+      uri: `/${lang}/special`,
+    };
 
-  let result = await getListBuilder(query, models.Art);
-  organizeData(result.data);
-
-  let data = {
-    title: 'Coloring Page Search',
-    data: result.data,
-    page: result.page,
-    length: result.length,
-    recordsFiltered: result.recordsFiltered,
-    recordsTotal: result.recordsTotal,
-    translate,
-    categories,
-    languages,
-    lang,
-    uri: `/${lang}/search?search=${search}`,
-  };
-
-  res.render('search', data);
+    res.render('special', data);
+
+  })().catch(next);
 
 });
 
-router.get('/:lang/special', async (req, res, next) => {
-  let lang = ensureLanguage(req.params.lang);
+router.get('/:lang/albums', function (req, res, next) {
+  (async function () {
 
-  let query = {
-    page: req.query.page,
-    length: req.query.length,
-    orderBy: 'publishTime',
-    order: 'desc',
-    base: { status: 9000 },
-    filters: { hasSpecial: true },
-  }
+    let lang = ensureLanguage(req.params.lang);
+
+    // 专辑
+    let albums = await models.ArtAlbum
+      .find({ pid: 'art', enabled: true })
+      .sort({ order: 'asc' })
+      .populate('title')
+      .populate('slogon')
+      .select('tag title slogon contents')
+      .lean()
+      .exec();
 
-  let result = await getListBuilder(query, models.Art);
-  organizeData(result.data);
 
-  let data = {
-    title: 'Special Coloring Page',
-    data: result.data,
-    page: result.page,
-    length: result.length,
-    recordsFiltered: result.recordsFiltered,
-    recordsTotal: result.recordsTotal,
-    translate,
-    languages,
-    lang,
-    uri: `/${lang}/special`,
-  };
+    for (let doc of albums) {
+      doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`;
+      doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`;
+      doc.title = doc.title ? doc.title[lang] : '';
+      doc.slogon = doc.slogon ? doc.slogon[lang] : '';
+      doc.size = doc.contents.length;
+    }
+
+    let data = {
+      title: meta.albumsTitle[lang],
+      description: meta.albumsDescription[lang],
+      data: albums,
+      length: albums.length,
+      translate,
+      languages,
+      lang,
+      uri: `/${lang}/albums`,
+    };
 
-  res.render('special', data);
+    res.render('albums', data);
+
+  })().catch(next);
 
 });
 
-router.get('/:lang/albums', async (req, res, next) => {
-  let lang = ensureLanguage(req.params.lang);
 
-  // 专辑
-  let albums = await models.ArtAlbum
-    .find({ pid: 'art', enabled: true })
-    .sort({ order: 'asc' })
-    .populate('title')
-    .populate('slogon')
-    .select('tag title slogon contents')
-    .lean()
-    .exec();
+router.get('/:lang/album/:id', function (req, res, next) {
+  (async function () {
+
+    let lang = ensureLanguage(req.params.lang);
+    let id = req.params.id;
+    validateId(id);
+
+    // 专辑
+    let doc = await models.ArtAlbum
+      .findById(id)
+      .populate('title')
+      .populate('slogon')
+      .populate({ path: 'contents', select: artSelect })
+      .select('tag title slogon contents')
+      .lean()
+      .exec();
 
+    if (!doc) throw createError(404, 'Album Not Found!');
 
-  for (let doc of albums) {
     doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`;
     doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`;
     doc.title = doc.title ? doc.title[lang] : '';
     doc.slogon = doc.slogon ? doc.slogon[lang] : '';
     doc.size = doc.contents.length;
-  }
 
-  let data = {
-    title: 'Coloring Page Albums',
-    data: albums,
-    length: albums.length,
-    translate,
-    languages,
-    lang,
-    uri: `/${lang}/albums`,
-  };
+    organizeData(doc.contents, lang);
+
+
+    let data = {
+      title: `${doc.title}`,
+      description: `${doc.slogon}`,
+      data: doc,
+      translate,
+      languages,
+      lang,
+      uri: `/${lang}/album/${id}`,
+    };
+
+    res.render('album', data);
+  })().catch(next);
+
+});
+
+
+
+router.get('/:lang/designers', function (req, res, next) {
+  (async function () {
+
+    let lang = ensureLanguage(req.params.lang);
+
+
+    let docs = await models.Art.aggregate([
+      // 首先,过滤出 status = 9000 的文档
+      { $match: { status: 9000 } },
+
+      // 首先,根据 user 字段进行分组,并计算每个 user 出现的次数
+      { $group: { _id: '$user', count: { $sum: 1 } } },
+
+      // 然后,按照 count 字段进行降序排列
+      { $sort: { count: -1 } },
+
+      // 接着,与 users 集合进行连接,以获取用户的详细信息
+      {
+        $lookup: {
+          from: 'users', // 要连接的集合名称
+          localField: '_id', // 本地字段(即上一步分组后的 _id 字段)
+          foreignField: '_id', // 要连接的集合中的字段
+          as: 'userDetails' // 连接后结果存储在新字段 userDetails 中
+        }
+      },
+
+      // 展开 userDetails 数组,以便将用户信息提升到顶层
+      { $unwind: '$userDetails' },
 
-  res.render('albums', data);
+      // 调整输出格式,只保留需要的字段
+      { $project: { _id: 1, user: '$_id', count: 1, username: '$userDetails.username', name: '$userDetails.name' } },
+
+      // 限制返回的记录数量
+      { $limit: 40 }
+    ]);
+
+
+
+    for (let doc of docs) {
+      doc.avatar = `/thumbs/v1/avatar/320/${doc._id}.jpeg`;
+    }
+
+    let data = {
+      title: meta.designerTitle[lang],
+      description: meta.designerDescription[lang],
+      data: docs,
+      length: docs.length,
+      translate,
+      languages,
+      lang,
+      uri: `/${lang}/designers`,
+    };
+
+    res.render('designers', data);
+
+  })().catch(next);
 
 });
 
 
-router.get('/:lang/album/:id', async (req, res, next) => {
-  let lang = ensureLanguage(req.params.lang);
-  let id = req.params.id;
 
-  // 专辑
-  let doc = await models.ArtAlbum
-    .findById(id)
-    .populate('title')
-    .populate('slogon')
-    .populate({ path: 'contents', select: "width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId" })
-    .select('tag title slogon contents')
-    .lean()
-    .exec();
+router.get('/:lang/designer/:id', function (req, res, next) {
+  (async function () {
+
+    let lang = ensureLanguage(req.params.lang);
+    let id = req.params.id;
+    validateId(id);
 
-  if (!doc) throw createError(404, 'Album Not Found!');
+    let user = await models.User.findById(id).select('name username');
+    if (!user) throw createError(404, 'User Not Found!');
+    let count = await models.Art.countDocuments({ user: id, status: 9000 });
+    user.count = count;
+    user.avatar = `/thumbs/v1/avatar/320/${user._id}.jpeg`;
 
-  doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`;
-  doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`;
-  doc.title = doc.title ? doc.title[lang] : '';
-  doc.slogon = doc.slogon ? doc.slogon[lang] : '';
-  doc.size = doc.contents.length;
 
-  organizeData(doc.contents);
+    // find user arts
+    // 算法: 排除掉主流的tag,用剩下的tag去检索,取最多12条记录
+    let query = {
+      page: req.query.page,
+      length: req.query.length,
+      search: req.query.search,
+      orderBy: 'publishTime',
+      order: 'desc',
+      base: { status: 9000 },
+      filters: { user: id },
+    }
+
+    let result = await getListBuilder(query, models.Art);
+    organizeData(result.data, lang);
+
 
+    let data = {
+      title: meta.designerTitle[lang],
+      description: meta.designerDescription[lang],
+      user,
+      data: result.data,
+      page: result.page,
+      length: result.length,
+      recordsFiltered: result.recordsFiltered,
+      recordsTotal: result.recordsTotal,
+      translate,
+      lang,
+      languages,
+      uri: `/${lang}/designer/${id}`,
+    };
 
-  let data = {
-    title: `${doc.title}`,
-    data: doc,
-    translate,
-    languages,
-    lang,
-    uri: `/${lang}/album/${id}`,
-  };
+    res.render('designer', data);
 
-  res.render('album', data);
+  })().catch(next);
 
 });
 
 
 
-router.get('/:lang/detail/:id', async (req, res) => {
-  let lang = ensureLanguage(req.params.lang);
-  let id = req.params.id;
 
-  let doc = await models.Art
-    .findById(id)
-    .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId desc name')
-    .populate('user', 'username name')
-    .lean()
-    .exec();
-  if (!doc) throw createError(404, 'Art Not Found!');
 
-  organizeDetail(doc, lang);
+function getRealId(str) {
+  let id = str;
+  let lastIndex = str.lastIndexOf('-');
+  if (lastIndex !== -1) {
+    id = str.substring(lastIndex + 1);
+  }
+  return id;
+}
 
-  // find relate
-  // 算法: 排除掉主流的tag,用剩下的tag去检索,取最多12条记录
-  let tags = [...doc.tags];
-  let cates = categories.map(e => e.id);
-  tags = tags.filter(e => !cates.includes(e));
-  let baseSort = { publishTime: 'desc' };
+router.get('/:lang/coloring-page/:str', function (req, res, next) {
+  (async function () {
+    let lang = ensureLanguage(req.params.lang);
+    let str = req.params.str;  // 拟人化的id,形如 beautiful-house-daldkaghlda3232, 最后一个-后面的才是真正的id
+    let id = getRealId(str);
+
+
+    validateId(id);
+
+    let doc = await models.Art
+      .findById(id)
+      .select(artSelect)
+      .populate('user', 'username name')
+      .lean()
+      .exec();
+    if (!doc) throw createError(404, 'Art Not Found!');
+
+    organizeDetail(doc, lang);
+
+    // find relate
+    // 算法: 排除掉主流的tag,用剩下的tag去检索,取最多12条记录
+    let tags = [...doc.tags];
+    let cates = categories.map(e => e.id);
+    tags = tags.filter(e => !cates.includes(e));
+
+    let query = {
+      page: req.query.page,
+      length: req.query.length,
+      search: req.query.search,
+      orderBy: 'publishTime',
+      order: 'desc',
+      base: { status: 9000 },
+      filters: { tags },
+    }
 
-  let relates = await models.Art
-    .find({ tags: { $in: tags }, status: 9000 })
-    .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId')
-    .sort(baseSort)
-    .limit(12)
-    .lean()
-    .exec();
-  organizeData(relates);
+    let result = await getListBuilder(query, models.Art);
+    organizeData(result.data, lang);
+
+
+    let data = {
+      title: `${doc.name.replace(/[_]+/g, '-')}`,
+      description: `${doc.desc}`,
+      detail: doc,
+      data: result.data,
+      page: result.page,
+      length: result.length,
+      recordsFiltered: result.recordsFiltered,
+      recordsTotal: result.recordsTotal,
+      translate,
+      lang,
+      languages,
+      relates: result.data,
+      uri: doc.uri,
+    };
+
+    res.render('detail', data);
+  })().catch(next);
 
+});
 
-  let data = {
-    title: `Coloring Page ${doc.name}`,
-    data: doc,
-    translate,
-    lang,
-    languages,
-    relates,
-    uri: `/${lang}/detail/${id}`,
-  };
 
-  res.render('detail', data);
+router.get('/:lang/detail/:id', function (req, res, next) {
+  (async function () {
+    let lang = ensureLanguage(req.params.lang);
+    let id = req.params.id;
+
+    validateId(id);
+
+    let doc = await models.Art
+      .findById(id)
+      .select(artSelect)
+      .populate('user', 'username name')
+      .lean()
+      .exec();
+    if (!doc) throw createError(404, 'Art Not Found!');
+
+    organizeDetail(doc, lang);
+
+    // find relate
+    // 算法: 排除掉主流的tag,用剩下的tag去检索,取最多12条记录
+    let tags = [...doc.tags];
+    let cates = categories.map(e => e.id);
+    tags = tags.filter(e => !cates.includes(e));
+    let baseSort = { publishTime: 'desc' };
+
+    let relates = await models.Art
+      .find({ tags: { $in: tags }, status: 9000 })
+      .select(artSelect)
+      .sort(baseSort)
+      .limit(12)
+      .lean()
+      .exec();
+    organizeData(relates, lang);
+
+
+    let data = {
+      title: `${doc.name.replace(/[_]+/g, '-')}`,
+      description: `${doc.desc}`,
+      data: doc,
+      translate,
+      lang,
+      languages,
+      relates,
+      uri: `${lang}/detail/${id}`,
+    };
+
+    res.render('detail', data);
+  })().catch(next);
 
 });
 
 
-router.get('/play/:id', async (req, res) => {
-  let id = req.params.id;
-  let data = { id };
-  let doc = await models.Art.findById(id);
-  if (!doc) throw createError(404, 'Art Not Found!');
+router.get('/play/:id', function (req, res, next) {
+  (async function () {
+    let id = req.params.id;
+    let data = {
+      id,
+      title: meta.playTitle.en,
+      description: meta.playDescription.en,
+    };
+
+    validateId(id);
+    let doc = await models.Art.findById(id);
+    if (!doc) throw createError(404, 'Art Not Found!');
+
+    res.render('play', data);
+
+  })().catch(next);
 
-  res.render('play', data);
 });
 
 
-const organizeData = (data) => {
+router.get('/:lang/info', function (req, res, next) {
+  (async function () {
+    let lang = ensureLanguage(req.params.lang);
+    let data = {
+      title: meta.infoTitle[lang],
+      description: meta.infoDescription[lang],
+      lang,
+      languages,
+      translate,
+      uri: `/${lang}/info`,
+    };
+
+    res.render('info', data);
+  })().catch(next);
+
+});
+
+
+const organizeData = (data, lang) => {
   data.forEach(doc => {
     let host = config.resHost;
     let publishVersion = doc.publishVersion || 0;
@@ -397,6 +651,11 @@ const organizeData = (data) => {
 
     doc.zip = `${host}/zips/v2/number_mini/${version}/${doc._id}.zip`
 
+    let utf8name = encodeURIComponent(doc.name.replace(/[\s_]+/g, '-'));
+
+
+    doc.uri = `${lang}/coloring-page/${utf8name}-${doc._id}`;
+
     delete doc.hasSpecial;
     delete doc.useSpecialThumb;
     delete doc.publishVersion;
@@ -431,6 +690,9 @@ const organizeDetail = (doc, lang) => {
   doc.title = translate.titleTest[lang];
   doc.publishTime = format(new Date(doc.publishTime), 'yyyy/MM/dd');
 
+  let utf8name = encodeURIComponent(doc.name.replace(/[\s_]+/g, '-'));
+  doc.uri = `${utf8name}-${doc._id}`;
+
   delete doc.hasSpecial;
   delete doc.useSpecialThumb;
   delete doc.publishVersion;

+ 47 - 0
routes/res/thumbs.js

@@ -0,0 +1,47 @@
+var express = require('express');
+var router = express.Router();
+const fs = require('fs');
+var path = require('path');
+const createError = require('http-errors');
+const config = require('../../config/app');
+const { makeThumb } = require('../../libs/image');
+
+
+router.get(/^\/(avatar)\/(128|256|512|480|320|640|720)\/([a-f0-9]{24}).(png|jpg|jpeg)$/, function (req, res, next) {
+  (async function () {
+    let params = Object.values(req.params);
+    let [type, size, id, ext] = params;
+    var staticRoot = config.STATIC_DIR;
+    var thumbFilePath = path.resolve(staticRoot, 'thumbs/v1', [type, size, id].join('/')) + '.' + ext;
+    var originalFilePath = path.resolve(staticRoot, 'thumbs/v1', [type, 'original', id].join('/')) + '.' + ext;
+    let exists = fs.existsSync(originalFilePath);
+    let dir;
+    if (!exists) {
+      let data;
+      if (type == 'avatar') {
+        try {
+          data = fs.readFileSync(__dirname + `/../../dist/assets/avatar/${id}.jpeg`);
+        } catch (e) {
+          if (!data) data = fs.readFileSync(__dirname + `/../../dist/assets/avatar/default.jpeg`);
+        }
+      }
+
+      if (!data) throw createError(404, 'Page not found');
+
+      dir = path.dirname(originalFilePath);
+      fs.mkdirSync(dir, { recursive: true });
+
+      fs.writeFileSync(originalFilePath, data)
+    }
+
+    dir = path.dirname(thumbFilePath);
+    fs.mkdirSync(dir, { recursive: true });
+    await makeThumb(originalFilePath, thumbFilePath, size);
+
+    res.sendFile(thumbFilePath);
+  })().catch(next)
+});
+
+
+
+module.exports = router;

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2377 - 0
test/meta.html


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 421 - 0
test/meta2.html


+ 13 - 13
test/script.js

@@ -1,14 +1,14 @@
-// scripts.js
-document.addEventListener('DOMContentLoaded', function () {
-  var menuItems = document.querySelectorAll('.menu-item');
+let slideIndex = 0;
+showSlides();
 
-  menuItems.forEach(function (item) {
-    item.addEventListener('mouseover', function () {
-      this.querySelector('.submenu').style.display = 'block';
-    });
-
-    item.addEventListener('mouseout', function () {
-      this.querySelector('.submenu').style.display = 'none';
-    });
-  });
-});
+function showSlides() {
+  let i;
+  let slides = document.getElementsByClassName("mySlides");
+  for (i = 0; i < slides.length; i++) {
+    slides[i].style.display = "none";
+  }
+  slideIndex++;
+  if (slideIndex > slides.length) { slideIndex = 1 }
+  slides[slideIndex - 1].style.display = "block";
+  setTimeout(showSlides, 3000); // 更改图片间隔时间
+}

+ 10 - 57
test/styles.css

@@ -1,71 +1,24 @@
-/* styles.css */
 body {
   font-family: Arial, sans-serif;
-}
-
-nav {
-  background-color: #333;
-}
-
-.menu {
-  list-style: none;
-  padding: 0;
   margin: 0;
-  display: flex;
 }
 
-.menu-item {
+.slideshow-container {
   position: relative;
+  max-width: 1000px;
+  margin: auto;
 }
 
-.menu-item > a {
-  display: block;
-  padding: 15px 20px;
-  color: white;
-  text-decoration: none;
-  background-color: #333;
-  transition: background-color 0.3s;
-}
-
-.menu-item > a:hover {
-  background-color: #575757;
-}
-
-.submenu {
-  list-style: none;
-  padding: 0;
-  margin: 0;
+.mySlides {
   display: none;
-  position: absolute;
-  top: 100%;
-  left: 0;
-  background-color: #444;
-  min-width: 200px;
-  z-index: 1;
-}
-
-.submenu li {
-  border-bottom: 1px solid #555;
-}
-
-.submenu li:last-child {
-  border-bottom: none;
-}
-
-.submenu a {
-  padding: 10px 20px;
-  color: white;
-  text-decoration: none;
-  display: block;
-  background-color: #444;
-  transition: background-color 0.3s;
 }
 
-.submenu a:hover {
-  background-color: #555;
+.fade {
+  animation-name: fade;
+  animation-duration: 1.5s;
 }
 
-/* 当鼠标悬停在菜单项上时显示子菜单 */
-.menu-item:hover .submenu {
-  display: block;
+@keyframes fade {
+  from {opacity: .4} 
+  to {opacity: 1}
 }

+ 13 - 175
test/tags.html

@@ -4,184 +4,22 @@
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>渐变背景示例</title>
     <style>
-       .tag-cloud {
-          padding: 20px;
-        }
-
-       .tag {
-            display: inline-block;
-            padding: 5px 10px;
-            margin: 5px;
-            border-radius: 5px;
-            cursor: pointer;
-            background-color: #fefefe;
-            transition: color 0.3s ease;
-        }
-
-        .tag:hover {
-            transform: scale(2.0); /* 悬停时轻微放大 */
+        body {
+            /* 设置渐变背景 */
+            background: linear-gradient(to bottom, #feb47b, #ff7e5f);
+            /* 从左到右的渐变 */
+            height: 100vh;
+            /* 使背景覆盖整个视口高度 */
+            margin: 0;
+            /* 移除默认的body边距 */
         }
     </style>
 </head>
 
 <body>
-    <div class="tag-cloud">
-        <!-- 这里根据提供的tags数组生成标签 -->
-        <span class="tag" style="color: #FF5733;">animal</span>
-        <span class="tag" style="color: #33FF57;">scenery</span>
-        <span class="tag" style="color: #5733FF;">people</span>
-        <span class="tag" style="color: #FF33E7;">life</span>
-        <span class="tag" style="color: #33E7FF;">plant</span>
-        <span class="tag" style="color: #E7FF33;">girl</span>
-        <span class="tag" style="color: #FF3385;">flower</span>
-        <span class="tag" style="color: #3385FF;">fantasy</span>
-        <span class="tag" style="color: #85FF33;">data_good</span>
-        <span class="tag" style="color: #FF8533;">mandala</span>
-        <span class="tag" style="color: #33FF85;">food</span>
-        <span class="tag" style="color: #8533FF;">special_date</span>
-        <span class="tag" style="color: #FF3357;">nature</span>
-        <span class="tag" style="color: #3357FF;">cat</span>
-        <span class="tag" style="color: #57FF33;">landscape</span>
-        <span class="tag" style="color: #FF5785;">dog</span>
-        <span class="tag" style="color: #853357;">countryside</span>
-        <span class="tag" style="color: #5785FF;">forest</span>
-        <span class="tag" style="color: #FF8585;">ban</span>
-        <span class="tag" style="color: #85FF85;">flowers</span>
-        <span class="tag" style="color: #8585FF;">bird</span>
-        <span class="tag" style="color: #FF85E7;">river</span>
-        <span class="tag" style="color: #E785FF;">mountains</span>
-        <span class="tag" style="color: #85E7FF;">snow</span>
-        <span class="tag" style="color: #E7FF85;">recommend</span>
-        <span class="tag" style="color: #FFE733;">winter</span>
-        <span class="tag" style="color: #33FFE7;">house</span>
-        <span class="tag" style="color: #E733FF;">village</span>
-        <span class="tag" style="color: #FFE785;">heart</span>
-        <span class="tag" style="color: #85E733;">Christmas</span>
-        <span class="tag" style="color: #3385E7;">garden</span>
-        <span class="tag" style="color: #E73385;">butterfly</span>
-        <span class="tag" style="color: #8533E7;">fashion</span>
-        <span class="tag" style="color: #E78585;">summer</span>
-        <span class="tag" style="color: #85E785;">farm</span>
-        <span class="tag" style="color: #8585E7;">boy</span>
-        <span class="tag" style="color: #E78533;">place</span>
-        <span class="tag" style="color: #33E785;">sea</span>
-        <span class="tag" style="color: #E73357;">culture</span>
-        <span class="tag" style="color: #57E733;">car</span>
-        <span class="tag" style="color: #3357E7;">horse</span>
-        <span class="tag" style="color: #5733E7;">lake</span>
-        <span class="tag" style="color: #E75733;">autumn</span>
-        <span class="tag" style="color: #33E757;">tree</span>
-        <span class="tag" style="color: #E75785;">building</span>
-        <span class="tag" style="color: #85E757;">wild</span>
-        <span class="tag" style="color: #5785E7;">woman</span>
-        <span class="tag" style="color: #E757E7;">room</span>
-        <span class="tag" style="color: #E7E733;">park</span>
-        <span class="tag" style="color: #33E7E7;">ocean</span>
-        <span class="tag" style="color: #E733E7;">meadow</span>
-        <span class="tag" style="color: #E7E785;">rabbit</span>
-        <span class="tag" style="color: #85E7E7;">family</span>
-        <span class="tag" style="color: #E785E7;">patterns</span>
-        <span class="tag" style="color: #E7E757;">home</span>
-        <span class="tag" style="color: #57E7E7;">mountain</span>
-        <span class="tag" style="color: #E75733;">halloween</span>
-        <span class="tag" style="color: #33E757;">bridge</span>
-        <span class="tag" style="color: #E75785;">friends</span>
-        <span class="tag" style="color: #85E757;">city</span>
-        <span class="tag" style="color: #5785E7;">baby</span>
-        <span class="tag" style="color: #E757E7;">sunset</span>
-        <span class="tag" style="color: #E7E733;">simple</span>
-        <span class="tag" style="color: #33E7E7;">boat</span>
-        <span class="tag" style="color: #E733E7;">window</span>
-        <span class="tag" style="color: #E7E785;">plants</span>
-        <span class="tag" style="color: #85E7E7;">man</span>
-        <span class="tag" style="color: #E785E7;">trees</span>
-        <span class="tag" style="color: #E7E757;">fruit</span>
-        <span class="tag" style="color: #57E7E7;">rose</span>
-        <span class="tag" style="color: #E75733;">vacation</span>
-        <span class="tag" style="color: #33E757;">evening</span>
-        <span class="tag" style="color: #E75785;">castle</span>
-        <span class="tag" style="color: #85E757;">snowman</span>
-        <span class="tag" style="color: #5785E7;">street</span>
-        <span class="tag" style="color: #E757E7;">tiger</span>
-        <span class="tag" style="color: #E7E733;">grass</span>
-        <span class="tag" style="color: #33E7E7;">lady</span>
-        <span class="tag" style="color: #E733E7;">child</span>
-        <span class="tag" style="color: #E7E785;">vintage</span>
-        <span class="tag" style="color: #85E7E7;">holiday</span>
-        <span class="tag" style="color: #E785E7;">pasture</span>
-        <span class="tag" style="color: #E7E757;">deer</span>
-        <span class="tag" style="color: #57E7E7;">sweet</span>
-        <span class="tag" style="color: #E75733;">night</span>
-        <span class="tag" style="color: #33E757;">beach</span>
-        <span class="tag" style="color: #E75785;">travel</span>
-        <span class="tag" style="color: #85E757;">dress</span>
-        <span class="tag" style="color: #5785E7;">fish</span>
-        <span class="tag" style="color: #E757E7;">couple</span>
-        <span class="tag" style="color: #E7E733;">view</span>
-        <span class="tag" style="color: #33E7E7;">fairy</span>
-        <span class="tag" style="color: #E733E7;">harvest</span>
-        <span class="tag" style="color: #E7E785;">yard</span>
-        <span class="tag" style="color: #85E7E7;">sky</span>
-        <span class="tag" style="color: #E785E7;">toys</span>
-        <span class="tag" style="color: #E7E757;">wildflowers</span>
-        <span class="tag" style="color: #57E7E7;">love</span>
-        <span class="tag" style="color: #E75733;">pumpkin</span>
-        <span class="tag" style="color: #33E757;">water</span>
-        <span class="tag" style="color: #E75785;">transportation</span>
-        <span class="tag" style="color: #85E757;">birds</span>
-        <span class="tag" style="color: #5785E7;">rocks</span>
-        <span class="tag" style="color: #E757E7;">famous</span>
-        <span class="tag" style="color: #E7E733;">downtown</span>
-        <span class="tag" style="color: #33E7E7;">ice</span>
-        <span class="tag" style="color: #E733E7;">bear</span>
-        <span class="tag" style="color: #E7E785;">spring</span>
-        <span class="tag" style="color: #85E7E7;">road</span>
-        <span class="tag" style="color: #E785E7;">wolf</span>
-        <span class="tag" style="color: #E7E757;">unicorn</span>
-        <span class="tag" style="color: #57E7E7;">lion</span>
-        <span class="tag" style="color: #E75733;">stone</span>
-        <span class="tag" style="color: #33E757;">magic</span>
-        <span class="tag" style="color: #E75785;">cake</span>
-        <span class="tag" style="color: #85E757;">book</span>
-        <span class="tag" style="color: #5785E7;">furniture</span>
-        <span class="tag" style="color: #E757E7;">children</span>
-        <span class="tag" style="color: #E7E733;">fox</span>
-        <span class="tag" style="color: #33E7E7;">zhy03re</span>
-        <span class="tag" style="color: #E733E7;">cartoon</span>
-        <span class="tag" style="color: #E7E785;">duck</span>
-        <span class="tag" style="color: #85E7E7;">owl</span>
-        <span class="tag" style="color: #E785E7;">squirrel</span>
-        <span class="tag" style="color: #E7E757;">sport</span>
-        <span class="tag" style="color: #57E7E7;">angel</span>
-        <span class="tag" style="color: #E75733;">cub</span>
-        <span class="tag" style="color: #33E757;">decorations</span>
-        <span class="tag" style="color: #E75785;">pond</span>
-        <span class="tag" style="color: #85E757;">sheep</span>
-        <span class="tag" style="color: #5785E7;">fence</span>
-        <span class="tag" style="color: #E757E7;">interior</span>
-        <span class="tag" style="color: #E7E733;">coastline</span>
-        <span class="tag" style="color: #33E7E7;">beauty</span>
-        <span class="tag" style="color: #E733E7;">chicken</span>
-        <span class="tag" style="color: #E7E785;">train</span>
-        <span class="tag" style="color: #85E7E7;">mermaid</span>
-        <span class="tag" style="color: #E785E7;">history</span>
-        <span class="tag" style="color: #E7E757;">moon</span>
-        <span class="tag" style="color: #57E7E7;">picnic</span>
-        <span class="tag" style="color: #E75733;">bedroom</span>
-        <span class="tag" style="color: #33E757;">blooming</span>
-        <span class="tag" style="color: #E75785;">sunflower</span>
-        <span class="tag" style="color: #85E757;">mystery</span>
-        <span class="tag" style="color: #5785E7;">lovers</span>
-        <span class="tag" style="color: #E757E7;">stairs</span>
-        <span class="tag" style="color: #E7E733;">walk</span>
-        <span class="tag" style="color: #33E7E7;">jungle</span>
-        <span class="tag" style="color: #E733E7;">livingroom</span>
-        <span class="tag" style="color: #E7E785;">thanksgiving</span>
-        <span class="tag" style="color: #85E7E7;">kingdom</span>
-        <span class="tag" style="color: #E785E7;">mother and daughter</span>
-        <span class="tag" style="color: #E7E757;">domestic</span>
-        <span class="tag" style="color: #57E7E7;">mother</span>
-        <span class="tag" style="color: #E75733;">lotus</span>
-  </div>
-  </body>
+    <h1>欢迎来到渐变背景的世界!</h1>
+</body>
+
+</html>

+ 106 - 21
test/test.html

@@ -1,29 +1,114 @@
 <!DOCTYPE html>
 <html lang="zh-CN">
+
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>下拉菜单示例</title>
-    <link rel="stylesheet" href="styles.css">
+    <title>头像卡片</title>
+    <style>
+        .container {
+            display: grid;
+            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+            gap: 16px;
+            width: 90%;
+            margin: 20px auto;
+        }
+
+        .card {
+
+            border: 1px solid #ccc;
+            padding: 20px;
+            max-width: 200px;
+            text-align: center;
+            border-radius: 10px;
+            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+        }
+
+        .card img {
+            border-radius: 50%;
+            width: 100px;
+            height: 100px;
+            object-fit: cover;
+        }
+
+        .card .info {
+            margin-top: 15px;
+        }
+
+        .card .info p {
+            margin: 5px 0;
+        }
+    </style>
 </head>
+
 <body>
-    <nav>
-        <ul class="menu">
-            <li class="menu-item" id="menu-home">
-                <a href="#">首页</a>
-                <ul class="submenu" id="submenu-home">
-                    <li><a href="#">分类</a></li>
-                    <li><a href="#">专辑</a></li>
-                    <li><a href="#">彩绘专区</a></li>
-                    <li><a href="#">关于</a></li>
-                    <li><a href="#">APP下载</a></li>
-                    <li><a href="#">联系我们</a></li>
-                    <li><a href="#">意见反馈</a></li>
-                </ul>
-            </li>
-        </ul>
-    </nav>
-
-    <script src="scripts.js"></script>
+
+    <div class="container">
+        <div class="card">
+            <img src="http://localhost:3000/thumbs/v1/avatar/320/5b965144028d9b3606010b13.jpeg" alt="头像">
+            <div class="info">
+                <p><strong>姓名</strong>: 张三</p>
+                <p><strong>作品数量</strong>: 20</p>
+            </div>
+        </div>
+        <div class="card">
+            <img src="http://localhost:3000/thumbs/v1/avatar/320/5b965144028d9b3606010b13.jpeg" alt="头像">
+            <div class="info">
+                <p><strong>姓名</strong>: 张三</p>
+                <p><strong>作品数量</strong>: 20</p>
+            </div>
+        </div>
+        <div class="card">
+            <img src="http://localhost:3000/thumbs/v1/avatar/320/5b965144028d9b3606010b13.jpeg" alt="头像">
+            <div class="info">
+                <p><strong>姓名</strong>: 张三</p>
+                <p><strong>作品数量</strong>: 20</p>
+            </div>
+        </div>
+        <div class="card">
+            <img src="http://localhost:3000/thumbs/v1/avatar/320/5b965144028d9b3606010b13.jpeg" alt="头像">
+            <div class="info">
+                <p><strong>姓名</strong>: 张三</p>
+                <p><strong>作品数量</strong>: 20</p>
+            </div>
+        </div>
+        <div class="card">
+            <img src="http://localhost:3000/thumbs/v1/avatar/320/5b965144028d9b3606010b13.jpeg" alt="头像">
+            <div class="info">
+                <p><strong>姓名</strong>: 张三</p>
+                <p><strong>作品数量</strong>: 20</p>
+            </div>
+        </div>
+        <div class="card">
+            <img src="http://localhost:3000/thumbs/v1/avatar/320/5b965144028d9b3606010b13.jpeg" alt="头像">
+            <div class="info">
+                <p><strong>姓名</strong>: 张三</p>
+                <p><strong>作品数量</strong>: 20</p>
+            </div>
+        </div>
+        <div class="card">
+            <img src="http://localhost:3000/thumbs/v1/avatar/320/5b965144028d9b3606010b13.jpeg" alt="头像">
+            <div class="info">
+                <p><strong>姓名</strong>: 张三</p>
+                <p><strong>作品数量</strong>: 20</p>
+            </div>
+        </div>
+        <div class="card">
+            <img src="http://localhost:3000/thumbs/v1/avatar/320/5b965144028d9b3606010b13.jpeg" alt="头像">
+            <div class="info">
+                <p><strong>姓名</strong>: 张三</p>
+                <p><strong>作品数量</strong>: 20</p>
+            </div>
+        </div>
+        <div class="card">
+            <img src="http://localhost:3000/thumbs/v1/avatar/320/5b965144028d9b3606010b13.jpeg" alt="头像">
+            <div class="info">
+                <p><strong>姓名</strong>: 张三</p>
+                <p><strong>作品数量</strong>: 20</p>
+            </div>
+        </div>
+    </div>
+
 </body>
-</html>
+
+</html>

+ 91 - 0
views/404.ejs

@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <%- include('common-meta') %>
+    <style>
+      body {
+        color: white;
+        background-color: #fff3f3;
+        display: flex;
+        justify-content: center;
+        flex-direction: column;
+        align-items: center;
+        width: 100vw;
+        height: 100vh;
+        margin: 0;
+      }
+
+      img {
+        width: 60%;
+        height: auto;
+      }
+
+      .container {
+        position: relative;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+      }
+
+      .text-container {
+        position: absolute;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        padding-top: 260px;
+      }
+
+      .not-found-text {
+        color: #F94936;
+        font-size: 52px;
+        font-weight: 600;
+        padding: 20px;
+      }
+
+      .back-home-text {
+        color: #F94936;
+        font-size: 20px;
+      }
+
+      /* for mobile */
+      @media (max-width: 768px) {
+
+        img {
+          width: 90%;
+          height: auto;
+        }
+
+        .not-found-text {
+          font-size: 36px;
+          padding: 10px;
+        }
+
+        .back-home-text {
+          font-size: 18px;
+        }
+
+        .text-container {
+
+          padding-top: 180px;
+        }
+
+      }
+    </style>
+</head>
+
+<body>
+
+  <div class="container">
+    <img src="/assets/icon/404.png" alt="404 Not Found">
+    <div class="text-container">
+      <div class="not-found-text">Page Not Found</div>
+      <a href="/" class="back-home-text">BACK TO HOME PAGE</a>
+    </div>
+  </div>
+
+</body>
+
+</html>

+ 23 - 19
views/album.ejs

@@ -1,32 +1,36 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="<%= lang %>">
+
 <head>
-  <%- include('head') %>
+  <%- include('common-meta') %>
+  <link rel="stylesheet" href="/stylesheets/styles.css">
   <link rel="stylesheet" href="/stylesheets/album.css">
 </head>
 
-<body>  
+<body>
   <%- include('header') %>
 
-  <div class="album-header">
-    <h1 style="color: purple"><%= data.title %></h1>
-    <img style="border-radius: 8px;" src="<%= data.cover %>" alt="<%= data.title %>">
-    <div style="color: gray; font-size: 18px; padding: 10px"><%= data.slogon %></div>
-  </div>
-
-  <div class="content">
-    <div class="image-grid">
-        <% data.contents.forEach(item => { %>
-            <a href="/<%= lang %>/detail/<%= item._id %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
-        <% }); %>
+    <div class="album-header">
+      <h1 style="color: purple">
+        <%= data.title %>
+      </h1>
+      <img style="border-radius: 8px;" src="<%= data.cover %>" alt="<%= data.title %>">
+      <div style="color: gray; font-size: 18px; padding: 10px">
+        <%= data.slogon %>
+      </div>
     </div>
-  </div>
 
-  <div style="padding: 40px"></div>
+    <div class="content">
+      <div class="image-grid">
+        <% data.contents.forEach(item=> { %>
+          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>"></a>
+          <% }); %>
+      </div>
+    </div>
 
-
-</body>
-</html>
+    <div style="padding: 40px"></div>
 
 
+</body>
 
+</html>

+ 26 - 14
views/albums.ejs

@@ -1,28 +1,40 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="<%= lang %>">
+
 <head>
-  <%- include('head') %>
+  <%- include('common-meta') %>
+
+  <link rel="alternate" href="https://art.pcoloring.com/en/albums" hrefLang="en" />
+  <link rel="alternate" href="https://art.pcoloring.com/zh/albums" hrefLang="zh" />
+  <link rel="alternate" href="https://art.pcoloring.com/es/albums" hrefLang="es" />
+  <link rel="alternate" href="https://art.pcoloring.com/pt/albums" hrefLang="pt" />
+  <link rel="alternate" href="https://art.pcoloring.com/ja/albums" hrefLang="ja" />
+
+  <link rel="stylesheet" href="/stylesheets/styles.css">
+
 </head>
 
-<body>  
+<body>
   <%- include('header') %>
 
-  <h1 style="display: flex; justify-content: center; padding: 10px; color: purple"><%= translate.allAlbums[lang] %></h1>
+    <h1 style="display: flex; justify-content: center; padding: 10px; color: purple">
+      <%= translate.allAlbums[lang] %>
+    </h1>
 
-  <div class="content">
-    <div class="album-grid">
-        <% data.forEach(album => { %>
+    <div class="content">
+      <div class="album-grid">
+        <% data.forEach(album=> { %>
           <div class="album-grid-card">
-            <a href="/<%= lang %>/album/<%= album._id %>"><img src="<%= album.cover %>" alt="<%= album.title %>" ></a>
-            <div style="padding: 0px 0px 4px 4px"><%= album.title %></div>
+            <a href="/<%= lang %>/album/<%= album._id %>"><img src="<%= album.cover %>" alt="<%= album.title %>"></a>
+            <div style="padding: 0px 0px 4px 4px">
+              <%= album.title %>
+            </div>
           </div>
-        <% }); %>
+          <% }); %>
+      </div>
     </div>
-  </div>
 
 
 </body>
-</html>
-
-
 
+</html>

+ 29 - 18
views/category.ejs

@@ -1,31 +1,42 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="<%= lang %>">
+
 <head>
-  <%- include('head') %>
+  <%- include('common-meta') %>
+
+  <link rel="alternate" href="https://art.pcoloring.com/en/category" hrefLang="en" />
+  <link rel="alternate" href="https://art.pcoloring.com/zh/category" hrefLang="zh" />
+  <link rel="alternate" href="https://art.pcoloring.com/es/category" hrefLang="es" />
+  <link rel="alternate" href="https://art.pcoloring.com/pt/category" hrefLang="pt" />
+  <link rel="alternate" href="https://art.pcoloring.com/ja/category" hrefLang="ja" />
+
+  <link rel="stylesheet" href="/stylesheets/styles.css">
   <link rel="stylesheet" href="/stylesheets/category.css">
 </head>
 
 <body>
   <%- include('header') %>
-  <div class="category">  
-    <% categories.forEach(item => { %>
-      <a href="/<%= lang %>/category/<%= item.id %>" class="<%= tag == item.id ? 'selected' : '' %>"><%= item[lang] %></a>
-    <% }); %>
-    <a href="/<%= lang %>/tag" style="color: #ff4081"><%= translate.more[lang] %>>>></a>
-  </div>
-  
-  <div class="content">
-    <div class="image-grid">
-        <% data.forEach(item => { %>
-            <a href="/<%= lang %>/detail/<%= item._id %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
+    <div class="category">
+      <% categories.forEach(item=> { %>
+        <a href="/<%= lang %>/category/<%= item.id %>" class="<%= tag == item.id ? 'selected' : '' %>">
+          <%= item[lang] %>
+        </a>
         <% }); %>
+          <a href="/<%= lang %>/tag" style="color: #ff4081">
+            <%= translate.more[lang] %>>>>
+          </a>
     </div>
-  </div>
-
-  <%- include('pagination') %>
 
-</body>
-</html>
+    <div class="content">
+      <div class="image-grid">
+        <% data.forEach(item=> { %>
+          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>"></a>
+          <% }); %>
+      </div>
+    </div>
 
+    <%- include('pagination') %>
 
+</body>
 
+</html>

+ 2 - 2
views/head.ejs → views/common-meta.ejs

@@ -1,5 +1,5 @@
 <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title><%= title %></title>
   <link rel="icon" href="/assets/icon/favicon.ico" type="image/x-icon">
-  <link rel="stylesheet" href="/stylesheets/styles.css">
+  <title><%= title %></title>
+  <meta name="description" content="<%= description %>" />

+ 37 - 0
views/designer.ejs

@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="<%= lang %>">
+
+<head>
+  <%- include('common-meta') %>
+  <link rel="stylesheet" href="/stylesheets/styles.css">
+  <link rel="stylesheet" href="/stylesheets/designer.css">
+</head>
+
+<body>
+  <%- include('header') %>
+
+    <div style="display: flex; justify-content: center; align-items: center; margin-top: 20px;">
+      <div class="card" style="width: 200px;">
+        <a href="/<%= lang %>/designer/<%= user._id %>"><img src="<%= user.avatar %>" alt="<%= user.username %>"></a>
+        <div class="info">
+          <p><strong><%= user.username %></strong></p>
+          <p><%= translate.worksCount[lang] %>: <strong><%= user.count %></strong></p>
+        </div>
+      </div>
+    </div>
+
+    <div class="content">
+      <div class="image-grid">
+        <% data.forEach(item=> { %>
+          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>"></a>
+          <% }); %>
+      </div>
+    </div>
+
+    <div style="padding: 40px"></div>
+
+  <%- include('pagination') %>
+
+</body>
+
+</html>

+ 38 - 0
views/designers.ejs

@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="<%= lang %>">
+
+<head>
+  <%- include('common-meta') %>
+
+  <link rel="alternate" href="https://art.pcoloring.com/en/designers" hrefLang="en" />
+  <link rel="alternate" href="https://art.pcoloring.com/zh/designers" hrefLang="zh" />
+  <link rel="alternate" href="https://art.pcoloring.com/es/designers" hrefLang="es" />
+  <link rel="alternate" href="https://art.pcoloring.com/pt/designers" hrefLang="pt" />
+  <link rel="alternate" href="https://art.pcoloring.com/ja/designers" hrefLang="ja" />
+
+  <link rel="stylesheet" href="/stylesheets/designer.css">
+
+</head>
+
+<body>
+  <%- include('header') %>
+
+    <h1 style="display: flex; justify-content: center; padding: 10px; color: purple">
+      <%= translate.designerColumn[lang] %>
+    </h1>
+
+    <div class="container">
+      <% data.forEach(item=> { %>
+        <div class="card">
+          <a href="/<%= lang %>/designer/<%= item._id %>"><img src="<%= item.avatar %>" alt="<%= item.username %>"></a>
+          <div class="info">
+            <p><strong><%= item.username %></strong></p>
+            <p><%= translate.worksCount[lang] %>: <strong><%= item.count %></strong></p>
+          </div>
+        </div>
+        <% }); %>
+    </div>
+
+</body>
+
+</html>

+ 29 - 18
views/detail.ejs

@@ -1,39 +1,50 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="<%= lang %>">
+
 <head>
-  <%- include('head') %>
+  <%- include('common-meta') %>
+  
+  <link rel="alternate" href="https://art.pcoloring.com/en/<%= uri.substring(3) %>" hrefLang="en" />
+  <link rel="alternate" href="https://art.pcoloring.com/zh/<%= uri.substring(3) %>" hrefLang="zh" />
+  <link rel="alternate" href="https://art.pcoloring.com/es/<%= uri.substring(3) %>" hrefLang="es" />
+  <link rel="alternate" href="https://art.pcoloring.com/pt/<%= uri.substring(3) %>" hrefLang="pt" />
+  <link rel="alternate" href="https://art.pcoloring.com/ja/<%= uri.substring(3) %>" hrefLang="ja" />
+
+  <link rel="stylesheet" href="/stylesheets/styles.css">
   <link rel="stylesheet" href="/stylesheets/detail.css">
 </head>
 
 <body>
     <%- include('header') %>
     <div class="details">
-        <div class="poster">
-            <img src="<%= data.thumb %>" alt="<%= data.title %>">
-        </div>
+        <div class="poster"><img src="<%= detail.thumb %>" alt="<%= detail.title %>"></div>
         <div class="description">
-            <div style="font-size: 30px; font-weight: 700;"><%= data.title %></div>
-            <p><%= translate.designer[lang] %>: <%= data.user.username %></p>
-            <p><%= translate.publishTime[lang] %>: <%= data.publishTime %></p>
-            <p><%= translate.tag[lang] %>:
-                <% data.tags.forEach(tag => { %>
-                    <a href="/<%= lang %>/tag/<%= tag %>" class="tag-button"><%= tag %></a>
-                <% }); %>
+            <div style="font-size: 30px; font-weight: 700;"><%= detail.title %></div>
+            <p><%= translate.designer[lang] %>: <a href="/<%= lang %>/designer/<%= detail.user._id %>" class="tag-button"><%= detail.user.username %></a></p>
+            <p><%= translate.publishTime[lang] %>: <%= detail.publishTime %></p>
+            <p>
+            <%= translate.tag[lang] %>:
+            <% detail.tags.forEach(tag=> { %>
+                <a href="/<%= lang %>/tag/<%= tag %>" class="tag-button"><%= tag %></a>
+            <% }); %>
             </p>
-            <div><%= data.desc %></div>
-            <a href="/play/<%= data._id %>" class="play-button"><%= translate.play[lang] %></a>
+            <div><%= detail.desc %></div>
+            <a href="/play/<%= detail._id %>" class="play-button"><%= translate.play[lang] %></a>
         </div>
     </div>
 
-    <p style="display: flex; justify-content: center; color: #777; font-size: 18px; font-weight: 500;">猜您喜欢:<p>
+    <p style="display: flex; justify-content: center; color: #777; font-size: 18px; font-weight: 500;"><%= translate.mayYouLike[lang] %>:</p>
 
     <div class="content" style="margin-bottom: 40px;">
         <div class="image-grid">
-            <% relates.forEach(item => { %>
-                <a href="/<%= lang %>/detail/<%= item._id %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
+            <% relates.forEach(item=> { %>
+            <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>"></a>
             <% }); %>
         </div>
     </div>
 
+    <%- include('pagination') %>
+
 </body>
-</html>
+
+</html>

+ 6 - 29
views/footer.ejs

@@ -1,29 +1,6 @@
-<link rel="stylesheet" href="/stylesheets/footer.css">
-<footer>
-  <!-- <div class="footer-content">
-    <div class="footer-section">
-        <h3>关于Art Number Coloring</h3>
-        <p>Art Number Coloring是一款专为艺术爱好者设计的数字涂色游戏应用,拥有海量的线条底图资源,并且完全免费。精美的构图,多彩的颜色,绝佳的游戏体验,在感受色彩艺术气息的同时,为您的休闲娱乐生活带来意想不到的愉悦。</p>
-    </div>
-    <div class="footer-section">
-        <h3>App下载</h3>
-        <ul>
-            <li><a href="#">iOS下载</a></li>
-            <li><a href="#">Android下载</a></li>
-        </ul>
-    </div>
-    <div class="footer-section">
-        <h3>联系我们</h3>
-        <p>邮箱: contact@artnumbercoloring.com</p>
-        <p>电话: +8613530139503</p>
-    </div>
-    <div class="footer-section">
-        <h3>意见反馈</h3>
-        <p>我们非常重视您的意见,请点击<a href="#">这里</a>进行反馈。</p>
-    </div>
-  </div> -->
-  <div class="footer-bottom">
-      <p>Copyright &copy; 2025 Art Number Coloring All Rights Reserved</p>
-  </div>
-</footer>
-
+<link rel="stylesheet" href="/stylesheets/tag.css">
+<div class="tag-cloud">
+    <% tags.forEach(item=> { %>
+    <a href="/<%= lang %>/tag/<%= item.tag %>" class="tag" style="color: <%= item.color %>;"><%= item.tag %></a>
+    <% }); %>
+</div>

+ 35 - 20
views/header.ejs

@@ -2,37 +2,44 @@
 
 <header class="header">
   <div class="dropdown">
-    <svg class="dropbtn" style="margin-right: 5px;" width="24px" height="24px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-      <path opacity="0.5" d="M21 6L3 6" stroke="#1C274C" stroke-width="2.0" stroke-linecap="round"/>
-      <path opacity="0.5" d="M21 10L3 10" stroke="#1C274C" stroke-width="2.0" stroke-linecap="round"/>
-      <path opacity="0.5" d="M10 14H3" stroke="#1C274C" stroke-width="2.0" stroke-linecap="round"/>
-      <path opacity="0.5" d="M10 18H3" stroke="#1C274C" stroke-width="2.0" stroke-linecap="round"/>
-      <path d="M14 15L17.5 18L21 15" stroke="#1C274C" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
-      </svg>
+    <svg class="dropbtn" style="margin-right: 5px;" width="24px" height="24px" viewBox="0 0 24 24" fill="none"
+      xmlns="http://www.w3.org/2000/svg">
+      <path opacity="0.5" d="M21 6L3 6" stroke="#1C274C" stroke-width="2.0" stroke-linecap="round" />
+      <path opacity="0.5" d="M21 10L3 10" stroke="#1C274C" stroke-width="2.0" stroke-linecap="round" />
+      <path opacity="0.5" d="M10 14H3" stroke="#1C274C" stroke-width="2.0" stroke-linecap="round" />
+      <path opacity="0.5" d="M10 18H3" stroke="#1C274C" stroke-width="2.0" stroke-linecap="round" />
+      <path d="M14 15L17.5 18L21 15" stroke="#1C274C" stroke-width="2.5" stroke-linecap="round"
+        stroke-linejoin="round" />
+    </svg>
     <div class="dropdown-home-content">
       <a href="/<%= lang %>" class="<%= uri == `/${lang}` ? 'selected' : '' %>"><%= translate.homePage[lang] %></a>
       <a href="/<%= lang %>/category" class="<%= uri.includes(`/${lang}/category`) ? 'selected' : '' %>"><%= translate.categoryPage[lang] %></a>
       <a href="/<%= lang %>/tag" class="<%= uri.includes(`/${lang}/tag`) ? 'selected' : '' %>"><%= translate.tagPage[lang] %></a>
       <a href="/<%= lang %>/albums" class="<%= uri.includes(`/${lang}/albums`) ? 'selected' : '' %>"><%= translate.album[lang] %></a>
       <a href="/<%= lang %>/special" class="<%= uri.includes(`/${lang}/special`) ? 'selected' : '' %>"><%= translate.special[lang] %></a>
+      <a href="/<%= lang %>/designers" class="<%= uri.includes(`/${lang}/designers`) ? 'selected' : '' %>"><%= translate.designerColumn[lang] %></a>
+      <div class="divider"></div>
+      <a href="/<%= lang %>/my-works"><%= translate.my[lang] %></a>
       <div class="divider"></div>
-      <a href="/<%= lang %>/about"><%= translate.about[lang] %></a>
-      <a href="/<%= lang %>/contact-us"><%= translate.contactUs[lang] %></a>
-      <a href="/<%= lang %>/feedback"><%= translate.feedback[lang] %></a>
+      <a href="/<%= lang %>/info#app" class="<%= uri.includes(`/${lang}/info#app`) ? 'selected' : '' %>"><%= translate.app[lang] %></a>
+      <a href="/<%= lang %>/info#about" class="<%= uri.includes(`/${lang}/info#about`) ? 'selected' : '' %>"><%= translate.about[lang] %></a>
+      <a href="/<%= lang %>/info#contact" class="<%= uri.includes(`/${lang}/info#contact`) ? 'selected' : '' %>"><%= translate.contactUs[lang] %></a>
       <div class="divider"></div>
       <p class="copyright">Copyright &copy; 2025 Art Number Coloring All Rights Reserved</p>
     </div>
   </div>
 
-  <a href="/<%= lang %>"><img src="/assets/svg/logo.svg", alt="Art Number Coloring"></a>
+  <a href="/<%= lang %>"><img src="/assets/svg/logo.svg" , alt="Art Number Coloring"></a>
 
   <div class="search-container">
     <form action="/<%= lang %>/search" method="GET" class="search-box">
       <input type="text" name="search" placeholder="Search...">
       <button type="submit">
-          <svg fill="#000000" width="16px" height="16px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-              <path d="M12.027 9.92L16 13.95 14 16l-4.075-3.976A6.465 6.465 0 0 1 6.5 13C2.91 13 0 10.083 0 6.5 0 2.91 2.917 0 6.5 0 10.09 0 13 2.917 13 6.5a6.463 6.463 0 0 1-.973 3.42zM1.997 6.452c0 2.48 2.014 4.5 4.5 4.5 2.48 0 4.5-2.015 4.5-4.5 0-2.48-2.015-4.5-4.5-4.5-2.48 0-4.5 2.014-4.5 4.5z" fill-rule="evenodd"/>
-          </svg>
+        <svg fill="#000000" width="16px" height="16px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+          <path
+            d="M12.027 9.92L16 13.95 14 16l-4.075-3.976A6.465 6.465 0 0 1 6.5 13C2.91 13 0 10.083 0 6.5 0 2.91 2.917 0 6.5 0 10.09 0 13 2.917 13 6.5a6.463 6.463 0 0 1-.973 3.42zM1.997 6.452c0 2.48 2.014 4.5 4.5 4.5 2.48 0 4.5-2.015 4.5-4.5 0-2.48-2.015-4.5-4.5-4.5-2.48 0-4.5 2.014-4.5 4.5z"
+            fill-rule="evenodd" />
+        </svg>
       </button>
     </form>
   </div>
@@ -41,16 +48,24 @@
   <div class="header-right">
 
     <div class="dropdown">
-      <svg class="dropbtn" width="24px" height="24px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg" aria-labelledby="languageIconTitle" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none" color="#000000"> <title id="languageIconTitle">Language</title> <circle cx="12" cy="12" r="10"/> <path stroke-linecap="round" d="M12,22 C14.6666667,19.5757576 16,16.2424242 16,12 C16,7.75757576 14.6666667,4.42424242 12,2 C9.33333333,4.42424242 8,7.75757576 8,12 C8,16.2424242 9.33333333,19.5757576 12,22 Z"/> <path stroke-linecap="round" d="M2.5 9L21.5 9M2.5 15L21.5 15"/> </svg>
+      <svg class="dropbtn" width="24px" height="24px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg"
+        aria-labelledby="languageIconTitle" stroke="#000000" stroke-width="1.5" stroke-linecap="round"
+        stroke-linejoin="round" fill="none" color="#000000">
+        <title id="languageIconTitle">Language</title>
+        <circle cx="12" cy="12" r="10" />
+        <path stroke-linecap="round"
+          d="M12,22 C14.6666667,19.5757576 16,16.2424242 16,12 C16,7.75757576 14.6666667,4.42424242 12,2 C9.33333333,4.42424242 8,7.75757576 8,12 C8,16.2424242 9.33333333,19.5757576 12,22 Z" />
+        <path stroke-linecap="round" d="M2.5 9L21.5 9M2.5 15L21.5 15" />
+      </svg>
       <div class="dropdown-content">
-        <% languages.forEach(lg => { %>
-            <a href="/<%= lg.code %>" class="<%= lg.code == lang ? 'selected' : '' %>" style="font-size: 16px"><%= lg.title %></a>
-        <% }); %>
+        <% languages.forEach(lg=> { %>
+          <a href="/<%= lg.code %>" class="<%= lg.code == lang ? 'selected' : '' %>" style="font-size: 16px"><%= lg.title %></a>
+          <% }); %>
       </div>
     </div>
 
-    <a href="/<%= lang %>/app" class="header-right-btn"><%= translate.app[lang] %></a>
-    <a href="/<%= lang %>/my-works" class="header-right-btn"><%= translate.my[lang] %></a>
+    <!-- <a href="/<%= lang %>/app" class="header-right-btn"><%= translate.app[lang] %></a>
+    <a href="/<%= lang %>/my-works" class="header-right-btn"><%= translate.my[lang] %></a> -->
   </div>
 
 </header>

+ 1 - 1
views/hot-section.ejs

@@ -8,7 +8,7 @@
   <div class="content">
     <div class="image-grid">
         <% recommend.forEach(item => { %>
-            <a href="/<%= lang %>/detail/<%= item._id %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
+            <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
         <% }); %>
     </div>
   </div>

+ 13 - 2
views/index.ejs

@@ -1,8 +1,17 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="<%= lang %>">
+
 <head>
-  <%- include('head') %>
+  <%- include('common-meta') %>
+  <link rel="alternate" href="https://art.pcoloring.com/en" hrefLang="en" />
+  <link rel="alternate" href="https://art.pcoloring.com/zh" hrefLang="zh" />
+  <link rel="alternate" href="https://art.pcoloring.com/es" hrefLang="es" />
+  <link rel="alternate" href="https://art.pcoloring.com/pt" hrefLang="pt" />
+  <link rel="alternate" href="https://art.pcoloring.com/ja" hrefLang="ja" />
+
+  <link rel="stylesheet" href="/stylesheets/styles.css">
 </head>
+
 <body>
   <%- include('header') %>
   <%- include('banner') %>
@@ -10,6 +19,8 @@
   <%- include('album-section') %>
   <%- include('hot-section') %>
   <%- include('special-section') %>
+  <%- include('footer') %>
   <div style="height: 50px;"></div>
 </body>
+
 </html>

+ 167 - 0
views/info.ejs

@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<html lang="<%= lang %>">
+
+<head>
+  <%- include('common-meta') %>
+
+  <link rel="alternate" href="https://art.pcoloring.com/en/info" hrefLang="en" />
+  <link rel="alternate" href="https://art.pcoloring.com/zh/info" hrefLang="zh" />
+  <link rel="alternate" href="https://art.pcoloring.com/es/info" hrefLang="es" />
+  <link rel="alternate" href="https://art.pcoloring.com/pt/info" hrefLang="pt" />
+  <link rel="alternate" href="https://art.pcoloring.com/ja/info" hrefLang="ja" />
+
+  <link rel="stylesheet" href="/stylesheets/styles.css">
+  <link rel="stylesheet" href="/stylesheets/info.css">
+
+  <script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.5.8/lottie.min.js"></script>
+  
+</head>
+
+<body>
+  <section id="about" class="full-screen about-container">
+    <div class="about-content">
+      <div class="section-title">
+        <%= translate.aboutX[lang] %>
+      </div>
+      <div class="company-info" style="padding-bottom: 40px;">
+        <%= translate.companyIntroduction[lang] %>
+      </div>
+      <div class="slideshow-container">
+
+        <div class="mySlides fade">
+          <img src="/assets/icon/color-by-number.webp" alt="color-by-number app"
+            style="width:100%; border-radius: 16px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);">
+        </div>
+
+        <div class="mySlides fade">
+          <img src="/assets/icon/jigsaw-puzzle.webp" alt="jigsaw-puzzle app"
+            style="width:100%; border-radius: 16px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);">
+        </div>
+
+        <div class="mySlides fade">
+          <img src="/assets/icon/art-puzzle.webp" alt="art-puzzle app"
+            style="width:100%; border-radius: 16px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);">
+        </div>
+
+        <div class="mySlides fade">
+          <img src="/assets/icon/find-difference.webp" alt="find-difference app"
+            style="width:100%; border-radius: 16px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);">
+        </div>
+
+        <div class="mySlides fade">
+          <img src="/assets/icon/soduko.webp" alt="soduko app"
+            style="width:100%; border-radius: 16px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);">
+        </div>
+
+      </div>
+
+      <div>
+  </section>
+
+
+  <section id="app" class="full-screen app-container">
+    <div class="left">
+      <div style="display: flex;">
+        <img class="app-icon" src="/assets/icon/logo_640x640.webp" alt="app icon">
+        <ul style="font-size: 18px;">
+          <li>
+            <%= translate.goodExperience[lang] %>
+          </li>
+          <li>
+            <%= translate.massivePictures[lang] %>
+          </li>
+          <li>
+            <%= translate.dailyUpdate[lang] %>
+          </li>
+          <li>
+            <%= translate.easyShare[lang] %>
+          </li>
+          <li>
+            <%= translate.offlineColoring[lang] %>
+          </li>
+        </ul>
+      </div>
+      <div class="app-name">Art Number Coloring</div>
+      <p class="app-description">
+        <%= translate.appIntroduction[lang] %>
+      </p>
+
+      <a class="app-download-btn" href="itms-apps://itunes.apple.com/app/id1575480118?action=write-review"><img
+          src="/assets/icon/icon-app-store.svg" alt="app store icon"></a>
+      <a class="app-download-btn"
+        href="https://play.google.com/store/apps/details?id=com.pcoloring.art.puzzle.color.by.number&pcampaignid=web_share"><img
+          src="/assets/icon/icon-google-play.svg" alt="google play icon"></a>
+    </div>
+    <div class="right">
+      <div id="lottie" style="width: 80%"></div>
+    </div>
+  </section>
+
+
+
+  <section id="contact" class="full-screen contact-container">
+    <div class="contact-content">
+      <div class="section-title" style="padding: 0px 0px 60px 0px;">
+        <%= translate.contactX[lang] %>
+      </div>
+      <div style="font-size: 20px;">
+        <%= translate.questionAndPartnership[lang] %>
+      </div>
+      <p style="font-size: 20px;">
+        <%= translate.happyToHear[lang] %>
+      </p>
+      <div style="display: flex;">
+        <img width="32px" src="/assets/svg/email.svg" alt="email">
+        <a href="mailto:icolor_support@jccy-tech.com">
+          <%= translate.supportOrFeedback[lang] %>
+        </a>
+      </div>
+
+      <div style="display: flex; padding-top: 40px;">
+        <img width="28px" src="/assets/svg/location.svg" alt="location">
+        <div style="font-size: 24px; font-weight: bold; padding-left: 10px;">
+          <%= translate.beijing[lang] %>
+        </div>
+      </div>
+      <p>
+        <%= translate.addr[lang] %>
+      </p>
+    </div>
+  </section>
+
+
+
+  <script>
+    // 当页面加载完成后执行
+    document.addEventListener('DOMContentLoaded', function () {
+      // 加载 Lottie 动画
+      lottie.loadAnimation({
+        container: document.getElementById('lottie'),
+        renderer: 'svg',
+        loop: true,
+        autoplay: true,
+        path: '/assets/lottie/splash/data.json'
+      });
+    });
+
+    let slideIndex = 0;
+    showSlides();
+
+    function showSlides() {
+      let i;
+      let slides = document.getElementsByClassName("mySlides");
+      for (i = 0; i < slides.length; i++) {
+        slides[i].style.display = "none";
+      }
+      slideIndex++;
+      if (slideIndex > slides.length) { slideIndex = 1 }
+      slides[slideIndex - 1].style.display = "block";
+      setTimeout(showSlides, 3000); // 更改图片间隔时间
+    }
+
+  </script>
+
+
+</body>
+
+</html>

+ 9 - 6
views/latest-section.ejs

@@ -1,16 +1,19 @@
-
 <div class="content-wrapper">
   <div class="content-title">
-    <div style="font-size: 20px; font-weight: bold;"><%= translate.latest[lang] %>:</div>
-    <a href="/<%= lang %>/category/latest"><%= translate.more[lang] %>>>></a>
+    <div style="font-size: 20px; font-weight: bold;">
+      <%= translate.latest[lang] %>:
+    </div>
+    <a href="/<%= lang %>/category/latest">
+      <%= translate.more[lang] %>>>>
+    </a>
   </div>
 
   <div class="content">
     <div class="image-grid">
-        <% latest.forEach(item => { %>
-            <a href="/<%= lang %>/detail/<%= item._id %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
+      <% latest.forEach(item=> { %>
+        <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>"></a>
         <% }); %>
     </div>
   </div>
 
-</div>
+</div>

+ 3 - 7
views/pagination.ejs

@@ -13,17 +13,13 @@ const showEllipsis = totalPages > 6 && (startPage > 1 || endPage < totalPages);
     <span style="font-size: 15px; font-family:sans-serif; margin-right: 20px;"><%= translate.total[lang] %> <%= recordsFiltered %> <%= translate.item[lang] %>,  <%= length %><%= translate.item[lang] %>/<%= translate.page[lang] %></span>
     <ul>
         <% for (let i = startPage; i <= endPage; i++) { %>
-            <li class="<%= i === page ? 'active' : '' %>">
-                <a href="<%= uri %><%= uri.includes('?')? '&' : '?' %>page=<%= i %>&length=<%= length %>" class="<%= i === page ? 'active' : '' %>"> <%= i %> </a>
-            </li>
+        <li class="<%= i === page ? 'active' : '' %>"><a href="<%= uri %><%= uri.includes('?')? '&' : '?' %>page=<%= i %>&length=<%= length %>" class="<%= i === page ? 'active' : '' %>"> <%= i %> </a></li>
         <% } %>
         <% if (showEllipsis) { %>
-            <li class="ellipsis">...</li>
+        <li class="ellipsis">...</li>
         <% } %>
         <% if (endPage < totalPages) { %>
-            <li>
-                <a href="<%= uri %><%= uri.includes('?')? '&' : '?' %>page=<%= totalPages %>&length=<%= length %>"><%= totalPages %></a>
-            </li>
+        <li><a href="<%= uri %>page=<%= totalPages %>&length=<%= length %>"><%= totalPages %></a></li>
         <% } %>
     </ul>
     <input type="number" id="pageInput" min="1" max="<%= totalPages %>">

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 2
views/play.ejs


+ 20 - 12
views/search.ejs

@@ -1,24 +1,32 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="<%= lang %>">
+
 <head>
-  <%- include('head') %>
+  <%- include('common-meta') %>
+
+  <link rel="alternate" href="https://art.pcoloring.com/en/search" hrefLang="en" />
+  <link rel="alternate" href="https://art.pcoloring.com/zh/search" hrefLang="zh" />
+  <link rel="alternate" href="https://art.pcoloring.com/es/search" hrefLang="es" />
+  <link rel="alternate" href="https://art.pcoloring.com/pt/search" hrefLang="pt" />
+  <link rel="alternate" href="https://art.pcoloring.com/ja/search" hrefLang="ja" />
+
+  <link rel="stylesheet" href="/stylesheets/styles.css">
+
 </head>
 
 <body>
   <%- include('header') %>
 
-  <div class="content">
-    <div class="image-grid">
-        <% data.forEach(item => { %>
-            <a href="/<%= lang %>/detail/<%= item._id %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
-        <% }); %>
+    <div class="content">
+      <div class="image-grid">
+        <% data.forEach(item=> { %>
+          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>"></a>
+          <% }); %>
+      </div>
     </div>
-  </div>
 
-  <%- include('pagination') %>
+    <%- include('pagination') %>
 
 </body>
-</html>
-
-
 
+</html>

+ 1 - 1
views/special-section.ejs

@@ -8,7 +8,7 @@
   <div class="content">
     <div class="image-grid">
         <% special.forEach(item => { %>
-            <a href="/<%= lang %>/detail/<%= item._id %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
+            <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
         <% }); %>
     </div>
   </div>

+ 24 - 14
views/special.ejs

@@ -1,24 +1,34 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="<%= lang %>">
+
 <head>
-  <%- include('head') %>
+  <%- include('common-meta') %>
+
+  <link rel="alternate" href="https://art.pcoloring.com/en/special" hrefLang="en" />
+  <link rel="alternate" href="https://art.pcoloring.com/zh/special" hrefLang="zh" />
+  <link rel="alternate" href="https://art.pcoloring.com/es/special" hrefLang="es" />
+  <link rel="alternate" href="https://art.pcoloring.com/pt/special" hrefLang="pt" />
+  <link rel="alternate" href="https://art.pcoloring.com/ja/special" hrefLang="ja" />
+
+  <link rel="stylesheet" href="/stylesheets/styles.css">
+
 </head>
 
-<body>  
+<body>
   <%- include('header') %>
-  <h1 style="display: flex; justify-content: center; padding: 10px; color: purple"><%= translate.special[lang] %></h1>
-  <div class="content">
-    <div class="image-grid">
-        <% data.forEach(item => { %>
-            <a href="/<%= lang %>/detail/<%= item._id %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
-        <% }); %>
+    <h1 style="display: flex; justify-content: center; padding: 10px; color: purple">
+      <%= translate.special[lang] %>
+    </h1>
+    <div class="content">
+      <div class="image-grid">
+        <% data.forEach(item=> { %>
+          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>"></a>
+          <% }); %>
+      </div>
     </div>
-  </div>
 
-  <%- include('pagination') %>
+    <%- include('pagination') %>
 
 </body>
-</html>
-
-
 
+</html>

+ 24 - 17
views/tag.ejs

@@ -1,31 +1,38 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="<%= lang %>">
+
 <head>
-  <%- include('head') %>
+  <%- include('common-meta') %>
+
+  <link rel="alternate" href="https://art.pcoloring.com/en/tag" hrefLang="en" />
+  <link rel="alternate" href="https://art.pcoloring.com/zh/tag" hrefLang="zh" />
+  <link rel="alternate" href="https://art.pcoloring.com/es/tag" hrefLang="es" />
+  <link rel="alternate" href="https://art.pcoloring.com/pt/tag" hrefLang="pt" />
+  <link rel="alternate" href="https://art.pcoloring.com/ja/tag" hrefLang="ja" />
+
+  <link rel="stylesheet" href="/stylesheets/styles.css">
   <link rel="stylesheet" href="/stylesheets/tag.css">
 </head>
 
 <body>
   <%- include('header') %>
 
-  <div class="tag-cloud">
-    <% tags.forEach(item => { %>
-      <a href="/<%= lang %>/tag/<%= item.tag %>" class="tag <%= item.tag == tag ? 'selected' : '' %>" style="color: <%= item.color %>;"><%= item.tag %></a>
-    <% }); %>
-  </div>
-  
-  <div class="content">
-    <div class="image-grid">
-        <% data.forEach(item => { %>
-            <a href="/<%= lang %>/detail/<%= item._id %>"><img src="<%= item.thumb %>" alt="<%= item.name %>" ></a>
+    <div class="tag-cloud">
+      <% tags.forEach(item=> { %>
+        <a href="/<%= lang %>/tag/<%= item.tag %>" class="tag <%= item.tag == tag ? 'selected' : '' %>" style="color: <%= item.color %>;"><%= item.tag %></a>
         <% }); %>
     </div>
-  </div>
-
-  <%- include('pagination') %>
 
-</body>
-</html>
+    <div class="content">
+      <div class="image-grid">
+        <% data.forEach(item=> { %>
+          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>"></a>
+          <% }); %>
+      </div>
+    </div>
 
+    <%- include('pagination') %>
 
+</body>
 
+</html>

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů