├── .autod.conf.js ├── .gitignore ├── .prettierrc ├── .travis.yml ├── README.md ├── app ├── controller │ ├── backend │ │ ├── animate.ts │ │ ├── blog.ts │ │ ├── category.ts │ │ ├── cloud.ts │ │ ├── comic.ts │ │ ├── comment.ts │ │ ├── config.ts │ │ ├── danmu.ts │ │ ├── data.ts │ │ ├── eposide.ts │ │ ├── key.ts │ │ ├── order.ts │ │ ├── post.ts │ │ ├── rate.ts │ │ ├── record.ts │ │ ├── report.ts │ │ ├── season.ts │ │ ├── shop.ts │ │ ├── source.ts │ │ ├── test.ts │ │ ├── tools.ts │ │ ├── uploads.ts │ │ └── user.ts │ └── frontend │ │ ├── animate.ts │ │ ├── auth.ts │ │ ├── blog.ts │ │ ├── comic.ts │ │ ├── comment.ts │ │ ├── common.ts │ │ ├── config.ts │ │ ├── danmu.ts │ │ ├── post.ts │ │ └── user.ts ├── data │ └── sensitive │ │ ├── 广告.txt │ │ ├── 政治类-反动词库.txt │ │ ├── 政治类.txt │ │ ├── 暴恐词库.txt │ │ ├── 色情词库.txt │ │ └── 骂人.txt ├── extend │ └── helper.ts ├── middleware │ ├── auth.ts │ ├── error_handler.ts │ ├── log.ts │ └── verify.ts ├── model │ ├── animate.ts │ ├── blog.ts │ ├── category.ts │ ├── cloud.ts │ ├── comic.ts │ ├── comment.ts │ ├── config.ts │ ├── count.ts │ ├── danmu.ts │ ├── data.ts │ ├── eposide.ts │ ├── history.ts │ ├── key.ts │ ├── order.ts │ ├── post.ts │ ├── rate.ts │ ├── record.ts │ ├── relation.ts │ ├── report.ts │ ├── season.ts │ ├── shop.ts │ ├── source.ts │ └── user.ts ├── router.ts ├── router │ ├── backend.ts │ └── frontend.ts ├── schedule │ ├── cloud.ts │ ├── count.ts │ ├── keyExpired.ts │ ├── source.ts │ └── userLevel.ts ├── service │ ├── animate.ts │ ├── blog.ts │ ├── category.ts │ ├── cloud.ts │ ├── comic.ts │ ├── comment.ts │ ├── config.ts │ ├── count.ts │ ├── danmu.ts │ ├── data.ts │ ├── eposide.ts │ ├── history.ts │ ├── key.ts │ ├── order.ts │ ├── post.ts │ ├── rate.ts │ ├── record.ts │ ├── relation.ts │ ├── report.ts │ ├── season.ts │ ├── shop.ts │ ├── source.ts │ ├── sourceInit.ts │ ├── tools.ts │ ├── upload.ts │ ├── user.ts │ └── utils.ts └── utils │ ├── aggregation.ts │ ├── code.ts │ ├── common.ts │ └── validate.ts ├── appveyor.yml ├── config ├── config.default.ts ├── config.local.ts ├── config.prod.ts └── plugin.ts ├── package-lock.json ├── package.json ├── public ├── animate │ ├── .gitkeep │ ├── config.5f4d0223b835cb7dcc5f7cfe.js │ ├── favicon.ico │ ├── index.html │ ├── layouts__index.e9cb324f.async.js │ ├── p___type___cate.5532478c.async.js │ ├── p__all.a265350a.async.js │ ├── p__index.2bb95199.chunk.css │ ├── p__index.ed68ced8.async.js │ ├── p__new.df8045f0.async.js │ ├── p__slug___slug.a5c7cb5e.async.js │ ├── p__video___id.d3d8d29b.async.js │ ├── static │ │ └── level.367681e1.png │ ├── umi.145470b3.css │ ├── umi.e3fc3073.js │ ├── vendors.352803b1.async.js │ └── vendors.d3154d8d.chunk.css ├── backend │ ├── .gitkeep │ ├── 39.0a4252d9.chunk.css │ ├── 39.35ffdfc4.async.js │ ├── 40.001ea15d.chunk.css │ ├── 40.fb5fb0a7.async.js │ ├── 41.c1e24e75.async.js │ ├── 41.d8453312.chunk.css │ ├── 42.9f1c8c53.async.js │ ├── 42.aaf47cd3.chunk.css │ ├── 43.19fc0c45.async.js │ ├── 43.3cdfd391.chunk.css │ ├── 44.61c3b9ca.chunk.css │ ├── 44.ad215b2d.async.js │ ├── 45.a7ddf820.chunk.css │ ├── 45.cdeb6458.async.js │ ├── 46.b6a1e6e7.async.js │ ├── 47.c1d300d3.async.js │ ├── 48.ee9fbb50.async.js │ ├── 49.98935d92.async.js │ ├── 50.6428f49c.async.js │ ├── 51.b71f8b7e.async.js │ ├── 52.2b99592d.async.js │ ├── 53.5cf5ac90.async.js │ ├── asset-manifest.json │ ├── favicon.png │ ├── icons │ │ ├── icon-128x128.png │ │ ├── icon-192x192.png │ │ └── icon-512x512.png │ ├── index.html │ ├── layouts__BasicLayout.d3412cd4.async.js │ ├── layouts__BasicLayout.da708105.chunk.css │ ├── layouts__UserLayout.5c668c46.async.js │ ├── layouts__UserLayout.5c6c76cb.chunk.css │ ├── manifest.json │ ├── p__404.52ce823f.async.js │ ├── p__Animate__Category.cd05ecff.async.js │ ├── p__Animate__Category.ebd72d81.chunk.css │ ├── p__Animate__Create.6db17ede.async.js │ ├── p__Animate__Info.9fb4de16.async.js │ ├── p__Animate__List.28b46331.async.js │ ├── p__Auth__Login.ca86c235.async.js │ ├── p__Auth__Login.cd7ff8de.chunk.css │ ├── p__Blog__Category.d69333ad.chunk.css │ ├── p__Blog__Category.e1bb5e2b.async.js │ ├── p__Blog__List.87ff1296.async.js │ ├── p__Cloud__history.d2cccbdd.async.js │ ├── p__Cloud__list.19de8acd.async.js │ ├── p__Cloud__source.b5706508.async.js │ ├── p__Comic__Category.6b3d395d.async.js │ ├── p__Comic__Category.988c10d6.chunk.css │ ├── p__Comic__Create.835207cc.async.js │ ├── p__Comic__Info.6a0b6b39.async.js │ ├── p__Comic__List.e47ac14e.async.js │ ├── p__Comment__List.c884f1fb.async.js │ ├── p__Config__index.4897debd.async.js │ ├── p__Danmu__List.5340676e.async.js │ ├── p__Dashboard__Analysis.0a7c8df8.async.js │ ├── p__Dashboard__Workplace.155f5173.async.js │ ├── p__Image__List.24ae7204.chunk.css │ ├── p__Image__List.49296031.async.js │ ├── p__Image__Type.5a9b5dd8.async.js │ ├── p__Post__Category.70688f5d.chunk.css │ ├── p__Post__Category.9d168a3f.async.js │ ├── p__Post__Create.cb8e1806.async.js │ ├── p__Post__Info.01c25e37.async.js │ ├── p__Post__List.fc54c1d0.async.js │ ├── p__Rate__List.2c49b406.async.js │ ├── p__Report__List.047ae486.async.js │ ├── p__Shop__Key.ab1d7415.async.js │ ├── p__Shop__List.0d9d4231.async.js │ ├── p__Shop__Order.37df1b52.async.js │ ├── p__Tools__index.04aa8b68.async.js │ ├── p__Tools__index.de73b1a5.chunk.css │ ├── p__User__Create.599463c4.async.js │ ├── p__User__Info.0ca6b282.async.js │ ├── p__User__List.1a9dba4b.async.js │ ├── precache-manifest.fddcb9a7bbc3ba01897b6f30ec8bebfc.js │ ├── pwacompat.min.js │ ├── service-worker.js │ ├── umi.66c2ee9d.js │ ├── umi.b0c61fc2.css │ ├── vendors.3ff16a55.chunk.css │ ├── vendors.48811c04.async.js │ └── workbox-v3.6.3 │ │ ├── workbox-background-sync.dev.js │ │ ├── workbox-background-sync.dev.js.map │ │ ├── workbox-background-sync.prod.js │ │ ├── workbox-background-sync.prod.js.map │ │ ├── workbox-broadcast-cache-update.dev.js │ │ ├── workbox-broadcast-cache-update.dev.js.map │ │ ├── workbox-broadcast-cache-update.prod.js │ │ ├── workbox-broadcast-cache-update.prod.js.map │ │ ├── workbox-cache-expiration.dev.js │ │ ├── workbox-cache-expiration.dev.js.map │ │ ├── workbox-cache-expiration.prod.js │ │ ├── workbox-cache-expiration.prod.js.map │ │ ├── workbox-cacheable-response.dev.js │ │ ├── workbox-cacheable-response.dev.js.map │ │ ├── workbox-cacheable-response.prod.js │ │ ├── workbox-cacheable-response.prod.js.map │ │ ├── workbox-core.dev.js │ │ ├── workbox-core.dev.js.map │ │ ├── workbox-core.prod.js │ │ ├── workbox-core.prod.js.map │ │ ├── workbox-google-analytics.dev.js │ │ ├── workbox-google-analytics.dev.js.map │ │ ├── workbox-google-analytics.prod.js │ │ ├── workbox-google-analytics.prod.js.map │ │ ├── workbox-navigation-preload.dev.js │ │ ├── workbox-navigation-preload.dev.js.map │ │ ├── workbox-navigation-preload.prod.js │ │ ├── workbox-navigation-preload.prod.js.map │ │ ├── workbox-precaching.dev.js │ │ ├── workbox-precaching.dev.js.map │ │ ├── workbox-precaching.prod.js │ │ ├── workbox-precaching.prod.js.map │ │ ├── workbox-range-requests.dev.js │ │ ├── workbox-range-requests.dev.js.map │ │ ├── workbox-range-requests.prod.js │ │ ├── workbox-range-requests.prod.js.map │ │ ├── workbox-routing.dev.js │ │ ├── workbox-routing.dev.js.map │ │ ├── workbox-routing.prod.js │ │ ├── workbox-routing.prod.js.map │ │ ├── workbox-strategies.dev.js │ │ ├── workbox-strategies.dev.js.map │ │ ├── workbox-strategies.prod.js │ │ ├── workbox-strategies.prod.js.map │ │ ├── workbox-streams.dev.js │ │ ├── workbox-streams.dev.js.map │ │ ├── workbox-streams.prod.js │ │ ├── workbox-streams.prod.js.map │ │ ├── workbox-sw.js │ │ └── workbox-sw.js.map ├── comic │ ├── .gitkeep │ ├── config.5f4d0223b835cb7dcc5f7cfe.js │ ├── favicon.ico │ ├── index.html │ ├── layouts__index.ff960953.async.js │ ├── p___type___cate.6513e301.async.js │ ├── p__all.8d958120.async.js │ ├── p__index.2bb95199.chunk.css │ ├── p__index.8aa2637d.async.js │ ├── p__new.db132284.async.js │ ├── p__picture___id.6784d699.async.js │ ├── p__slug___slug.da3445ee.async.js │ ├── static │ │ └── level.367681e1.png │ ├── umi.145470b3.css │ ├── umi.2bebf4c1.js │ ├── vendors.c2c120cc.chunk.css │ └── vendors.fc042039.async.js ├── default │ ├── config.5f4d0223b835cb7dcc5f7cfe.js │ └── index.html ├── img │ ├── animate │ │ └── .gitkeep │ ├── avatar │ │ └── .gitkeep │ ├── background │ │ └── .gitkeep │ ├── blog │ │ └── .gitkeep │ ├── comic │ │ └── .gitkeep │ ├── config │ │ └── .gitkeep │ ├── others │ │ └── .gitkeep │ └── post │ │ └── .gitkeep ├── mobile │ ├── .gitkeep │ ├── config.5f4d0223b835cb7dcc5f7cfe.js │ ├── favicon.ico │ ├── index.html │ ├── layouts__index.9bec27f2.async.js │ ├── p__allAnimate.6c135b69.async.js │ ├── p__allComic.2c6242bf.async.js │ ├── p__allPost.6555837d.async.js │ ├── p__animate___slug.490167f2.async.js │ ├── p__animate__video___id.1163785b.async.js │ ├── p__auth__login.ec3a5464.async.js │ ├── p__auth__register.8fc48ca4.async.js │ ├── p__auth__reset.74e3e768.async.js │ ├── p__auth__verify.9d1f2216.async.js │ ├── p__cate___type___kind___id.37f3b414.async.js │ ├── p__cate__comic___cate.95b9faab.async.js │ ├── p__cate__post___cate.4e3d9a1b.async.js │ ├── p__comic___slug.ff7f1f34.async.js │ ├── p__comic__picture___id.fe1838f2.async.js │ ├── p__index.55649140.async.js │ ├── p__newAnimate.68c96382.async.js │ ├── p__newComic.d3e05092.async.js │ ├── p__post___slug.5b893658.async.js │ ├── p__search___slug_.f6d686c1.async.js │ ├── p__shop.60d89fb1.async.js │ ├── p__user___layout.774bd03a.async.js │ ├── umi.129de048.js │ ├── umi.f7aeab43.css │ ├── vendors.62b52d83.async.js │ └── vendors.86cc7b00.chunk.css ├── picture │ └── .gitkeep ├── post │ ├── .gitkeep │ ├── config.5f4d0223b835cb7dcc5f7cfe.js │ ├── favicon.ico │ ├── index.html │ ├── layouts__index.d91217eb.async.js │ ├── p__index.0884df45.chunk.css │ ├── p__index.5c57e20d.async.js │ ├── p__slug___slug.9dfd5d78.async.js │ ├── umi.402bb0b9.css │ ├── umi.d4a29c83.js │ ├── vendors.78031e80.async.js │ └── vendors.874228e2.chunk.css ├── search │ ├── .gitkeep │ ├── config.5f4d0223b835cb7dcc5f7cfe.js │ ├── favicon.ico │ ├── index.html │ ├── layouts__index.8ad5c278.async.js │ ├── p__index.5edd9e7e.chunk.css │ ├── p__index.df2eaf78.async.js │ ├── umi.3164075f.js │ ├── umi.84535a2b.css │ ├── vendors.3f9a036b.chunk.css │ └── vendors.4df73923.async.js ├── user │ ├── .gitkeep │ ├── config.5f4d0223b835cb7dcc5f7cfe.js │ ├── favicon.ico │ ├── index.html │ ├── layouts__index.aec24ff4.async.js │ ├── p___id.225b3142.async.js │ ├── p__auth___layout.66566813.async.js │ ├── p__index.85da8b5e.async.js │ ├── umi.d5f2dd23.css │ ├── umi.f29b0a74.js │ ├── vendors.51154c40.async.js │ └── vendors.f43368eb.chunk.css └── web │ ├── LICENSE │ ├── README.md │ ├── components │ ├── Button │ │ └── index.jsx │ ├── Context │ │ └── index.jsx │ ├── Footer │ │ ├── index.jsx │ │ └── index.less │ ├── IconFont │ │ └── index.jsx │ ├── IndexList │ │ ├── index.jsx │ │ └── index.less │ ├── IndexScroll │ │ ├── index.jsx │ │ └── index.less │ ├── List │ │ ├── index.jsx │ │ └── index.less │ ├── Nav │ │ ├── index.jsx │ │ └── index.less │ └── NewList │ │ ├── index.jsx │ │ └── index.less │ ├── next.config.js │ ├── package.json │ ├── pages │ ├── _app.js │ ├── global.css │ ├── index.js │ └── index.less │ └── utils │ ├── api.js │ ├── request.js │ └── util.js ├── tsconfig.json ├── tslint.json └── yarn.lock /.autod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | write: true, 5 | plugin: 'autod-egg', 6 | prefix: '^', 7 | devprefix: '^', 8 | exclude: [ 9 | 'test/fixtures', 10 | 'coverage', 11 | ], 12 | dep: [ 13 | 'egg', 14 | 'egg-scripts', 15 | ], 16 | devdep: [ 17 | 'autod', 18 | 'autod-egg', 19 | 'egg-bin', 20 | 'tslib', 21 | 'typescript', 22 | ], 23 | keep: [ 24 | ], 25 | semver: [ 26 | ], 27 | test: 'scripts', 28 | }; 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | npm-debug.log 3 | node_modules/ 4 | coverage/ 5 | .idea/ 6 | run/ 7 | logs/ 8 | typings/ 9 | .DS_Store 10 | .vscode 11 | *.swp 12 | *.lock 13 | !.autod.conf.js 14 | 15 | test/**/*.js 16 | config/**/*.js 17 | app/**/*.js 18 | app/**/**/*.js 19 | app/**/*.map 20 | test/**/*.map 21 | config/**/*.map 22 | public/img/* 23 | 24 | /public/web/_next/ 25 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | # .prettierrc 2 | useTabs: false 3 | semi: true 4 | tabWidth: 4 5 | trailingComma: 'es5' 6 | singleQuote: true 7 | arrowParens: 'always' 8 | printWidth: 120 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | language: node_js 3 | node_js: 4 | - '8' 5 | before_install: 6 | - npm i npminstall -g 7 | install: 8 | - npminstall 9 | script: 10 | - npm run ci 11 | after_script: 12 | - npminstall codecov && codecov 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | 基于 nodejs 的视频播放系统 4 | 5 | 使用文档: https://qinvideo.org 6 | 7 | 视频安装教程:https://qinvideo.org/course/videos/install 8 | 9 | ## 交流 10 | 11 | 使用上的问题以及产品设计方面的想法可以提 issue, 这样才会进入到后续的开发计划中, QQ 群只提供一个相互交流的地方, QQ 群:1007111688 12 | 13 | ## 功能 14 | 15 | - 视频: 可通过采集苹果 CMS 的资源站点批量导入, 也可手动添加; 16 | - 漫画: 手动添加; 17 | - 文章: 以专栏的形式给用户显示所有文章; 18 | - 博客: 只支持在 APP 上显示, 可以发送图文视频等; 19 | - 评论: 支持一级嵌套的评论; 20 | - 弹幕: 视频可支持; 21 | - 商品: 激活码以及订单还有商品等配套; 22 | - 评分: 支持手动给视频等打分; 23 | 24 | ## 设计 25 | 26 | - 后台: egg.js + mongodb + redis, 主要从查询速度以及并发等重新考虑; 27 | - PC 前端: 模块化设计, 保证各部分的独一更新; 28 | - 移动 Web 端: 专为移动端优化设计; 29 | - 安卓 APP: 基于 React Native, 从设计交互以及页面调整做了较多的改动; 30 | 31 | ## 更新记录 32 | 33 | - 2020.08.29: v2.2.3, 后台文章无法删除的 BUG, 用户中心图片不显示; 34 | - 2020.07.15: v2.2.2, 注册的一些 BUG 修复, 前端的样式调整; 35 | - 2020.06.25: v2.2.1, 修复资源池导入的一些 BUG, 此外修复了一些显示的 BUG; 36 | - 2020.06.05: v2.2.0, 采用中间表结构, 评论数等在夜间用定时任务去统计, 减少查询的负担; 37 | - 2020.05.05: v2.1.0, 对缓存进行优化, 减少资源占用, 新增弹幕审核等功能; 38 | - 2020.04.05: v2.0.8, 采集的番剧在保存时会自动创建分类, 提高某些耗时接口的缓存为 1 天; 39 | - 2020.03.08: v2.0.7, 一些使用 BUG 的修复; 40 | - 2020.02.28: v2.0.6, 批量新增剧集, 支持苹果 CMS 的格式; 41 | - 2020.02.20: v2.0.5, 字幕以及清晰度的切换; 42 | - 2020.02.04: v2.0.4, 用户删除之后的处理; 43 | - 2020.02.04: v2.0.3, 首页渲染等 BUG 修复; 44 | - 2020.02.02: v2.0.2, 关于我们, 播放等 BUG 修复; 45 | - 2020.01.29: v2.0.1, 新增前端用户评分的功能; 46 | - 2020.01.01: v2.0.0, 基本功能 OK; 47 | -------------------------------------------------------------------------------- /app/controller/backend/cloud.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class CloudController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | const result = await service.cloud.query(query).catch(() => 28000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async info() { 15 | const { ctx, service } = this; 16 | const id = ctx.params.id; 17 | 18 | ctx.helper.validate('id', { id }); 19 | 20 | const result = await service.cloud.info(id).catch(() => 28001); 21 | ctx.helper.send(result); 22 | } 23 | 24 | async save() { 25 | const { ctx, service } = this; 26 | const { ids } = ctx.request.body; 27 | 28 | ctx.helper.validate('ids', { ids }); 29 | 30 | if (ids.length > 0) { 31 | const result = await service.cloud.save(ids).catch(() => 28003); 32 | ctx.helper.send(result); 33 | } else { 34 | service.cloud.save(ids).catch(() => 28003); 35 | ctx.helper.success('mission start'); 36 | } 37 | } 38 | 39 | async destroy() { 40 | const { ctx, service } = this; 41 | const id = ctx.params.id; 42 | 43 | ctx.helper.validate('id', { id }); 44 | 45 | const result = await service.cloud.destroy([id]).catch(() => 28004); 46 | ctx.helper.send(result); 47 | } 48 | 49 | async destroyMany() { 50 | const { ctx, service } = this; 51 | const { ids } = ctx.request.body; 52 | 53 | ctx.helper.validate('ids', { ids }); 54 | 55 | const result = await service.cloud.destroy(ids).catch(() => 28004); 56 | ctx.helper.send(result); 57 | } 58 | } 59 | 60 | export default CloudController; 61 | -------------------------------------------------------------------------------- /app/controller/backend/comment.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class CommentController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | const result = await service.comment.query(query).catch(() => 17000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async info() { 15 | const { ctx, service } = this; 16 | const target = ctx.params.id; 17 | const query = ctx.query; 18 | 19 | query.target = target; 20 | 21 | ctx.helper.validate('query', query); 22 | 23 | const result = await service.comment.info(query).catch(() => 17001); 24 | ctx.helper.send(result); 25 | } 26 | 27 | async create() { 28 | const { ctx, service } = this; 29 | const data = ctx.request.body; 30 | const userId = ctx.state.user.id; 31 | 32 | data.author = userId; 33 | ctx.helper.validate('comment', data, true); 34 | 35 | const result = await service.comment.create(data).catch(() => 17002); 36 | ctx.helper.send(result); 37 | } 38 | 39 | async update() { 40 | const { ctx, service } = this; 41 | const data = ctx.request.body; 42 | const id = ctx.params.id; 43 | 44 | ctx.helper.validate('id', { id }); 45 | ctx.helper.validate('comment', data); 46 | 47 | const result = await service.comment.update([ id ], data).catch(() => 17003); 48 | ctx.helper.send(result); 49 | } 50 | 51 | async updateMany() { 52 | const { ctx, service } = this; 53 | const data = ctx.request.body; 54 | const { ids } = data; 55 | 56 | ctx.helper.validate('ids', { ids }); 57 | ctx.helper.validate('comment', data); 58 | 59 | const result = await service.comment.update(ids, data).catch(() => 17003); 60 | ctx.helper.send(result); 61 | } 62 | 63 | async destroy() { 64 | const { ctx, service } = this; 65 | const id = ctx.params.id; 66 | 67 | ctx.helper.validate('id', { id }); 68 | 69 | const result = await service.comment.destroy([ id ]).catch(() => 17004); 70 | ctx.helper.send(result); 71 | } 72 | 73 | async destroyMany() { 74 | const { ctx, service } = this; 75 | const { ids } = ctx.request.body; 76 | 77 | ctx.helper.validate('ids', { ids }); 78 | 79 | const result = await service.comment.destroy(ids).catch(() => 17004); 80 | ctx.helper.send(result); 81 | } 82 | } 83 | 84 | export default CommentController; 85 | -------------------------------------------------------------------------------- /app/controller/backend/config.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class ConfigController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | 7 | const result = await service.config.info().catch(() => 22000); 8 | ctx.helper.send(result); 9 | } 10 | 11 | async create() { 12 | const { ctx, service } = this; 13 | const data = ctx.request.body; 14 | 15 | ctx.helper.validate('config', data); 16 | 17 | const result = await service.config.create(data).catch(() => 22001); 18 | ctx.helper.send(result); 19 | } 20 | 21 | async existAdmin() { 22 | const { ctx, service } = this; 23 | 24 | const result = await service.user.exist({}); 25 | if (result) { 26 | ctx.helper.send('already'); 27 | } 28 | ctx.helper.send('init'); 29 | } 30 | 31 | async initAdmin() { 32 | const { ctx, service } = this; 33 | const data = ctx.request.body; 34 | 35 | const info = await service.user.exist({}); 36 | if (info) return 22005; 37 | 38 | data.status = 'publish'; 39 | ctx.helper.validate('user', data, true); 40 | 41 | data.level = 100; 42 | data.password = ctx.helper.MD5(ctx.app.config.salt + data.password); 43 | const result = await service.user.create(data).catch(() => 22006); 44 | ctx.helper.send(result); 45 | } 46 | } 47 | 48 | export default ConfigController; 49 | -------------------------------------------------------------------------------- /app/controller/backend/data.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class DataController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { target, startTime, endTime } = ctx.query; 7 | 8 | ctx.helper.validate('string', { string: target }); 9 | ctx.helper.validate('id', { id: startTime }); 10 | ctx.helper.validate('id', { id: endTime }); 11 | 12 | const result = await service.data.query({ target, startTime, endTime }).catch(() => 26000); 13 | ctx.helper.send(result); 14 | } 15 | 16 | async todayData() { 17 | const { ctx, service } = this; 18 | 19 | const result = await service.data.todayData().catch(() => 26000); 20 | ctx.helper.send(result); 21 | } 22 | 23 | async search() { 24 | const { ctx, service } = this; 25 | const { startTime, endTime } = ctx.query; 26 | 27 | ctx.helper.validate('id', { id: startTime }); 28 | ctx.helper.validate('id', { id: endTime }); 29 | 30 | const result = await service.data.search({ startTime, endTime }).catch(() => 26000); 31 | ctx.helper.send(result); 32 | } 33 | 34 | async activeSort() { 35 | const { ctx, service } = this; 36 | const { type, startTime, endTime } = ctx.query; 37 | 38 | ctx.helper.validate('id', { id: type }); 39 | ctx.helper.validate('id', { id: startTime }); 40 | ctx.helper.validate('id', { id: endTime }); 41 | 42 | const result = await service.data.activeSort({ type, startTime, endTime }).catch(() => 26000); 43 | ctx.helper.send(result); 44 | } 45 | 46 | async workData() { 47 | const { ctx, service } = this; 48 | 49 | const result = await service.data.workData().catch(() => 26000); 50 | ctx.helper.send(result); 51 | } 52 | } 53 | 54 | export default DataController; 55 | -------------------------------------------------------------------------------- /app/controller/backend/key.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class KeyController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | const result = await service.key.query(query).catch(() => 19000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async info() { 15 | const { ctx, service } = this; 16 | const id = ctx.params.id; 17 | 18 | ctx.helper.validate('id', { id }); 19 | 20 | const result = await service.key.info(id).catch(() => 19001); 21 | ctx.helper.send(result); 22 | } 23 | 24 | async create() { 25 | const { ctx, service } = this; 26 | const data = ctx.request.body; 27 | const userId = ctx.state.user.id; 28 | 29 | data.author = userId; 30 | ctx.helper.validate('key', data, true); 31 | 32 | const result = await service.key.create(data).catch(() => 19002); 33 | ctx.helper.send(result); 34 | } 35 | 36 | async update() { 37 | const { ctx, service } = this; 38 | const data = ctx.request.body; 39 | const id = ctx.params.id; 40 | 41 | ctx.helper.validate('id', { id }); 42 | ctx.helper.validate('key', data); 43 | 44 | const result = await service.key.update([ id ], data).catch(() => 19003); 45 | ctx.helper.send(result); 46 | } 47 | 48 | async updateMany() { 49 | const { ctx, service } = this; 50 | const data = ctx.request.body; 51 | const { ids } = data; 52 | 53 | ctx.helper.validate('ids', { ids }); 54 | ctx.helper.validate('key', data); 55 | 56 | const result = await service.key.update(ids, data).catch(() => 19003); 57 | ctx.helper.send(result); 58 | } 59 | 60 | async destroy() { 61 | const { ctx, service } = this; 62 | const id = ctx.params.id; 63 | 64 | ctx.helper.validate('id', { id }); 65 | 66 | const result = await service.key.destroy([ id ]).catch(() => 19004); 67 | ctx.helper.send(result); 68 | } 69 | 70 | async destroyMany() { 71 | const { ctx, service } = this; 72 | const { ids } = ctx.request.body; 73 | 74 | ctx.helper.validate('ids', { ids }); 75 | 76 | const result = await service.key.destroy(ids).catch(() => 19004); 77 | ctx.helper.send(result); 78 | } 79 | } 80 | 81 | export default KeyController; 82 | -------------------------------------------------------------------------------- /app/controller/backend/order.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class OrderController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | const result = await service.order.query(query).catch(() => 20000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async create() { 15 | const { ctx, service } = this; 16 | const { user, shop } = ctx.request.body; 17 | 18 | ctx.helper.validate('id', { id: user }); 19 | ctx.helper.validate('id', { id: shop }); 20 | 21 | const result = await service.order.create({ user, shop }).catch(() => 20001); 22 | ctx.helper.send(result); 23 | } 24 | 25 | async info() { 26 | const { ctx, service } = this; 27 | const id = ctx.params.id; 28 | 29 | ctx.helper.validate('id', { id }); 30 | 31 | const result = await service.order.info(id).catch(() => 20001); 32 | ctx.helper.send(result); 33 | } 34 | 35 | async destroy() { 36 | const { ctx, service } = this; 37 | const id = ctx.params.id; 38 | 39 | ctx.helper.validate('id', { id }); 40 | 41 | const result = await service.order.destroy([id]).catch(() => 20004); 42 | ctx.helper.send(result); 43 | } 44 | 45 | async destroyMany() { 46 | const { ctx, service } = this; 47 | const { ids } = ctx.request.body; 48 | 49 | ctx.helper.validate('ids', { ids }); 50 | 51 | const result = await service.order.destroy(ids).catch(() => 20004); 52 | ctx.helper.send(result); 53 | } 54 | } 55 | 56 | export default OrderController; 57 | -------------------------------------------------------------------------------- /app/controller/backend/post.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class PostController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | const result = await service.post.query(query).catch(() => 14000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async info() { 15 | const { ctx, service } = this; 16 | const id = ctx.params.id; 17 | 18 | ctx.helper.validate('id', { id }); 19 | 20 | const result = await service.post.info(id).catch(() => 14001); 21 | ctx.helper.send(result); 22 | } 23 | 24 | async create() { 25 | const { ctx, service } = this; 26 | const data = ctx.request.body; 27 | const userId = ctx.state.user.id; 28 | 29 | data.author = userId; 30 | ctx.helper.validate('post', data, true); 31 | 32 | const result = await service.post.create(data).catch(() => 14002); 33 | ctx.helper.send(result); 34 | } 35 | 36 | async update() { 37 | const { ctx, service } = this; 38 | const data = ctx.request.body; 39 | const id = ctx.params.id; 40 | 41 | ctx.helper.validate('id', { id }); 42 | ctx.helper.validate('post', data); 43 | 44 | const result = await service.post.update([ id ], data).catch(() => 14003); 45 | ctx.helper.send(result); 46 | } 47 | 48 | async updateMany() { 49 | const { ctx, service } = this; 50 | const data = ctx.request.body; 51 | const { ids } = data; 52 | 53 | ctx.helper.validate('ids', { ids }); 54 | ctx.helper.validate('post', data); 55 | 56 | const result = await service.post.update(ids, data).catch(() => 14003); 57 | ctx.helper.send(result); 58 | } 59 | 60 | async destroy() { 61 | const { ctx, service } = this; 62 | const id = ctx.params.id; 63 | 64 | ctx.helper.validate('id', { id }); 65 | 66 | const result = await service.post.destroy([ id ]).catch(() => 14004); 67 | ctx.helper.send(result); 68 | } 69 | 70 | async destroyMany() { 71 | const { ctx, service } = this; 72 | const { ids } = ctx.request.body; 73 | 74 | ctx.helper.validate('ids', { ids }); 75 | 76 | const result = await service.post.destroy(ids).catch(() => 14004); 77 | ctx.helper.send(result); 78 | } 79 | } 80 | 81 | export default PostController; 82 | -------------------------------------------------------------------------------- /app/controller/backend/rate.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class RateController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | const result = await service.rate.query(query).catch(() => 29000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async info() { 15 | const { ctx, service } = this; 16 | const id = ctx.params.id; 17 | 18 | ctx.helper.validate('id', { id }); 19 | 20 | const result = await service.rate.info(id).catch(() => 29001); 21 | ctx.helper.send(result); 22 | } 23 | 24 | async create() { 25 | const { ctx, service } = this; 26 | const data = ctx.request.body; 27 | const userId = ctx.state.user.id; 28 | 29 | data.author = userId; 30 | ctx.helper.validate('rate', data, true); 31 | 32 | const result = await service.rate.create(data).catch(() => 29002); 33 | ctx.helper.send(result); 34 | } 35 | 36 | async update() { 37 | const { ctx, service } = this; 38 | const data = ctx.request.body; 39 | const id = ctx.params.id; 40 | 41 | ctx.helper.validate('id', { id }); 42 | ctx.helper.validate('rate', data); 43 | 44 | const result = await service.rate.update([ id ], data).catch(() => 29003); 45 | ctx.helper.send(result); 46 | } 47 | 48 | async updateMany() { 49 | const { ctx, service } = this; 50 | const data = ctx.request.body; 51 | const { ids } = data; 52 | 53 | ctx.helper.validate('ids', { ids }); 54 | ctx.helper.validate('rate', data); 55 | 56 | const result = await service.rate.update(ids, data).catch(() => 29003); 57 | ctx.helper.send(result); 58 | } 59 | 60 | async destroy() { 61 | const { ctx, service } = this; 62 | const id = ctx.params.id; 63 | 64 | ctx.helper.validate('id', { id }); 65 | 66 | const result = await service.rate.destroy([ id ]).catch(() => 29004); 67 | ctx.helper.send(result); 68 | } 69 | 70 | async destroyMany() { 71 | const { ctx, service } = this; 72 | const { ids } = ctx.request.body; 73 | 74 | ctx.helper.validate('ids', { ids }); 75 | 76 | const result = await service.rate.destroy(ids).catch(() => 29004); 77 | ctx.helper.send(result); 78 | } 79 | } 80 | 81 | export default RateController; 82 | -------------------------------------------------------------------------------- /app/controller/backend/record.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class RecordController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | const result = await service.record.query(query).catch(() => 31000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async destroy() { 15 | const { ctx, service } = this; 16 | const id = ctx.params.id; 17 | 18 | ctx.helper.validate('id', { id }); 19 | 20 | const result = await service.record.destroy([id]).catch(() => 31004); 21 | ctx.helper.send(result); 22 | } 23 | 24 | async destroyMany() { 25 | const { ctx, service } = this; 26 | const { ids } = ctx.request.body; 27 | 28 | ctx.helper.validate('ids', { ids }); 29 | 30 | const result = await service.record.destroy(ids).catch(() => 31004); 31 | ctx.helper.send(result); 32 | } 33 | } 34 | 35 | export default RecordController; 36 | -------------------------------------------------------------------------------- /app/controller/backend/report.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class ReportController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | const result = await service.report.query(query).catch(() => 23000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async info() { 15 | const { ctx, service } = this; 16 | const id = ctx.params.id; 17 | 18 | ctx.helper.validate('id', { id }); 19 | 20 | const result = await service.report.info(id).catch(() => 23001); 21 | ctx.helper.send(result); 22 | } 23 | 24 | async create() { 25 | const { ctx, service } = this; 26 | const data = ctx.request.body; 27 | const userId = ctx.state.user.id; 28 | 29 | data.author = userId; 30 | ctx.helper.validate('report', data, true); 31 | 32 | const result = await service.report.create(data).catch(() => 23002); 33 | ctx.helper.send(result); 34 | } 35 | 36 | async update() { 37 | const { ctx, service } = this; 38 | const data = ctx.request.body; 39 | const id = ctx.params.id; 40 | 41 | ctx.helper.validate('id', { id }); 42 | ctx.helper.validate('report', data); 43 | 44 | const result = await service.report.update([ id ], data).catch(() => 23003); 45 | ctx.helper.send(result); 46 | } 47 | 48 | async updateMany() { 49 | const { ctx, service } = this; 50 | const data = ctx.request.body; 51 | const { ids } = data; 52 | 53 | ctx.helper.validate('ids', { ids }); 54 | ctx.helper.validate('report', data); 55 | 56 | const result = await service.report.update(ids, data).catch(() => 23003); 57 | ctx.helper.send(result); 58 | } 59 | 60 | async destroy() { 61 | const { ctx, service } = this; 62 | const id = ctx.params.id; 63 | 64 | ctx.helper.validate('id', { id }); 65 | 66 | const result = await service.report.destroy([ id ]).catch(() => 23004); 67 | ctx.helper.send(result); 68 | } 69 | 70 | async destroyMany() { 71 | const { ctx, service } = this; 72 | const { ids } = ctx.request.body; 73 | 74 | ctx.helper.validate('ids', { ids }); 75 | 76 | const result = await service.report.destroy(ids).catch(() => 23004); 77 | ctx.helper.send(result); 78 | } 79 | } 80 | 81 | export default ReportController; 82 | -------------------------------------------------------------------------------- /app/controller/backend/season.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class SeasonController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('cateQuery', query); 9 | 10 | const result = await service.season.query(query).catch(() => 16000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async info() { 15 | const { ctx, service } = this; 16 | const id = ctx.params.id; 17 | 18 | ctx.helper.validate('id', { id }); 19 | 20 | const result = await service.season.info(id).catch(() => 16001); 21 | ctx.helper.send(result); 22 | } 23 | 24 | async create() { 25 | const { ctx, service } = this; 26 | const data = ctx.request.body; 27 | 28 | ctx.helper.validate('season', data, true); 29 | 30 | const result = await service.season.create(data).catch(() => 16002); 31 | ctx.helper.send(result); 32 | } 33 | 34 | async update() { 35 | const { ctx, service } = this; 36 | const data = ctx.request.body; 37 | const id = ctx.params.id; 38 | 39 | ctx.helper.validate('id', { id }); 40 | ctx.helper.validate('season', data); 41 | 42 | const result = await service.season.update([ id ], data).catch(() => 16003); 43 | ctx.helper.send(result); 44 | } 45 | 46 | async updateMany() { 47 | const { ctx, service } = this; 48 | const data = ctx.request.body; 49 | const { ids } = data; 50 | 51 | ctx.helper.validate('ids', { ids }); 52 | ctx.helper.validate('season', data); 53 | 54 | const result = await service.season.update(ids, data).catch(() => 16003); 55 | ctx.helper.send(result); 56 | } 57 | 58 | async destroy() { 59 | const { ctx, service } = this; 60 | const id = ctx.params.id; 61 | 62 | ctx.helper.validate('id', { id }); 63 | 64 | const result = await service.season.destroy([ id ]).catch(() => 16004); 65 | ctx.helper.send(result); 66 | } 67 | 68 | async destroyMany() { 69 | const { ctx, service } = this; 70 | const { ids, type } = ctx.request.body; 71 | 72 | ctx.helper.validate('ids', { ids }); 73 | 74 | const result = await service.season.destroy(ids, type).catch(() => 16004); 75 | ctx.helper.send(result); 76 | } 77 | } 78 | 79 | export default SeasonController; 80 | -------------------------------------------------------------------------------- /app/controller/backend/shop.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class ShopController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | const result = await service.shop.query(query).catch(() => 21000); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async info() { 15 | const { ctx, service } = this; 16 | const id = ctx.params.id; 17 | 18 | ctx.helper.validate('id', { id }); 19 | 20 | const result = await service.shop.info(id).catch(() => 21001); 21 | ctx.helper.send(result); 22 | } 23 | 24 | async create() { 25 | const { ctx, service } = this; 26 | const data = ctx.request.body; 27 | const userId = ctx.state.user.id; 28 | 29 | data.author = userId; 30 | ctx.helper.validate('shop', data, true); 31 | 32 | const result = await service.shop.create(data).catch(() => 21002); 33 | ctx.helper.send(result); 34 | } 35 | 36 | async update() { 37 | const { ctx, service } = this; 38 | const data = ctx.request.body; 39 | const id = ctx.params.id; 40 | 41 | ctx.helper.validate('id', { id }); 42 | ctx.helper.validate('shop', data); 43 | 44 | const result = await service.shop.update([ id ], data).catch(() => 21003); 45 | ctx.helper.send(result); 46 | } 47 | 48 | async updateMany() { 49 | const { ctx, service } = this; 50 | const data = ctx.request.body; 51 | const { ids } = data; 52 | 53 | ctx.helper.validate('ids', { ids }); 54 | ctx.helper.validate('shop', data); 55 | 56 | const result = await service.shop.update(ids, data).catch(() => 21003); 57 | ctx.helper.send(result); 58 | } 59 | 60 | async destroy() { 61 | const { ctx, service } = this; 62 | const id = ctx.params.id; 63 | 64 | ctx.helper.validate('id', { id }); 65 | 66 | const result = await service.shop.destroy([ id ]).catch(() => 21004); 67 | ctx.helper.send(result); 68 | } 69 | 70 | async destroyMany() { 71 | const { ctx, service } = this; 72 | const { ids } = ctx.request.body; 73 | 74 | ctx.helper.validate('ids', { ids }); 75 | 76 | const result = await service.shop.destroy(ids).catch(() => 21004); 77 | ctx.helper.send(result); 78 | } 79 | } 80 | 81 | export default ShopController; 82 | -------------------------------------------------------------------------------- /app/controller/backend/test.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class UserController extends Controller { 4 | async test() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | const result = await service.maccms.list(query).catch(() => 11000); 9 | ctx.helper.send(result); 10 | } 11 | } 12 | 13 | export default UserController; 14 | -------------------------------------------------------------------------------- /app/controller/backend/tools.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class ConfigController extends Controller { 4 | async replace() { 5 | const { ctx, service } = this; 6 | const data = ctx.request.body; 7 | 8 | ctx.helper.validate('replace', data); 9 | 10 | const result = await service.tools.replace(data).catch(() => 27001); 11 | ctx.helper.send(result); 12 | } 13 | 14 | async downImg() { 15 | const { ctx, service } = this; 16 | const data = ctx.request.body; 17 | 18 | ctx.helper.validate('downimg', data); 19 | 20 | const result = await service.tools.downloadImg(data).catch(() => 27003); 21 | ctx.helper.send(result); 22 | } 23 | 24 | async upload() { 25 | const { ctx, service } = this; 26 | const files = ctx.request.files; 27 | const { type } = ctx.request.body; 28 | 29 | ctx.helper.validate('string', { string: type }); 30 | 31 | const result = await service.tools.upload(files, type).catch(() => 27004); 32 | ctx.helper.send(result); 33 | } 34 | } 35 | 36 | export default ConfigController; 37 | -------------------------------------------------------------------------------- /app/controller/backend/uploads.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class UploadsController extends Controller { 4 | async uploadImg() { 5 | const { ctx, service } = this; 6 | const files = ctx.request.files; 7 | const { type } = ctx.request.body; 8 | 9 | ctx.helper.validate('string', { string: type }); 10 | 11 | const result = await service.upload.uploadImg(files, type).catch(() => 25000); 12 | ctx.helper.send(result); 13 | } 14 | 15 | async queryImg() { 16 | const { ctx, service } = this; 17 | const { type } = ctx.params; 18 | const query = ctx.query; 19 | 20 | ctx.helper.validate('string', { string: type }); 21 | ctx.helper.validate('query', query); 22 | 23 | const result = await service.upload.queryImg(type, query).catch(() => 25001); 24 | ctx.helper.send(result); 25 | } 26 | 27 | async deleteImg() { 28 | const { ctx, service } = this; 29 | const { ids } = ctx.request.body; 30 | 31 | ctx.helper.validate('ids', { ids }); 32 | 33 | const result = await service.upload.deleteImg(ids).catch(() => 25002); 34 | ctx.helper.send(result); 35 | } 36 | } 37 | 38 | export default UploadsController; 39 | -------------------------------------------------------------------------------- /app/controller/frontend/animate.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class AnimateController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | query.status = 'publish'; 11 | 12 | const key = ctx.helper.getQueryOrder(query); 13 | await service.utils.cacheInit(`animate${key}`, async () => { 14 | return await service.animate.query(query).catch(() => 12000); 15 | }); 16 | } 17 | 18 | async info() { 19 | const { ctx, service } = this; 20 | const id = ctx.params.id; 21 | const userId = ctx.state.user.id; 22 | 23 | ctx.helper.validate('id', { id }); 24 | 25 | let result = await service.utils.cacheGet(`animateSlug${id}`); 26 | 27 | if (!result) { 28 | result = await service.animate.slug(id).catch(() => 12001); 29 | typeof result !== 'number' && service.utils.cacheSet(`animateSlug${id}`, result); 30 | } 31 | 32 | if (typeof result !== 'number' && userId) { 33 | const isLiked = await service.relation.exist({ 34 | target: result._id, 35 | author: userId, 36 | }); 37 | result.isLiked = isLiked; 38 | } 39 | if (Array.isArray(result.eposides)) { 40 | result.eposides.sort((a: any, b: any) => a.sort - b.sort); 41 | } 42 | ctx.helper.send(result); 43 | } 44 | 45 | async relative() { 46 | const { ctx, service } = this; 47 | const id = ctx.params.id; 48 | 49 | ctx.helper.validate('id', { id }); 50 | 51 | await service.utils.cacheInit(`animateRelative${id}`, async () => { 52 | return await service.animate.relative(id).catch(() => 18001); 53 | }); 54 | } 55 | 56 | async play() { 57 | const { ctx, service } = this; 58 | const id = ctx.params.id; 59 | const level = ctx.state.user.level; 60 | 61 | ctx.helper.validate('id', { id }); 62 | 63 | const result = await service.eposide.animateInfo(id, level).catch(() => 18001); 64 | 65 | service.history.playCreate(result, 'Eposide'); 66 | 67 | ctx.helper.send(result); 68 | } 69 | } 70 | 71 | export default AnimateController; 72 | -------------------------------------------------------------------------------- /app/controller/frontend/comic.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class ComicController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | query.status = 'publish'; 11 | 12 | const key = ctx.helper.getQueryOrder(query); 13 | await service.utils.cacheInit(`comic${key}`, async () => { 14 | return await service.comic.query(query).catch(() => 13000); 15 | }); 16 | } 17 | 18 | async info() { 19 | const { ctx, service } = this; 20 | const id = ctx.params.id; 21 | const userId = ctx.state.user.id; 22 | 23 | ctx.helper.validate('id', { id }); 24 | 25 | let result = await service.utils.cacheGet(`comicSlug${id}`); 26 | 27 | if (!result) { 28 | result = await service.comic.slug(id).catch(() => 13001); 29 | typeof result !== 'number' && service.utils.cacheSet(`comicSlug${id}`, result); 30 | } 31 | 32 | if (typeof result !== 'number' && userId) { 33 | const isLiked = await service.relation.exist({ 34 | target: result._id, 35 | author: userId, 36 | }); 37 | result.isLiked = isLiked; 38 | } 39 | if (Array.isArray(result.eposides)) { 40 | result.eposides.sort((a: any, b: any) => a.sort - b.sort); 41 | } 42 | ctx.helper.send(result); 43 | } 44 | 45 | async relative() { 46 | const { ctx, service } = this; 47 | const id = ctx.params.id; 48 | 49 | ctx.helper.validate('id', { id }); 50 | 51 | await service.utils.cacheInit(`comicRelative${id}`, async () => { 52 | return await service.comic.relative(id).catch(() => 18001); 53 | }); 54 | } 55 | 56 | async play() { 57 | const { ctx, service } = this; 58 | const id = ctx.params.id; 59 | const level = ctx.state.user.level; 60 | 61 | ctx.helper.validate('id', { id }); 62 | 63 | let result = await service.eposide.comicInfo(id, level).catch(() => 18001); 64 | 65 | service.history.playCreate(result, 'Eposide'); 66 | 67 | ctx.helper.send(result); 68 | } 69 | } 70 | 71 | export default ComicController; 72 | -------------------------------------------------------------------------------- /app/controller/frontend/config.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class ConfigController extends Controller { 4 | async info() { 5 | const { ctx, service } = this; 6 | const result = await service.config.simpleInfo().catch(() => 22000); 7 | ctx.helper.send(result); 8 | } 9 | 10 | async home() { 11 | const { service } = this; 12 | 13 | await service.utils.cacheInit('commonHome', async () => { 14 | return await service.config.home('pcIndex').catch(() => 22003); 15 | }); 16 | } 17 | 18 | async mobile() { 19 | const { service } = this; 20 | 21 | await service.utils.cacheInit('commonMobile', async () => { 22 | return await service.config.home('h5Index').catch(() => 22003); 23 | }); 24 | } 25 | 26 | async appInfo() { 27 | const { service } = this; 28 | 29 | await service.utils.cacheInit('commonApp', async () => { 30 | return await service.config.appInfo().catch(() => 22007); 31 | }); 32 | } 33 | } 34 | 35 | export default ConfigController; 36 | -------------------------------------------------------------------------------- /app/controller/frontend/danmu.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class DanmuController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { id } = ctx.query; 7 | 8 | ctx.helper.validate('id', { id }); 9 | const query = { 10 | page: 1, 11 | size: 10000, 12 | target: id, 13 | sortBy: 'time', 14 | status: 'publish', 15 | }; 16 | 17 | await service.utils.cacheInit(`danmu${id}`, async () => { 18 | return await service.danmu.query(query).catch(() => 15000); 19 | }); 20 | } 21 | 22 | async create() { 23 | const { ctx, service } = this; 24 | const data = ctx.request.body; 25 | const userId = ctx.state.user.id; 26 | const level = ctx.state.user.level; 27 | 28 | data.target = data.id; 29 | data.author = userId; 30 | ctx.helper.validate('danmu', data, true); 31 | 32 | const sensitive = await service.utils.isSensitiveWord(data.text); 33 | 34 | if (sensitive) { 35 | ctx.helper.error(10019); 36 | } 37 | 38 | const configInfo = await service.config.cacheInfo(); 39 | 40 | if (configInfo.danmuAuth && level === 0) { 41 | ctx.helper.send(10014); 42 | } 43 | 44 | data.status = 'publish'; 45 | if (configInfo.danmuVerify) { 46 | data.status = 'draft'; 47 | } 48 | 49 | const result = await service.danmu.create(data).catch(() => 15002); 50 | 51 | service.count.init(result, 'danmu'); 52 | 53 | ctx.helper.send(result); 54 | } 55 | } 56 | 57 | export default DanmuController; 58 | -------------------------------------------------------------------------------- /app/controller/frontend/post.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | 3 | class PostController extends Controller { 4 | async query() { 5 | const { ctx, service } = this; 6 | const { query } = ctx; 7 | 8 | ctx.helper.validate('query', query); 9 | 10 | query.status = 'publish'; 11 | 12 | const key = ctx.helper.getQueryOrder(query); 13 | await service.utils.cacheInit(`post${key}`, async () => { 14 | return await service.post.query(query).catch(() => 14000); 15 | }); 16 | } 17 | 18 | async info() { 19 | const { ctx, service } = this; 20 | const id = ctx.params.id; 21 | const userId = ctx.state.user.id; 22 | 23 | ctx.helper.validate('id', { id }); 24 | 25 | let result = await service.utils.cacheGet(`postSlug${id}`); 26 | 27 | if (!result) { 28 | result = await service.post.slug(id).catch(() => 14001); 29 | typeof result !== 'number' && service.utils.cacheSet(`postSlug${id}`, result); 30 | } 31 | 32 | if (typeof result !== 'number' && userId) { 33 | const isLiked = await service.relation.exist({ 34 | target: result._id, 35 | author: userId, 36 | }); 37 | result.isLiked = isLiked; 38 | } 39 | 40 | service.history.playCreate(result, 'Post'); 41 | 42 | ctx.helper.send(result); 43 | } 44 | 45 | async relative() { 46 | const { ctx, service } = this; 47 | const id = ctx.params.id; 48 | 49 | ctx.helper.validate('id', { id }); 50 | 51 | await service.utils.cacheInit(`postRelative${id}`, async () => { 52 | return await service.post.relative(id).catch(() => 18001); 53 | }); 54 | } 55 | } 56 | 57 | export default PostController; 58 | -------------------------------------------------------------------------------- /app/data/sensitive/广告.txt: -------------------------------------------------------------------------------- 1 | 兼职 2 | 招聘 3 | 网络 4 | QQ 5 | 招聘 6 | 有意者 7 | 到货 8 | 本店 9 | 代购 10 | 扣扣 11 | 客服 12 | 微店 13 | 兼职 14 | 兼值 15 | 淘宝 16 | 小姐 17 | 妓女 18 | 包夜 19 | 3P 20 | LY 21 | JS 22 | 狼友 23 | 技师 24 | 推油 25 | 胸推 26 | BT 27 | 毒龙 28 | 口爆 29 | 兼职 30 | 楼凤 31 | 足交 32 | 口暴 33 | 口交 34 | 全套 35 | SM 36 | 桑拿 37 | 吞精 38 | 咪咪 39 | 婊子 40 | 乳方 41 | 操逼 42 | 全职 43 | 性伴侣 44 | 网购 45 | 网络工作 46 | 代理 47 | 专业代理 48 | 帮忙点一下 49 | 帮忙点下 50 | 请点击进入 51 | 详情请进入 52 | 私人侦探 53 | 私家侦探 54 | 针孔摄象 55 | 调查婚外情 56 | 信用卡提现 57 | 无抵押贷款 58 | 广告代理 59 | 原音铃声 60 | 借腹生子 61 | 找个妈妈 62 | 找个爸爸 63 | 代孕妈妈 64 | 代生孩子 65 | 代开发票 66 | 腾讯客服电话 67 | 销售热线 68 | 免费订购热线 69 | 低价出售 70 | 款到发货 71 | 回复可见 72 | 连锁加盟 73 | 加盟连锁 74 | 免费二级域名 75 | 免费使用 76 | 免费索取 77 | 蚁力神 78 | 婴儿汤 79 | 售肾 80 | 刻章办 81 | 买小车 82 | 套牌车 83 | 玛雅网 84 | 电脑传讯 85 | 视频来源 86 | 下载速度 87 | 高清在线 88 | 全集在线 89 | 在线播放 90 | txt下载 91 | 六位qq 92 | 6位qq 93 | 位的qq 94 | 个qb 95 | 送qb 96 | 用刀横向切腹 97 | 完全自杀手册 98 | 四海帮 99 | 足球投注 100 | 地下钱庄 101 | 中国复兴党 102 | 阿波罗网 103 | 曾道人 104 | 六合彩 105 | 改卷内幕 106 | 替考试 107 | 隐形耳机 108 | 出售答案 109 | 考中答案 110 | 答an 111 | da案 112 | 资金周转 113 | 救市 114 | 股市圈钱 115 | 崩盘 116 | 资金短缺 117 | 证监会 118 | 质押贷款 119 | 小额贷款 120 | 周小川 121 | 刘明康 122 | 尚福林 123 | 孔丹 124 | -------------------------------------------------------------------------------- /app/data/sensitive/暴恐词库.txt: -------------------------------------------------------------------------------- 1 | 福音会 2 | 中国教徒 3 | 统一教 4 | 观音法门 5 | 清海无上师 6 | 盘古 7 | 李洪志 8 | 志洪李 9 | 李宏志 10 | 轮功 11 | 法轮 12 | 轮法功 13 | 三去车仑 14 | 氵去车仑 15 | 发论工 16 | 法x功 17 | 法o功 18 | 法0功 19 | 法一轮一功 20 | 轮子功 21 | 车仑工力 22 | 法lun 23 | fa轮 24 | 法lg 25 | flg 26 | fl功 27 | falungong 28 | 大法弟子 29 | 大纪元 30 | dajiyuan 31 | 明慧网 32 | 明慧周报 33 | 正见网 34 | 新唐人 35 | 伪火 36 | 退党 37 | tuidang 38 | 退dang 39 | 超越红墙 40 | 自fen 41 | 真善忍 42 | 九评 43 | 9评 44 | 9ping 45 | 九ping 46 | jiuping 47 | 藏字石 48 | 集体自杀 49 | 自sha 50 | zi杀 51 | suicide 52 | titor 53 | 逢8必灾 54 | 逢八必灾 55 | 逢9必乱 56 | 逢九必乱 57 | 朱瑟里诺 58 | 根达亚文明 59 | 诺查丹玛斯 60 | 人类灭亡进程表 61 | 按照马雅历法 62 | 推背图 63 | 推bei图 64 | 济世灵文 65 | 诸世纪 66 | 电狗 67 | 电话定位器 68 | 电话拦截器 69 | 电话窃听 70 | 电话监 71 | 电话交友 72 | 电话追杀系统 73 | 电击枪 74 | 电鸡 75 | 电警棒 76 | 枪出售 77 | 枪的制 78 | 枪货到 79 | 枪决女犯 80 | 枪模 81 | 枪手 82 | 枪销售 83 | 枪械制 84 | 枪子弹 85 | 售步枪 86 | 售纯度 87 | 售单管 88 | 售弹簧刀 89 | 售防身 90 | 售狗子 91 | 售虎头 92 | 售火药 93 | 售假币 94 | 售健卫 95 | 售军用 96 | 售猎枪 97 | 售氯胺 98 | 售麻醉 99 | 售枪支 100 | 售热武 101 | 售三棱 102 | 售手枪 103 | 售五四 104 | 售一元硬 105 | 售子弹 106 | 售左轮 107 | 亚砷(酸)酐 108 | 亚砷酸钾 109 | 亚砷酸钠 110 | 亚硒酸 111 | 亚硒酸二钠 112 | 亚硒酸镁 113 | 亚硒酸钠 114 | 亚硒酸氢钠 115 | 亚硝酸乙酯 116 | 亚硝酰乙氧 117 | 氧化二丁基锡 118 | 氧化汞 119 | 氧化铊 120 | 氧化亚铊 121 | 氧氯化磷 122 | 原装弹 123 | 原子弹方法 124 | 原子弹清单 125 | 安眠酮 126 | 代血浆 127 | 普萘洛尔 128 | 呋塞米 129 | 西布曲明 130 | testosterone 131 | 胰岛素样生长因子 132 | 促红细胞生成素 133 | 地西泮 134 | 尼可刹米 135 | 甲睾酮 136 | adrenaline 137 | erythropoietin 138 | 地奈德 139 | 莫达非尼 140 | 氯噻嗪 141 | 苯巴比妥 142 | 促性腺激素 143 | 泼尼松 144 | 麻黄草 145 | 雄烯二醇 146 | 地塞米松 147 | tamoxifen 148 | strychnine 149 | androst 150 | 新型毒品 151 | 杜冷丁 152 | 兴奋剂 153 | mdma 154 | 海洛因 155 | 海luo因 156 | heroin 157 | diamorphine 158 | diacetylmorphine 159 | 鸦片 160 | 阿芙蓉 161 | 咖啡因 162 | cocain 163 | 三唑仑 164 | 美沙酮 165 | 麻古 166 | k粉 167 | 凯他敏 168 | ketamine 169 | 冰毒 170 | 苯丙胺 171 | cannabis 172 | 大麻 173 | 爱他死 174 | 氯胺酮 175 | benzodiazepines 176 | 甲基安非他明 177 | 安非他命 178 | 吗啡 -------------------------------------------------------------------------------- /app/data/sensitive/骂人.txt: -------------------------------------------------------------------------------- 1 | 你妈死了 2 | 傻逼 3 | 操 4 | 你妈 5 | 傻X 6 | -------------------------------------------------------------------------------- /app/extend/helper.ts: -------------------------------------------------------------------------------- 1 | import rules from '../utils/validate'; 2 | import codes from '../utils/code'; 3 | import * as common from '../utils/common'; 4 | 5 | module.exports = { 6 | // 处理成功响应 7 | success(data = null) { 8 | this.ctx.body = { 9 | code: 10000, 10 | data, 11 | }; 12 | throw 'code'; 13 | }, 14 | 15 | // 处理失败响应 16 | error(code) { 17 | this.ctx.body = { 18 | code, 19 | msg: codes[code], 20 | }; 21 | throw 'code'; 22 | }, 23 | 24 | status(status) { 25 | this.ctx.status = status; 26 | this.ctx.body = { 27 | code: 10010, 28 | }; 29 | throw 'code'; 30 | }, 31 | 32 | // 自己判断 33 | send(data) { 34 | typeof data === 'number' ? this.error(data) : this.success(data); 35 | }, 36 | 37 | validate(type: string, data: any, force: boolean = false) { 38 | try { 39 | const rule = rules(type, force); 40 | this.ctx.validate(rule, data); 41 | const ruleKeys = Object.keys(rule); 42 | 43 | for (const key in data) { 44 | if (!ruleKeys.includes(key)) { 45 | delete data[key]; 46 | } 47 | } 48 | } catch (err) { 49 | this.ctx.body = { 50 | code: 10004, 51 | msg: err, 52 | }; 53 | throw 'code'; 54 | } 55 | }, 56 | ...common, 57 | }; 58 | -------------------------------------------------------------------------------- /app/middleware/auth.ts: -------------------------------------------------------------------------------- 1 | import * as jwt from 'jsonwebtoken'; 2 | 3 | export default (level) => { 4 | return async (ctx, next) => { 5 | const token = ctx.header.authorization; 6 | 7 | const level0 = { 8 | name: null, 9 | level: 0, 10 | }; 11 | 12 | if (token) { 13 | try { 14 | const { id } = await jwt.verify(token, ctx.app.config.tokenSecret); 15 | const userInfo = await ctx.service.user.info(id); 16 | 17 | if (userInfo) { 18 | userInfo.id = userInfo._id.toString(); 19 | ctx.state.user = userInfo; 20 | } else { 21 | ctx.state.user = level0; 22 | } 23 | } catch (error) { 24 | return ctx.helper.status(401); 25 | } 26 | } else { 27 | ctx.state.user = level0; 28 | } 29 | 30 | if (ctx.state.user.level >= level) { 31 | await next(); 32 | } else { 33 | return ctx.helper.error(10003); 34 | } 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /app/middleware/error_handler.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return async function errorHandler(ctx, next) { 3 | try { 4 | await next(); 5 | } catch (err) { 6 | // 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志 7 | ctx.app.emit('error', err, ctx); 8 | 9 | const status = err.status || 500; 10 | // 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息 11 | const error = status === 500 && ctx.app.config.env === 'prod' ? 'Internal Server Error' : err.message; 12 | 13 | // 从 error 对象上读出各个属性,设置到响应中 14 | if (!ctx.body.code) { 15 | ctx.body = { error }; 16 | if (status === 422) { 17 | ctx.body.detail = err.errors; 18 | } 19 | ctx.status = status; 20 | } 21 | } 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /app/middleware/log.ts: -------------------------------------------------------------------------------- 1 | export default (type?: string) => { 2 | return async (ctx, next) => { 3 | ctx.service.data.create('request'); 4 | if (type) { 5 | const title = ctx?.query?.title || undefined; 6 | ctx.service.data.create(type, title); 7 | } 8 | 9 | await next(); 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /app/middleware/verify.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return async (ctx, next) => { 3 | const configInfo = await ctx.service.config.cacheInfo(); 4 | 5 | if (configInfo.userVerify && ctx.state.user.level > 0 && ctx.state.user.status !== 'publish') { 6 | if (ctx.state.user.status === 'draft') { 7 | return ctx.helper.error(10015); 8 | } else { 9 | return ctx.helper.error(10017); 10 | } 11 | } else { 12 | await next(); 13 | } 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /app/model/blog.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const BlogSchema = new Schema( 6 | { 7 | author: { 8 | type: Schema.Types.ObjectId, 9 | ref: 'User', 10 | }, 11 | status: { 12 | type: String, 13 | enum: ['draft', 'publish', 'reject'], 14 | default: 'draft', 15 | }, 16 | target: { type: Schema.Types.ObjectId, refPath: 'onModel', index: true }, 17 | onModel: { 18 | type: String, 19 | enum: ['Comic', 'Animate', 'Eposide', 'Post', 'Blog'], 20 | }, 21 | content: { 22 | type: String, 23 | }, 24 | image: [{ type: String }], 25 | video: { type: String }, 26 | link: { type: String }, 27 | hot: { 28 | type: Number, 29 | default: 0, 30 | }, 31 | tag: [{ type: Schema.Types.ObjectId, ref: 'Category' }], 32 | addons: Schema.Types.Mixed, 33 | }, 34 | { 35 | timestamps: true, 36 | toJSON: { virtuals: true }, 37 | } 38 | ); 39 | 40 | BlogSchema.virtual('countPlay', { 41 | ref: 'History', 42 | localField: '_id', 43 | foreignField: 'target', 44 | count: true, 45 | }); 46 | 47 | BlogSchema.virtual('countLike', { 48 | ref: 'Relation', 49 | localField: '_id', 50 | foreignField: 'target', 51 | count: true, 52 | }); 53 | 54 | BlogSchema.virtual('countComment', { 55 | ref: 'Comment', 56 | localField: '_id', 57 | foreignField: 'target', 58 | options: { match: { status: 'publish' } }, 59 | count: true, 60 | }); 61 | 62 | return mongoose.model('Blog', BlogSchema); 63 | }; 64 | -------------------------------------------------------------------------------- /app/model/category.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const CategorySchema = new Schema( 6 | { 7 | name: { 8 | // 别名, 唯一标识符 9 | type: String, 10 | required: true, 11 | index: true, 12 | trim: true, 13 | }, 14 | type: { 15 | type: String, 16 | enum: ['aarea', 'ayear', 'akind', 'atag', 'carea', 'cyear', 'ckind', 'ctag', 'pkind', 'ptag', 'blog'], 17 | required: true, 18 | }, 19 | cover: { 20 | type: String, 21 | default: '', 22 | }, 23 | show: { type: Boolean, default: true }, 24 | introduce: { 25 | type: String, 26 | default: '', 27 | }, 28 | }, 29 | { 30 | timestamps: true, 31 | toJSON: { virtuals: true }, 32 | } 33 | ); 34 | 35 | return mongoose.model('Category', CategorySchema); 36 | }; 37 | -------------------------------------------------------------------------------- /app/model/comment.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const CommentSchema = new Schema( 6 | { 7 | author: { type: Schema.Types.ObjectId, ref: 'User' }, // 用户名 8 | target: { type: Schema.Types.ObjectId, refPath: 'onModel', index: true }, 9 | onModel: { 10 | type: String, 11 | enum: ['Comic', 'Animate', 'Eposide', 'Post', 'Blog'], 12 | }, 13 | replyTo: { type: Schema.Types.ObjectId, ref: 'User' }, // 回复人 14 | parent: { type: Schema.Types.ObjectId, ref: 'Comment' }, // 父级 15 | content: { type: String, required: true }, // 内容 16 | status: { 17 | type: String, 18 | enum: ['draft', 'publish', 'reject'], 19 | default: 'draft', 20 | }, 21 | addons: Schema.Types.Mixed, 22 | }, 23 | { 24 | timestamps: true, 25 | toJSON: { virtuals: true }, 26 | } 27 | ); 28 | 29 | CommentSchema.virtual('countLike', { 30 | ref: 'Relation', 31 | localField: '_id', 32 | foreignField: 'target', 33 | count: true, 34 | }); 35 | 36 | CommentSchema.virtual('children', { 37 | ref: 'Comment', 38 | localField: '_id', 39 | foreignField: 'parent', 40 | }); 41 | 42 | CommentSchema.virtual('childrenCount', { 43 | ref: 'Comment', 44 | localField: '_id', 45 | foreignField: 'parent', 46 | options: { match: { status: 'publish' } }, 47 | count: true, 48 | }); 49 | 50 | return mongoose.model('Comment', CommentSchema); 51 | }; 52 | -------------------------------------------------------------------------------- /app/model/count.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const CountSchema = new mongoose.Schema( 6 | { 7 | target: { type: Schema.Types.ObjectId, required: true, refPath: 'onModel', index: true }, 8 | onModel: { 9 | type: String, 10 | required: true, 11 | enum: ['Eposide', 'Post', 'User', 'Comic', 'Animate', 'Blog'], 12 | }, 13 | comment: { type: Number, default: 0 }, 14 | subComment: { type: Number, default: 0 }, 15 | danmu: { type: Number, default: 0 }, 16 | play: { type: Number, default: 0 }, // 用户 17 | view: { type: Number, default: 0 }, // 游客 18 | rateStar: { type: Number, default: 0 }, 19 | rateCount: { type: Number, default: 0 }, 20 | like: { type: Number, default: 0 }, 21 | addons: Schema.Types.Mixed, 22 | }, 23 | { 24 | timestamps: true, 25 | toJSON: { virtuals: true }, 26 | } 27 | ); 28 | 29 | return mongoose.model('Count', CountSchema); 30 | }; 31 | -------------------------------------------------------------------------------- /app/model/danmu.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const DanmuSchema = new mongoose.Schema( 6 | { 7 | target: { type: Schema.Types.ObjectId, ref: 'Eposide', index: true }, 8 | author: { type: Schema.Types.ObjectId, ref: 'User' }, 9 | time: { type: Number, default: 0, index: true }, 10 | text: { type: String, required: true }, 11 | color: { type: Number, default: 16777215 }, 12 | type: { type: Number, default: 0 }, 13 | status: { 14 | type: String, 15 | enum: ['draft', 'publish', 'reject'], 16 | default: 'draft', 17 | }, 18 | ip: String, 19 | referer: String, 20 | addons: Schema.Types.Mixed, 21 | }, 22 | { 23 | timestamps: true, 24 | toJSON: { virtuals: true }, 25 | } 26 | ); 27 | 28 | return mongoose.model('Danmu', DanmuSchema); 29 | }; 30 | -------------------------------------------------------------------------------- /app/model/data.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const DataSchema = new mongoose.Schema( 6 | { 7 | target: { 8 | type: String, 9 | index: true, 10 | }, 11 | type: { 12 | type: String, 13 | enum: [ 14 | 'register', 15 | 'login', 16 | 'blog', 17 | 'comment', 18 | 'post', 19 | 'animate', 20 | 'play', 21 | 'comic', 22 | 'read', 23 | 'danmu', 24 | 'order', 25 | 'report', 26 | 'key', 27 | 'search', 28 | 'other', 29 | 'request', 30 | 'email', 31 | 'reset', 32 | 'verify', 33 | ], 34 | default: 'other', 35 | required: true, 36 | index: true, 37 | }, 38 | author: String, 39 | ip: String, 40 | referer: String, 41 | addons: Schema.Types.Mixed, 42 | }, 43 | { 44 | timestamps: true, 45 | toJSON: { virtuals: true }, 46 | } 47 | ); 48 | 49 | return mongoose.model('Data', DataSchema); 50 | }; 51 | -------------------------------------------------------------------------------- /app/model/history.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const HistorySchema = new mongoose.Schema( 6 | { 7 | target: { type: Schema.Types.ObjectId, required: true, refPath: 'onModel', index: true }, 8 | onModel: { 9 | type: String, 10 | required: true, 11 | enum: ['Eposide', 'Post', 'User'], 12 | }, 13 | author: { type: Schema.Types.ObjectId, ref: 'User' }, 14 | addons: Schema.Types.Mixed, 15 | }, 16 | { 17 | timestamps: true, 18 | toJSON: { virtuals: true }, 19 | } 20 | ); 21 | 22 | return mongoose.model('History', HistorySchema); 23 | }; 24 | -------------------------------------------------------------------------------- /app/model/key.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const KeySchema = new Schema( 6 | { 7 | key: { 8 | // 密钥 9 | type: String, 10 | required: true, 11 | unique: true, 12 | }, 13 | status: { 14 | type: String, 15 | enum: ['draft', 'publish', 'reject'], 16 | default: 'draft', 17 | }, 18 | price: { type: Number, required: true }, 19 | expired: { type: Number, required: true }, 20 | author: { 21 | type: Schema.Types.ObjectId, 22 | ref: 'User', 23 | }, 24 | addons: Schema.Types.Mixed, 25 | }, 26 | { 27 | timestamps: true, 28 | toJSON: { virtuals: true }, 29 | } 30 | ); 31 | 32 | return mongoose.model('Key', KeySchema); 33 | }; 34 | -------------------------------------------------------------------------------- /app/model/order.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const OrderSchema = new Schema( 6 | { 7 | shop: { 8 | type: Schema.Types.ObjectId, 9 | ref: 'Shop', 10 | required: true, 11 | }, 12 | user: { 13 | type: Schema.Types.ObjectId, 14 | ref: 'User', 15 | required: true, 16 | }, 17 | addons: Schema.Types.Mixed, 18 | }, 19 | { 20 | timestamps: true, 21 | toJSON: { virtuals: true }, 22 | } 23 | ); 24 | 25 | return mongoose.model('Order', OrderSchema); 26 | }; 27 | -------------------------------------------------------------------------------- /app/model/rate.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const RateSchema = new Schema( 6 | { 7 | target: { type: Schema.Types.ObjectId, required: true, refPath: 'onModel', index: true }, 8 | onModel: { 9 | type: String, 10 | required: true, 11 | enum: ['Comic', 'Animate'], 12 | }, 13 | rate: { 14 | type: Number, 15 | required: true, 16 | index: true, 17 | }, 18 | content: { 19 | type: String, 20 | required: false, 21 | }, 22 | author: { type: Schema.Types.ObjectId, ref: 'User', required: true }, 23 | addons: Schema.Types.Mixed, 24 | }, 25 | { 26 | timestamps: true, 27 | toJSON: { virtuals: true }, 28 | } 29 | ); 30 | 31 | return mongoose.model('Rate', RateSchema); 32 | }; 33 | -------------------------------------------------------------------------------- /app/model/record.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const RecordSchema = new Schema( 6 | { 7 | source: { type: Schema.Types.ObjectId, ref: 'Source' }, 8 | total: { type: Number, default: 0 }, 9 | success: { type: Number, default: 0 }, 10 | fail: { type: Number, default: 0 }, 11 | author: { type: Schema.Types.ObjectId, ref: 'User' }, 12 | type: { type: String, enum: ['import', 'save'] }, 13 | kind: { type: String, enum: ['Animate', 'Comic', 'All'] }, 14 | content: [{ type: String }], 15 | addons: Schema.Types.Mixed, 16 | }, 17 | { 18 | timestamps: true, 19 | toJSON: { virtuals: true }, 20 | } 21 | ); 22 | 23 | return mongoose.model('Record', RecordSchema); 24 | }; 25 | -------------------------------------------------------------------------------- /app/model/relation.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const RelationSchema = new Schema( 6 | { 7 | target: { type: Schema.Types.ObjectId, required: true, refPath: 'onModel', index: true }, 8 | onModel: { 9 | type: String, 10 | required: true, 11 | enum: ['Comic', 'Animate', 'User', 'Post', 'Comment', 'Blog'], 12 | }, 13 | author: { type: Schema.Types.ObjectId, ref: 'User', required: true }, 14 | addons: Schema.Types.Mixed, 15 | }, 16 | { 17 | timestamps: true, 18 | toJSON: { virtuals: true }, 19 | } 20 | ); 21 | 22 | return mongoose.model('Relation', RelationSchema); 23 | }; 24 | -------------------------------------------------------------------------------- /app/model/report.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const ReportSchema = new Schema( 6 | { 7 | author: { type: Schema.Types.ObjectId, ref: 'User' }, 8 | content: { type: String, required: true }, 9 | target: { type: Schema.Types.ObjectId, required: true, refPath: 'onModel', index: true }, 10 | onModel: { 11 | type: String, 12 | required: true, 13 | enum: ['Comic', 'Animate', 'Comment', 'User', 'Post', 'Eposide', 'Blog'], 14 | }, 15 | status: { 16 | type: String, 17 | enum: ['draft', 'publish', 'reject'], 18 | default: 'draft', 19 | }, 20 | addons: Schema.Types.Mixed, 21 | }, 22 | { 23 | timestamps: true, 24 | toJSON: { virtuals: true }, 25 | } 26 | ); 27 | 28 | return mongoose.model('Report', ReportSchema); 29 | }; 30 | -------------------------------------------------------------------------------- /app/model/season.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const SeasonSchema = new Schema( 6 | { 7 | name: { 8 | type: String, 9 | required: true, 10 | index: true, 11 | trim: true, 12 | }, 13 | cover: { 14 | type: String, 15 | default: '', 16 | }, 17 | introduce: { 18 | type: String, 19 | default: '', 20 | }, 21 | impress: { 22 | type: String, 23 | default: '', 24 | }, 25 | type: { 26 | type: String, 27 | enum: ['animate', 'comic', 'post'], 28 | required: true, 29 | }, 30 | addons: Schema.Types.Mixed, 31 | }, 32 | { 33 | timestamps: true, 34 | toJSON: { virtuals: true }, 35 | } 36 | ); 37 | 38 | SeasonSchema.virtual('animate', { 39 | ref: 'Animate', 40 | localField: '_id', 41 | foreignField: 'seasonRelate', 42 | options: { sort: { season: -1 } }, 43 | }); 44 | 45 | SeasonSchema.virtual('comic', { 46 | ref: 'Comic', 47 | localField: '_id', 48 | foreignField: 'seasonRelate', 49 | options: { sort: { season: -1 } }, 50 | }); 51 | 52 | SeasonSchema.virtual('post', { 53 | ref: 'Post', 54 | localField: '_id', 55 | foreignField: 'seasonRelate', 56 | options: { sort: { season: -1 } }, 57 | }); 58 | 59 | SeasonSchema.virtual('animateCount', { 60 | ref: 'Animate', 61 | localField: '_id', 62 | foreignField: 'seasonRelate', 63 | count: true, 64 | }); 65 | 66 | SeasonSchema.virtual('comicCount', { 67 | ref: 'Comic', 68 | localField: '_id', 69 | foreignField: 'seasonRelate', 70 | count: true, 71 | }); 72 | 73 | SeasonSchema.virtual('postCount', { 74 | ref: 'Post', 75 | localField: '_id', 76 | foreignField: 'seasonRelate', 77 | count: true, 78 | }); 79 | 80 | return mongoose.model('Season', SeasonSchema); 81 | }; 82 | -------------------------------------------------------------------------------- /app/model/shop.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const ShopSchema = new Schema( 6 | { 7 | title: { 8 | // 标题 9 | type: String, 10 | required: true, 11 | }, 12 | status: { 13 | type: String, 14 | enum: ['draft', 'publish', 'reject'], 15 | default: 'draft', 16 | }, 17 | price: { type: Number, required: true }, // 价格 18 | upLevel: { type: Number }, // 提升至等级 19 | addScore: { type: Number }, // 添加积分 20 | addExpired: { type: Number }, 21 | introduce: String, 22 | cover: String, 23 | addons: Schema.Types.Mixed, 24 | }, 25 | { 26 | timestamps: true, 27 | toJSON: { virtuals: true }, 28 | } 29 | ); 30 | 31 | return mongoose.model('Shop', ShopSchema); 32 | }; 33 | -------------------------------------------------------------------------------- /app/model/source.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const SourceSchema = new Schema( 6 | { 7 | source: { type: String }, 8 | api: { type: String }, 9 | cat: { type: Number }, 10 | type: { type: String, enum: ['Animate', 'Comic'] }, 11 | update: { type: Boolean, default: false }, 12 | slugPrefix: { type: String, default: '' }, 13 | mode: { type: String, default: 'maccms' }, 14 | addons: Schema.Types.Mixed, 15 | }, 16 | { 17 | timestamps: true, 18 | toJSON: { virtuals: true }, 19 | } 20 | ); 21 | 22 | return mongoose.model('Source', SourceSchema); 23 | }; 24 | -------------------------------------------------------------------------------- /app/model/user.ts: -------------------------------------------------------------------------------- 1 | export default (app) => { 2 | const mongoose = app.mongoose; 3 | const Schema = mongoose.Schema; 4 | 5 | const UserSchema = new Schema( 6 | { 7 | name: { 8 | type: String, 9 | required: true, 10 | unique: true, 11 | index: true, 12 | trim: true, 13 | }, // 用户名 14 | password: { type: String, required: true }, // 密码 15 | refreshToken: { type: String, required: true }, // refresh token 16 | email: { type: String, required: true, unique: true }, // 邮箱 17 | level: { type: Number, default: 1 }, // 等级 18 | score: { type: Number, default: 0 }, // 积分 19 | avatar: { type: String, default: '' }, // 头像 20 | background: { type: String, default: '' }, // 背景图 21 | introduce: { type: String, default: '' }, // 简介 22 | status: { 23 | type: String, 24 | enum: ['draft', 'publish', 'reject'], 25 | default: 'draft', 26 | }, 27 | money: { type: Number, default: 0 }, // 金钱 28 | expired: { type: Number, default: 0 }, // 会员过期时间 29 | error: { type: Number, default: 0 }, // 违规行为统计 30 | addons: Schema.Types.Mixed, 31 | }, 32 | { 33 | timestamps: true, 34 | toJSON: { virtuals: true }, 35 | } 36 | ); 37 | 38 | UserSchema.virtual('countAnimate', { 39 | ref: 'History', 40 | localField: '_id', 41 | foreignField: 'author', 42 | count: true, 43 | }); 44 | 45 | UserSchema.virtual('countComic', { 46 | ref: 'Relation', 47 | localField: '_id', 48 | foreignField: 'author', 49 | count: true, 50 | }); 51 | 52 | UserSchema.virtual('countPost', { 53 | ref: 'Comment', 54 | localField: '_id', 55 | foreignField: 'author', 56 | count: true, 57 | }); 58 | 59 | UserSchema.virtual('countComment', { 60 | ref: 'Comment', 61 | localField: '_id', 62 | foreignField: 'author', 63 | count: true, 64 | }); 65 | 66 | return mongoose.model('User', UserSchema); 67 | }; 68 | -------------------------------------------------------------------------------- /app/router.ts: -------------------------------------------------------------------------------- 1 | import { Application } from 'egg'; 2 | import frontend from './router/frontend'; 3 | import backend from './router/backend'; 4 | 5 | export default (app: Application) => { 6 | frontend(app); 7 | backend(app); 8 | }; 9 | -------------------------------------------------------------------------------- /app/schedule/cloud.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | schedule: { 3 | cron: '0 0 4 * * *', 4 | type: 'all', // 指定所有的 worker 都需要执行 5 | }, 6 | async task(ctx) { 7 | const data = await ctx.service.config.info(); 8 | const { autoUpdate = false } = data; 9 | if (autoUpdate) { 10 | await ctx.service.cloud.autoUpdate(); 11 | } 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /app/schedule/count.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | schedule: { 3 | cron: '0 30 3 * * *', 4 | type: 'all', // 指定所有的 worker 都需要执行 5 | }, 6 | async task(ctx) { 7 | await ctx.service.count.syncEposide(); 8 | await ctx.service.count.syncAnimate(); 9 | await ctx.service.count.syncComic(); 10 | await ctx.service.count.syncPost(); 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /app/schedule/keyExpired.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | schedule: { 3 | interval: '15m', // 1 分钟间隔 4 | type: 'all', // 指定所有的 worker 都需要执行 5 | }, 6 | async task(ctx) { 7 | await ctx.model.Key.updateMany({ expired: { $lt: 900 } }, { $set: { expired: Number(0), status: 'reject' } }); 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /app/schedule/source.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | schedule: { 3 | cron: '0 0 3 * * *', 4 | type: 'all', // 指定所有的 worker 都需要执行 5 | }, 6 | async task(ctx) { 7 | const data = await ctx.model.Source.find({ update: true }); 8 | const dataArr = JSON.parse(JSON.stringify(data)); 9 | if (dataArr.length > 0) { 10 | for (const item of dataArr) { 11 | await ctx.service.source.import(item._id, 'day'); 12 | } 13 | } 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /app/schedule/userLevel.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | schedule: { 3 | interval: '15m', // 1 分钟间隔 4 | type: 'all', // 指定所有的 worker 都需要执行 5 | }, 6 | async task(ctx) { 7 | await ctx.model.User.updateMany( 8 | { expired: { $gte: 900 }, level: { $lte: 99 } }, 9 | { $inc: { expired: -900 } }, 10 | ); 11 | await ctx.model.User.updateMany( 12 | { expired: { $lt: 900 }, level: { $lte: 99 } }, 13 | { $set: { expired: Number(0), level: Number(1) } }, 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /app/service/danmu.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg'; 2 | 3 | interface Query { 4 | page: number; 5 | size: number; 6 | sortBy: string; 7 | status: string; 8 | sortOrder?: number; 9 | title?: string; 10 | target?: string; 11 | } 12 | 13 | class DanmuService extends Service { 14 | async query({ page, size, sortBy = 'createdAt', sortOrder = -1, title, target, status }: Query) { 15 | const skip: number = (page - 1) * size; 16 | const limit: number = size; 17 | 18 | const query: any = {}; 19 | title && (query.text = { $regex: title, $options: '$i' }); 20 | target && (query.target = target); 21 | status && (query.status = status); 22 | 23 | const result = await this.ctx.model.Danmu.find(query) 24 | .sort({ [sortBy]: sortOrder, _id: -1 }) 25 | .skip(skip) 26 | .limit(limit) 27 | .populate({ 28 | path: 'target', 29 | populate: [ 30 | { 31 | path: 'target', 32 | select: 'title slug', 33 | }, 34 | ], 35 | select: 'title target onModel', 36 | }) 37 | .populate({ path: 'author', select: 'name avatar level introduce background' }); 38 | 39 | const total = await this.ctx.model.Danmu.find(query).countDocuments(); 40 | 41 | return { 42 | list: result, 43 | total, 44 | }; 45 | } 46 | 47 | async info(id: string) { 48 | const data = await this.ctx.model.Danmu.findById(id).populate('target'); 49 | return data; 50 | } 51 | 52 | async create(data: any) { 53 | const result = await this.ctx.model.Danmu.create(data); 54 | return result; 55 | } 56 | 57 | async update(ids: string[], data: any) { 58 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 59 | const result = await this.ctx.model.Danmu.updateMany(query, { $set: data }); 60 | return result; 61 | } 62 | 63 | async destroy(ids: string[]) { 64 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 65 | const result = await this.ctx.model.Danmu.deleteMany(query); 66 | return result; 67 | } 68 | } 69 | 70 | export default DanmuService; 71 | -------------------------------------------------------------------------------- /app/service/history.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg'; 2 | 3 | class HistoryService extends Service { 4 | async query(query, type) { 5 | let result = await this.ctx.model.History.find(query) 6 | .sort({ createdAt: -1 }) 7 | .limit(100) 8 | .populate({ 9 | path: 'target', 10 | populate: [ 11 | { 12 | path: 'target', 13 | select: 'title slug _id coverVertical', 14 | }, 15 | ], 16 | select: 'title _id sort target onModel', 17 | }); 18 | 19 | result = JSON.parse(JSON.stringify(result)) 20 | .filter((item) => 21 | ['animate', 'comic'].includes(type) ? item?.target?.onModel.toLowerCase() === type : true 22 | ) 23 | .slice(0, 20) 24 | .map((item) => ({ 25 | ...item, 26 | belong: item?.target?.target, 27 | })); 28 | 29 | return result; 30 | } 31 | 32 | async playCreate(result: any, type: string) { 33 | if (typeof result === 'number') return; 34 | const { _id } = result; 35 | 36 | this.create(_id, type); 37 | } 38 | 39 | async create(target: string, onModel: string) { 40 | const { state, service } = this.ctx; 41 | const author = state.user.id; 42 | 43 | const data = { 44 | author, 45 | target, 46 | onModel, 47 | }; 48 | 49 | if (author) { 50 | this.update(data); 51 | } 52 | 53 | const type = author ? 'play' : 'view'; 54 | service.count.create(author, target, onModel, type); 55 | } 56 | 57 | async update(data: any) { 58 | let update = { updatedAt: new Date() }; 59 | let options = { upsert: true, new: true, setDefaultsOnInsert: true }; 60 | const result = await this.ctx.model.History.findOneAndUpdate(data, update, options); 61 | return result; 62 | } 63 | 64 | async destroy(ids: string[]) { 65 | const result = await this.ctx.model.History.deleteMany({ 66 | _id: { $in: ids }, 67 | }); 68 | return result; 69 | } 70 | } 71 | 72 | export default HistoryService; 73 | -------------------------------------------------------------------------------- /app/service/order.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg'; 2 | 3 | class OrderService extends Service { 4 | async query({ page, size, sortBy = 'createdAt', sortOrder = -1, title, author }) { 5 | const skip: number = (page - 1) * size; 6 | const limit: number = size; 7 | 8 | const query: any = {}; 9 | title && (query._id = { $in: [title] }); 10 | author && (query.user = author); 11 | 12 | const result = await this.ctx.model.Order.find(query) 13 | .sort({ [sortBy]: sortOrder, _id: -1 }) 14 | .skip(skip) 15 | .limit(limit) 16 | .populate('shop'); 17 | 18 | const total = await this.ctx.model.Order.find(query).countDocuments(); 19 | 20 | return { 21 | list: result, 22 | total, 23 | }; 24 | } 25 | 26 | async info(id: string) { 27 | const data = await this.ctx.model.Order.findById(id).populate('shop'); 28 | return data; 29 | } 30 | 31 | async create(data: any) { 32 | const { user, shop } = data; 33 | 34 | const userInfo = await this.ctx.service.user.info(user); 35 | const shopInfo = await this.ctx.service.shop.info(shop); 36 | 37 | const { money, _id, score, expired } = userInfo; 38 | const { price, upLevel = 0, addScore = 0, addExpired = 0 } = shopInfo; 39 | 40 | if (money < price) return 20005; 41 | 42 | await this.ctx.service.user.update([_id], { 43 | level: upLevel, 44 | score: score + addScore, 45 | expired: expired + addExpired, 46 | money: money - price, 47 | }); 48 | 49 | const result = await this.ctx.model.Order.create(data); 50 | return result; 51 | } 52 | 53 | async update(ids: string[], data: any) { 54 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 55 | const result = await this.ctx.model.Order.updateMany(query, { $set: data }); 56 | return result; 57 | } 58 | 59 | async destroy(ids: string[]) { 60 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 61 | const result = await this.ctx.model.Order.deleteMany(query); 62 | return result; 63 | } 64 | } 65 | 66 | export default OrderService; 67 | -------------------------------------------------------------------------------- /app/service/rate.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg'; 2 | 3 | class RateService extends Service { 4 | async query({ page, size, sortBy, sortOrder }) { 5 | const skip: number = (page - 1) * size; 6 | const limit: number = size; 7 | 8 | const query: any = {}; 9 | 10 | const result = await this.ctx.model.Rate.find(query) 11 | .sort({ [sortBy]: sortOrder, _id: -1 }) 12 | .skip(skip) 13 | .limit(limit) 14 | .populate({ path: 'author', select: 'name avatar level introduce background' }) 15 | .populate('target'); 16 | 17 | const total = await this.ctx.model.Rate.find(query).countDocuments(); 18 | 19 | return { 20 | list: result, 21 | total, 22 | }; 23 | } 24 | 25 | async info(id: string) { 26 | const result = await this.ctx.model.Rate.findById(id) 27 | .populate({ path: 'author', select: 'name avatar level introduce background' }) 28 | .populate('target'); 29 | return result; 30 | } 31 | 32 | async exist(author: string, target: string) { 33 | const result = await this.ctx.model.Rate.findOne({ author, target }); 34 | return result; 35 | } 36 | 37 | async create(data: any) { 38 | const exist = await this.exist(data.author, data.target); 39 | if (exist) return 29005; 40 | 41 | const result = await this.ctx.model.Rate.create(data); 42 | return result; 43 | } 44 | 45 | async update(ids: Array, data: any) { 46 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 47 | const result = await this.ctx.model.Rate.updateMany(query, { $set: data }); 48 | return result; 49 | } 50 | 51 | async destroy(ids: Array) { 52 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 53 | const result = await this.ctx.model.Rate.deleteMany(query); 54 | return result; 55 | } 56 | } 57 | 58 | export default RateService; 59 | -------------------------------------------------------------------------------- /app/service/record.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg'; 2 | 3 | class RecordService extends Service { 4 | async query({ page, size, sortBy = 'createdAt', sortOrder = -1, type, title }) { 5 | const skip: number = (page - 1) * size; 6 | const limit: number = size; 7 | 8 | const query: any = {}; 9 | title && (query._id = { $in: [title] }); 10 | type && (query.type = type); 11 | 12 | const result = await this.ctx.model.Record.find(query) 13 | .sort({ [sortBy]: sortOrder, _id: -1 }) 14 | .skip(skip) 15 | .limit(limit) 16 | .populate('source') 17 | .populate({ path: 'author', select: 'name' }); 18 | 19 | const total = await this.ctx.model.Record.find(query).countDocuments(); 20 | 21 | return { 22 | list: result, 23 | total, 24 | }; 25 | } 26 | 27 | async create(data: any) { 28 | const result = await this.ctx.model.Record.create(data); 29 | return result.toJSON(); 30 | } 31 | 32 | async update(ids: string[], data: any) { 33 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 34 | const result = await this.ctx.model.Record.updateMany(query, { $set: data }); 35 | return result; 36 | } 37 | 38 | async destroy(ids: string[]) { 39 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 40 | const result = await this.ctx.model.Record.deleteMany(query); 41 | return result; 42 | } 43 | 44 | async simpleCreate(id, type, kind, total = 0) { 45 | const { state } = this.ctx; 46 | const author = state && state.user && state.user.id; 47 | 48 | const result = await this.create({ 49 | source: id, 50 | type, 51 | author, 52 | kind, 53 | total, 54 | }); 55 | 56 | return result; 57 | } 58 | 59 | async simpleUpdate(data) { 60 | const { total, success, fail, content, _id } = data; 61 | await this.update([_id], { total, success, fail, content }); 62 | } 63 | } 64 | 65 | export default RecordService; 66 | -------------------------------------------------------------------------------- /app/service/relation.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg'; 2 | 3 | class RelationService extends Service { 4 | async query({ page, size, sortBy = 'createdAt', sortOrder = -1, onModel, author, update }) { 5 | const skip: number = (page - 1) * size; 6 | const limit: number = size; 7 | 8 | const query: any = {}; 9 | onModel && (query.onModel = onModel); 10 | author && (query.author = author); 11 | 12 | const result = await this.ctx.model.Relation.find(query) 13 | .sort({ [sortBy]: sortOrder, _id: -1 }) 14 | .skip(skip) 15 | .limit(limit) 16 | .populate({ 17 | path: 'target', 18 | select: 'title slug coverVertical updatedAt author', 19 | populate: [ 20 | { 21 | path: 'countEposide', 22 | }, 23 | { 24 | path: 'author', 25 | }, 26 | ], 27 | match: { status: 'publish', isUpdate: !!update }, 28 | }); 29 | 30 | const total = await this.ctx.model.Relation.find(query).countDocuments(); 31 | 32 | return { 33 | list: result.filter((item) => item.target), 34 | total, 35 | }; 36 | } 37 | 38 | async exist(data: any) { 39 | const result = await this.ctx.model.Relation.findOne(data); 40 | return !!result; 41 | } 42 | 43 | async toggle(data: any) { 44 | const info = await this.ctx.model.Relation.findOne(data); 45 | 46 | let result; 47 | if (info) { 48 | result = await this.destroy(data); 49 | } else { 50 | result = await this.create(data); 51 | } 52 | 53 | return result; 54 | } 55 | 56 | async create(data: any) { 57 | const result = await this.ctx.model.Relation.create(data); 58 | return result; 59 | } 60 | 61 | async destroy(data: any) { 62 | const result = await this.ctx.model.Relation.deleteOne(data); 63 | return result; 64 | } 65 | } 66 | 67 | export default RelationService; 68 | -------------------------------------------------------------------------------- /app/service/report.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg'; 2 | 3 | class ReportService extends Service { 4 | async query({ page, size, sortBy = 'createdAt', sortOrder = -1, title, status }) { 5 | const skip: number = (page - 1) * size; 6 | const limit: number = size; 7 | 8 | const query: any = {}; 9 | title && (query.content = { $regex: title, $options: '$i' }); 10 | status && (query.status = status); 11 | 12 | const result = await this.ctx.model.Report.find(query) 13 | .sort({ [sortBy]: sortOrder, _id: -1 }) 14 | .skip(skip) 15 | .limit(limit) 16 | .populate({ path: 'author', select: 'name avatar level introduce background' }) 17 | .populate({ 18 | path: 'target', 19 | populate: [ 20 | { 21 | path: 'target', 22 | select: 'title slug', 23 | }, 24 | ], 25 | select: 'title target onModel name content', 26 | }); 27 | 28 | const total = await this.ctx.model.Report.find(query).countDocuments(); 29 | 30 | return { 31 | list: result, 32 | total, 33 | }; 34 | } 35 | 36 | async info(id: string) { 37 | const data = await this.ctx.model.Report.findById(id) 38 | .populate({ path: 'author', select: 'name avatar level introduce background' }) 39 | .populate('target'); 40 | return data; 41 | } 42 | 43 | async create(data: any) { 44 | const result = await this.ctx.model.Report.create(data); 45 | return result; 46 | } 47 | 48 | async exist(data: any) { 49 | const result = await this.ctx.model.Report.findOne(data); 50 | return result; 51 | } 52 | 53 | async update(ids: Array, data: any) { 54 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 55 | const result = await this.ctx.model.Report.updateMany(query, { $set: data }); 56 | return result; 57 | } 58 | 59 | async destroy(ids: Array) { 60 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 61 | const result = await this.ctx.model.Report.deleteMany(query); 62 | return result; 63 | } 64 | } 65 | 66 | export default ReportService; 67 | -------------------------------------------------------------------------------- /app/service/shop.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg'; 2 | 3 | class ShopService extends Service { 4 | async query({ page, size, sortBy = 'createdAt', sortOrder = -1, title, status }) { 5 | const skip: number = (page - 1) * size; 6 | const limit: number = size; 7 | 8 | const query: any = {}; 9 | title && (query.title = { $regex: title, $options: '$i' }); 10 | status && (query.status = status); 11 | 12 | const result = await this.ctx.model.Shop.find(query) 13 | .sort({ [sortBy]: sortOrder, _id: -1 }) 14 | .skip(skip) 15 | .limit(limit); 16 | 17 | const total = await this.ctx.model.Shop.find(query).countDocuments(); 18 | 19 | return { 20 | list: result, 21 | total, 22 | }; 23 | } 24 | 25 | async info(id: string) { 26 | const data = await this.ctx.model.Shop.findById(id); 27 | return data; 28 | } 29 | 30 | async create(data: any) { 31 | const result = await this.ctx.model.Shop.create(data); 32 | return result; 33 | } 34 | 35 | async update(ids: string[], data: any) { 36 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 37 | const result = await this.ctx.model.Shop.updateMany(query, { $set: data }); 38 | return result; 39 | } 40 | 41 | async destroy(ids: string[]) { 42 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 43 | const result = await this.ctx.model.Shop.deleteMany(query); 44 | return result; 45 | } 46 | } 47 | 48 | export default ShopService; 49 | -------------------------------------------------------------------------------- /app/service/source.ts: -------------------------------------------------------------------------------- 1 | import { Service } from 'egg'; 2 | 3 | class SourceService extends Service { 4 | async query({ page, size, sortBy = 'createdAt', sortOrder = -1, title }) { 5 | const skip: number = (page - 1) * size; 6 | const limit: number = size; 7 | 8 | const query: any = {}; 9 | title && (query.source = { $regex: title, $options: '$i' }); 10 | 11 | const result = await this.ctx.model.Source.find(query) 12 | .sort({ [sortBy]: sortOrder, _id: -1 }) 13 | .skip(skip) 14 | .limit(limit); 15 | 16 | const total = await this.ctx.model.Source.find(query).countDocuments(); 17 | 18 | return { 19 | list: result, 20 | total, 21 | }; 22 | } 23 | 24 | async info(id: string) { 25 | const result = await this.ctx.app.model.Source.findById(id); 26 | return result; 27 | } 28 | 29 | async create(data: any) { 30 | const result = await this.ctx.model.Source.create(data); 31 | return result; 32 | } 33 | 34 | async update(ids: string[], data: any) { 35 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 36 | const result = await this.ctx.model.Source.updateMany(query, { $set: data }); 37 | return result; 38 | } 39 | 40 | async destroy(ids: string[]) { 41 | const query = ids.length > 0 ? { _id: { $in: ids } } : {}; 42 | const result = await this.ctx.model.Source.deleteMany(query); 43 | return result; 44 | } 45 | 46 | async import(source: string, type: string) { 47 | const info = await this.info(source); 48 | 49 | const hour = type === 'day' ? 24 : type === 'week' ? 168 : null; 50 | this.ctx.service.sourceInit[info.mode](info, hour); 51 | 52 | return true; 53 | } 54 | } 55 | 56 | export default SourceService; 57 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '8' 4 | 5 | install: 6 | - ps: Install-Product node $env:nodejs_version 7 | - npm i npminstall && node_modules\.bin\npminstall 8 | 9 | test_script: 10 | - node --version 11 | - npm --version 12 | - npm run test 13 | 14 | build: off 15 | -------------------------------------------------------------------------------- /config/config.default.ts: -------------------------------------------------------------------------------- 1 | import { EggAppConfig, EggAppInfo, PowerPartial } from "egg"; 2 | import * as path from "path"; 3 | import * as os from "os"; 4 | 5 | export default (appInfo: EggAppInfo) => { 6 | const config = {} as PowerPartial; 7 | 8 | // override config from framework / plugin 9 | // use for cookie sign key, should change to your own and keep security 10 | config.keys = appInfo.name + "_1571145237182_4234"; 11 | 12 | // add your egg config in here 13 | config.middleware = ["errorHandler"]; 14 | 15 | // add your special config in here 16 | const bizConfig = { 17 | sourceUrl: `https://github.com/eggjs/examples/tree/master/${appInfo.name}`, 18 | proxy: true, 19 | ipHeaders: "X-Real-Ip, X-Forwarded-For", 20 | maxProxyCount: 1, 21 | hostHeaders: "X-Forwarded-Host", 22 | security: { 23 | csrf: { 24 | enable: false, 25 | }, 26 | }, 27 | multipart: { 28 | mode: "file", 29 | fileSize: "20mb", 30 | tmpdir: path.join(os.tmpdir(), "uploads", appInfo.name), 31 | cleanSchedule: { 32 | cron: "0 30 4 * * *", 33 | }, 34 | fileExtensions: [".ico"], 35 | }, 36 | 37 | validate: { 38 | convert: true, 39 | // validateRoot: false, 40 | }, 41 | 42 | redis: { 43 | client: { 44 | port: 6379, 45 | host: "127.0.0.1", 46 | password: "", 47 | db: 0, 48 | }, 49 | }, 50 | 51 | mongoose: { 52 | client: { 53 | // url: 'mongodb://demo.qinvideo.org:27017/qinvideo', 54 | url: "mongodb://localhost:27017/qinvideo", 55 | options: {}, 56 | }, 57 | }, 58 | 59 | authUrl: "https://m.qinmei.video/auth/verify", // 邮件验证账户的地址 60 | salt: "qinmei", // 密码盐值 61 | tokenSecret: "qinmei", // 登录密钥 62 | expired: 600, // redis缓存有效期, 是个随机数, 如果是3600, 那么会在3600~7200之间, 即一到两倍之间 63 | expiredCount: 100, // 接口缓存写入数据库的数量 64 | caculateCount: 100, // 评论播放等统计接口的计算缓存量 65 | }; 66 | 67 | return { 68 | ...config, 69 | ...bizConfig, 70 | }; 71 | }; 72 | -------------------------------------------------------------------------------- /config/config.local.ts: -------------------------------------------------------------------------------- 1 | import { EggAppConfig, PowerPartial } from 'egg'; 2 | 3 | export default () => { 4 | const config: PowerPartial = {}; 5 | return config; 6 | }; 7 | -------------------------------------------------------------------------------- /config/config.prod.ts: -------------------------------------------------------------------------------- 1 | import { EggAppConfig, PowerPartial } from 'egg'; 2 | 3 | export default () => { 4 | const config: PowerPartial = {}; 5 | return config; 6 | }; 7 | -------------------------------------------------------------------------------- /config/plugin.ts: -------------------------------------------------------------------------------- 1 | import { EggPlugin } from 'egg'; 2 | 3 | const plugin: EggPlugin = { 4 | // static: true, 5 | validate: { 6 | enable: true, 7 | package: 'egg-validate', 8 | }, 9 | mongoose: { 10 | enable: true, 11 | package: 'egg-mongoose', 12 | }, 13 | redis: { 14 | enable: true, 15 | package: 'egg-redis', 16 | }, 17 | }; 18 | 19 | export default plugin; 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qinvideo-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "egg": { 7 | "typescript": true, 8 | "declarations": true 9 | }, 10 | "scripts": { 11 | "start": "egg-scripts start --daemon --title=egg-server-qinvideo-node", 12 | "stop": "egg-scripts stop --title=egg-server-qinvideo-node", 13 | "dev": "egg-bin dev", 14 | "debug": "egg-bin debug", 15 | "test-local": "egg-bin test", 16 | "test": "npm run lint -- --fix && npm run test-local", 17 | "cov": "egg-bin cov", 18 | "tsc": "ets && tsc -p tsconfig.json", 19 | "ci": "npm run lint && npm run cov && npm run tsc", 20 | "autod": "autod", 21 | "lint": "tslint --project . -c tslint.json", 22 | "clean": "ets clean" 23 | }, 24 | "dependencies": { 25 | "@sendgrid/mail": "^6.4.0", 26 | "cheerio": "^1.0.0-rc.3", 27 | "egg": "^2.6.1", 28 | "egg-logger": "^2.4.1", 29 | "egg-mongoose": "^3.2.0", 30 | "egg-redis": "^2.4.0", 31 | "egg-scripts": "^2.6.0", 32 | "egg-validate": "^2.0.2", 33 | "jsonwebtoken": "^8.5.1", 34 | "moment": "^2.24.0", 35 | "nodemailer": "^6.3.1", 36 | "request": "^2.88.0", 37 | "request-promise": "^4.2.4", 38 | "short-uuid": "^3.1.1", 39 | "superagent": "^5.1.0", 40 | "uuid": "^3.3.3" 41 | }, 42 | "devDependencies": { 43 | "@types/mocha": "^2.2.40", 44 | "@types/node": "^7.0.12", 45 | "@types/supertest": "^2.0.0", 46 | "autod": "^3.0.1", 47 | "autod-egg": "^1.1.0", 48 | "egg-bin": "^4.11.0", 49 | "egg-ci": "^1.8.0", 50 | "egg-mock": "^3.16.0", 51 | "tslib": "^1.9.0", 52 | "tslint": "^5.0.0", 53 | "tslint-config-egg": "^1.0.0", 54 | "typescript": "<=3.8.5" 55 | }, 56 | "engines": { 57 | "node": ">=8.9.0" 58 | }, 59 | "ci": { 60 | "version": "8" 61 | }, 62 | "repository": { 63 | "type": "git", 64 | "url": "" 65 | }, 66 | "eslintIgnore": [ 67 | "coverage" 68 | ], 69 | "author": "qinmei", 70 | "license": "MIT" 71 | } 72 | -------------------------------------------------------------------------------- /public/animate/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/animate/.gitkeep -------------------------------------------------------------------------------- /public/animate/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/animate/favicon.ico -------------------------------------------------------------------------------- /public/animate/index.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /public/animate/p__index.2bb95199.chunk.css: -------------------------------------------------------------------------------- 1 | .index___28nWn{min-height:calc(100vh - 260px);padding-top:60px}.index___28nWn .header___2ZAQi{width:100%;height:360px;margin-bottom:15px;background-size:cover;background-position:50%;display:flex;justify-content:flex-end;align-items:center}.index___28nWn .header___2ZAQi .title___2cyli{min-width:35%;color:hsla(0,0%,100%,.8)}.index___28nWn .header___2ZAQi .title___2cyli span{display:block;font-size:36px;margin:40px 0 20px}.index___28nWn .header___2ZAQi .title___2cyli p{font-size:20px;margin-bottom:0} 2 | -------------------------------------------------------------------------------- /public/animate/p__new.df8045f0.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[5],{lMKa:function(e,t,a){"use strict";a.r(t);a("IzEo");var n=a("bx4M"),i=a("mrSG"),o=a("q1tI"),r=a.n(o),s=a("MuoO"),c=a("L6Kr"),d=a("zqYd"),m=a("LLXN"),p=class extends o["Component"]{constructor(){super(...arguments),this.state={cate:"newIndexNewAnimate"},this.initData=(()=>{var e=this.props.dispatch;e({type:"animate/queryList",payload:{query:{update:!0,size:100,page:1},addon:{type:"newIndexNewAnimate"}}})})}componentDidMount(){this.initData()}render(){var e=this.props.animate,t=e.newIndexNewAnimate.list||[];return r.a.createElement("div",{style:{paddingBottom:"20px"}},r.a.createElement("div",{className:"container"},r.a.createElement(c["a"],{img:window.config.newAnimate,span:Object(m["formatMessage"])({id:"common.menu.newAnimate"})}),r.a.createElement(n["a"],{bordered:!1},r.a.createElement(d["b"],{list:t}))))}};p=Object(i["a"])([Object(s["connect"])(e=>{var t=e.animate,a=e.category,n=e.loading;return{animate:t,category:a,loading:n.models.animate}})],p),t["default"]=p}}]); -------------------------------------------------------------------------------- /public/animate/static/level.367681e1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/animate/static/level.367681e1.png -------------------------------------------------------------------------------- /public/backend/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/backend/.gitkeep -------------------------------------------------------------------------------- /public/backend/39.0a4252d9.chunk.css: -------------------------------------------------------------------------------- 1 | .antd-pro-components-setting-drawer-index-content{position:relative;min-height:100%;background:#fff}.antd-pro-components-setting-drawer-index-blockChecbox{display:flex}.antd-pro-components-setting-drawer-index-blockChecbox .antd-pro-components-setting-drawer-index-item{position:relative;margin-right:16px;border-radius:4px;cursor:pointer}.antd-pro-components-setting-drawer-index-blockChecbox .antd-pro-components-setting-drawer-index-item img{width:48px}.antd-pro-components-setting-drawer-index-blockChecbox .antd-pro-components-setting-drawer-index-selectIcon{position:absolute;top:0;right:0;width:100%;height:100%;padding-top:15px;padding-left:24px;color:#1890ff;font-weight:700;font-size:14px}.antd-pro-components-setting-drawer-index-color_block{display:inline-block;width:38px;height:22px;margin:4px 12px 4px 4px;vertical-align:middle;border-radius:4px;cursor:pointer}.antd-pro-components-setting-drawer-index-title{margin-bottom:12px;color:rgba(0,0,0,.85);font-size:14px;line-height:22px}.antd-pro-components-setting-drawer-index-handle{position:absolute;top:240px;right:300px;z-index:0;display:flex;justify-content:center;align-items:center;width:48px;height:48px;font-size:16px;text-align:center;background:#1890ff;border-radius:4px 0 0 4px;cursor:pointer;pointer-events:auto}.antd-pro-components-setting-drawer-index-productionHint{margin-top:16px;font-size:12px} 2 | .antd-pro-components-setting-drawer-theme-color-themeColor{margin-top:24px;overflow:hidden}.antd-pro-components-setting-drawer-theme-color-themeColor .antd-pro-components-setting-drawer-theme-color-title{margin-bottom:12px;color:rgba(0,0,0,.65);font-size:14px;line-height:22px}.antd-pro-components-setting-drawer-theme-color-themeColor .antd-pro-components-setting-drawer-theme-color-colorBlock{float:left;width:20px;height:20px;margin-right:8px;color:#fff;font-weight:700;text-align:center;border-radius:2px;cursor:pointer} 3 | -------------------------------------------------------------------------------- /public/backend/40.001ea15d.chunk.css: -------------------------------------------------------------------------------- 1 | .antd-pro-components-charts-mini-progress-index-miniProgress{position:relative;width:100%;padding:5px 0}.antd-pro-components-charts-mini-progress-index-miniProgress .antd-pro-components-charts-mini-progress-index-progressWrap{position:relative;background-color:#f5f5f5}.antd-pro-components-charts-mini-progress-index-miniProgress .antd-pro-components-charts-mini-progress-index-progress{width:0;height:100%;background-color:#1890ff;border-radius:1px 0 0 1px;transition:all .4s cubic-bezier(.08,.82,.17,1) 0s}.antd-pro-components-charts-mini-progress-index-miniProgress .antd-pro-components-charts-mini-progress-index-target{position:absolute;top:0;bottom:0}.antd-pro-components-charts-mini-progress-index-miniProgress .antd-pro-components-charts-mini-progress-index-target span{position:absolute;top:0;left:0;width:2px;height:4px;border-radius:100px}.antd-pro-components-charts-mini-progress-index-miniProgress .antd-pro-components-charts-mini-progress-index-target span:last-child{top:auto;bottom:0} 2 | -------------------------------------------------------------------------------- /public/backend/40.fb5fb0a7.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[40],{"lfV+":function(e,r,n){"use strict";n.r(r);n("5Dmo");var t=n("3S7+"),a=n("q1tI"),s=n.n(a),o=n("vDV7"),c=n.n(o),l=function(e){var r=e.target,n=e.color,a=void 0===n?"rgb(19, 194, 194)":n,o=e.strokeWidth,l=e.percent;return s.a.createElement("div",{className:c.a.miniProgress},s.a.createElement(t["a"],{title:"\u76ee\u6807\u503c: ".concat(r,"%")},s.a.createElement("div",{className:c.a.target,style:{left:r?"".concat(r,"%"):null}},s.a.createElement("span",{style:{backgroundColor:a||null}}),s.a.createElement("span",{style:{backgroundColor:a||null}}))),s.a.createElement("div",{className:c.a.progressWrap},s.a.createElement("div",{className:c.a.progress,style:{backgroundColor:a||null,width:l?"".concat(l,"%"):null,height:o||null}})))};r["default"]=l},vDV7:function(e,r,n){e.exports={miniProgress:"antd-pro-components-charts-mini-progress-index-miniProgress",progressWrap:"antd-pro-components-charts-mini-progress-index-progressWrap",progress:"antd-pro-components-charts-mini-progress-index-progress",target:"antd-pro-components-charts-mini-progress-index-target"}}}]); -------------------------------------------------------------------------------- /public/backend/42.aaf47cd3.chunk.css: -------------------------------------------------------------------------------- 1 | .antd-pro-components-charts-radar-index-radar .antd-pro-components-charts-radar-index-legend{margin-top:16px}.antd-pro-components-charts-radar-index-radar .antd-pro-components-charts-radar-index-legend .antd-pro-components-charts-radar-index-legendItem{position:relative;color:rgba(0,0,0,.45);line-height:22px;text-align:center;cursor:pointer}.antd-pro-components-charts-radar-index-radar .antd-pro-components-charts-radar-index-legend .antd-pro-components-charts-radar-index-legendItem p{margin:0}.antd-pro-components-charts-radar-index-radar .antd-pro-components-charts-radar-index-legend .antd-pro-components-charts-radar-index-legendItem h6{margin-top:4px;margin-bottom:0;padding-left:16px;color:rgba(0,0,0,.85);font-size:24px;line-height:32px}.antd-pro-components-charts-radar-index-radar .antd-pro-components-charts-radar-index-legend .antd-pro-components-charts-radar-index-legendItem:after{position:absolute;top:8px;right:0;width:1px;height:40px;background-color:#e8e8e8;content:""}.antd-pro-components-charts-radar-index-radar .antd-pro-components-charts-radar-index-legend>:last-child .antd-pro-components-charts-radar-index-legendItem:after{display:none}.antd-pro-components-charts-radar-index-radar .antd-pro-components-charts-radar-index-legend .antd-pro-components-charts-radar-index-dot{position:relative;top:-1px;display:inline-block;width:6px;height:6px;margin-right:6px;border-radius:6px} 2 | -------------------------------------------------------------------------------- /public/backend/43.3cdfd391.chunk.css: -------------------------------------------------------------------------------- 1 | .antd-pro-components-charts-tag-cloud-index-tagCloud{overflow:hidden}.antd-pro-components-charts-tag-cloud-index-tagCloud canvas{transform-origin:0 0} 2 | -------------------------------------------------------------------------------- /public/backend/44.61c3b9ca.chunk.css: -------------------------------------------------------------------------------- 1 | .antd-pro-components-charts-timeline-chart-index-timelineChart{background:#fff} 2 | -------------------------------------------------------------------------------- /public/backend/44.ad215b2d.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[44],{Q9DM:function(e,t,n){e.exports={timelineChart:"antd-pro-components-charts-timeline-chart-index-timelineChart"}},YWDv:function(e,t,n){"use strict";n.r(t);var a,r,i=n("p0pE"),o=n.n(i),l=n("gWZ8"),c=n.n(l),s=n("2Taf"),y=n.n(s),u=n("vZ4D"),m=n.n(u),p=n("l4Ni"),d=n.n(p),h=n("ujKo"),v=n.n(h),f=n("MhPg"),x=n.n(f),k=n("q1tI"),g=n.n(k),E=n("yP6+"),w=n("cQSq"),b=n.n(w),C=n("RFWI"),M=n("Q9DM"),D=n.n(M),W=(a=Object(C["a"])(),a(r=function(e){function t(){return y()(this,t),d()(this,v()(t).apply(this,arguments))}return x()(t,e),m()(t,[{key:"render",value:function(){var e,t=this.props,n=t.title,a=t.height,r=void 0===a?400:a,i=t.padding,l=void 0===i?[60,20,40,40]:i,s=t.titleMap,y=void 0===s?{y1:"y1",y2:"y2"}:s,u=t.borderWidth,m=void 0===u?2:u,p=t.data,d=Array.isArray(p)?p:[{x:0,y1:0,y2:0}];d.sort(function(e,t){return e.x-t.x}),d[0]&&d[0].y1&&d[0].y2&&(e=Math.max(c()(d).sort(function(e,t){return t.y1-e.y1})[0].y1,c()(d).sort(function(e,t){return t.y2-e.y2})[0].y2));var h=new b.a({state:{start:d[0].x,end:d[d.length-1].x}}),v=h.createView();v.source(d).transform({type:"filter",callback:function(e){var t=e.x;return t<=h.state.end&&t>=h.state.start}}).transform({type:"map",callback:function(e){var t=o()({},e);return t[y.y1]=e.y1,t[y.y2]=e.y2,t}}).transform({type:"fold",fields:[y.y1,y.y2],key:"key",value:"value"});var f={type:"time",tickInterval:36e5,mask:"HH:mm",range:[0,1]},x={x:f,value:{max:e,min:0}};return g.a.createElement("div",{className:D.a.timelineChart,style:{height:r+30}},g.a.createElement("div",null,n&&g.a.createElement("h4",null,n),g.a.createElement(E["Chart"],{height:r,padding:l,data:v,scale:x,forceFit:!0},g.a.createElement(E["Axis"],{name:"x"}),g.a.createElement(E["Tooltip"],null),g.a.createElement(E["Legend"],{name:"key",position:"top"}),g.a.createElement(E["Geom"],{type:"line",position:"x*value",size:m,color:"key"}))))}}]),t}(g.a.Component))||r);t["default"]=W}}]); -------------------------------------------------------------------------------- /public/backend/45.a7ddf820.chunk.css: -------------------------------------------------------------------------------- 1 | .antd-pro-components-charts-water-wave-index-waterWave{position:relative;display:inline-block;transform-origin:left}.antd-pro-components-charts-water-wave-index-waterWave .antd-pro-components-charts-water-wave-index-text{position:absolute;top:32px;left:0;width:100%;text-align:center}.antd-pro-components-charts-water-wave-index-waterWave .antd-pro-components-charts-water-wave-index-text span{color:rgba(0,0,0,.45);font-size:14px;line-height:22px}.antd-pro-components-charts-water-wave-index-waterWave .antd-pro-components-charts-water-wave-index-text h4{color:rgba(0,0,0,.85);font-size:24px;line-height:32px}.antd-pro-components-charts-water-wave-index-waterWave .antd-pro-components-charts-water-wave-index-waterWaveCanvasWrapper{transform:scale(.5);transform-origin:0 0} 2 | -------------------------------------------------------------------------------- /public/backend/46.b6a1e6e7.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[46],{LJjV:function(e,t,n){"use strict";n.r(t);var a,i,o,r,s,l,c=n("2Taf"),d=n.n(c),h=n("vZ4D"),u=n.n(h),p=n("l4Ni"),v=n.n(p),f=n("ujKo"),m=n.n(f),y=n("MhPg"),w=n.n(y),b=n("SQvw"),g=n.n(b),E=n("q1tI"),L=n.n(E),k=n("yP6+"),x=n("fqkP"),z=n.n(x),P=n("UjoV"),j=n.n(P),H=n("RFWI"),R=n("iPxP"),X=n.n(R),A=(a=Object(H["a"])(),i=j()(),o=z()(400),a((l=function(e){function t(){var e,n;d()(this,t);for(var a=arguments.length,i=new Array(a),o=0;o0&&g.a.createElement(b["Chart"],{animate:k,scale:P,height:j,forceFit:o,data:i,padding:w},g.a.createElement(b["Axis"],r()({key:"axis-x",name:"x",label:!1,line:!1,tickLine:!1,grid:!1},v)),g.a.createElement(b["Axis"],r()({key:"axis-y",name:"y",label:!1,line:!1,tickLine:!1,grid:!1},f)),g.a.createElement(b["Tooltip"],{showTitle:!1,crosshairs:!1}),g.a.createElement(b["Geom"],{type:"area",position:"x*y",color:c,tooltip:A,shape:"smooth",style:{fillOpacity:1}}),x?g.a.createElement(b["Geom"],{type:"line",position:"x*y",shape:"smooth",color:d,size:u,tooltip:!1}):g.a.createElement("span",{style:{display:"none"}}))))}}]),t}(g.a.PureComponent))||n);t["default"]=P}}]); -------------------------------------------------------------------------------- /public/backend/49.98935d92.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[49],{gMQw:function(e,t,n){"use strict";n.r(t);var a,i,o=n("2Taf"),r=n.n(o),c=n("vZ4D"),s=n.n(c),l=n("l4Ni"),h=n.n(l),p=n("ujKo"),u=n.n(p),d=n("MhPg"),m=n.n(d),v=n("q1tI"),f=n.n(v),y=n("yP6+"),w=n("RFWI"),g=n("iPxP"),E=n.n(g),F=(a=Object(w["a"])(),a(i=function(e){function t(){return r()(this,t),h()(this,u()(t).apply(this,arguments))}return m()(t,e),s()(t,[{key:"render",value:function(){var e=this.props,t=e.height,n=e.forceFit,a=void 0===n||n,i=e.color,o=void 0===i?"#1890FF":i,r=e.data,c=void 0===r?[]:r,s={x:{type:"cat"},y:{min:0}},l=[36,5,30,5],h=["x*y",function(e,t){return{name:e,value:t}}],p=t+54;return f.a.createElement("div",{className:E.a.miniChart,style:{height:t}},f.a.createElement("div",{className:E.a.chartContent},f.a.createElement(y["Chart"],{scale:s,height:p,forceFit:a,data:c,padding:l},f.a.createElement(y["Tooltip"],{showTitle:!1,crosshairs:!1}),f.a.createElement(y["Geom"],{type:"interval",position:"x*y",color:o,tooltip:h}))))}}]),t}(f.a.Component))||i);t["default"]=F}}]); -------------------------------------------------------------------------------- /public/backend/50.6428f49c.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[50],{CneN:function(e,t,a){"use strict";a.r(t);a("14J3");var o=a("BMrR"),r=(a("jCWc"),a("kPKH")),m=a("q1tI"),l=a.n(m),n=a("Y2fQ"),c=a("ZhIB"),f=a.n(c),i=a("KTCi"),d={xs:24,sm:12,md:12,lg:12,xl:6,style:{marginBottom:24}},s=Object(m["memo"])(function(e){var t=e.loading,a=e.allData;return l.a.createElement(o["a"],{gutter:24},l.a.createElement(r["a"],d,l.a.createElement(i["a"],{bordered:!1,title:Object(n["formatMessage"])({id:"home.count.animate.draft"}),loading:t,total:f()(a.animateDraft).format("0,0"),footer:l.a.createElement(i["b"],{label:Object(n["formatMessage"])({id:"home.count.animate"}),value:f()(a.animateAll).format("0,0")})})),l.a.createElement(r["a"],d,l.a.createElement(i["a"],{bordered:!1,title:Object(n["formatMessage"])({id:"home.count.post.draft"}),loading:t,total:f()(a.postDraft).format("0,0"),footer:l.a.createElement(i["b"],{label:Object(n["formatMessage"])({id:"home.count.post"}),value:f()(a.postAll).format("0,0")})})),l.a.createElement(r["a"],d,l.a.createElement(i["a"],{bordered:!1,title:Object(n["formatMessage"])({id:"home.count.comic.draft"}),loading:t,total:f()(a.reportDraft).format("0,0"),footer:l.a.createElement(i["b"],{label:Object(n["formatMessage"])({id:"home.count.comic"}),value:f()(a.reportAll).format("0,0")})})),l.a.createElement(r["a"],d,l.a.createElement(i["a"],{bordered:!1,title:Object(n["formatMessage"])({id:"home.count.comment.draft"}),loading:t,total:f()(a.commentDraft).format("0,0"),footer:l.a.createElement(i["b"],{label:Object(n["formatMessage"])({id:"home.count.comment"}),value:f()(a.commentAll).format("0,0")})})))});t["default"]=s}}]); -------------------------------------------------------------------------------- /public/backend/53.5cf5ac90.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[53],{"/1a9":function(e,t,a){"use strict";a.r(t);a("IzEo");var n,r,o,c=a("bx4M"),i=(a("R9oj"),a("ECub")),l=(a("+L6B"),a("2/Rp")),s=a("2Taf"),u=a.n(s),m=a("vZ4D"),p=a.n(m),h=a("l4Ni"),d=a.n(h),f=a("ujKo"),y=a.n(f),E=a("MhPg"),b=a.n(E),v=a("q1tI"),w=a.n(v),g=a("MuoO"),k=a("LLXN"),j=a("yP6+"),M=a("cQSq"),O=a.n(M),x=a("+n12"),C=(n=Object(g["connect"])(function(e){var t=e.data;return{data:t}}),n((o=function(e){function t(){var e,a;u()(this,t);for(var n=arguments.length,r=new Array(n),o=0;oQin Video
-------------------------------------------------------------------------------- /public/backend/layouts__UserLayout.5c6c76cb.chunk.css: -------------------------------------------------------------------------------- 1 | .antd-pro-layouts-user-layout-container{display:flex;flex-direction:column;height:100vh;overflow:auto;background:#f0f2f5}.antd-pro-layouts-user-layout-lang{width:100%;height:40px;line-height:44px;text-align:right}.antd-pro-layouts-user-layout-lang .ant-dropdown-trigger{margin-right:24px}.antd-pro-layouts-user-layout-content{flex:1 1;padding:32px 0}@media (min-width:768px){.antd-pro-layouts-user-layout-container{background-image:url("https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg");background-repeat:no-repeat;background-position:center 110px;background-size:100%}.antd-pro-layouts-user-layout-content{padding:32px 0 24px}}.antd-pro-layouts-user-layout-top{text-align:center}.antd-pro-layouts-user-layout-header{height:44px;line-height:44px}.antd-pro-layouts-user-layout-header a{text-decoration:none}.antd-pro-layouts-user-layout-logo{height:44px;margin-right:16px;vertical-align:top}.antd-pro-layouts-user-layout-title{position:relative;top:2px;color:rgba(0,0,0,.85);font-weight:600;font-size:33px;font-family:Avenir,Helvetica Neue,Arial,Helvetica,sans-serif}.antd-pro-layouts-user-layout-desc{margin-top:12px;margin-bottom:40px;color:rgba(0,0,0,.45);font-size:14px} 2 | -------------------------------------------------------------------------------- /public/backend/manifest.json: -------------------------------------------------------------------------------- 1 | {"name":"Qin Video","short_name":"Qin Video","display":"standalone","start_url":"./?utm_source=homescreen","theme_color":"#002140","background_color":"#001529","icons":[{"src":"/backend/icons/icon-192x192.png","sizes":"192x192"},{"src":"/backend/icons/icon-128x128.png","sizes":"128x128"},{"src":"/backend/icons/icon-512x512.png","sizes":"512x512"}]} -------------------------------------------------------------------------------- /public/backend/p__404.52ce823f.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[3],{w2l6:function(e,t,n){"use strict";n.r(t);var a=n("q1tI"),c=n.n(a),p=n("wY1l"),i=n.n(p),o=n("LLXN"),s=n("luV/");t["default"]=function(){return c.a.createElement(s["a"],{type:"404",linkElement:i.a,desc:Object(o["formatMessage"])({id:"app.exception.description.404"}),backText:Object(o["formatMessage"])({id:"app.exception.back"})})}}}]); -------------------------------------------------------------------------------- /public/backend/p__Animate__Category.ebd72d81.chunk.css: -------------------------------------------------------------------------------- 1 | .antd-pro-pages-animate-category-main{display:flex;width:100%;height:100%;padding-top:16px;padding-bottom:16px;overflow:auto;background-color:#fff}.antd-pro-pages-animate-category-main .antd-pro-pages-animate-category-leftmenu{width:160px;text-align:center;border-right:1px solid #e8e8e8}.antd-pro-pages-animate-category-main .antd-pro-pages-animate-category-leftmenu .ant-menu-inline{border:none}.antd-pro-pages-animate-category-main .antd-pro-pages-animate-category-leftmenu .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected{font-weight:700}.antd-pro-pages-animate-category-main .antd-pro-pages-animate-category-right{flex:1 1;padding:8px 40px}.antd-pro-pages-animate-category-main .antd-pro-pages-animate-category-right .antd-pro-pages-animate-category-title{margin-bottom:12px;color:rgba(0,0,0,.85);font-weight:500;font-size:20px;line-height:28px}.antd-pro-pages-animate-category-main .ant-list-split .ant-list-item:last-child{border-bottom:1px solid #e8e8e8}.antd-pro-pages-animate-category-main .ant-list-item{padding-top:14px;padding-bottom:14px}.ant-list-item-meta .taobao{display:block;color:#ff4000;font-size:48px;line-height:48px;border-radius:4px}.ant-list-item-meta .dingding{margin:2px;padding:6px;color:#fff;font-size:32px;line-height:32px;background-color:#2eabff;border-radius:4px}.ant-list-item-meta .alipay{color:#2eabff;font-size:48px;line-height:48px;border-radius:4px}font.strong{color:#52c41a}font.medium{color:#faad14}font.weak{color:#f5222d}@media screen and (max-width:768px){.antd-pro-pages-animate-category-main{flex-direction:column}.antd-pro-pages-animate-category-main .antd-pro-pages-animate-category-leftmenu{width:100%;border:none}.antd-pro-pages-animate-category-main .antd-pro-pages-animate-category-right{padding:40px}} 2 | -------------------------------------------------------------------------------- /public/backend/p__Animate__Create.6db17ede.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[5],{enlI:function(t,a,n){"use strict";n.r(a);n("T2oS");var e,i,o,r=n("W9HT"),s=(n("miYZ"),n("tsqr")),c=n("2Taf"),u=n.n(c),p=n("vZ4D"),l=n.n(p),f=n("l4Ni"),m=n.n(f),d=n("ujKo"),h=n.n(d),y=n("MhPg"),g=n.n(y),v=n("q1tI"),w=n.n(v),b=n("MuoO"),k=n("3a4m"),T=n.n(k),C=n("LLXN"),I=n("x+KT"),M=(e=Object(b["connect"])(function(t){var a=t.animate,n=t.category,e=t.loading;return{animate:a,category:n,loading:e.effects["animate/postAnimate"]}}),e((o=function(t){function a(){var t,n;u()(this,a);for(var e=arguments.length,i=new Array(e),o=0;o400&&(e="horizontal"),window.innerWidth<768&&t>400&&(e="horizontal"),n.setState({mode:e})})};var a=e.match,o=e.location,r={blog:d.a.createElement(y["FormattedMessage"],{id:"menu.blog.category.blog"})},s=o.pathname.replace("".concat(a.path,"/"),"");return n.state={mode:"inline",menuMap:r,selectKey:r[s]?s:"blog"},n}return h()(t,e),r()(t,[{key:"componentDidMount",value:function(){window.addEventListener("resize",this.resize),this.resize()}},{key:"componentWillUnmount",value:function(){window.removeEventListener("resize",this.resize)}},{key:"render",value:function(){var e=this,t=this.props.children,n=this.state,a=n.mode,i=n.selectKey;return d.a.createElement(b["a"],{style:{height:"100%"}},d.a.createElement("div",{className:w.a.main,ref:function(t){e.main=t}},d.a.createElement("div",{className:w.a.leftmenu},d.a.createElement(p["a"],{mode:a,selectedKeys:[i],onClick:this.selectKey},this.getmenu())),d.a.createElement("div",{className:w.a.right},t)))}}],[{key:"getDerivedStateFromProps",value:function(e,t){var n=e.match,a=e.location,i=a.pathname.replace("".concat(n.path,"/"),"");return i=t.menuMap[i]?i:"blog",i!==t.selectKey?{selectKey:i}:null}}]),t}(g["Component"]);t["default"]=E},k4qJ:function(e,t,n){e.exports={main:"antd-pro-pages-blog-category-main",leftmenu:"antd-pro-pages-blog-category-leftmenu",right:"antd-pro-pages-blog-category-right",title:"antd-pro-pages-blog-category-title"}}}]); -------------------------------------------------------------------------------- /public/backend/p__Comic__Category.988c10d6.chunk.css: -------------------------------------------------------------------------------- 1 | .antd-pro-pages-comic-category-main{display:flex;width:100%;height:100%;padding-top:16px;padding-bottom:16px;overflow:auto;background-color:#fff}.antd-pro-pages-comic-category-main .antd-pro-pages-comic-category-leftmenu{width:160px;text-align:center;border-right:1px solid #e8e8e8}.antd-pro-pages-comic-category-main .antd-pro-pages-comic-category-leftmenu .ant-menu-inline{border:none}.antd-pro-pages-comic-category-main .antd-pro-pages-comic-category-leftmenu .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected{font-weight:700}.antd-pro-pages-comic-category-main .antd-pro-pages-comic-category-right{flex:1 1;padding:8px 40px}.antd-pro-pages-comic-category-main .antd-pro-pages-comic-category-right .antd-pro-pages-comic-category-title{margin-bottom:12px;color:rgba(0,0,0,.85);font-weight:500;font-size:20px;line-height:28px}.antd-pro-pages-comic-category-main .ant-list-split .ant-list-item:last-child{border-bottom:1px solid #e8e8e8}.antd-pro-pages-comic-category-main .ant-list-item{padding-top:14px;padding-bottom:14px}.ant-list-item-meta .taobao{display:block;color:#ff4000;font-size:48px;line-height:48px;border-radius:4px}.ant-list-item-meta .dingding{margin:2px;padding:6px;color:#fff;font-size:32px;line-height:32px;background-color:#2eabff;border-radius:4px}.ant-list-item-meta .alipay{color:#2eabff;font-size:48px;line-height:48px;border-radius:4px}font.strong{color:#52c41a}font.medium{color:#faad14}font.weak{color:#f5222d}@media screen and (max-width:768px){.antd-pro-pages-comic-category-main{flex-direction:column}.antd-pro-pages-comic-category-main .antd-pro-pages-comic-category-leftmenu{width:100%;border:none}.antd-pro-pages-comic-category-main .antd-pro-pages-comic-category-right{padding:40px}} 2 | -------------------------------------------------------------------------------- /public/backend/p__Comic__Create.835207cc.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[15],{"+tXX":function(t,n,e){"use strict";e.r(n);e("T2oS");var a,o,c,i=e("W9HT"),r=(e("miYZ"),e("tsqr")),s=e("2Taf"),u=e.n(s),l=e("vZ4D"),p=e.n(l),m=e("l4Ni"),d=e.n(m),f=e("ujKo"),h=e.n(f),y=e("MhPg"),g=e.n(y),v=e("q1tI"),w=e.n(v),b=e("MuoO"),C=e("3a4m"),k=e.n(C),M=e("LLXN"),S=e("+tEW"),T=(a=Object(b["connect"])(function(t){var n=t.comic,e=t.category,a=t.loading;return{comic:n,category:e,loading:a.effects["comic/postComic"]}}),a((c=function(t){function n(){var t,e;u()(this,n);for(var a=arguments.length,o=new Array(a),c=0;c400&&(e="horizontal"),window.innerWidth<768&&t>400&&(e="horizontal"),n.setState({mode:e})})};var a=e.match,o=e.location,r={pkind:f.a.createElement(y["FormattedMessage"],{id:"menu.animate.category.kind"}),ptag:f.a.createElement(y["FormattedMessage"],{id:"menu.animate.category.tag"})},s=o.pathname.replace("".concat(a.path,"/"),"");return n.state={mode:"inline",menuMap:r,selectKey:r[s]?s:"pkind"},n}return p()(t,e),r()(t,[{key:"componentDidMount",value:function(){window.addEventListener("resize",this.resize),this.resize()}},{key:"componentWillUnmount",value:function(){window.removeEventListener("resize",this.resize)}},{key:"render",value:function(){var e=this,t=this.props.children,n=this.state,a=n.mode,i=n.selectKey;return f.a.createElement(k["a"],null,f.a.createElement("div",{className:K.a.main,ref:function(t){e.main=t}},f.a.createElement("div",{className:K.a.leftmenu},f.a.createElement(d["a"],{mode:a,selectedKeys:[i],onClick:this.selectKey},this.getmenu())),f.a.createElement("div",{className:K.a.right},t)))}}],[{key:"getDerivedStateFromProps",value:function(e,t){var n=e.match,a=e.location,i=a.pathname.replace("".concat(n.path,"/"),"");return i=t.menuMap[i]?i:"pkind",i!==t.selectKey?{selectKey:i}:null}}]),t}(h["Component"]);t["default"]=z}}]); -------------------------------------------------------------------------------- /public/backend/p__Post__Create.cb8e1806.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[26],{V8qc:function(t,n,e){"use strict";e.r(n);e("T2oS");var o,a,s,i=e("W9HT"),c=(e("miYZ"),e("tsqr")),r=e("2Taf"),p=e.n(r),u=e("vZ4D"),l=e.n(u),d=e("l4Ni"),f=e.n(d),h=e("ujKo"),g=e.n(h),m=e("MhPg"),v=e.n(m),y=e("q1tI"),w=e.n(y),b=e("MuoO"),k=e("3a4m"),M=e.n(k),j=e("LLXN"),q=e("gegZ"),O=(o=Object(b["connect"])(function(t){var n=t.post,e=t.category,o=t.loading;return{post:n,category:e,loading:o.effects["post/postPost"]}}),o((s=function(t){function n(){var t,e;p()(this,n);for(var o=arguments.length,a=new Array(o),s=0;s{return!s.some(s=>t.headers.has(s)&&e.headers.has(s))||s.every(s=>{const a=t.headers.has(s)===e.headers.has(s),n=t.headers.get(s)===e.headers.get(s);return a&&n})};var s={CACHE_UPDATED:"CACHE_UPDATED"};const a=(t,e,a,n)=>{"BroadcastChannel"in self&&t&&t.postMessage({type:s.CACHE_UPDATED,meta:n,payload:{cacheName:e,updatedUrl:a}})};class n{constructor(t,{headersToCheck:e,source:s}={}){this.t=t,this.e=e||["content-length","etag","last-modified"],this.s=s||"workbox-broadcast-cache-update"}a(){return"BroadcastChannel"in self&&!this.n&&(this.n=new BroadcastChannel(this.t)),this.n}notifyIfUpdated(t,s,n,c){e(t,s,this.e)||a(this.a(),c,n,this.s)}}return t.BroadcastCacheUpdate=n,t.Plugin=class{constructor(t,e){this.c=new n(t,e)}cacheDidUpdate({cacheName:t,oldResponse:e,newResponse:s,request:a}){e&&this.c.notifyIfUpdated(e,s,a.url,t)}},t.broadcastUpdate=a,t.messageTypes=s,t}({}); 2 | 3 | //# sourceMappingURL=workbox-broadcast-cache-update.prod.js.map 4 | -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-broadcast-cache-update.prod.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"names":[],"mappings":"","sources":["packages/workbox-broadcast-cache-update/browser.mjs"],"sourcesContent":["this.workbox=this.workbox||{},this.workbox.broadcastUpdate=function(t){\"use strict\";try{self.workbox.v[\"workbox:broadcast-cache-update:3.6.3\"]=1}catch(t){}const e=(t,e,s)=>{return!s.some(s=>t.headers.has(s)&&e.headers.has(s))||s.every(s=>{const a=t.headers.has(s)===e.headers.has(s),n=t.headers.get(s)===e.headers.get(s);return a&&n})};var s={CACHE_UPDATED:\"CACHE_UPDATED\"};const a=(t,e,a,n)=>{\"BroadcastChannel\"in self&&t&&t.postMessage({type:s.CACHE_UPDATED,meta:n,payload:{cacheName:e,updatedUrl:a}})};class n{constructor(t,{headersToCheck:e,source:s}={}){this.t=t,this.e=e||[\"content-length\",\"etag\",\"last-modified\"],this.s=s||\"workbox-broadcast-cache-update\"}a(){return\"BroadcastChannel\"in self&&!this.n&&(this.n=new BroadcastChannel(this.t)),this.n}notifyIfUpdated(t,s,n,c){e(t,s,this.e)||a(this.a(),c,n,this.s)}}return t.BroadcastCacheUpdate=n,t.Plugin=class{constructor(t,e){this.c=new n(t,e)}cacheDidUpdate({cacheName:t,oldResponse:e,newResponse:s,request:a}){e&&this.c.notifyIfUpdated(e,s,a.url,t)}},t.broadcastUpdate=a,t.messageTypes=s,t}({});\n"],"file":"workbox-broadcast-cache-update.prod.js"} -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-cacheable-response.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.cacheableResponse=function(t){"use strict";try{self.workbox.v["workbox:cacheable-response:3.6.3"]=1}catch(t){}class s{constructor(t={}){this.t=t.statuses,this.s=t.headers}isResponseCacheable(t){let s=!0;return this.t&&(s=this.t.includes(t.status)),this.s&&s&&(s=Object.keys(this.s).some(s=>t.headers.get(s)===this.s[s])),s}}return t.CacheableResponse=s,t.Plugin=class{constructor(t){this.e=new s(t)}cacheWillUpdate({response:t}){return this.e.isResponseCacheable(t)?t:null}},t}({}); 2 | 3 | //# sourceMappingURL=workbox-cacheable-response.prod.js.map 4 | -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-cacheable-response.prod.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"names":[],"mappings":"","sources":["packages/workbox-cacheable-response/browser.mjs"],"sourcesContent":["this.workbox=this.workbox||{},this.workbox.cacheableResponse=function(t){\"use strict\";try{self.workbox.v[\"workbox:cacheable-response:3.6.3\"]=1}catch(t){}class s{constructor(t={}){this.t=t.statuses,this.s=t.headers}isResponseCacheable(t){let s=!0;return this.t&&(s=this.t.includes(t.status)),this.s&&s&&(s=Object.keys(this.s).some(s=>t.headers.get(s)===this.s[s])),s}}return t.CacheableResponse=s,t.Plugin=class{constructor(t){this.e=new s(t)}cacheWillUpdate({response:t}){return this.e.isResponseCacheable(t)?t:null}},t}({});\n"],"file":"workbox-cacheable-response.prod.js"} -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-google-analytics.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.googleAnalytics=function(e,n,t,o,r,c,s){"use strict";try{self.workbox.v["workbox:google-analytics:3.6.3"]=1}catch(e){}const l=/^\/(\w+\/)?collect/,i=(a=babelHelpers.asyncToGenerator(function*(e){return yield new Promise(function(n,t){const o=new FileReader;o.onloadend=function(){return n(o.result)},o.onerror=function(){return t(o.error)},o.readAsText(e)})}),function(e){return a.apply(this,arguments)});var a;const w=e=>(u=babelHelpers.asyncToGenerator(function*(n){let t,{url:o,requestInit:r,timestamp:c}=n;if(o=new URL(o),r.body){const e=r.body instanceof Blob?yield i(r.body):r.body;t=new URLSearchParams(e)}else t=o.searchParams;const s=c-(Number(t.get("qt"))||0),l=Date.now()-s;if(t.set("qt",l),e.parameterOverrides)for(const n of Object.keys(e.parameterOverrides)){const o=e.parameterOverrides[n];t.set(n,o)}"function"==typeof e.hitFilter&&e.hitFilter.call(null,t),r.body=t.toString(),r.method="POST",r.mode="cors",r.credentials="omit",r.headers={"Content-Type":"text/plain"},n.url=`${o.origin}${o.pathname}`}),function(e){return u.apply(this,arguments)});var u;return e.initialize=((e={})=>{const i=t.cacheNames.getGoogleAnalyticsName(e.cacheName),a=new n.Plugin("workbox-google-analytics",{maxRetentionTime:2880,callbacks:{requestWillReplay:w(e)}}),u=[(e=>{const n=new c.NetworkFirst({cacheName:e});return new o.Route(({url:e})=>"www.google-analytics.com"===e.hostname&&"/analytics.js"===e.pathname,n,"GET")})(i),(e=>{const n=new c.NetworkFirst({cacheName:e});return new o.Route(({url:e})=>"www.googletagmanager.com"===e.hostname&&"/gtag/js"===e.pathname,n,"GET")})(i),...(e=>{const n=({url:e})=>"www.google-analytics.com"===e.hostname&&l.test(e.pathname),t=new s.NetworkOnly({plugins:[e]});return[new o.Route(n,t,"GET"),new o.Route(n,t,"POST")]})(a)],f=new r.Router;for(const e of u)f.registerRoute(e);self.addEventListener("fetch",e=>{const n=f.handleRequest(e);n&&e.respondWith(n)})}),e}({},workbox.backgroundSync,workbox.core._private,workbox.routing,workbox.routing,workbox.strategies,workbox.strategies); 2 | 3 | //# sourceMappingURL=workbox-google-analytics.prod.js.map 4 | -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-google-analytics.prod.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"names":[],"mappings":"","sources":["packages/workbox-google-analytics/browser.mjs"],"sourcesContent":["this.workbox=this.workbox||{},this.workbox.googleAnalytics=function(e,n,t,o,r,c,s){\"use strict\";try{self.workbox.v[\"workbox:google-analytics:3.6.3\"]=1}catch(e){}const l=/^\\/(\\w+\\/)?collect/,i=(a=babelHelpers.asyncToGenerator(function*(e){return yield new Promise(function(n,t){const o=new FileReader;o.onloadend=function(){return n(o.result)},o.onerror=function(){return t(o.error)},o.readAsText(e)})}),function(e){return a.apply(this,arguments)});var a;const w=e=>(u=babelHelpers.asyncToGenerator(function*(n){let t,{url:o,requestInit:r,timestamp:c}=n;if(o=new URL(o),r.body){const e=r.body instanceof Blob?yield i(r.body):r.body;t=new URLSearchParams(e)}else t=o.searchParams;const s=c-(Number(t.get(\"qt\"))||0),l=Date.now()-s;if(t.set(\"qt\",l),e.parameterOverrides)for(const n of Object.keys(e.parameterOverrides)){const o=e.parameterOverrides[n];t.set(n,o)}\"function\"==typeof e.hitFilter&&e.hitFilter.call(null,t),r.body=t.toString(),r.method=\"POST\",r.mode=\"cors\",r.credentials=\"omit\",r.headers={\"Content-Type\":\"text/plain\"},n.url=`${o.origin}${o.pathname}`}),function(e){return u.apply(this,arguments)});var u;return e.initialize=((e={})=>{const i=t.cacheNames.getGoogleAnalyticsName(e.cacheName),a=new n.Plugin(\"workbox-google-analytics\",{maxRetentionTime:2880,callbacks:{requestWillReplay:w(e)}}),u=[(e=>{const n=new c.NetworkFirst({cacheName:e});return new o.Route(({url:e})=>\"www.google-analytics.com\"===e.hostname&&\"/analytics.js\"===e.pathname,n,\"GET\")})(i),(e=>{const n=new c.NetworkFirst({cacheName:e});return new o.Route(({url:e})=>\"www.googletagmanager.com\"===e.hostname&&\"/gtag/js\"===e.pathname,n,\"GET\")})(i),...(e=>{const n=({url:e})=>\"www.google-analytics.com\"===e.hostname&&l.test(e.pathname),t=new s.NetworkOnly({plugins:[e]});return[new o.Route(n,t,\"GET\"),new o.Route(n,t,\"POST\")]})(a)],f=new r.Router;for(const e of u)f.registerRoute(e);self.addEventListener(\"fetch\",e=>{const n=f.handleRequest(e);n&&e.respondWith(n)})}),e}({},workbox.backgroundSync,workbox.core._private,workbox.routing,workbox.routing,workbox.strategies,workbox.strategies);\n"],"file":"workbox-google-analytics.prod.js"} -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-navigation-preload.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.navigationPreload=function(t){"use strict";try{self.workbox.v["workbox:navigation-preload:3.6.3"]=1}catch(t){}function e(){return Boolean(self.registration&&self.registration.navigationPreload)}return t.disable=function(){e()&&self.addEventListener("activate",t=>{t.waitUntil(self.registration.navigationPreload.disable().then(()=>{}))})},t.enable=function(t){e()&&self.addEventListener("activate",e=>{e.waitUntil(self.registration.navigationPreload.enable().then(()=>{t&&self.registration.navigationPreload.setHeaderValue(t)}))})},t.isSupported=e,t}({}); 2 | 3 | //# sourceMappingURL=workbox-navigation-preload.prod.js.map 4 | -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-navigation-preload.prod.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"names":[],"mappings":"","sources":["packages/workbox-navigation-preload/browser.mjs"],"sourcesContent":["this.workbox=this.workbox||{},this.workbox.navigationPreload=function(t){\"use strict\";try{self.workbox.v[\"workbox:navigation-preload:3.6.3\"]=1}catch(t){}function e(){return Boolean(self.registration&&self.registration.navigationPreload)}return t.disable=function(){e()&&self.addEventListener(\"activate\",t=>{t.waitUntil(self.registration.navigationPreload.disable().then(()=>{}))})},t.enable=function(t){e()&&self.addEventListener(\"activate\",e=>{e.waitUntil(self.registration.navigationPreload.enable().then(()=>{t&&self.registration.navigationPreload.setHeaderValue(t)}))})},t.isSupported=e,t}({});\n"],"file":"workbox-navigation-preload.prod.js"} -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-range-requests.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.rangeRequests=function(e,n){"use strict";try{self.workbox.v["workbox:range-requests:3.6.3"]=1}catch(e){}let t=(r=babelHelpers.asyncToGenerator(function*(e,t){try{const r=e.headers.get("range");if(!r)throw new n.WorkboxError("no-range-header");const s=function(e){const t=e.trim().toLowerCase();if(!t.startsWith("bytes="))throw new n.WorkboxError("unit-must-be-bytes",{normalizedRangeHeader:t});if(t.includes(","))throw new n.WorkboxError("single-range-only",{normalizedRangeHeader:t});const r=/(\d*)-(\d*)/.exec(t);if(null===r||!r[1]&&!r[2])throw new n.WorkboxError("invalid-range-values",{normalizedRangeHeader:t});return{start:""===r[1]?null:Number(r[1]),end:""===r[2]?null:Number(r[2])}}(r),a=yield t.blob(),i=function(e,t,r){const s=e.size;if(r>s||t<0)throw new n.WorkboxError("range-not-satisfiable",{size:s,end:r,start:t});let a,i;return null===t?(a=s-r,i=s):null===r?(a=t,i=s):(a=t,i=r+1),{start:a,end:i}}(a,s.start,s.end),l=a.slice(i.start,i.end),o=l.size,u=new Response(l,{status:206,statusText:"Partial Content",headers:t.headers});return u.headers.set("Content-Length",o),u.headers.set("Content-Range",`bytes ${i.start}-${i.end-1}/`+a.size),u}catch(e){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}),function(e,n){return r.apply(this,arguments)});var r;return e.createPartialResponse=t,e.Plugin=class{cachedResponseWillBeUsed({request:e,cachedResponse:n}){return babelHelpers.asyncToGenerator(function*(){return n&&e.headers.has("range")?yield t(e,n):n})()}},e}({},workbox.core._private); 2 | 3 | //# sourceMappingURL=workbox-range-requests.prod.js.map 4 | -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-range-requests.prod.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"names":[],"mappings":"","sources":["packages/workbox-range-requests/browser.mjs"],"sourcesContent":["this.workbox=this.workbox||{},this.workbox.rangeRequests=function(e,n){\"use strict\";try{self.workbox.v[\"workbox:range-requests:3.6.3\"]=1}catch(e){}let t=(r=babelHelpers.asyncToGenerator(function*(e,t){try{const r=e.headers.get(\"range\");if(!r)throw new n.WorkboxError(\"no-range-header\");const s=function(e){const t=e.trim().toLowerCase();if(!t.startsWith(\"bytes=\"))throw new n.WorkboxError(\"unit-must-be-bytes\",{normalizedRangeHeader:t});if(t.includes(\",\"))throw new n.WorkboxError(\"single-range-only\",{normalizedRangeHeader:t});const r=/(\\d*)-(\\d*)/.exec(t);if(null===r||!r[1]&&!r[2])throw new n.WorkboxError(\"invalid-range-values\",{normalizedRangeHeader:t});return{start:\"\"===r[1]?null:Number(r[1]),end:\"\"===r[2]?null:Number(r[2])}}(r),a=yield t.blob(),i=function(e,t,r){const s=e.size;if(r>s||t<0)throw new n.WorkboxError(\"range-not-satisfiable\",{size:s,end:r,start:t});let a,i;return null===t?(a=s-r,i=s):null===r?(a=t,i=s):(a=t,i=r+1),{start:a,end:i}}(a,s.start,s.end),l=a.slice(i.start,i.end),o=l.size,u=new Response(l,{status:206,statusText:\"Partial Content\",headers:t.headers});return u.headers.set(\"Content-Length\",o),u.headers.set(\"Content-Range\",`bytes ${i.start}-${i.end-1}/`+a.size),u}catch(e){return new Response(\"\",{status:416,statusText:\"Range Not Satisfiable\"})}}),function(e,n){return r.apply(this,arguments)});var r;return e.createPartialResponse=t,e.Plugin=class{cachedResponseWillBeUsed({request:e,cachedResponse:n}){return babelHelpers.asyncToGenerator(function*(){return n&&e.headers.has(\"range\")?yield t(e,n):n})()}},e}({},workbox.core._private);\n"],"file":"workbox-range-requests.prod.js"} -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-streams.prod.js: -------------------------------------------------------------------------------- 1 | this.workbox=this.workbox||{},this.workbox.streams=function(e){"use strict";try{self.workbox.v["workbox:streams:3.6.3"]=1}catch(e){}function n(e){const n=e.map(e=>Promise.resolve(e).then(e=>(e=e).body&&e.body.getReader?e.body.getReader():e.getReader?e.getReader():new Response(e).body.getReader()));var t;let r,s;let o=0;return{done:new Promise((e,n)=>{r=e,s=n}),stream:new ReadableStream({pull(e){return n[o].then(e=>e.read()).then(t=>{if(t.done)return++o>=n.length?(e.close(),void r()):this.pull(e);e.enqueue(t.value)}).catch(e=>{throw s(e),e})},cancel(){r()}})}}function t(e={}){const n=new Headers(e);return n.has("content-type")||n.set("content-type","text/html"),n}function r(e,r){const{done:s,stream:o}=n(e),u=t(r);return{done:s,response:new Response(o,{headers:u})}}let s=void 0;function o(){if(void 0===s)try{new ReadableStream({start(){}}),s=!0}catch(e){s=!1}return s}return e.concatenate=n,e.concatenateToResponse=r,e.isSupported=o,e.strategy=function(e,n){return s=babelHelpers.asyncToGenerator(function*({event:s,url:u,params:i}){if(o()){const{done:t,response:o}=r(e.map(function(e){return e({event:s,url:u,params:i})}),n);return s.waitUntil(t),o}const c=yield Promise.all(e.map(function(e){return e({event:s,url:u,params:i})}).map((a=babelHelpers.asyncToGenerator(function*(e){const n=yield e;return n instanceof Response?n.blob():n}),function(e){return a.apply(this,arguments)})));var a;const l=t(n);return new Response(new Blob(c),{headers:l})}),function(e){return s.apply(this,arguments)};var s},e}({}); 2 | 3 | //# sourceMappingURL=workbox-streams.prod.js.map 4 | -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-streams.prod.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"names":[],"mappings":"","sources":["packages/workbox-streams/browser.mjs"],"sourcesContent":["this.workbox=this.workbox||{},this.workbox.streams=function(e){\"use strict\";try{self.workbox.v[\"workbox:streams:3.6.3\"]=1}catch(e){}function n(e){const n=e.map(e=>Promise.resolve(e).then(e=>(e=e).body&&e.body.getReader?e.body.getReader():e.getReader?e.getReader():new Response(e).body.getReader()));var t;let r,s;let o=0;return{done:new Promise((e,n)=>{r=e,s=n}),stream:new ReadableStream({pull(e){return n[o].then(e=>e.read()).then(t=>{if(t.done)return++o>=n.length?(e.close(),void r()):this.pull(e);e.enqueue(t.value)}).catch(e=>{throw s(e),e})},cancel(){r()}})}}function t(e={}){const n=new Headers(e);return n.has(\"content-type\")||n.set(\"content-type\",\"text/html\"),n}function r(e,r){const{done:s,stream:o}=n(e),u=t(r);return{done:s,response:new Response(o,{headers:u})}}let s=void 0;function o(){if(void 0===s)try{new ReadableStream({start(){}}),s=!0}catch(e){s=!1}return s}return e.concatenate=n,e.concatenateToResponse=r,e.isSupported=o,e.strategy=function(e,n){return s=babelHelpers.asyncToGenerator(function*({event:s,url:u,params:i}){if(o()){const{done:t,response:o}=r(e.map(function(e){return e({event:s,url:u,params:i})}),n);return s.waitUntil(t),o}const c=yield Promise.all(e.map(function(e){return e({event:s,url:u,params:i})}).map((a=babelHelpers.asyncToGenerator(function*(e){const n=yield e;return n instanceof Response?n.blob():n}),function(e){return a.apply(this,arguments)})));var a;const l=t(n);return new Response(new Blob(c),{headers:l})}),function(e){return s.apply(this,arguments)};var s},e}({});\n"],"file":"workbox-streams.prod.js"} -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-sw.js: -------------------------------------------------------------------------------- 1 | var workbox=function(){"use strict";try{self.workbox.v["workbox:sw:3.6.3"]=1}catch(t){}const t="https://storage.googleapis.com/workbox-cdn/releases/3.6.3",e={backgroundSync:"background-sync",broadcastUpdate:"broadcast-cache-update",cacheableResponse:"cacheable-response",core:"core",expiration:"cache-expiration",googleAnalytics:"google-analytics",navigationPreload:"navigation-preload",precaching:"precaching",rangeRequests:"range-requests",routing:"routing",strategies:"strategies",streams:"streams"};return new class{constructor(){return this.v={},this.t={debug:"localhost"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.e=this.t.debug?"dev":"prod",this.s=!1,new Proxy(this,{get(t,s){if(t[s])return t[s];const o=e[s];return o&&t.loadModule(`workbox-${o}`),t[s]}})}setConfig(t={}){if(this.s)throw new Error("Config must be set before accessing workbox.* modules");Object.assign(this.t,t),this.e=this.t.debug?"dev":"prod"}skipWaiting(){self.addEventListener("install",()=>self.skipWaiting())}clientsClaim(){self.addEventListener("activate",()=>self.clients.claim())}loadModule(t){const e=this.o(t);try{importScripts(e),this.s=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}o(e){if(this.t.modulePathCb)return this.t.modulePathCb(e,this.t.debug);let s=[t];const o=`${e}.${this.e}.js`,r=this.t.modulePathPrefix;return r&&""===(s=r.split("/"))[s.length-1]&&s.splice(s.length-1,1),s.push(o),s.join("/")}}}(); 2 | 3 | //# sourceMappingURL=workbox-sw.js.map 4 | -------------------------------------------------------------------------------- /public/backend/workbox-v3.6.3/workbox-sw.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"names":[],"mappings":"","sources":["packages/workbox-sw/browser.mjs"],"sourcesContent":["var workbox=function(){\"use strict\";try{self.workbox.v[\"workbox:sw:3.6.3\"]=1}catch(t){}const t=\"https://storage.googleapis.com/workbox-cdn/releases/3.6.3\",e={backgroundSync:\"background-sync\",broadcastUpdate:\"broadcast-cache-update\",cacheableResponse:\"cacheable-response\",core:\"core\",expiration:\"cache-expiration\",googleAnalytics:\"google-analytics\",navigationPreload:\"navigation-preload\",precaching:\"precaching\",rangeRequests:\"range-requests\",routing:\"routing\",strategies:\"strategies\",streams:\"streams\"};return new class{constructor(){return this.v={},this.t={debug:\"localhost\"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.e=this.t.debug?\"dev\":\"prod\",this.s=!1,new Proxy(this,{get(t,s){if(t[s])return t[s];const o=e[s];return o&&t.loadModule(`workbox-${o}`),t[s]}})}setConfig(t={}){if(this.s)throw new Error(\"Config must be set before accessing workbox.* modules\");Object.assign(this.t,t),this.e=this.t.debug?\"dev\":\"prod\"}skipWaiting(){self.addEventListener(\"install\",()=>self.skipWaiting())}clientsClaim(){self.addEventListener(\"activate\",()=>self.clients.claim())}loadModule(t){const e=this.o(t);try{importScripts(e),this.s=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}o(e){if(this.t.modulePathCb)return this.t.modulePathCb(e,this.t.debug);let s=[t];const o=`${e}.${this.e}.js`,r=this.t.modulePathPrefix;return r&&\"\"===(s=r.split(\"/\"))[s.length-1]&&s.splice(s.length-1,1),s.push(o),s.join(\"/\")}}}();\n"],"file":"workbox-sw.js"} -------------------------------------------------------------------------------- /public/comic/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/comic/.gitkeep -------------------------------------------------------------------------------- /public/comic/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/comic/favicon.ico -------------------------------------------------------------------------------- /public/comic/index.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /public/comic/p__index.2bb95199.chunk.css: -------------------------------------------------------------------------------- 1 | .index___28nWn{min-height:calc(100vh - 260px);padding-top:60px}.index___28nWn .header___2ZAQi{width:100%;height:360px;margin-bottom:15px;background-size:cover;background-position:50%;display:flex;justify-content:flex-end;align-items:center}.index___28nWn .header___2ZAQi .title___2cyli{min-width:35%;color:hsla(0,0%,100%,.8)}.index___28nWn .header___2ZAQi .title___2cyli span{display:block;font-size:36px;margin:40px 0 20px}.index___28nWn .header___2ZAQi .title___2cyli p{font-size:20px;margin-bottom:0} 2 | -------------------------------------------------------------------------------- /public/comic/p__new.db132284.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[5],{lMKa:function(e,t,a){"use strict";a.r(t);a("IzEo");var n=a("bx4M"),o=a("mrSG"),c=a("q1tI"),i=a.n(c),r=a("MuoO"),s=a("L6Kr"),d=a("zqYd"),m=a("LLXN"),p=class extends c["Component"]{constructor(){super(...arguments),this.state={cate:"newIndexNewComic"},this.initData=(()=>{var e=this.props.dispatch;e({type:"comic/queryList",payload:{query:{update:!0,size:100,page:1},addon:{type:"newIndexNewComic"}}})})}componentDidMount(){this.initData()}render(){var e=this.props.comic,t=e.newIndexNewComic.list||[];return i.a.createElement("div",{style:{paddingBottom:"20px"}},i.a.createElement("div",{className:"container"},i.a.createElement(s["a"],{img:window.config.newComic,span:Object(m["formatMessage"])({id:"header.newComic"})}),i.a.createElement(n["a"],{bordered:!1},i.a.createElement(d["b"],{list:t}))))}};p=Object(o["a"])([Object(r["connect"])(e=>{var t=e.comic,a=e.category,n=e.loading;return{comic:t,category:a,loading:n.models.comic}})],p),t["default"]=p}}]); -------------------------------------------------------------------------------- /public/comic/static/level.367681e1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/comic/static/level.367681e1.png -------------------------------------------------------------------------------- /public/img/animate/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/img/animate/.gitkeep -------------------------------------------------------------------------------- /public/img/avatar/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/img/avatar/.gitkeep -------------------------------------------------------------------------------- /public/img/background/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/img/background/.gitkeep -------------------------------------------------------------------------------- /public/img/blog/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/img/blog/.gitkeep -------------------------------------------------------------------------------- /public/img/comic/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/img/comic/.gitkeep -------------------------------------------------------------------------------- /public/img/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/img/config/.gitkeep -------------------------------------------------------------------------------- /public/img/others/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/img/others/.gitkeep -------------------------------------------------------------------------------- /public/img/post/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/img/post/.gitkeep -------------------------------------------------------------------------------- /public/mobile/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/mobile/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/mobile/favicon.ico -------------------------------------------------------------------------------- /public/mobile/index.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /public/mobile/p__allPost.6555837d.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[4],{"9JuW":function(t,e,a){"use strict";a.r(e);a("IzEo");var s=a("bx4M"),i=a("p0pE"),r=a.n(i),o=a("mrSG"),n=a("q1tI"),l=a.n(n),p=a("MuoO"),y=a("mOtZ"),c=a("WqWh"),u=a("gmfD"),d=a("Qp6+"),h=a("nLTe"),g=a("3a4m"),m=a.n(g),v=class extends n["Component"]{constructor(t){super(t),this.initData=(t=>{var e=t.query,a=void 0===e?this.state.query:e,s=t.filter,i=void 0===s?this.state.filter:s,o=this.props.dispatch;o({type:"post/queryList",payload:{query:r()({},a,i),addon:{type:"allPost"}}}),m.a.replace({pathname:"/allPost",query:r()({},a,i)}),window.scrollTo(0,0)}),this.initCategory=(()=>{var t=this.props.dispatch;t({type:"category/queryList",payload:{params:{type:"post"}}})}),this.listQuery=(t=>{var e=this.state.query,a=r()({},e,t);this.setState({query:a},()=>{this.initData({})})}),this.filterQuery=(t=>{var e=this.state.filter,a=r()({},e,t);this.setState({filter:a,query:r()({},this.state.query,{page:1})},()=>{this.initData({})})}),this.toggleList=(t=>{this.setState({list:t})});var e=t.location.query;this.state={cate:"allPost",query:{sortBy:e.sortBy||"updatedAt",sortOrder:e.sortOrder||-1,size:e.size?parseInt(e.size):21,page:e.page?parseInt(e.page):1},filter:{area:e.area||"",year:e.year||"",kind:e.kind||"",tag:e.tag||""},list:!!e.list&&"true"===e.list}}componentDidMount(){this.initData({}),this.initCategory()}render(){var t=this.state,e=t.cate,a=t.query,i=t.filter,o=(t.list,this.props),n=o.post,p=o.category,g=o.loadingPost,m=o.location.pathname,v=n[e]?n[e].list:[],q=n[e]?n[e].total:0;return l.a.createElement("div",null,l.a.createElement(h["a"],{active:m}),l.a.createElement("div",{className:"container"},l.a.createElement(d["a"],{value:i,onChange:this.filterQuery,list:p.all,type:"p"}),l.a.createElement(s["a"],{bordered:!1,style:{marginBottom:"20px",padding:"0",margin:"15px 0"},loading:g},l.a.createElement(y["a"],{value:a,onChange:this.listQuery,type:"post"}),l.a.createElement(u["a"],{list:v}),l.a.createElement(c["a"],{value:r()({},a,{total:q}),onChange:this.listQuery}))))}};v=Object(o["a"])([Object(p["connect"])(t=>{var e=t.post,a=t.category,s=t.loading;return{post:e,category:a,loadingPost:s.models.post,loadingCategory:s.models.category}})],v),e["default"]=v}}]); -------------------------------------------------------------------------------- /public/mobile/p__auth__verify.9d1f2216.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[10],{yJjE:function(t,e,n){"use strict";n.r(e);n("T2oS");var a=n("W9HT"),r=n("d6i3"),s=n.n(r),i=(n("miYZ"),n("tsqr")),o=n("1l/V"),c=n.n(o),u=n("fFZ8"),d=n.n(u),l=n("q1tI"),p=n.n(l),f=n("vOnD"),h=n("LLXN"),m=n("Gwa3"),x=n("3a4m"),g=n.n(x),w=n("zz3R");function v(){var t=d()(["\n width: 100%;\n padding: 60px 40px;\n margin-top: 60px;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n"]);return v=function(){return t},t}var k=f["a"].div(v());class y extends l["Component"]{constructor(){var t;super(...arguments),t=this,this.state={loading:!1,result:!1},this.initData=c()(s.a.mark(function e(){var n,a,r;return s.a.wrap(function(e){while(1)switch(e.prev=e.next){case 0:if(n=t.props.location,a=n.query.token,!a){e.next=11;break}return t.setState({loading:!0}),e.next=6,w["a"].authVerifyCode({data:{token:a}});case 6:r=e.sent,r&&i["a"].success,t.setState({loading:!1,result:!!r}),e.next=12;break;case 11:g.a.replace({pathname:"/auth/login"});case 12:case"end":return e.stop()}},e)}))}componentDidMount(){this.initData()}render(){var t=this.state,e=t.loading,n=t.result;return p.a.createElement(k,{color:this.context},p.a.createElement(a["a"],{size:"large",spinning:e},Object(h["formatMessage"])({id:n?"user.status.code.success":"user.status.code.error"})))}}y.contextType=m["a"],e["default"]=y}}]); -------------------------------------------------------------------------------- /public/mobile/p__cate__comic___cate.95b9faab.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[12],{O5Ex:function(t,e,a){"use strict";a.r(e);a("IzEo");var n=a("bx4M"),r=a("p0pE"),i=a.n(r),s=a("fFZ8"),c=a.n(s),o=a("mrSG"),p=a("q1tI"),l=a.n(p),m=a("MuoO"),u=a("mOtZ"),h=a("WqWh"),d=a("faBS"),y=a("/88p"),g=a("vOnD"),v=a("3a4m"),b=a.n(v);function q(){var t=c()(["\n margin-top: 40px;\n\n .header {\n display: inline-block;\n padding: 0 10px;\n margin: 0 15px;\n height: 40px;\n line-height: 40px;\n border-bottom: solid 2px black;\n font-size: 15px;\n }\n"]);return q=function(){return t},t}var E=g["a"].div(q()),x=class extends p["Component"]{constructor(t){var e;super(t),e=this,this.initData=function(t){var a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:e.state.query,n=e.props,r=n.dispatch,s=n.location.pathname,c=Object(d["a"])({cate:t,query:a,type:"kind"});r({type:"comic/queryList",payload:{query:c,addon:{type:t}}}),b.a.push({pathname:s,query:i()({},a)}),e.setState({cate:t})},this.listQuery=(t=>{var e=this.state,a=e.query,n=e.cate,r=i()({},a,t);this.setState({query:r}),this.initData(n,r)});var a=t.location.query;this.state={cate:"",query:{key:a.key||"time",sort:!!a.sort&&"true"===a.sort,list:!a.list||"true"===a.list,size:21,page:a.page?parseInt(a.page):1}}}static getDerivedStateFromProps(t,e){var a=t.match.params.cate;return a!==e.cate?i()({},e,{cate:a}):null}componentDidMount(){var t=this.props.match,e=t.params.cate;this.initData(e)}componentDidUpdate(t,e){var a=this.props.match.params.cate,n=t.match.params.cate;n!==a&&this.initData(a)}render(){var t=this.state,e=t.cate,a=t.query,r=this.props,s=r.comic,c=r.category,o=r.loading,p=a.list,m=c[e]||{},d=s[e]||[],g=s.total[e]||0;return l.a.createElement(E,null,l.a.createElement("div",{className:"header"},l.a.createElement("span",null,m.slug)),l.a.createElement("div",{className:"container"},l.a.createElement(n["a"],{bordered:!1,style:{marginBottom:"20px"},loading:o},l.a.createElement(u["a"],{value:a,onChange:this.listQuery}),p?l.a.createElement(y["b"],{type:"comic",list:d}):l.a.createElement(y["a"],{type:"comic",list:d}),l.a.createElement(h["a"],{value:i()({},a,{total:g}),onChange:this.listQuery}))))}};x=Object(o["a"])([Object(m["connect"])(t=>{var e=t.comic,a=t.category,n=t.loading;return{comic:e,category:a,loading:n.models.comic||n.models.category}})],x),e["default"]=x}}]); -------------------------------------------------------------------------------- /public/mobile/p__cate__post___cate.4e3d9a1b.async.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([[13],{WmM1:function(t,a,e){"use strict";e.r(a);e("IzEo");var n=e("bx4M"),r=e("p0pE"),s=e.n(r),i=e("fFZ8"),o=e.n(i),p=e("mrSG"),c=e("q1tI"),l=e.n(c),d=e("MuoO"),u=e("vOnD"),h=e("WqWh"),m=e("faBS"),g=e("gmfD"),y=e("3a4m"),v=e.n(y);function q(){var t=o()(["\n margin-top: 40px;\n\n .header {\n display: inline-block;\n padding: 0 10px;\n margin: 0 15px;\n height: 40px;\n line-height: 40px;\n border-bottom: solid 2px black;\n font-size: 15px;\n }\n\n .content {\n padding: 15px;\n\n .ant-divider-horizontal {\n margin: 15px 0;\n }\n }\n"]);return q=function(){return t},t}var x=u["a"].div(q()),b=class extends c["Component"]{constructor(t){super(t),this.initData=(t=>{var a=t.cate,e=void 0===a?this.state.cate:a,n=t.query,r=void 0===n?this.state.query:n,i=this.props,o=i.dispatch,p=i.location.pathname,c=Object(m["a"])({cate:e,query:r,type:"post"});o({type:"post/queryList",payload:{query:s()({},c),addon:{type:e}}}),v.a.push({pathname:p,query:s()({},r)})}),this.listQuery=(t=>{var a=this.state.query,e=s()({},a,t);this.setState({query:e}),this.initData({query:e})});var a=t.location.query;this.state={cate:"",query:{size:10,page:a.page?parseInt(a.page):1}}}static getDerivedStateFromProps(t,a){var e=t.match.params.cate;return e!==a.cate?s()({},a,{cate:e}):null}componentDidMount(){var t=this.props.match,a=t.params.cate;this.initData({cate:a})}componentDidUpdate(t,a){var e=this.props.match.params.cate,n=t.match.params.cate;n!==e&&this.initData({cate:e})}render(){var t=this.state,a=t.cate,e=t.query,r=this.props,i=r.post,o=r.category,p=r.loadingPost,c=i[a]||[],d=i.total[a]||0,u=o[a]||{};return l.a.createElement(x,null,l.a.createElement("div",{className:"header"},l.a.createElement("span",null,u.slug)),l.a.createElement("div",{className:"content"},l.a.createElement(n["a"],{bordered:!1,style:{margin:"15px 0"},loading:p},l.a.createElement(g["a"],{list:c}),l.a.createElement(h["a"],{value:s()({},e,{total:d}),onChange:this.listQuery}))))}};b=Object(p["a"])([Object(d["connect"])(t=>{var a=t.post,e=t.category,n=t.loading;return{post:a,category:e,loadingPost:n.models.post,loadingCategory:n.models.category}})],b),a["default"]=b}}]); -------------------------------------------------------------------------------- /public/picture/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/picture/.gitkeep -------------------------------------------------------------------------------- /public/post/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/post/.gitkeep -------------------------------------------------------------------------------- /public/post/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/post/favicon.ico -------------------------------------------------------------------------------- /public/post/index.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /public/post/p__index.0884df45.chunk.css: -------------------------------------------------------------------------------- 1 | .index___28nWn{min-height:calc(100vh - 260px);padding-top:60px;display:flex;width:100%}.index___28nWn .menu___2moP7{width:180px;margin-right:30px}.index___28nWn .menu___2moP7 .logo___36Z1w{width:100%}.index___28nWn .menu___2moP7 .logo___36Z1w img{width:100%}.index___28nWn .menu___2moP7 .list___1o6ac{font-size:16px;cursor:pointer;margin-top:30px;color:#222;line-height:28px;padding-left:10px;display:flex;justify-content:flex-start;align-items:center}.index___28nWn .menu___2moP7 .list___1o6ac i{font-size:25px;margin-right:25px}.index___28nWn .menu___2moP7 .list___1o6ac.active___NyTyt,.index___28nWn .menu___2moP7 .list___1o6ac:hover{color:#1890ff}.index___28nWn .content___3My1A{width:calc(100% - 440px)}.index___28nWn .sidebar___Kw1la{width:260px;margin-left:30px} 2 | -------------------------------------------------------------------------------- /public/search/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/search/.gitkeep -------------------------------------------------------------------------------- /public/search/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/search/favicon.ico -------------------------------------------------------------------------------- /public/search/index.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /public/search/p__index.5edd9e7e.chunk.css: -------------------------------------------------------------------------------- 1 | .index___28nWn{min-height:calc(100vh - 260px);padding-top:100px;width:100%}.index___28nWn .title___2cyli{display:flex;justify-content:center;align-items:center}.index___28nWn .title___2cyli img{width:160px;height:100px;object-fit:contain;margin-right:30px}.index___28nWn .footer___m_FDZ{display:flex;justify-content:center} 2 | -------------------------------------------------------------------------------- /public/user/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/user/.gitkeep -------------------------------------------------------------------------------- /public/user/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qinmei/qinVideo/7c935f2b563d1189e126618da80ae06d3eb14c15/public/user/favicon.ico -------------------------------------------------------------------------------- /public/user/index.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /public/web/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Qinmei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /public/web/README.md: -------------------------------------------------------------------------------- 1 | # qinVideo-addon-2.0 2 | 给qinVideo-2.0版本提供相关支持 3 | -------------------------------------------------------------------------------- /public/web/components/Button/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useMemo } from "react"; 2 | import styled from "styled-components"; 3 | import { hexToRgb } from "../../utils/util"; 4 | import { ColorContext } from "../Context"; 5 | 6 | const Wrapper = styled.div` 7 | background-color: ${props => props.color}; 8 | padding: 6px 16px; 9 | border-radius: 4px; 10 | cursor: pointer; 11 | color: rgba(255, 255, 255, 0.8); 12 | 13 | &:hover { 14 | background-color: ${props => hexToRgb(props.color, 0.8)}; 15 | } 16 | `; 17 | 18 | const reactComponent = props => { 19 | const { onChange } = props; 20 | const value = useContext(ColorContext); 21 | const color = useMemo(() => value, [value]); 22 | return ( 23 | 24 | {props.children} 25 | 26 | ); 27 | }; 28 | 29 | export default reactComponent; 30 | -------------------------------------------------------------------------------- /public/web/components/Context/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const ColorContext = React.createContext(''); 4 | -------------------------------------------------------------------------------- /public/web/components/Footer/index.less: -------------------------------------------------------------------------------- 1 | .footer { 2 | width: 100%; 3 | height: 260px; 4 | color: rgba(255, 255, 255, 0.8); 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | 9 | .con { 10 | width: 100%; 11 | display: grid; 12 | grid-template-areas: "left right"; 13 | 14 | .left { 15 | grid-area: left; 16 | 17 | span { 18 | display: block; 19 | font-size: 32px; 20 | margin-bottom: 20px; 21 | } 22 | 23 | p { 24 | margin-bottom: 0; 25 | 26 | &:nth-child(2) { 27 | margin-bottom: 20px; 28 | } 29 | } 30 | } 31 | 32 | .right { 33 | grid-area: right; 34 | 35 | span { 36 | font-size: 24px; 37 | display: block; 38 | margin-bottom: 20px; 39 | } 40 | 41 | p { 42 | cursor: pointer; 43 | } 44 | 45 | .circle { 46 | margin-top: 20px; 47 | display: flex; 48 | 49 | .list { 50 | display: inline-block; 51 | cursor: pointer; 52 | width: 40px; 53 | height: 40px; 54 | border-radius: 50%; 55 | border: 2px solid hsla(0, 0%, 100%, 0.5); 56 | font-size: 18px; 57 | display: flex; 58 | justify-content: center; 59 | align-items: center; 60 | margin-right: 20px; 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /public/web/components/IconFont/index.jsx: -------------------------------------------------------------------------------- 1 | import { Icon } from 'antd'; 2 | 3 | const IconFont = Icon.createFromIconfontCN({ 4 | scriptUrl: '//at.alicdn.com/t/font_825484_vf682mobb.js', 5 | }); 6 | 7 | export default IconFont; 8 | -------------------------------------------------------------------------------- /public/web/components/IndexList/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import IconFont from "../IconFont"; 3 | import Button from "../Button"; 4 | import List from "../List"; 5 | import Router from "next/router"; 6 | import styles from "./index.less"; 7 | 8 | const reactComponent = props => { 9 | const { 10 | value: { list = [], info = {} } 11 | } = props; 12 | 13 | const [number, setNumber] = useState(12); 14 | 15 | const goCategory = ele => { 16 | if (ele.status === "custom") { 17 | if (ele._id === "newIndexNewAnimate") { 18 | Router.push(`/${ele.type}/new`); 19 | } else { 20 | let sortBy = "createdAt"; 21 | if (/newIndexRandom/.test(ele._id)) { 22 | sortBy = "title"; 23 | } else if (/newIndexPlay/.test(ele._id)) { 24 | sortBy = "countPlay"; 25 | } else if (/newIndexRate/.test(ele._id)) { 26 | sortBy = "countStar"; 27 | } 28 | Router.push(`/${ele.type}/all?sortBy=${sortBy}&sortOrder=-1`); 29 | } 30 | } else { 31 | Router.push(`/${ele.type}/${ele.kind.slice(1)}/${ele._id}`); 32 | } 33 | }; 34 | 35 | useEffect(() => { 36 | const width = window.outerWidth; 37 | const number = width >= 1600 ? 16 : 12; 38 | setNumber(number); 39 | }); 40 | 41 | return ( 42 |
43 |
44 |
45 | 46 | {info.name} 47 |
48 | 55 |
56 | 57 |
58 | ); 59 | }; 60 | 61 | export default reactComponent; 62 | -------------------------------------------------------------------------------- /public/web/components/IndexList/index.less: -------------------------------------------------------------------------------- 1 | .indexList { 2 | width: 100%; 3 | 4 | .head { 5 | width: 100%; 6 | height: 60px; 7 | display: flex; 8 | justify-content: space-between; 9 | align-items: center; 10 | margin-top: 15px; 11 | 12 | .left { 13 | display: flex; 14 | justify-content: flex-start; 15 | align-items: center; 16 | 17 | i { 18 | font-size: 26px; 19 | margin-right: 15px; 20 | } 21 | 22 | span { 23 | font-size: 18px; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /public/web/components/IndexScroll/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from "react"; 2 | import { Carousel } from "antd"; 3 | import Router from "next/router"; 4 | import styles from "./index.less"; 5 | import styled from "styled-components"; 6 | 7 | const List = styled.div` 8 | background-color: rgba(0, 0, 0, 0.1); 9 | height: ${props => props.height}px; 10 | cursor: pointer; 11 | background-size: cover; 12 | background-position: center; 13 | background-image: url(${props => props.cover}); 14 | 15 | span { 16 | display: block; 17 | padding: 0 15px; 18 | height: 30px; 19 | line-height: 30px; 20 | color: white; 21 | font-size: 18px; 22 | background: linear-gradient( 23 | to bottom, 24 | rgba(0, 0, 0, 0.2), 25 | rgba(0, 0, 0, 0) 26 | ); 27 | } 28 | `; 29 | 30 | const reactComponent = props => { 31 | const { 32 | value: { scroll = [], top = [], type = "animate" } 33 | } = props; 34 | 35 | const [height, setHeight] = useState(200); 36 | 37 | let scrollRef = useRef(null); 38 | 39 | const goAnimate = slug => { 40 | Router.push(`/${type}/slug/${slug}`); 41 | }; 42 | 43 | useEffect(() => { 44 | setHeight(scrollRef.current.clientHeight); 45 | }); 46 | 47 | return ( 48 |
49 |
50 | 51 | {scroll.slice(0, 6).map(item => ( 52 | { 56 | goAnimate(item.slug); 57 | }} 58 | key={item._id} 59 | > 60 | {item.title} 61 | 62 | ))} 63 | 64 |
65 | {top.slice(0, 6).map(item => ( 66 |
{ 70 | goAnimate(item.slug); 71 | }} 72 | key={item._id} 73 | > 74 | {item.title} 75 |
76 | ))} 77 |
78 | ); 79 | }; 80 | 81 | export default reactComponent; 82 | -------------------------------------------------------------------------------- /public/web/components/IndexScroll/index.less: -------------------------------------------------------------------------------- 1 | .indexScroll { 2 | width: 100%; 3 | display: grid; 4 | grid-template-columns: repeat(5, 1fr); 5 | grid-gap: 15px; 6 | 7 | .scroll { 8 | grid-area: ~"1 / 1 / 3 / 3"; 9 | border-radius: 4px; 10 | overflow: hidden; 11 | } 12 | 13 | .top { 14 | width: 100%; 15 | height: 0; 16 | padding-bottom: 56.75%; 17 | border-radius: 4px; 18 | background-color: rgba(0, 0, 0, 0.1); 19 | background-size: cover; 20 | background-position: center; 21 | cursor: pointer; 22 | overflow: hidden; 23 | position: relative; 24 | 25 | span { 26 | position: absolute; 27 | bottom: 0; 28 | left: 0; 29 | width: 100%; 30 | line-height: 30px; 31 | padding: 0 15px; 32 | text-align: center; 33 | color: white; 34 | white-space: nowrap; 35 | text-overflow: ellipsis; 36 | overflow: hidden; 37 | background: linear-gradient(to top, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0)); 38 | } 39 | } 40 | } 41 | 42 | .list { 43 | background-color: rgba(0, 0, 0, 0.1); 44 | cursor: pointer; 45 | background-size: cover; 46 | background-position: center; 47 | 48 | span { 49 | display: block; 50 | padding: 0 15px; 51 | height: 30px; 52 | line-height: 30px; 53 | color: white; 54 | font-size: 18px; 55 | background: linear-gradient( 56 | to bottom, 57 | rgba(0, 0, 0, 0.2), 58 | rgba(0, 0, 0, 0) 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /public/web/components/List/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Router from "next/router"; 3 | import { fixedZero } from "../../utils/util"; 4 | import { Empty } from "antd"; 5 | import styles from "./index.less"; 6 | import moment from "moment"; 7 | 8 | const inThisWeek = time => { 9 | const update = moment(time); 10 | const now = new Date(); 11 | let day = now.getDay(); 12 | now.setHours(0); 13 | now.setMinutes(0); 14 | now.setSeconds(0); 15 | 16 | if (day === 0) { 17 | day = 6; 18 | } else { 19 | day -= 1; 20 | } 21 | 22 | const beginTime = now.getTime() - day * 1000 * 3600 * 24; 23 | 24 | return update.valueOf() > beginTime; 25 | }; 26 | 27 | const goAnimate = (type, slug) => { 28 | Router.push(`/${type}/slug/${slug}`); 29 | }; 30 | 31 | const reactComponent = props => { 32 | const { list, type, update = false, status = false } = props; 33 | 34 | return ( 35 | <> 36 | {list.length === 0 ? ( 37 | 38 | ) : ( 39 |
40 | {list.map(item => ( 41 |
{ 45 | goAnimate(type, item.slug); 46 | }} 47 | key={item._id} 48 | > 49 | {item.title} 50 | {update && ( 51 |
56 | {fixedZero(item.lastEposide ? item.lastEposide.sort : 0)} 57 |
58 | )} 59 | 60 |
{item.countStar}
61 |
62 | ))} 63 |
64 | )} 65 | 66 | ); 67 | }; 68 | 69 | export default reactComponent; 70 | -------------------------------------------------------------------------------- /public/web/components/List/index.less: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | margin-bottom: 40px; 3 | display: grid; 4 | grid-gap: 40px 20px; 5 | grid-template-columns: repeat(4, calc((100% - 60px) / 4)); 6 | 7 | @media (min-width: 768px) { 8 | grid-template-columns: repeat(4, calc((100% - 60px) / 4)); 9 | } 10 | 11 | @media (min-width: 992px) { 12 | grid-template-columns: repeat(5, calc((100% - 80px) / 5)); 13 | } 14 | 15 | @media (min-width: 1200px) { 16 | grid-template-columns: repeat(6, calc((100% - 100px) / 6)); 17 | } 18 | 19 | @media (min-width: 1600px) { 20 | grid-template-columns: repeat(8, calc((100% - 140px) / 8)); 21 | } 22 | 23 | .top { 24 | height: 0; 25 | padding-top: 140%; 26 | background-color: rgba(0, 0, 0, 0.1); 27 | background-size: cover; 28 | background-position: center; 29 | border-radius: 6px; 30 | vertical-align: bottom; 31 | position: relative; 32 | cursor: pointer; 33 | 34 | span { 35 | display: block; 36 | width: 100%; 37 | height: 30px; 38 | line-height: 30px; 39 | overflow: hidden; 40 | text-overflow: ellipsis; 41 | white-space: nowrap; 42 | text-align: center; 43 | } 44 | 45 | .update { 46 | position: absolute; 47 | right: 0; 48 | top: 0; 49 | padding: 3px 10px; 50 | color: white; 51 | background-color: rgba(0, 0, 0, 0.5); 52 | border-radius: 0 4px 0 4px; 53 | } 54 | 55 | .updateActive { 56 | background-color: rgb(242, 93, 142); 57 | } 58 | 59 | .rate { 60 | position: absolute; 61 | left: -5px; 62 | top: 5px; 63 | padding: 1px 15px; 64 | min-width: 30px; 65 | color: white; 66 | background-color: rgba(0, 0, 0, 0.7); 67 | border-radius: 0 2px 2px 0; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /public/web/components/Nav/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Router from "next/router"; 3 | import IconFont from "../IconFont"; 4 | import { hexToRgb } from "../../utils/util"; 5 | import styles from "./index.less"; 6 | 7 | const goToPath = path => { 8 | Router.push(path); 9 | }; 10 | 11 | const Header = props => { 12 | const { config } = props; 13 | const { color, pcMenu = [] } = config; 14 | 15 | const headerMenu = pcMenu.map(item => ( 16 | { 18 | goToPath(item.link); 19 | }} 20 | key={item.link} 21 | > 22 | {item.title} 23 | 24 | )); 25 | 26 | return ( 27 |
28 |
29 |
30 | 31 | 32 |
{headerMenu}
33 | goToPath("/search")} /> 34 | goToPath("/user")} /> 35 |
36 |
37 |
38 | ); 39 | }; 40 | 41 | export default Header; 42 | -------------------------------------------------------------------------------- /public/web/components/Nav/index.less: -------------------------------------------------------------------------------- 1 | .nav { 2 | width: 100%; 3 | height: 40px; 4 | line-height: 40px; 5 | color: #fff; 6 | position: fixed; 7 | top: 0; 8 | z-index: 999; 9 | 10 | .bg { 11 | position: absolute; 12 | top: 40px; 13 | left: 0; 14 | width: 100vw; 15 | height: calc(100vh - 40px); 16 | z-index: -1; 17 | background-color: rgba(0, 0, 0, 0.3); 18 | } 19 | } 20 | 21 | .menu { 22 | width: 100%; 23 | height: 40px; 24 | display: flex; 25 | justify-content: space-between; 26 | align-items: center; 27 | 28 | i { 29 | height: 40px; 30 | width: 30px; 31 | cursor: pointer; 32 | display: flex; 33 | align-items: center; 34 | 35 | &:nth-child(1) { 36 | justify-content: flex-start; 37 | font-size: 20px; 38 | } 39 | &:nth-child(3) { 40 | justify-content: center; 41 | margin-right: 20px; 42 | font-weight: bold; 43 | font-size: 18px; 44 | } 45 | &:nth-child(4) { 46 | justify-content: flex-end; 47 | font-size: 18px; 48 | } 49 | } 50 | } 51 | 52 | .main { 53 | width: calc(100% - 100px); 54 | height: 100%; 55 | display: flex; 56 | justify-content: space-around; 57 | align-items: center; 58 | user-select: none; 59 | 60 | span { 61 | padding: 0 15px; 62 | cursor: pointer; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /public/web/components/NewList/index.less: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | width: 100%; 3 | 4 | .header { 5 | width: 100%; 6 | height: 60px; 7 | display: flex; 8 | justify-content: space-between; 9 | align-items: center; 10 | margin-top: 15px; 11 | 12 | .left { 13 | display: flex; 14 | justify-content: flex-start; 15 | align-items: center; 16 | 17 | i { 18 | font-size: 26px; 19 | margin-right: 15px; 20 | } 21 | 22 | span { 23 | font-size: 18px; 24 | } 25 | } 26 | .right { 27 | width: calc(100% - 160px); 28 | display: flex; 29 | justify-content: space-around; 30 | height: 80%; 31 | padding: 0 30px; 32 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 33 | 34 | .list { 35 | width: calc(80% / 7); 36 | display: flex; 37 | justify-content: center; 38 | align-items: center; 39 | border-bottom: solid 1px transparent; 40 | margin-bottom: -1px; 41 | position: relative; 42 | cursor: pointer; 43 | 44 | &.active { 45 | border-bottom: solid 1px rgb(85, 85, 85); 46 | color: rgb(85, 85, 85); 47 | 48 | &:after { 49 | content: ""; 50 | position: absolute; 51 | bottom: 0; 52 | width: 0; 53 | height: 0; 54 | border: solid 5px transparent; 55 | border-bottom: solid 5px rgb(85, 85, 85); 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | .content { 63 | margin-top: 15px; 64 | 65 | .section { 66 | display: none; 67 | 68 | &.active { 69 | display: block; 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /public/web/next.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const withPlugins = require("next-compose-plugins"); 3 | const withCss = require("@zeit/next-css"); 4 | const withLess = require("@zeit/next-less"); 5 | 6 | const nextConfig = { 7 | assetPrefix: "/web", 8 | distDir: "_next", 9 | webpack: (config, { isServer }) => { 10 | if (isServer) { 11 | const antStyles = /antd\/.*?\/style\/css.*?/; 12 | const origExternals = [...config.externals]; 13 | config.externals = [ 14 | (context, request, callback) => { 15 | if (request.match(antStyles)) return callback(); 16 | if (typeof origExternals[0] === "function") { 17 | origExternals[0](context, request, callback); 18 | } else { 19 | callback(); 20 | } 21 | }, 22 | ...(typeof origExternals[0] === "function" ? [] : origExternals) 23 | ]; 24 | 25 | config.module.rules.unshift({ 26 | test: antStyles, 27 | use: "null-loader" 28 | }); 29 | } 30 | return config; 31 | } 32 | }; 33 | 34 | module.exports = withPlugins( 35 | [ 36 | [withCss], 37 | [ 38 | withLess, 39 | { 40 | cssModules: true, 41 | cssLoaderOptions: { 42 | localIdentName: "[local]___[hash:base64:5]" 43 | } 44 | } 45 | ] 46 | ], 47 | nextConfig 48 | ); 49 | -------------------------------------------------------------------------------- /public/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "with-ant-design", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "dev": "next", 6 | "build": "next build", 7 | "start": "next start", 8 | "export": "next export" 9 | }, 10 | "dependencies": { 11 | "@zeit/next-css": "^1.0.1", 12 | "@zeit/next-less": "^1.0.1", 13 | "antd": "^3.9.2", 14 | "babel-plugin-import": "^1.9.1", 15 | "isomorphic-unfetch": "^3.0.0", 16 | "less": "^3.10.3", 17 | "moment": "^2.24.0", 18 | "next": "latest", 19 | "next-compose-plugins": "^2.2.0", 20 | "null-loader": "2.0.0", 21 | "qs": "^6.9.1", 22 | "react": "^16.7.0", 23 | "react-dom": "^16.7.0", 24 | "react-helmet": "^5.2.1", 25 | "styled-components": "^4.4.1" 26 | }, 27 | "license": "ISC", 28 | "devDependencies": { 29 | "babel-plugin-styled-components": "^1.10.6" 30 | } 31 | } -------------------------------------------------------------------------------- /public/web/pages/_app.js: -------------------------------------------------------------------------------- 1 | import App from "next/app"; 2 | import React from "react"; 3 | import "./global.css"; 4 | 5 | export default class MyApp extends App { 6 | static async getInitialProps({ Component, ctx }) { 7 | let pageProps = {}; 8 | 9 | if (Component.getInitialProps) { 10 | pageProps = await Component.getInitialProps(ctx); 11 | } 12 | 13 | return { pageProps }; 14 | } 15 | 16 | render() { 17 | const { Component, pageProps } = this.props; 18 | return ; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /public/web/pages/global.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | #root { 4 | height: auto; 5 | user-select: none; 6 | } 7 | 8 | ::-webkit-scrollbar { 9 | display: none; 10 | } 11 | 12 | body { 13 | margin: 0; 14 | background-color: rgba(240, 242, 245, 1); 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | } 20 | 21 | @media (min-width: 768px) { 22 | .container { 23 | max-width: 720px; 24 | } 25 | } 26 | 27 | @media (min-width: 992px) { 28 | .container { 29 | max-width: 960px; 30 | } 31 | } 32 | 33 | @media (min-width: 1200px) { 34 | .container { 35 | max-width: 1140px; 36 | } 37 | } 38 | 39 | @media (min-width: 1400px) { 40 | .container { 41 | max-width: 1240px; 42 | } 43 | } 44 | 45 | @media (min-width: 1600px) { 46 | .container { 47 | max-width: 1320px; 48 | } 49 | } 50 | 51 | @media (min-width: 1900px) { 52 | .container { 53 | max-width: 1400px; 54 | } 55 | } 56 | 57 | @media (min-width: 2500px) { 58 | .container { 59 | max-width: 1600px; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /public/web/pages/index.less: -------------------------------------------------------------------------------- 1 | .index { 2 | min-height: calc(100vh - 260px); 3 | 4 | .header { 5 | width: 100%; 6 | height: 360px; 7 | margin-bottom: 15px; 8 | background-size: cover; 9 | background-position: center; 10 | display: flex; 11 | justify-content: flex-end; 12 | align-items: center; 13 | 14 | .title { 15 | min-width: 35%; 16 | color: rgba(255, 255, 255, 0.8); 17 | 18 | span { 19 | display: block; 20 | font-size: 36px; 21 | margin: 40px 0 20px 0; 22 | } 23 | 24 | p { 25 | font-size: 20px; 26 | margin-bottom: 0; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/web/utils/api.js: -------------------------------------------------------------------------------- 1 | const prefix = "http://localhost:7001"; 2 | 3 | const api = { 4 | config: `${prefix}/api/v2/config`, 5 | home: `${prefix}/api/v2/home` 6 | }; 7 | 8 | export default api; 9 | -------------------------------------------------------------------------------- /public/web/utils/request.js: -------------------------------------------------------------------------------- 1 | import { stringify } from "qs"; 2 | import fetch from "isomorphic-unfetch"; 3 | 4 | /** 5 | * Requests a URL, returning a promise. 6 | * 7 | * @param {string} url The URL we want to request 8 | * @param {object} [option] The options we want to pass to "fetch" 9 | * @return {object} An object containing either "data" or "err" 10 | */ 11 | 12 | const codeStatus = res => { 13 | if (res.code === 10000) { 14 | return res.data; 15 | } 16 | return false; 17 | }; 18 | 19 | const urlInit = (url, options, method) => { 20 | let link = url; 21 | const result = { method }; 22 | let defaultHeader = { 23 | Accept: "application/json", 24 | "Content-Type": "application/json; charset=utf-8" 25 | }; 26 | const { params, query, data, formData, headers } = options; 27 | 28 | /* eslint-disable*/ 29 | if (params) { 30 | Object.keys(params).map(item => { 31 | if (!params[item] && params[item] !== 0) delete params[item]; 32 | }); 33 | 34 | link = link.replace( 35 | /\/:(\w+)/gm, 36 | index => `/${params[`${index.replace(/\/:/g, "")}`]}` 37 | ); 38 | } 39 | 40 | if (query) { 41 | Object.keys(query).map(item => { 42 | if (!query[item] && query[item] !== 0) delete query[item]; 43 | }); 44 | 45 | link += `?${stringify(query)}`; 46 | } 47 | 48 | if (data) { 49 | const newData = data; 50 | Object.keys(newData).map(item => { 51 | if (!newData[item] && newData[item] !== 0) delete newData[item]; 52 | }); 53 | 54 | result.body = JSON.stringify(newData); 55 | } 56 | 57 | if (formData) { 58 | defaultHeader = null; 59 | result.body = formData; 60 | } 61 | 62 | result.headers = { 63 | ...defaultHeader, 64 | ...headers 65 | }; 66 | 67 | return { 68 | url: link, 69 | body: { 70 | ...result 71 | } 72 | }; 73 | }; 74 | 75 | const fetchPromise = async ({ url, body }) => { 76 | return fetch(url, body) 77 | .then(res => res.json()) 78 | .then(codeStatus); 79 | }; 80 | 81 | const request = async (url, option, method) => { 82 | const result = urlInit(url, option, method); 83 | return fetchPromise(result); 84 | }; 85 | 86 | export default request; 87 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "jsx": "preserve", 5 | "target": "es2017", 6 | "module": "commonjs", 7 | "strict": true, 8 | "noImplicitAny": false, 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "charset": "utf8", 12 | "allowJs": false, 13 | "pretty": true, 14 | "noEmitOnError": false, 15 | "noUnusedLocals": true, 16 | "noUnusedParameters": false, 17 | "allowUnreachableCode": false, 18 | "allowUnusedLabels": false, 19 | "strictPropertyInitialization": false, 20 | "noFallthroughCasesInSwitch": true, 21 | "skipLibCheck": true, 22 | "skipDefaultLibCheck": true, 23 | "inlineSourceMap": true, 24 | "importHelpers": true 25 | }, 26 | "exclude": ["public", "app/views", "node_modules*"] 27 | } 28 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-config-egg" 4 | ], 5 | "rules": { 6 | "no-unused-expression": [ 7 | true, 8 | "allow-fast-null-checks" 9 | ], 10 | "array-bracket-spacing": false, 11 | "linebreak-style": [ 12 | false, 13 | "unix" 14 | ] 15 | } 16 | } --------------------------------------------------------------------------------