show.blade.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  1. @extends('library.layouts.app')
  2. @section('title', $anthology['title'] . ' · 巴利书库')
  3. @section('breadcrumb')
  4. <li class="breadcrumb-item">
  5. <a href="{{ route('library.home') }}">首页</a>
  6. </li>
  7. <li class="breadcrumb-item">
  8. <a href="{{ route('library.anthology.index') }}">文集</a>
  9. </li>
  10. <li class="breadcrumb-item active">
  11. {{ $anthology['title'] }}
  12. </li>
  13. @endsection
  14. @once
  15. @push('styles')
  16. <link rel="preconnect" href="https://fonts.googleapis.com">
  17. <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&family=Noto+Sans+SC:wght@300;400;500&display=swap" rel="stylesheet">
  18. <style>
  19. body {
  20. background: var(--sf-pale) !important;
  21. font-family: 'Noto Sans SC', sans-serif;
  22. }
  23. /* Hero */
  24. .anthology-hero {
  25. background: linear-gradient(135deg, var(--ink) 0%, #2d2010 100%);
  26. padding: 2.5rem 0;
  27. }
  28. .hero-inner {
  29. display: flex;
  30. gap: 2.25rem;
  31. align-items: flex-start;
  32. }
  33. /* Book cover */
  34. .book-cover-3d {
  35. width: 155px;
  36. min-width: 155px;
  37. height: 215px;
  38. border-radius: 3px 9px 9px 3px;
  39. display: flex;
  40. flex-direction: column;
  41. align-items: center;
  42. justify-content: center;
  43. padding: 1.25rem .9rem;
  44. position: relative;
  45. overflow: hidden;
  46. box-shadow: -4px 0 0 rgba(0, 0, 0, .3), -6px 4px 14px rgba(0, 0, 0, .4), 4px 4px 18px rgba(0, 0, 0, .3);
  47. flex-shrink: 0;
  48. }
  49. .book-cover-3d img {
  50. position: absolute;
  51. inset: 0;
  52. width: 100%;
  53. height: 100%;
  54. object-fit: cover;
  55. }
  56. .book-cover-3d::before {
  57. content: '';
  58. position: absolute;
  59. left: 0;
  60. top: 0;
  61. bottom: 0;
  62. width: 13px;
  63. background: linear-gradient(to right, rgba(0, 0, 0, .4), rgba(0, 0, 0, .1));
  64. border-radius: 3px 0 0 3px;
  65. z-index: 2;
  66. }
  67. .book-cover-3d::after {
  68. content: '';
  69. position: absolute;
  70. inset: 0;
  71. background: repeating-linear-gradient(45deg, transparent, transparent 8px, rgba(255, 255, 255, .015) 8px, rgba(255, 255, 255, .015) 9px);
  72. z-index: 1;
  73. }
  74. .book-text-wrap {
  75. position: relative;
  76. z-index: 3;
  77. text-align: center;
  78. }
  79. .book-title-text {
  80. font-family: 'Noto Serif SC', serif;
  81. font-size: 1.05rem;
  82. font-weight: 600;
  83. color: #fff;
  84. line-height: 1.65;
  85. letter-spacing: .13em;
  86. word-break: break-all;
  87. }
  88. .book-divider {
  89. width: 32px;
  90. height: 1px;
  91. background: var(--sf);
  92. margin: .65rem auto;
  93. }
  94. .book-sub-text {
  95. font-size: .65rem;
  96. color: rgba(255, 255, 255, .5);
  97. letter-spacing: .06em;
  98. line-height: 1.5;
  99. }
  100. /* Hero right */
  101. .hero-content {
  102. flex: 1;
  103. min-width: 0;
  104. }
  105. .hero-title {
  106. font-family: 'Noto Serif SC', serif;
  107. font-size: 1.75rem;
  108. font-weight: 700;
  109. color: #fff;
  110. line-height: 1.3;
  111. margin-bottom: .4rem;
  112. }
  113. .hero-subtitle {
  114. font-size: .88rem;
  115. color: rgba(255, 255, 255, .45);
  116. font-style: italic;
  117. letter-spacing: .04em;
  118. margin-bottom: 1.1rem;
  119. }
  120. .hero-tags {
  121. display: flex;
  122. flex-wrap: wrap;
  123. gap: .35rem;
  124. margin-bottom: 1.3rem;
  125. }
  126. .hero-tag {
  127. font-size: .72rem;
  128. padding: 2px 9px;
  129. border-radius: 20px;
  130. background: rgba(200, 134, 10, .2);
  131. color: var(--sf);
  132. border: 1px solid rgba(200, 134, 10, .3);
  133. }
  134. .hero-info-row {
  135. display: flex;
  136. flex-wrap: wrap;
  137. gap: 1.4rem;
  138. margin-bottom: 1.3rem;
  139. }
  140. .hi-item {
  141. display: flex;
  142. align-items: center;
  143. gap: .45rem;
  144. }
  145. .hi-label {
  146. font-size: .72rem;
  147. color: rgba(255, 255, 255, .4);
  148. letter-spacing: .04em;
  149. display: block;
  150. }
  151. .hi-value {
  152. font-size: .83rem;
  153. color: rgba(255, 255, 255, .82);
  154. display: block;
  155. }
  156. .hi-avatar {
  157. width: 26px;
  158. height: 26px;
  159. border-radius: 50%;
  160. display: flex;
  161. align-items: center;
  162. justify-content: center;
  163. font-size: .68rem;
  164. font-weight: 700;
  165. flex-shrink: 0;
  166. }
  167. .hero-desc {
  168. font-size: .85rem;
  169. color: rgba(255, 255, 255, .6);
  170. line-height: 1.85;
  171. margin-bottom: 1.6rem;
  172. max-width: 600px;
  173. }
  174. .btn-read-primary {
  175. background: var(--sf);
  176. color: var(--ink);
  177. font-weight: 700;
  178. font-size: .88rem;
  179. padding: .55rem 1.6rem;
  180. border-radius: 6px;
  181. border: none;
  182. cursor: pointer;
  183. text-decoration: none;
  184. display: inline-flex;
  185. align-items: center;
  186. gap: .45rem;
  187. transition: background .2s, transform .15s;
  188. }
  189. .btn-read-primary:hover {
  190. background: #dea020;
  191. color: var(--ink);
  192. transform: translateY(-1px);
  193. }
  194. .btn-outline-hero {
  195. background: transparent;
  196. color: rgba(255, 255, 255, .7);
  197. font-size: .85rem;
  198. padding: .5rem 1.3rem;
  199. border-radius: 6px;
  200. border: 1px solid rgba(255, 255, 255, .2);
  201. cursor: pointer;
  202. text-decoration: none;
  203. display: inline-flex;
  204. align-items: center;
  205. gap: .4rem;
  206. transition: all .2s;
  207. margin-left: .65rem;
  208. }
  209. .btn-outline-hero:hover {
  210. border-color: rgba(255, 255, 255, .5);
  211. color: #fff;
  212. }
  213. /* Section card */
  214. .sec-card {
  215. background: var(--card-bg);
  216. border: 1px solid var(--bdr);
  217. border-radius: 10px;
  218. overflow: hidden;
  219. margin-bottom: 1.3rem;
  220. }
  221. .sec-header {
  222. padding: .85rem 1.4rem;
  223. border-bottom: 1px solid var(--bdr);
  224. display: flex;
  225. align-items: center;
  226. gap: .55rem;
  227. }
  228. .sec-bar {
  229. width: 3px;
  230. height: 15px;
  231. background: var(--sf);
  232. border-radius: 2px;
  233. flex-shrink: 0;
  234. }
  235. .sec-title {
  236. font-family: 'Noto Serif SC', serif;
  237. font-size: .9rem;
  238. font-weight: 600;
  239. color: var(--ink-soft);
  240. letter-spacing: .04em;
  241. }
  242. .sec-count {
  243. margin-left: auto;
  244. font-size: .75rem;
  245. color: var(--ink-muted);
  246. background: var(--sf-light);
  247. padding: 2px 8px;
  248. border-radius: 10px;
  249. }
  250. /* About */
  251. .sec-body {
  252. padding: 1.15rem 1.4rem;
  253. font-size: .855rem;
  254. color: var(--ink-soft);
  255. line-height: 1.95;
  256. }
  257. .sec-body p {
  258. margin-bottom: .8rem;
  259. }
  260. .sec-body p:last-child {
  261. margin-bottom: 0;
  262. }
  263. /* TOC */
  264. .toc-ul {
  265. list-style: none;
  266. padding: .35rem 0;
  267. margin: 0;
  268. }
  269. .toc-ul li a {
  270. display: flex;
  271. align-items: center;
  272. padding: .65rem 1.4rem;
  273. text-decoration: none;
  274. border-bottom: 1px solid rgba(232, 221, 208, .5);
  275. transition: background .15s;
  276. }
  277. .toc-ul li:last-child a {
  278. border-bottom: none;
  279. }
  280. .toc-ul li a:hover {
  281. background: var(--sf-pale);
  282. }
  283. .toc-num {
  284. font-size: .72rem;
  285. color: var(--ink-muted);
  286. width: 26px;
  287. flex-shrink: 0;
  288. }
  289. .toc-name {
  290. font-size: .855rem;
  291. color: var(--ink-soft);
  292. flex: 1;
  293. line-height: 1.4;
  294. }
  295. .toc-ul li a:hover .toc-name {
  296. color: var(--sf);
  297. }
  298. .toc-arrow {
  299. color: var(--bdr);
  300. font-size: .85rem;
  301. }
  302. .toc-ul li a:hover .toc-arrow {
  303. color: var(--sf);
  304. }
  305. /* Sidebar */
  306. .sb-card {
  307. background: var(--card-bg);
  308. border: 1px solid var(--bdr);
  309. border-radius: 10px;
  310. overflow: hidden;
  311. margin-bottom: 1.15rem;
  312. }
  313. .sb-head {
  314. padding: .8rem 1.2rem;
  315. border-bottom: 1px solid var(--bdr);
  316. font-family: 'Noto Serif SC', serif;
  317. font-size: .875rem;
  318. font-weight: 600;
  319. color: var(--ink-soft);
  320. letter-spacing: .04em;
  321. display: flex;
  322. align-items: center;
  323. gap: .45rem;
  324. }
  325. .sb-head::before {
  326. content: '';
  327. display: block;
  328. width: 3px;
  329. height: 13px;
  330. background: var(--sf);
  331. border-radius: 2px;
  332. }
  333. .smeta-row {
  334. display: flex;
  335. padding: .7rem 1.2rem;
  336. border-bottom: 1px solid var(--bdr);
  337. font-size: .8rem;
  338. align-items: flex-start;
  339. gap: .45rem;
  340. }
  341. .smeta-row:last-child {
  342. border-bottom: none;
  343. }
  344. .smeta-label {
  345. color: var(--ink-muted);
  346. min-width: 65px;
  347. flex-shrink: 0;
  348. }
  349. .smeta-value {
  350. color: var(--ink-soft);
  351. font-weight: 500;
  352. }
  353. .smeta-value a {
  354. color: var(--sf);
  355. text-decoration: none;
  356. }
  357. .smeta-value a:hover {
  358. text-decoration: underline;
  359. }
  360. /* Author card */
  361. .author-block {
  362. display: flex;
  363. align-items: center;
  364. gap: .8rem;
  365. padding: 1.1rem 1.2rem;
  366. }
  367. .author-av-lg {
  368. width: 48px;
  369. height: 48px;
  370. border-radius: 50%;
  371. display: flex;
  372. align-items: center;
  373. justify-content: center;
  374. font-size: .95rem;
  375. font-weight: 700;
  376. flex-shrink: 0;
  377. }
  378. .author-av-img {
  379. width: 48px;
  380. height: 48px;
  381. border-radius: 50%;
  382. object-fit: cover;
  383. flex-shrink: 0;
  384. }
  385. .hi-avatar {
  386. width: 26px;
  387. height: 26px;
  388. border-radius: 50%;
  389. display: flex;
  390. align-items: center;
  391. justify-content: center;
  392. font-size: .68rem;
  393. font-weight: 700;
  394. flex-shrink: 0;
  395. }
  396. .hi-avatar-img {
  397. width: 26px;
  398. height: 26px;
  399. border-radius: 50%;
  400. object-fit: cover;
  401. flex-shrink: 0;
  402. }
  403. .author-block-name {
  404. font-weight: 600;
  405. font-size: .9rem;
  406. color: var(--ink);
  407. margin-bottom: .18rem;
  408. }
  409. .author-block-stats {
  410. font-size: .75rem;
  411. color: var(--ink-muted);
  412. }
  413. .author-bio {
  414. font-size: .78rem;
  415. color: var(--ink-muted);
  416. line-height: 1.65;
  417. padding: 0 1.2rem 1.1rem;
  418. border-top: 1px solid var(--bdr);
  419. padding-top: .9rem;
  420. }
  421. /* Related */
  422. .related-ul {
  423. list-style: none;
  424. padding: 0;
  425. margin: 0;
  426. }
  427. .related-ul li a {
  428. display: flex;
  429. align-items: center;
  430. gap: .7rem;
  431. padding: .7rem 1.2rem;
  432. border-bottom: 1px solid var(--bdr);
  433. text-decoration: none;
  434. transition: background .15s;
  435. }
  436. .related-ul li:last-child a {
  437. border-bottom: none;
  438. }
  439. .related-ul li a:hover {
  440. background: var(--sf-pale);
  441. }
  442. .related-cover-mini {
  443. width: 34px;
  444. height: 46px;
  445. border-radius: 2px 5px 5px 2px;
  446. display: flex;
  447. align-items: center;
  448. justify-content: center;
  449. font-size: .6rem;
  450. color: rgba(255, 255, 255, .8);
  451. font-family: 'Noto Serif SC', serif;
  452. flex-shrink: 0;
  453. text-align: center;
  454. line-height: 1.3;
  455. }
  456. .related-t {
  457. font-size: .8rem;
  458. color: var(--ink-soft);
  459. font-weight: 500;
  460. margin-bottom: .18rem;
  461. line-height: 1.3;
  462. }
  463. .related-ul li a:hover .related-t {
  464. color: var(--sf);
  465. }
  466. .related-a {
  467. font-size: .7rem;
  468. color: var(--ink-muted);
  469. }
  470. @media (max-width: 900px) {
  471. .hero-inner {
  472. flex-direction: column;
  473. align-items: center;
  474. }
  475. .book-cover-3d {
  476. height: 170px;
  477. }
  478. }
  479. </style>
  480. @endpush
  481. @endonce
  482. @section('content')
  483. {{-- Hero --}}
  484. <div class="anthology-hero">
  485. <div class="container-xl">
  486. <div class="hero-inner">
  487. {{-- 3D Book Cover --}}
  488. <div class="book-cover-3d" style="{{ $anthology['cover_image'] ? '' : 'background: ' . $anthology['cover_gradient'] }}">
  489. @if($anthology['cover_image'])
  490. <img src="{{ $anthology['cover_image'] }}" alt="{{ $anthology['title'] }}">
  491. @else
  492. <div class="book-text-wrap">
  493. <div class="book-title-text">{{ $anthology['title'] }}</div>
  494. <div class="book-divider"></div>
  495. <div class="book-sub-text">{{ $anthology['subtitle'] ?? '' }}</div>
  496. </div>
  497. @endif
  498. </div>
  499. {{-- Content --}}
  500. <div class="hero-content">
  501. <div class="hero-title">{{ $anthology['title'] }}</div>
  502. @if(!empty($anthology['subtitle']))
  503. <div class="hero-subtitle">{{ $anthology['subtitle'] }}</div>
  504. @endif
  505. @if(!empty($anthology['tags']))
  506. <div class="hero-tags">
  507. @foreach($anthology['tags'] as $tag)
  508. <span class="hero-tag">{{ $tag }}</span>
  509. @endforeach
  510. </div>
  511. @endif
  512. <div class="hero-info-row">
  513. <div class="hi-item">
  514. @if(!empty($anthology['author']['avatar']))
  515. <img src="{{ $anthology['author']['avatar'] }}" class="hi-avatar-img" alt="">
  516. @else
  517. <div class="hi-avatar" style="background: {{ $anthology['author']['color'] }}; color: #fff">
  518. {{ $anthology['author']['initials'] }}
  519. </div>
  520. @endif
  521. <div>
  522. <span class="hi-label">作者</span>
  523. <span class="hi-value">{{ $anthology['author']['name'] }}</span>
  524. </div>
  525. </div>
  526. <div class="hi-item">
  527. <div>
  528. <span class="hi-label">最后更新</span>
  529. <span class="hi-value">{{ $anthology['updated_at'] }}</span>
  530. </div>
  531. </div>
  532. <div class="hi-item">
  533. <div>
  534. <span class="hi-label">章节数</span>
  535. <span class="hi-value">{{ $anthology['children_number'] }} 章节</span>
  536. </div>
  537. </div>
  538. <div class="hi-item">
  539. <div>
  540. <span class="hi-label">创建时间</span>
  541. <span class="hi-value">{{ $anthology['created_at'] }}</span>
  542. </div>
  543. </div>
  544. </div>
  545. @if(!empty($anthology['description']))
  546. <div class="hero-desc">{{ $anthology['description'] }}</div>
  547. @endif
  548. <div>
  549. @if(!empty($anthology['articles']))
  550. <a href="{{ route('library.anthology.read', ['anthology' => $anthology['id'], 'article' => $anthology['articles'][0]['id']]) }}" class="btn-read-primary">
  551. <i class="ti ti-book-2"></i>
  552. 在线阅读
  553. </a>
  554. @endif
  555. <a href="{{ config('mint.server.dashboard_base_path') }}/workspace/anthology/{{ $anthology['id'] }}" class="btn-outline-hero">
  556. <i class="ti ti-pencil"></i>
  557. 在编辑器中打开
  558. </a>
  559. </div>
  560. </div>
  561. </div>
  562. </div>
  563. </div>
  564. {{-- Body --}}
  565. <div class="page-body" style="background: var(--sf-pale);">
  566. <div class="container-xl">
  567. <div class="row mt-2">
  568. {{-- Left --}}
  569. <div class="col-lg-8">
  570. {{-- About --}}
  571. @if(!empty($anthology['about']))
  572. <div class="sec-card">
  573. <div class="sec-header">
  574. <div class="sec-bar"></div>
  575. <div class="sec-title">关于本文集</div>
  576. </div>
  577. <div class="sec-body">
  578. @foreach(explode("\n", $anthology['about']) as $para)
  579. @if(trim($para))
  580. <p>{{ trim($para) }}</p>
  581. @endif
  582. @endforeach
  583. </div>
  584. </div>
  585. @endif
  586. {{-- TOC --}}
  587. <div class="sec-card">
  588. <div class="sec-header">
  589. <div class="sec-bar"></div>
  590. <div class="sec-title">目录</div>
  591. <div class="sec-count">{{ $anthology['children_number'] }} 章节</div>
  592. </div>
  593. <ul class="toc-ul">
  594. @foreach($anthology['articles'] as $article)
  595. <li>
  596. <a href="{{ route('library.anthology.read', ['anthology' => $anthology['id'], 'article' => $article['id']]) }}">
  597. <span class="toc-num">{{ str_pad($article['order'], 2, '0', STR_PAD_LEFT) }}</span>
  598. <span class="toc-name">{{ $article['title'] }}</span>
  599. <span class="toc-arrow">›</span>
  600. </a>
  601. </li>
  602. @endforeach
  603. </ul>
  604. </div>
  605. </div>{{-- /col --}}
  606. {{-- Sidebar --}}
  607. <div class="col-lg-4">
  608. {{-- Meta --}}
  609. <div class="sb-card">
  610. <div class="sb-head">文集信息</div>
  611. <div class="smeta-row">
  612. <span class="smeta-label">作者</span>
  613. <span class="smeta-value"><a href="#">{{ $anthology['author']['name'] }}</a></span>
  614. </div>
  615. @if(!empty($anthology['language']))
  616. <div class="smeta-row">
  617. <span class="smeta-label">语言</span>
  618. <span class="smeta-value">{{ $anthology['language'] }}</span>
  619. </div>
  620. @endif
  621. <div class="smeta-row">
  622. <span class="smeta-label">章节</span>
  623. <span class="smeta-value">{{ $anthology['children_number'] }} 章节</span>
  624. </div>
  625. <div class="smeta-row">
  626. <span class="smeta-label">创建</span>
  627. <span class="smeta-value">{{ $anthology['created_at'] }}</span>
  628. </div>
  629. <div class="smeta-row">
  630. <span class="smeta-label">更新</span>
  631. <span class="smeta-value">{{ $anthology['updated_at'] }}</span>
  632. </div>
  633. @if(!empty($anthology['category']))
  634. <div class="smeta-row">
  635. <span class="smeta-label">分类</span>
  636. <span class="smeta-value">{{ $anthology['category'] }}</span>
  637. </div>
  638. @endif
  639. </div>
  640. {{-- Author --}}
  641. <div class="sb-card">
  642. <div class="sb-head">作者</div>
  643. <div class="author-block">
  644. @if(!empty($anthology['author']['avatar']))
  645. <img src="{{ $anthology['author']['avatar'] }}" class="author-av-img" alt="">
  646. @else
  647. <div class="author-av-lg" style="background: {{ $anthology['author']['color'] }}; color: #fff">
  648. {{ $anthology['author']['initials'] }}
  649. </div>
  650. @endif
  651. <div>
  652. <div class="author-block-name">{{ $anthology['author']['name'] }}</div>
  653. <div class="author-block-stats">
  654. @if($anthology['author']['article_count'])
  655. {{ $anthology['author']['article_count'] }} 篇文章
  656. @endif
  657. </div>
  658. </div>
  659. </div>
  660. @if(!empty($anthology['author']['bio']))
  661. <div class="author-bio">{{ $anthology['author']['bio'] }}</div>
  662. @endif
  663. </div>
  664. {{-- Related --}}
  665. @if($related->count())
  666. <div class="sb-card">
  667. <div class="sb-head">相关文集</div>
  668. <ul class="related-ul">
  669. @foreach($related as $rel)
  670. <li>
  671. <a href="{{ route('library.anthology.show', $rel['id']) }}">
  672. <div class="related-cover-mini" style="background: {{ $rel['cover_gradient'] }}">
  673. {{ mb_substr($rel['title'], 0, 4) }}
  674. </div>
  675. <div>
  676. <div class="related-t">{{ $rel['title'] }}</div>
  677. <div class="related-a">{{ $rel['author_name'] }}</div>
  678. </div>
  679. </a>
  680. </li>
  681. @endforeach
  682. </ul>
  683. </div>
  684. @endif
  685. </div>
  686. </div>
  687. </div>
  688. </div>
  689. @endsection