1
0

article-detail.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  1. <template>
  2. <view class="app-page">
  3. <view v-if="loading != 'success'" class="loading-wrap">
  4. <tm-skeleton model="card"></tm-skeleton>
  5. <tm-skeleton model="card"></tm-skeleton>
  6. <tm-skeleton model="card"></tm-skeleton>
  7. </view>
  8. <block v-else>
  9. <!-- 顶部信息 -->
  10. <view class="head ma-24">
  11. <view class="title">{{ result.title }}</view>
  12. <view class="detail">
  13. <view class="author">
  14. <text class="author-name">博主:{{ author.nickname }}</text>
  15. <text class="author-time">时间:{{ { d: result.createTime, f: 'yyyy年MM月dd日 星期w' } | formatTime }}</text>
  16. </view>
  17. <view class="cover" v-if="result.thumbnail"><image class="cover-img" mode="aspectFill" :src="calcUrl(result.thumbnail)"></image></view>
  18. <view class="count" :class="{ 'no-thumbnail': !result.thumbnail }">
  19. <view class="count-item">
  20. <text class="value">{{ result.visits }}</text>
  21. <text class="label">阅读</text>
  22. </view>
  23. <view class="count-item">
  24. <text class="value">{{ result.likes }}</text>
  25. <text class="label">喜欢</text>
  26. </view>
  27. <view class="count-item">
  28. <text class="value">{{ result.commentCount }}</text>
  29. <text class="label">评论</text>
  30. </view>
  31. <view class="count-item">
  32. <text class="value">{{ result.wordCount }}</text>
  33. <text class="label">字数</text>
  34. </view>
  35. </view>
  36. </view>
  37. </view>
  38. <!-- 分类 -->
  39. <view class="category">
  40. <view class="category-type">
  41. <text class="text-weight-b">分类:</text>
  42. <text v-if="result.categories.length == 0" class="category-tag is-empty">未选择分类</text>
  43. <block v-else>
  44. <text class="category-tag" v-for="(item, index) in result.categories" :key="index" @click="fnToCate(item)">{{ item.name }}</text>
  45. </block>
  46. </view>
  47. <view class="mt-18 category-type">
  48. <text class="text-weight-b">标签:</text>
  49. <text v-if="result.tags.length == 0" class="category-tag is-empty">未选择标签</text>
  50. <block v-else>
  51. <text class="category-tag" :style="{ backgroundColor: item.color }" v-for="(item, index) in result.tags" :key="index" @click="fnToTag(item)">
  52. {{ item.name }}
  53. </text>
  54. </block>
  55. </view>
  56. </view>
  57. <!-- 广告区域 -->
  58. <view v-if="haloAdConfig.articleDetail.use" class="ad-wrap ma-24 mb-0">
  59. <!-- #ifdef MP-WEIXIN -->
  60. <ad v-if="haloAdConfig.unitId" :unit-id="haloAdConfig.unitId"></ad>
  61. <!-- #endif -->
  62. <!-- #ifndef MP-WEIXIN -->
  63. <ad v-if="haloAdConfig.adpid" :adpid="haloAdConfig.adpid"></ad>
  64. <!-- #endif -->
  65. </view>
  66. <!-- 内容区域 -->
  67. <view class="content ml-24 mr-24">
  68. <!-- markdown渲染 -->
  69. <mp-html
  70. class="evan-markdown"
  71. lazy-load
  72. :domain="markdownConfig.domain"
  73. :loading-img="markdownConfig.loadingGif"
  74. :scroll-table="true"
  75. :selectable="true"
  76. :tag-style="markdownConfig.tagStyle"
  77. :container-style="markdownConfig.containStyle"
  78. :content="result.content"
  79. :markdown="true"
  80. :showLineNumber="true"
  81. :showLanguageName="true"
  82. :copyByLongPress="true"
  83. />
  84. <!-- 广告区域 -->
  85. <view v-if="haloAdConfig.articleDetail.use" class="ad-wrap mt-24 mb-24 ">
  86. <!-- #ifdef MP-WEIXIN -->
  87. <ad v-if="haloAdConfig.unitId" :unit-id="haloAdConfig.unitId"></ad>
  88. <!-- #endif -->
  89. <!-- #ifndef MP-WEIXIN -->
  90. <ad v-if="haloAdConfig.adpid" :adpid="haloAdConfig.adpid"></ad>
  91. <!-- #endif -->
  92. </view>
  93. <!-- 版权声明 -->
  94. <view v-if="copyright.use" class="copyright-wrap bg-white mt-24 pa-24 round-4">
  95. <view class="copyright-title text-weight-b">版权信息</view>
  96. <view class="copyright-content mt-12 grey-lighten-5 text-grey-darken-2 round-4 pt-12 pb-12 pl-24 pr-24 ">
  97. <view v-if="copyright.author" class="copyright-text text-size-s ">版权归属:{{ copyright.author }}</view>
  98. <view v-if="copyright.description" class="copyright-text text-size-s mt-12">版权说明:{{ copyright.description }}</view>
  99. <view v-if="copyright.violation" class="copyright-text text-size-s mt-12 text-red">侵权处理:{{ copyright.violation }}</view>
  100. </view>
  101. </view>
  102. <!-- 评论展示区域 -->
  103. <view class="comment-wrap bg-white mt-24 pa-24 round-4">
  104. <commentList
  105. :disallowComment="result.disallowComment"
  106. :postId="result.id"
  107. :post="result"
  108. @on-comment-detail="fnOnShowCommentDetail"
  109. @on-loaded="fnOnCommentLoaded"
  110. ></commentList>
  111. </view>
  112. </view>
  113. <!-- 弹幕效果 -->
  114. <barrage ref="barrage" :maxTop="240" type="leftBottom"></barrage>
  115. <!-- 返回顶部 -->
  116. <tm-flotbutton :offset="[16, 80]" icon="icon-angle-up" color="bg-gradient-light-blue-accent" @click="fnToTopPage()"></tm-flotbutton>
  117. <tm-flotbutton :actions="btnOption.actions" actions-pos="left" :show-text="true" color="bg-gradient-orange-accent" @change="fnOnFlotButtonChange"></tm-flotbutton>
  118. </block>
  119. <!-- 评论详情 -->
  120. <tm-poup v-model="commentDetail.show" height="auto" :round="6" :over-close="true" position="bottom">
  121. <view class="pa-24">
  122. <view class="poup-head pb-24">
  123. <view class="poup-title text-align-center text-size-g text-weight-b mb-32">评论详情</view>
  124. <comment-item :useContentBg="false" :useActions="false" :isChild="false" :comment="commentDetail.comment" :postId="result.id"></comment-item>
  125. </view>
  126. <scroll-view :scroll-y="true" class="poup-body">
  127. <view v-if="commentDetail.loading != 'success'" class="poup-loading-wrap flex flex-center">
  128. <view v-if="commentDetail.loading == 'loading'" class="loading flex flex-center flex-col">
  129. <text class="e-loading-icon iconfont icon-loading text-blue"></text>
  130. <view class="text-size-n text-grey-lighten-1 py-12 mt-12">加载中,请稍等...</view>
  131. </view>
  132. <view v-else-if="commentDetail.loading == 'error'" class="error">
  133. <tm-empty icon="icon-wind-cry" label="加载失败">
  134. <tm-button theme="bg-gradient-light-blue-accent" size="m" v-if="!disallowComment" @click="fnGetChildComments()">刷新试试</tm-button>
  135. </tm-empty>
  136. </view>
  137. </view>
  138. <block v-else>
  139. <view v-if="commentDetail.list.length == 0" class="poup-empty flex flex-center">
  140. <tm-empty icon="icon-shiliangzhinengduixiang-" label="没有更多评论啦~"></tm-empty>
  141. </view>
  142. <block v-else>
  143. <comment-item
  144. v-for="(comment, index) in commentDetail.list"
  145. :useContentBg="false"
  146. :useSolid="false"
  147. :useActions="false"
  148. :key="index"
  149. :isChild="false"
  150. :comment="comment"
  151. :postId="result.id"
  152. ></comment-item>
  153. </block>
  154. </block>
  155. </scroll-view>
  156. </view>
  157. </tm-poup>
  158. <!-- 海报 -->
  159. <tm-poup v-model="poster.show" width="90vw" height="auto" :round="6" :over-close="true" position="center">
  160. <view class="poster-content pt-12 bg-white">
  161. <view v-if="poster.loading" class="poster-loading flex flex-center text-grey-darken-1">
  162. <text class="e-loading-icon iconfont icon-loading"></text>
  163. <text class="ml-6">海报正在生成...</text>
  164. </view>
  165. <block v-if="poster.showCanvas">
  166. <r-canvas ref="rCanvas"></r-canvas>
  167. <view class="poster-save ma-24 mt-0 pt-20 flex flex-center">
  168. <tm-button theme="bg-gradient-light-blue-accent" size="m" @click="fnSavePoster()">保存到相册</tm-button>
  169. <tm-button v-if="false" theme="bg-gradient-orange-accent" size="m" @click="fnShareTo()">分享给好友</tm-button>
  170. <tm-button theme="bg-gradient-blue-grey-accent" size="m" @click="fnOnPosterClose()">关 闭</tm-button>
  171. </view>
  172. </block>
  173. </view>
  174. </tm-poup>
  175. </view>
  176. </template>
  177. <script>
  178. import MarkdownConfig from '@/common/markdown/markdown.config.js';
  179. import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
  180. import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
  181. import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
  182. import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
  183. import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
  184. import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
  185. import commentList from '@/components/comment-list/comment-list.vue';
  186. import commentItem from '@/components/comment-item/comment-item.vue';
  187. import rCanvas from '@/components/r-canvas/r-canvas.vue';
  188. import barrage from '@/components/barrage/barrage.vue';
  189. export default {
  190. components: {
  191. tmSkeleton,
  192. tmPoup,
  193. tmFlotbutton,
  194. tmButton,
  195. tmEmpty,
  196. mpHtml,
  197. commentList,
  198. commentItem,
  199. rCanvas,
  200. barrage
  201. },
  202. data() {
  203. return {
  204. loading: 'loading',
  205. markdownConfig: MarkdownConfig,
  206. btnOption: {
  207. actions: [
  208. {
  209. icon: 'icon-like',
  210. color: 'bg-gradient-orange-accent'
  211. },
  212. {
  213. icon: 'icon-commentdots-fill',
  214. color: 'bg-gradient-green-accent'
  215. },
  216. {
  217. icon: 'icon-share1',
  218. color: 'bg-gradient-blue-accent'
  219. }
  220. ]
  221. },
  222. queryParams: {
  223. articleId: null
  224. },
  225. result: {},
  226. commentDetail: {
  227. loading: 'loading',
  228. show: false,
  229. comment: {},
  230. postId: undefined,
  231. list: []
  232. },
  233. poster: {
  234. show: false,
  235. showCanvas: false,
  236. loading: true,
  237. res: null
  238. }
  239. };
  240. },
  241. computed: {
  242. copyright() {
  243. return getApp().globalData.copyright;
  244. },
  245. calcUrl() {
  246. return url => {
  247. if (this.$utils.checkIsUrl(url)) {
  248. return url;
  249. }
  250. return getApp().globalData.baseApiUrl + url;
  251. };
  252. },
  253. // 获取博主信息
  254. bloggerInfo() {
  255. let blogger = this.$tm.vx.getters().getBlogger;
  256. blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
  257. return blogger;
  258. }
  259. },
  260. onLoad(e) {
  261. this.fnSetPageTitle('文章加载中...');
  262. this.queryParams.articleId = e.articleId;
  263. this.fnGetData();
  264. },
  265. onPullDownRefresh() {
  266. this.fnGetData();
  267. },
  268. methods: {
  269. fnGetData() {
  270. this.loading = 'loading';
  271. uni.showLoading({
  272. mask: true,
  273. title: '加载中...'
  274. });
  275. this.$httpApi
  276. .getArticleDetail(this.queryParams.articleId)
  277. .then(res => {
  278. this.result = res.data;
  279. this.fnSetPageTitle('文章详情');
  280. this.loading = 'success';
  281. this.fnSetWxShareInfo();
  282. })
  283. .catch(err => {
  284. this.loading = 'error';
  285. })
  286. .finally(() => {
  287. uni.hideLoading();
  288. uni.stopPullDownRefresh();
  289. });
  290. },
  291. fnSetWxShareInfo() {
  292. // #ifdef MP-WEIXIN
  293. uni.$tm.vx.commit('setWxShare', {
  294. title: this.result.title,
  295. desc: this.result.summary,
  296. // imageUrl: this.poster.res.tempFilePath,
  297. imageUrl: this.$utils.checkThumbnailUrl(this.result.thumbnail),
  298. path: `/pagesA/article-detail/article-detail?articleId=${this.queryParams.articleId}`,
  299. copyLink: this.$haloConfig.apiUrl,
  300. query: {}
  301. });
  302. // #endif
  303. },
  304. // 浮动按钮点击
  305. fnOnFlotButtonChange(index) {
  306. switch (index) {
  307. case 0:
  308. this.fnDoLikes();
  309. break;
  310. case 1:
  311. this.fnToComment();
  312. break;
  313. case 2:
  314. this.fnShowShare();
  315. break;
  316. }
  317. },
  318. fnToComment() {
  319. if (this.result.disallowComment) {
  320. return uni.$tm.toast('文章已开启禁止评论!');
  321. }
  322. this.$Router.push({
  323. path: '/pagesA/comment/comment',
  324. query: {
  325. id: this.result.id,
  326. parentId: 0,
  327. title: this.result.title,
  328. from: 'posts',
  329. formPage: 'comment_list',
  330. type: 'post'
  331. }
  332. });
  333. },
  334. fnDoLikes() {
  335. this.$httpApi
  336. .postLikePost(this.result.id)
  337. .then(res => {
  338. if (res.status == 200) {
  339. this.result.likes += 1;
  340. uni.$tm.toast('\(^o^)/~点赞成功!');
  341. } else {
  342. uni.showToast({
  343. icon: 'none',
  344. title: res.message
  345. });
  346. }
  347. })
  348. .catch(err => {
  349. uni.showToast({
  350. icon: 'none',
  351. title: err.message
  352. });
  353. });
  354. },
  355. fnShowShare() {
  356. this.poster.show = true;
  357. setTimeout(() => {
  358. this.poster.showCanvas = true;
  359. this.fnCreatePoster(res => {
  360. this.poster.res = res;
  361. });
  362. }, 500);
  363. },
  364. // 绘制虚线:https://blog.csdn.net/a460550542/article/details/124821248
  365. drawDashedLine(ctx, x, y, w, h, pattern, color) {
  366. ctx.lineWidth = h;
  367. ctx.strokeStyle = color;
  368. ctx.beginPath();
  369. ctx.setLineDash(pattern);
  370. ctx.moveTo(x, y);
  371. ctx.lineTo(w, y);
  372. ctx.stroke();
  373. y += 20;
  374. },
  375. fnCreatePoster(callback) {
  376. this.$nextTick(async () => {
  377. const systemInfo = await uni.getSystemInfoSync();
  378. // 初始化
  379. await this.$refs.rCanvas.init({
  380. canvas_id: 'rCanvas',
  381. // canvas_width: systemInfo.windowWidth - uni.upx2px(76),
  382. canvas_width: 337,
  383. canvas_height: 460,
  384. background_color: 'rgba(255,255,255,0)'
  385. });
  386. // 画圆角背景
  387. await this.$refs.rCanvas
  388. .fillRoundRect({
  389. x: 0,
  390. y: 0,
  391. w: 337,
  392. h: 460,
  393. radius: 12,
  394. fill_color: '#fff'
  395. })
  396. .catch(err_msg => {
  397. uni.showToast({
  398. title: err_msg,
  399. icon: 'none'
  400. });
  401. });
  402. console.log(this.$utils.checkAvatarUrl(this.bloggerInfo.avatar, true));
  403. // 博主信息
  404. await this.$refs.rCanvas
  405. .drawImage({
  406. url: this.$utils.checkAvatarUrl(this.bloggerInfo.avatar, true),
  407. x: 12,
  408. y: 12,
  409. w: 48,
  410. h: 48,
  411. border_radius: 24
  412. })
  413. .catch(err_msg => {
  414. uni.showToast({
  415. title: err_msg,
  416. icon: 'none'
  417. });
  418. });
  419. await this.$refs.rCanvas
  420. .drawText({
  421. text: this.bloggerInfo.nickname,
  422. max_width: 0,
  423. x: 70,
  424. y: 30,
  425. font_color: '#000',
  426. font_size: 15
  427. })
  428. .catch(err_msg => {
  429. uni.showToast({
  430. title: err_msg,
  431. icon: 'none'
  432. });
  433. });
  434. await this.$refs.rCanvas
  435. .drawText({
  436. text: this.bloggerInfo.description,
  437. max_width: 0,
  438. x: 70,
  439. y: 52,
  440. font_color: '#666',
  441. font_size: 12
  442. })
  443. .catch(err_msg => {
  444. uni.showToast({
  445. title: err_msg,
  446. icon: 'none'
  447. });
  448. });
  449. // 文章封面图
  450. await this.$refs.rCanvas
  451. .drawImage({
  452. url: this.$utils.checkThumbnailUrl(this.result.thumbnail),
  453. x: 12,
  454. y: 75,
  455. w: 312,
  456. h: 180,
  457. border_radius: 6
  458. })
  459. .catch(err_msg => {
  460. uni.showToast({
  461. title: err_msg,
  462. icon: 'none'
  463. });
  464. });
  465. // 文章标题
  466. await this.$refs.rCanvas
  467. .drawText({
  468. text: this.result.title,
  469. max_width: 312,
  470. line_clamp: 1,
  471. x: 12,
  472. y: 285,
  473. font_weight: 'bold',
  474. font_color: '#333',
  475. font_size: 14
  476. })
  477. .catch(err_msg => {
  478. uni.showToast({
  479. title: err_msg,
  480. icon: 'none'
  481. });
  482. });
  483. await this.$refs.rCanvas
  484. .drawText({
  485. text: this.result.summary,
  486. max_width: 312,
  487. line_clamp: 2,
  488. x: 12,
  489. y: 310,
  490. font_color: '#333',
  491. font_size: 13,
  492. line_height: 20
  493. })
  494. .catch(err_msg => {
  495. uni.showToast({
  496. title: err_msg,
  497. icon: 'none'
  498. });
  499. });
  500. this.drawDashedLine(this.$refs.rCanvas.ctx, 14, 356, 332, 0.5, [8, 5, 5, 5], '#999');
  501. // 小程序信息
  502. await this.$refs.rCanvas
  503. .drawImage({
  504. url: this.$haloConfig.miniCodeImageUrl,
  505. x: 20,
  506. y: 360,
  507. w: 80,
  508. h: 80
  509. })
  510. .catch(err_msg => {
  511. uni.showToast({
  512. title: err_msg,
  513. icon: 'none'
  514. });
  515. });
  516. await this.$refs.rCanvas
  517. .drawText({
  518. text: '长按识别小程序',
  519. x: 150,
  520. y: 390,
  521. font_color: '#333',
  522. font_size: 15,
  523. font_weight: 'bold',
  524. line_height: 22
  525. })
  526. .catch(err_msg => {
  527. uni.showToast({
  528. title: err_msg,
  529. icon: 'none'
  530. });
  531. });
  532. await this.$refs.rCanvas
  533. .drawText({
  534. text: '关注我,给你分享更多有趣的知识',
  535. x: 115,
  536. y: 425,
  537. font_color: '#333',
  538. font_size: 12,
  539. line_height: 22
  540. })
  541. .catch(err_msg => {
  542. uni.showToast({
  543. title: err_msg,
  544. icon: 'none'
  545. });
  546. });
  547. // 生成海报
  548. await this.$refs.rCanvas.draw(res => {
  549. //res.tempFilePath:生成成功,返回base64图片
  550. // 保存图片
  551. this.poster.loading = false;
  552. callback(res);
  553. });
  554. });
  555. },
  556. fnOnPosterClose() {
  557. this.poster.show = false;
  558. this.poster.showCanvas = false;
  559. this.poster.loading = true;
  560. },
  561. fnSavePoster() {
  562. this.$refs.rCanvas.saveImage(this.poster.res.tempFilePath);
  563. },
  564. fnShareTo() {
  565. // #ifdef MP-WEIXIN
  566. uni.$tm.toast('点击右上角分享给好友!');
  567. // #endif
  568. // #ifdef APP-PLUS
  569. uni.share({
  570. provider: 'weixin',
  571. scene: 'WXSceneSession',
  572. type: 0,
  573. href: this.$haloConfig.apiUrl,
  574. title: this.result.title,
  575. summary: this.result.summary,
  576. imageUrl: this.poster.res.tempFilePath,
  577. success: function(res) {
  578. console.log('success:' + JSON.stringify(res));
  579. },
  580. fail: function(err) {
  581. console.log('fail:' + JSON.stringify(err));
  582. }
  583. });
  584. // #endif
  585. },
  586. fnOnShowCommentDetail(data) {
  587. const { postId, comment } = data;
  588. this.commentDetail.comment = comment;
  589. this.commentDetail.postId = postId;
  590. this.commentDetail.list = [];
  591. this.commentDetail.show = true;
  592. this.fnGetChildComments();
  593. },
  594. fnGetChildComments() {
  595. this.commentDetail.loading = 'loading';
  596. this.$httpApi
  597. .getPostChildrenCommentList(this.commentDetail.postId, this.commentDetail.comment.id, {})
  598. .then(res => {
  599. if (res.status == 200) {
  600. this.commentDetail.loading = 'success';
  601. this.commentDetail.list = res.data;
  602. } else {
  603. this.commentDetail.loading = 'error';
  604. }
  605. console.log('getPostChildrenCommentList', res);
  606. })
  607. .catch(err => {
  608. this.commentDetail.loading = 'error';
  609. });
  610. },
  611. fnToCate(category) {
  612. uni.navigateTo({
  613. url: `/pagesA/category-detail/category-detail?slug=${category.slug}&name=${category.name}`
  614. });
  615. },
  616. fnToTag(tag) {
  617. uni.navigateTo({
  618. url: `/pagesA/tag-detail/tag-detail?id=${tag.id}&slug=${tag.slug}&name=${tag.name}`
  619. });
  620. },
  621. async fnOnCommentLoaded(data) {
  622. const _list = [];
  623. const _handleData = list => {
  624. return new Promise(resolve => {
  625. if (list.length == 0) {
  626. resolve();
  627. } else {
  628. list.forEach(item => {
  629. _list.push(item);
  630. if (item.children && item.children.length != 0) {
  631. _handleData(item.children);
  632. }
  633. resolve();
  634. });
  635. }
  636. });
  637. };
  638. await _handleData(data);
  639. if (this.globalAppSettings.barrage.use) {
  640. this.$nextTick(() => {
  641. _handleAddBarrage();
  642. });
  643. }
  644. const _handleRemove = () => {
  645. this.$refs['barrage'].remove({
  646. duration: 5000, // 延迟关闭的时间
  647. speed: 1000 // 弹幕消失的速度
  648. });
  649. };
  650. let index = 0;
  651. const _handleAddBarrage = () => {
  652. setTimeout(() => {
  653. this.$refs['barrage'].add(_list[index]);
  654. index += 1;
  655. if (index < _list.length - 1) {
  656. _handleAddBarrage();
  657. } else {
  658. _handleRemove();
  659. }
  660. }, 1000);
  661. };
  662. }
  663. }
  664. };
  665. </script>
  666. <style lang="scss" scoped>
  667. .app-page {
  668. width: 100vw;
  669. min-height: 100vh;
  670. display: flex;
  671. flex-direction: column;
  672. box-sizing: border-box;
  673. background-color: #fafafd;
  674. }
  675. .loading-wrap {
  676. padding: 0 24rpx;
  677. height: inherit;
  678. background-color: #fff;
  679. }
  680. .head {
  681. display: flex;
  682. flex-direction: column;
  683. align-items: center;
  684. justify-content: center;
  685. padding: 36rpx 24rpx;
  686. background-color: #ffffff;
  687. box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
  688. // box-shadow: 0rpx 6rpx 30rpx rgba(182, 223, 255, 0.3);
  689. border-radius: 24rpx;
  690. .title {
  691. font-size: 36rpx;
  692. font-weight: 600;
  693. text-align: center;
  694. }
  695. .detail {
  696. width: 100%;
  697. margin-top: 24rpx;
  698. font-size: 26rpx;
  699. .author {
  700. text-align: center;
  701. font-size: 24rpx;
  702. color: #666;
  703. &-name {
  704. }
  705. &-time {
  706. margin-left: 36rpx;
  707. }
  708. }
  709. .cover {
  710. margin-top: 24rpx;
  711. width: 100%;
  712. height: 280rpx;
  713. &-img {
  714. width: 100%;
  715. height: 100%;
  716. border-radius: 12rpx;
  717. }
  718. }
  719. .count {
  720. margin-top: 24rpx;
  721. display: flex;
  722. justify-content: space-between;
  723. &.no-thumbnail {
  724. border-top: 2rpx solid #f2f2f2;
  725. padding-top: 12rpx;
  726. .count-item {
  727. position: relative;
  728. color: #666;
  729. &::after {
  730. content: '';
  731. position: absolute;
  732. right: 0;
  733. background-color: #eee;
  734. width: 2rpx;
  735. height: 32rpx;
  736. }
  737. &:last-child {
  738. &::after {
  739. display: none;
  740. }
  741. }
  742. }
  743. }
  744. &-item {
  745. flex: 1;
  746. display: flex;
  747. align-items: flex-end;
  748. justify-content: center;
  749. color: #666;
  750. .label {
  751. font-size: 24rpx;
  752. padding-left: 8rpx;
  753. }
  754. .value {
  755. font-size: 32rpx;
  756. }
  757. }
  758. }
  759. }
  760. }
  761. .category {
  762. margin: 0 24rpx;
  763. padding: 24rpx;
  764. background-color: #ffffff;
  765. border-radius: 12rpx;
  766. // box-shadow: 0rpx 0rpx 24rpx rgba(182, 223, 255, 0.3);
  767. box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
  768. // border: 2rpx solid #f8f8f8;
  769. font-size: 28rpx;
  770. &-type {
  771. line-height: 55rpx;
  772. }
  773. &-tag {
  774. background-color: #5bb8fa;
  775. color: #fff;
  776. padding: 6rpx 12rpx;
  777. border-radius: 6rpx;
  778. font-size: 24rpx;
  779. &.is-empty {
  780. background-color: #607d8b;
  781. }
  782. }
  783. }
  784. .category-tag + .category-tag {
  785. margin-left: 12rpx;
  786. }
  787. .content {
  788. margin-top: 24rpx;
  789. }
  790. .evan-markdown,
  791. .ad-wrap,
  792. .copyright-wrap,
  793. .comment-wrap {
  794. box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.03);
  795. }
  796. .copyright-title {
  797. position: relative;
  798. box-sizing: border-box;
  799. padding-left: 24rpx;
  800. font-size: 30rpx;
  801. &:before {
  802. content: '';
  803. position: absolute;
  804. left: 0rpx;
  805. top: 8rpx;
  806. width: 8rpx;
  807. height: 26rpx;
  808. background-color: rgb(3, 174, 252);
  809. border-radius: 6rpx;
  810. }
  811. }
  812. .poup-head {
  813. border-bottom: 2rpx solid #f5f5f5;
  814. }
  815. .poup-body {
  816. height: 50vh;
  817. }
  818. .poup-empty {
  819. width: 100%;
  820. height: 40vh;
  821. }
  822. .poup-loading-wrap {
  823. width: 100%;
  824. height: 40vh;
  825. .e-loading-icon {
  826. font-size: 80rpx;
  827. }
  828. }
  829. .poster-content {
  830. width: 100%;
  831. min-height: 60vh;
  832. overflow: hidden;
  833. }
  834. .copyright-text {
  835. line-height: 1.7;
  836. }
  837. .poster-loading {
  838. width: 100%;
  839. position: absolute;
  840. left: 0;
  841. top: 0;
  842. right: 0;
  843. bottom: 0;
  844. // background-color: rgba(0, 0, 0, 0.1);
  845. z-index: 666;
  846. }
  847. .poster-save {
  848. box-sizing: border-box;
  849. border-top: 2rpx dashed #eee;
  850. }
  851. </style>