小说小程序前端代码仓库(小程序)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

760 lines
15 KiB

  1. <template>
  2. <!-- 小说详情页面 -->
  3. <view class="novel-detail">
  4. <navbar title="小说详情" leftClick @leftClick="$utils.navigateBack" />
  5. <!-- 小说基本信息 -->
  6. <view class="novel-info">
  7. <view class="novel-cover">
  8. <image :src="novelData.image &&
  9. novelData.image.split(',')[0]" mode="aspectFill"></image>
  10. </view>
  11. <view class="novel-basic">
  12. <text class="title">{{ novelData.name }}</text>
  13. <view class="author-line">
  14. <text class="label">作者</text>
  15. <text class="author">{{ novelData.author }}</text>
  16. </view>
  17. <!-- <view class="status-line">
  18. <text class="label">完结</text>
  19. <text class="status">{{ novelData.status }}</text>
  20. </view> -->
  21. <view class="content-row">
  22. <view class="book-status">
  23. <text>{{novelData.status}}</text>
  24. </view>
  25. <view class="book-text">
  26. {{ novelData.service }}
  27. </view>
  28. </view>
  29. <view class="score-line">
  30. <text class="score">{{ novelData.qmNum || 0}}</text>
  31. <text class="score-label">作者累计亲密度值</text>
  32. </view>
  33. </view>
  34. </view>
  35. <!-- 推荐票数显示 -->
  36. <view class="recommendation-section">
  37. <view class="rec-left">
  38. <text class="rec-count">{{ novelData.tuiNum }}</text>
  39. <text class="rec-label">推荐票数</text>
  40. </view>
  41. <view class="rec-divider"></view>
  42. <view class="rec-right">
  43. <button class="recommend-btn" @click="$refs.novelVotePopup.open()">
  44. <text class="btn-icon">📑</text>
  45. 投推荐票
  46. </button>
  47. </view>
  48. </view>
  49. <!-- 阅读和收藏按钮 -->
  50. <!-- 我的等级 -->
  51. <view class="user-level">
  52. <view class="level-left">
  53. <view class="level-title">
  54. <!-- <text class="title-icon">👑</text> -->
  55. <image style="width: 30rpx;height: 30rpx;" src="/pages_order/static/book/level.png"
  56. mode="aspectFill"></image>
  57. <text>我的等级</text>
  58. </view>
  59. <view class="level-info">
  60. <image class="user-avatar"
  61. src="/pages_order/static/book/dj.png"
  62. mode="aspectFill"></image>
  63. <view class="user-details">
  64. <text class="username">
  65. <image
  66. src="https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain"
  67. mode="aspectFill"></image>
  68. <view class="name">
  69. 周海
  70. </view>
  71. </text>
  72. <view class="user-score">
  73. <text class="score-value">6785452</text>
  74. <text class="score-label">亲密值</text>
  75. </view>
  76. <text class="user-role">护书使者 五级</text>
  77. </view>
  78. </view>
  79. </view>
  80. <view class="level-right">
  81. <view class="rank-btn">
  82. <image class="rank-icon" src="/pages_order/static/book/bd.png" mode="aspectFit"></image>
  83. <text class="check-text">点击查看</text>
  84. </view>
  85. </view>
  86. </view>
  87. <!-- 小说简介 -->
  88. <view class="novel-intro">
  89. <view class="intro-title">
  90. <text>简介</text>
  91. </view>
  92. <view class="intro-content">
  93. {{ novelData.details }}
  94. </view>
  95. </view>
  96. <!-- 目录 -->
  97. <view class="novel-catalog" @click="$refs.chapterPopup.open()">
  98. <view class="catalog-header">
  99. <view class="catalog-title">
  100. <text class="title-icon">📖</text>
  101. <text>目录</text>
  102. </view>
  103. <view class="chapter-nav">
  104. <text class="current-chapter">第九集 - 高去与归来</text>
  105. <text class="nav-arrow">></text>
  106. </view>
  107. </view>
  108. </view>
  109. <!-- 书评区域 -->
  110. <view class="comments-section">
  111. <view class="comments-header">
  112. <view class="header-left">
  113. <text class="title-icon">📝</text>
  114. <text>书评</text>
  115. </view>
  116. <view class="header-right">
  117. <text @click="goToWriteReview">写书评</text>
  118. </view>
  119. </view>
  120. <view class="comment-list">
  121. <commentItem v-for="(item, index) in 10" :key="index" />
  122. </view>
  123. </view>
  124. <!-- 底部操作栏 -->
  125. <view class="novel-bottom">
  126. <view class="bottom-left">
  127. <view class="action-btn" @click="addToBookshelf">
  128. <view class="btn-icon">
  129. <uv-icon
  130. name="grid"
  131. color="#999"
  132. size="60rpx"
  133. ></uv-icon>
  134. </view>
  135. <text>加入书架</text>
  136. </view>
  137. <!-- <view class="action-btn" @click="goToGiftbox">
  138. <text class="btn-icon">🎁</text>
  139. <text>礼物盒</text>
  140. </view> -->
  141. <view class="action-btn"
  142. @click="$utils.navigateTo(`/pages_order/novel/Giftbox?id=${novelData.id}`)">
  143. <view class="btn-icon">
  144. <uv-icon
  145. name="gift"
  146. color="#999"
  147. size="60rpx"
  148. ></uv-icon>
  149. </view>
  150. <text>互动打赏</text>
  151. </view>
  152. </view>
  153. <view class="bottom-right">
  154. <button class="read-now-btn" @click="">立即阅读</button>
  155. </view>
  156. </view>
  157. <novelVotePopup ref="novelVotePopup"/>
  158. <chapterPopup ref="chapterPopup" />
  159. </view>
  160. </template>
  161. <script>
  162. import catalogpopup from '@/components/novel/CatalogPopup.vue'
  163. import chapterPopup from '../components/novel/chapterPopup.vue'
  164. import commentItem from '../components/comment/commentItem.vue'
  165. import novelVotePopup from '../components/novel/novelVotePopup.vue'
  166. import mixinsList from '@/mixins/list.js'
  167. export default {
  168. mixins: [mixinsList],
  169. components: {
  170. catalogpopup,
  171. chapterPopup,
  172. commentItem,
  173. novelVotePopup,
  174. },
  175. data() {
  176. return {
  177. novelData: {},
  178. isCollected: false,
  179. comments: [],
  180. currentIndex: 0,
  181. id : 0,
  182. bookLevel : {},
  183. mixinsListApi : 'getBookCommentList',
  184. }
  185. },
  186. computed: {
  187. userInfo() {
  188. return {
  189. avatar: '/static/images/avatar.jpg',
  190. level: '67级达人'
  191. }
  192. }
  193. },
  194. onLoad({id}) {
  195. this.id = id
  196. this.queryParams.id = id
  197. this.getDateil()
  198. this.getAchievement()
  199. },
  200. methods: {
  201. getDateil(){
  202. this.$fetch('getBookDetail', {
  203. id : this.id
  204. }).then(res => {
  205. this.novelData = res
  206. })
  207. },
  208. getAchievement(){
  209. this.$fetch('getAchievement', {
  210. id : this.id
  211. }).then(res => {
  212. this.bookLevel = res
  213. })
  214. },
  215. getBookCatalogList(){
  216. this.$fetch('getBookCatalogList', {
  217. id : this.id
  218. }).then(res => {
  219. })
  220. },
  221. toggleCollect() {
  222. this.isCollected = !this.isCollected
  223. },
  224. goToWriteReview() {
  225. uni.navigateTo({
  226. url: `/pages_order/novel/Review?title=${encodeURIComponent(this.novelData.title)}`
  227. })
  228. },
  229. addToBookshelf() {
  230. // TODO: 实现加入书架功能
  231. uni.showToast({
  232. title: '已加入书架',
  233. icon: 'success'
  234. })
  235. },
  236. toggleInteractive() {
  237. uni.navigateTo({
  238. url: `/pages_order/novel/Tipping?id=${this.novelData.id}`
  239. })
  240. },
  241. goToGiftbox() {
  242. uni.navigateTo({
  243. url: `/pages_order/novel/Giftbox?id=${this.novelData.id}`
  244. })
  245. },
  246. goToCommentReply() {
  247. uni.navigateTo({
  248. url: '/pages_order/novel/comments'
  249. })
  250. }
  251. }
  252. }
  253. </script>
  254. <style lang="scss" scoped>
  255. .novel-detail {
  256. min-height: 100vh;
  257. background-color: #f5f5f5;
  258. padding-bottom: calc(env(safe-area-inset-bottom) + 30rpx);
  259. .nav-header {
  260. display: flex;
  261. justify-content: space-between;
  262. align-items: center;
  263. padding: 20rpx 30rpx;
  264. background-color: transparent;
  265. position: fixed;
  266. top: 0;
  267. left: 0;
  268. right: 0;
  269. z-index: 100;
  270. }
  271. .novel-info {
  272. padding: 20rpx;
  273. display: flex;
  274. background: #fff;
  275. .novel-cover {
  276. width: 200rpx;
  277. height: 280rpx;
  278. margin-right: 20rpx;
  279. image {
  280. width: 100%;
  281. height: 100%;
  282. border-radius: 8rpx;
  283. }
  284. }
  285. .novel-basic {
  286. flex: 1;
  287. display: flex;
  288. flex-direction: column;
  289. justify-content: space-between;
  290. .title {
  291. font-size: 36rpx;
  292. font-weight: bold;
  293. margin-bottom: 16rpx;
  294. }
  295. .author-line,
  296. .status-line {
  297. display: flex;
  298. align-items: center;
  299. margin-bottom: 12rpx;
  300. font-size: 28rpx;
  301. color: #666;
  302. }
  303. .content-row {
  304. display: flex;
  305. align-items: center;
  306. margin-bottom: 10rpx;
  307. .book-status {
  308. flex-shrink: 0;
  309. text {
  310. font-size: 20rpx;
  311. color: #67C23A;
  312. background-color: rgba(103, 194, 58, 0.1);
  313. border-radius: 20rpx;
  314. padding: 4rpx 12rpx;
  315. }
  316. }
  317. .book-text{
  318. font-size: 20rpx;
  319. }
  320. }
  321. .label {
  322. color: #999;
  323. margin-right: 8rpx;
  324. }
  325. .score-line {
  326. margin-top: 16rpx;
  327. .score {
  328. font-size: 32rpx;
  329. color: #333;
  330. font-weight: bold;
  331. }
  332. .score-label {
  333. font-size: 24rpx;
  334. color: #999;
  335. margin-left: 8rpx;
  336. }
  337. }
  338. }
  339. }
  340. .recommendation-section {
  341. padding: 24rpx 32rpx;
  342. background: #fff;
  343. display: flex;
  344. justify-content: space-between;
  345. align-items: center;
  346. position: relative;
  347. .rec-left {
  348. display: flex;
  349. flex-direction: column;
  350. align-items: flex-start;
  351. margin-left: 70rpx;
  352. .rec-count {
  353. font-size: 44rpx;
  354. font-weight: 500;
  355. color: #333;
  356. line-height: 1.2;
  357. }
  358. .rec-label {
  359. font-size: 26rpx;
  360. color: #999;
  361. margin-top: 4rpx;
  362. }
  363. }
  364. .rec-divider {
  365. position: absolute;
  366. right: 160rpx;
  367. top: 20rpx;
  368. bottom: 20rpx;
  369. width: 2rpx;
  370. background: #eee;
  371. }
  372. .rec-right {
  373. flex-shrink: 0;
  374. .recommend-btn {
  375. background: #fff;
  376. color: #4a90e2;
  377. border: 2rpx solid #4a90e2;
  378. border-radius: 40rpx;
  379. padding: 12rpx 32rpx;
  380. font-size: 28rpx;
  381. display: flex;
  382. align-items: center;
  383. line-height: 1;
  384. height: 64rpx;
  385. .btn-icon {
  386. margin-right: 8rpx;
  387. font-size: 32rpx;
  388. }
  389. }
  390. }
  391. }
  392. .action-buttons {
  393. display: flex;
  394. padding: 30rpx;
  395. gap: 20rpx;
  396. button {
  397. flex: 1;
  398. height: 80rpx;
  399. border-radius: 40rpx;
  400. font-size: 32rpx;
  401. display: flex;
  402. align-items: center;
  403. justify-content: center;
  404. }
  405. .read-btn {
  406. background-color: #4a90e2;
  407. color: #fff;
  408. }
  409. .collect-btn {
  410. background-color: #f0f0f0;
  411. color: #666;
  412. }
  413. }
  414. .user-level {
  415. margin: 20rpx 30rpx;
  416. background-color: #fff;
  417. border-radius: 12rpx;
  418. padding: 24rpx 32rpx;
  419. display: flex;
  420. justify-content: space-between;
  421. align-items: stretch;
  422. .level-left {
  423. flex: 1;
  424. .level-title {
  425. display: flex;
  426. align-items: center;
  427. gap: 8rpx;
  428. margin-bottom: 20rpx;
  429. margin-left: 20rpx;
  430. .title-icon {
  431. font-size: 36rpx;
  432. color: #FFB800;
  433. }
  434. text {
  435. font-size: 32rpx;
  436. font-weight: 500;
  437. color: #333;
  438. }
  439. }
  440. .level-info {
  441. display: flex;
  442. align-items: flex-start;
  443. gap: 20rpx;
  444. .user-avatar {
  445. width: 80rpx;
  446. height: 80rpx;
  447. border-radius: 50%;
  448. border: 2rpx solid #f0f0f0;
  449. }
  450. .user-details {
  451. display: flex;
  452. flex-direction: column;
  453. gap: 8rpx;
  454. .username {
  455. display: flex;
  456. font-size: 28rpx;
  457. color: #333;
  458. font-weight: 500;
  459. image {
  460. width: 60rpx;
  461. height: 60rpx;
  462. }
  463. }
  464. .user-score {
  465. display: flex;
  466. align-items: center;
  467. gap: 8rpx;
  468. .score-value {
  469. font-size: 28rpx;
  470. color: #333;
  471. }
  472. .score-label {
  473. font-size: 24rpx;
  474. color: #999;
  475. }
  476. }
  477. .user-role {
  478. font-size: 24rpx;
  479. color: #666;
  480. background: #f5f5f5;
  481. padding: 4rpx 12rpx;
  482. border-radius: 4rpx;
  483. display: inline-block;
  484. }
  485. }
  486. }
  487. }
  488. .level-right {
  489. display: flex;
  490. align-items: center;
  491. .rank-btn {
  492. display: flex;
  493. flex-direction: column;
  494. align-items: center;
  495. justify-content: center;
  496. padding: 0 20rpx;
  497. .rank-icon {
  498. width: 200rpx;
  499. height: 60rpx;
  500. margin-bottom: 8rpx;
  501. }
  502. text {
  503. font-size: 26rpx;
  504. color: #333;
  505. line-height: 1.4;
  506. }
  507. .check-text {
  508. font-size: 22rpx;
  509. color: #999;
  510. }
  511. }
  512. }
  513. }
  514. .novel-intro {
  515. margin: 20rpx 30rpx;
  516. background-color: #fff;
  517. border-radius: 12rpx;
  518. padding: 24rpx;
  519. .intro-title {
  520. font-size: 32rpx;
  521. font-weight: 500;
  522. color: #333;
  523. margin-bottom: 16rpx;
  524. }
  525. .intro-content {
  526. font-size: 28rpx;
  527. color: #666;
  528. line-height: 1.6;
  529. display: flex;
  530. flex-direction: column;
  531. gap: 16rpx;
  532. text {
  533. display: block;
  534. }
  535. }
  536. }
  537. .comments-section {
  538. margin: 20rpx 30rpx;
  539. background-color: #fff;
  540. border-radius: 12rpx;
  541. padding: 24rpx;
  542. .comments-header {
  543. display: flex;
  544. align-items: center;
  545. margin-bottom: 24rpx;
  546. border-bottom: 2rpx solid #f5f5f5;
  547. padding-bottom: 24rpx;
  548. justify-content: flex-start;
  549. .header-left {
  550. display: flex;
  551. align-items: center;
  552. gap: 8rpx;
  553. .title-icon {
  554. font-size: 32rpx;
  555. }
  556. text {
  557. display: flex;
  558. align-items: center;
  559. font-size: 32rpx;
  560. font-weight: 500;
  561. color: #333;
  562. white-space: nowrap;
  563. }
  564. }
  565. .header-right {
  566. margin-left: auto;
  567. }
  568. }
  569. .comment-list {
  570. display: flex;
  571. flex-direction: column;
  572. gap: 32rpx;
  573. }
  574. .like-icon {
  575. font-size: 24rpx;
  576. color: #999;
  577. }
  578. .like-count {
  579. font-size: 24rpx;
  580. color: #999;
  581. }
  582. }
  583. .novel-catalog {
  584. margin: 20rpx 30rpx;
  585. background-color: #fff;
  586. border-radius: 12rpx;
  587. padding: 24rpx;
  588. .catalog-header {
  589. display: flex;
  590. justify-content: space-between;
  591. align-items: center;
  592. border-bottom: 2rpx solid #f5f5f5;
  593. .catalog-title {
  594. display: flex;
  595. align-items: center;
  596. gap: 8rpx;
  597. .title-icon {
  598. font-size: 32rpx;
  599. }
  600. text {
  601. font-size: 32rpx;
  602. font-weight: 500;
  603. color: #333;
  604. }
  605. }
  606. .chapter-nav {
  607. display: flex;
  608. align-items: center;
  609. gap: 8rpx;
  610. .current-chapter {
  611. font-size: 28rpx;
  612. color: #666;
  613. }
  614. .nav-arrow {
  615. font-size: 28rpx;
  616. color: #999;
  617. }
  618. }
  619. }
  620. }
  621. .novel-bottom {
  622. position: fixed;
  623. bottom: 0;
  624. left: 0;
  625. right: 0;
  626. height: 100rpx;
  627. background: #fff;
  628. display: flex;
  629. align-items: center;
  630. padding: 0 30rpx;
  631. padding-top: 15rpx;
  632. padding-bottom: env(safe-area-inset-bottom);
  633. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  634. gap: 40rpx;
  635. .bottom-left {
  636. display: flex;
  637. gap: 40rpx;
  638. .action-btn {
  639. display: flex;
  640. flex-direction: column;
  641. align-items: center;
  642. gap: 4rpx;
  643. .btn-icon {
  644. font-size: 40rpx;
  645. line-height: 1;
  646. }
  647. text {
  648. font-size: 24rpx;
  649. color: #666;
  650. }
  651. }
  652. }
  653. .bottom-right {
  654. flex: 1;
  655. display: flex;
  656. .read-now-btn {
  657. flex: 1;
  658. background: #1a237e;
  659. color: #fff;
  660. font-size: 32rpx;
  661. height: 80rpx;
  662. line-height: 80rpx;
  663. padding: 0 60rpx;
  664. border-radius: 40rpx;
  665. border: none;
  666. }
  667. }
  668. }
  669. }
  670. </style>