index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. <template>
  2. <!-- 首页 -->
  3. <div class="main-background">
  4. <!-- 轮播 -->
  5. <Carousel v-bind="carouselConfig" class="main-header-box small carousel-light">
  6. <Slide
  7. v-for="(item, key) in bannerData.content.value"
  8. :key="key"
  9. class="main-header-box small"
  10. >
  11. <img :src="item.image" />
  12. <div class="d-flex flex-row main-center-text">
  13. <div class="d-flex flex-column">
  14. <img src="@/assets/images/LargeTitle1.png" alt="闽南文化" />
  15. <img src="@/assets/images/LargeTitle2.png" alt="Mingnan Cultural" />
  16. </div>
  17. <img src="@/assets/images/LargeTitle3.png" alt="厦门" />
  18. </div>
  19. </Slide>
  20. <template #addons>
  21. <Navigation />
  22. <Pagination />
  23. </template>
  24. </Carousel>
  25. <!-- 数据统计 -->
  26. <section class="main-section main-background main-background-type2">
  27. <div class="content">
  28. <div class="title">
  29. <h2>数据统计</h2>
  30. </div>
  31. <SimplePageContentLoader :loader="statsData">
  32. <div class="d-flex row">
  33. <div
  34. class="col-12 col-md-6 col-lg-4 col-xl-4"
  35. v-for="(stat,key) in statsData.content.value"
  36. :key="key"
  37. >
  38. <div :class="`main-card-box type${stat.type}`">
  39. <div class="content">
  40. <h4>{{ stat.title || '\u200b' }}</h4>
  41. <div class="descs">
  42. <a
  43. v-for="(data, key2) in stat.datas"
  44. class="box"
  45. :key="key2"
  46. :href="data.link"
  47. >
  48. <h5>{{ data.title }}</h5>
  49. <p>{{ data.value }}</p>
  50. </a>
  51. </div>
  52. </div>
  53. </div>
  54. </div>
  55. </div>
  56. </SimplePageContentLoader>
  57. </div>
  58. </section>
  59. <!-- 保护区大事记 -->
  60. <section class="main-section main-background main-background-type2">
  61. <div class="content">
  62. <div class="title">
  63. <h2>保护区大事记</h2>
  64. </div>
  65. <SimplePageContentLoader :loader="recordData">
  66. <div class="main-time-line">
  67. <ScrollRect scroll="horizontal" containerClass="time-line-scroll">
  68. <div
  69. v-for="(item, index) in recordData.content.value"
  70. :key="index"
  71. class="item"
  72. :class="index % 2 === 0 ? 'top' : 'bottom'"
  73. >
  74. <div class="time">
  75. {{ DataDateUtils.formatDate(item.publishAt, 'YYYY-MM') }}
  76. </div>
  77. <div class="box">
  78. <div class="title">{{ item.title }}</div>
  79. <div class="content">{{ item.desc }}</div>
  80. </div>
  81. </div>
  82. </ScrollRect>
  83. </div>
  84. </SimplePageContentLoader>
  85. </div>
  86. </section>
  87. <!-- 精选推荐非遗 -->
  88. <section class="main-section main-background main-background-type1">
  89. <div class="content">
  90. <div class="title">
  91. <h2>精选非遗</h2>
  92. </div>
  93. <SimplePageContentLoader :loader="recommend2Data">
  94. <div class="main-grid9 d-flex flex-row flex-wrap justify-content-between">
  95. <NuxtLink
  96. v-for="(item, index) in recommend2Data.content.value"
  97. :key="index"
  98. :to="{ path: '/details/intangible', query: { id: item.id } }"
  99. >
  100. <ImageTitleBlock
  101. :image="item.image"
  102. :title="item.title"
  103. :desc="item.desc"
  104. />
  105. </NuxtLink>
  106. </div>
  107. </SimplePageContentLoader>
  108. </div>
  109. </section>
  110. <!-- 精选推荐文物 -->
  111. <section class="main-section main-background main-background-type1">
  112. <div class="content">
  113. <div class="title">
  114. <h2>文物古迹</h2>
  115. </div>
  116. <SimplePageContentLoader :loader="recommend1Data">
  117. <div class="main-grid9 d-flex flex-row flex-wrap justify-content-between">
  118. <NuxtLink
  119. v-for="(item, index) in recommend1Data.content.value"
  120. :key="index"
  121. :to="{ path: '/details/artifact', query: { id: item.id } }"
  122. >
  123. <ImageTitleBlock
  124. :image="item.image"
  125. :title="item.title"
  126. :desc="item.desc"
  127. />
  128. </NuxtLink>
  129. </div>
  130. </SimplePageContentLoader>
  131. </div>
  132. </section>
  133. <!-- 文旅推荐项目 -->
  134. <section class="main-section main-background main-background-type1">
  135. <div class="content">
  136. <div class="title">
  137. <h2>文旅推荐</h2>
  138. </div>
  139. <SimplePageContentLoader :loader="recommend3Data">
  140. <Carousel ref="carousel6Ref" v-bind="carousel2Config">
  141. <Slide
  142. v-for="(item, index) in recommend3Data.content.value"
  143. :key="index"
  144. >
  145. <NuxtLink :to="{ path: '/news/detail', query: { id: item.id } }">
  146. <ImageTitleBlock
  147. :image="item.image"
  148. :title="item.title"
  149. :desc="item.desc"
  150. />
  151. </NuxtLink>
  152. </Slide>
  153. </Carousel>
  154. </SimplePageContentLoader>
  155. <div class="simple-carousel2-left-right">
  156. <div @click="carousel6Ref?.prev()">←</div>
  157. <div @click="carousel6Ref?.next()">→</div>
  158. </div>
  159. </div>
  160. </section>
  161. <!-- 最新资讯动态 -->
  162. <section class="main-section main-background main-background-type1">
  163. <div class="content">
  164. <div class="title left-right">
  165. <h2>最新资讯动态</h2>
  166. <div class="small-more">
  167. <NuxtLink :to="{ path: '/news' }">
  168. <span>更多动态信息</span>
  169. <img src="@/assets/images/index/ButtonMore.png" alt="更多" />
  170. </NuxtLink>
  171. </div>
  172. </div>
  173. <SimplePageContentLoader :loader="newsData">
  174. <div class="main-grid9 d-flex flex-row flex-wrap justify-content-between">
  175. <NuxtLink
  176. v-for="(item, index) in newsData.content.value"
  177. :key="index"
  178. :to="{ path: '/news/detail', query: { id: item.id } }"
  179. >
  180. <ImageTitleBlock
  181. :image="item.image"
  182. :title="item.title"
  183. :desc="item.typeText"
  184. />
  185. </NuxtLink>
  186. </div>
  187. </SimplePageContentLoader>
  188. </div>
  189. </section>
  190. <!-- 介绍 -->
  191. <section class="main-introd main-section">
  192. <div class="main-introd-bg">
  193. <img class="a" src="@/assets/images/index/IntrodLeft.png" />
  194. <img class="b" src="@/assets/images/index/IntrodRight.jpg" />
  195. </div>
  196. <div class="row h-100">
  197. <div class="col-lg-6 col-md-6 col-sm-12">
  198. </div>
  199. <div class="col-lg-6 col-md-6 col-sm-12 p-5 d-flex flex-column justify-content-center">
  200. <h2>闽南文化生态保护区(厦门市)概况</h2>
  201. <p class="mt-4">
  202. <SimpleRemoveRichHtml :content="overviewData.content.value || ''" />
  203. </p>
  204. </div>
  205. </div>
  206. </section>
  207. </div>
  208. </template>
  209. <script setup lang="ts">
  210. import { Carousel, Slide, Pagination, Navigation } from 'vue3-carousel'
  211. import { ref } from 'vue';
  212. import { useRouter } from 'vue-router';
  213. import { useSSrSimpleDataLoader } from '@/composeable/SimpleDataLoader';
  214. import { NO_CONTENT_STRING } from '@/common/ConstStrings';
  215. import { DataDateUtils } from '@imengyu/js-request-transform';
  216. import { ScrollRect } from '@imengyu/vue-scroll-rect';
  217. import ImageTitleBlock from '@/components/parts/ImageTitleBlock.vue';
  218. import SimpleRemoveRichHtml from '@/components/display/SimpleRemoveRichHtml.vue';
  219. import SimplePageContentLoader from '@/components/content/SimplePageContentLoader.vue';
  220. import IndexContent from '@/api/introduction/IndexContent';
  221. import NewsIndexContent from '@/api/news/NewsIndexContent';
  222. import UnmoveableContent from '@/api/inheritor/UnmoveableContent';
  223. import ProjectContent from '@/api/research/ProjectContent';
  224. import ActivityContent from '@/api/inheritor/ActivityContent';
  225. import ProductContent from '@/api/fusion/ProductContent';
  226. import ProductsContent from '@/api/inheritor/ProductsContent';
  227. import ProjectsContent from '@/api/inheritor/ProjectsContent';
  228. import SeminarContent from '@/api/inheritor/SeminarContent';
  229. import CommonContent, { GetColumListParams, GetContentListParams, type GetContentListItem } from '@/api/CommonContent';
  230. const router = useRouter();
  231. const carouselConfig = {
  232. itemsToShow: 1,
  233. wrapAround: true,
  234. autoPlay: 5000,
  235. }
  236. const carousel2Config = {
  237. itemsToShow: 'auto',
  238. mouseWheel: true,
  239. wrapAround: true
  240. }
  241. const carousel6Ref = ref<any>(null);
  242. const bannerData = await useSSrSimpleDataLoader('banner', async () => {
  243. return (await IndexContent.getBanner()).map(p => p.toJSON());
  244. });
  245. const overviewData = await useSSrSimpleDataLoader('overview', async () => {
  246. return (await IndexContent.getColumList(new GetColumListParams().setSelfValues({
  247. modelId: 17,
  248. mainBodyColumnId: 234,
  249. }))).list[0]?.overview || NO_CONTENT_STRING
  250. });
  251. const recommend1Data = await useSSrSimpleDataLoader('recommend1', async () => {
  252. return (await UnmoveableContent.getContentList(new GetContentListParams(), 1, 9)).list.map(p => p.toJSON());
  253. });
  254. const recommend2Data = await useSSrSimpleDataLoader('recommend2', async () => {
  255. return (await ProjectsContent.getContentList(new GetContentListParams().setMainBodyColumnId([]), 1, 9)).list.map(p => p.toJSON());
  256. });
  257. const recommend3Data = await useSSrSimpleDataLoader('recommend3', async () => {
  258. return (await CommonContent.getContentList(new GetContentListParams()
  259. .setModelId(17)
  260. .setMainBodyColumnId(273)
  261. , 1, 8)).list.map(p => p.toJSON());
  262. });
  263. const newsData = await useSSrSimpleDataLoader('news', async () => {
  264. return (await NewsIndexContent.getContentList(new GetContentListParams()
  265. .setMainBodyColumnId([ 228/* , 298, 299 */ ])
  266. , 1, 9)).list.map(p => p.toJSON());
  267. });
  268. const statsData = await useSSrSimpleDataLoader('stats', async () => {
  269. const data = (await IndexContent.getStats());
  270. const semiCount = (await SeminarContent.getContentList(new GetContentListParams(), 1, 1)).total;
  271. const unmoveableCount = (await UnmoveableContent.getContentList(new GetContentListParams(), 1, 1)).total;
  272. return [
  273. {
  274. title: '非遗代表性项目',
  275. type: '1',
  276. datas: data.ichData.filter((p: any) => [ '人类非遗', '国家级', '省级', '市级' ].includes(p.level_text)).map((item: any) => {
  277. return {
  278. title: item.level_text,
  279. value: item.total,
  280. link: router.resolve({ path: '/inheritor/projects', query: { level: item.level } }).href,
  281. }
  282. })
  283. },
  284. {
  285. title: '非遗代表性传承人',
  286. type: '2',
  287. datas: data.inheritorData.filter((p: any) => [ '国家级', '省级', '市级'/* , '区县级' */ ].includes(p.title)).map((item: any) => {
  288. return {
  289. title: item.title,
  290. value: item.total,
  291. link: router.resolve({ path: '/inheritor/inheritor', query: { level: item.level } }).href
  292. }
  293. })
  294. },
  295. {
  296. title: '其他传承项目',
  297. type: '1',
  298. datas: [
  299. {
  300. title: '传习所',
  301. value: semiCount,
  302. link: router.resolve({ path: '/inheritor/seminar' }).href,
  303. },
  304. {
  305. title: '传统村落',
  306. value: data.villageData[0]?.total ?? 0,
  307. link: router.resolve({ path: '/village/index' }).href,
  308. },
  309. {
  310. title: '文物古迹',
  311. value: unmoveableCount,
  312. link: router.resolve({ path: '/inheritor/unmoveable' }).href,
  313. },
  314. ],
  315. },
  316. /*{
  317. title: '不可移动文物',
  318. type: '3',
  319. datas: data.crData.map((item: any) => {
  320. return {
  321. title: item.title,
  322. value: item.total
  323. }
  324. })
  325. },
  326. {
  327. title: '闽南文化重要相关文物古迹',
  328. type: '2',
  329. datas: data.minnanCr.map((item: any) => {
  330. return {
  331. title: item.title,
  332. value: item.total
  333. }
  334. })
  335. },
  336. {
  337. title: '重要相关历史风貌区',
  338. type: '1',
  339. datas: data.historyData.map((item: any) => {
  340. return {
  341. title: item.title,
  342. value: item.total
  343. }
  344. })
  345. },
  346. {
  347. title: '传习中心',
  348. type: '3',
  349. datas: data.ichCenter.map((item: any) => {
  350. return {
  351. title: item.title,
  352. value: item.total
  353. }
  354. })
  355. },*/
  356. ];
  357. });
  358. const recordData = await useSSrSimpleDataLoader('record', async () => {
  359. return (await CommonContent.getContentList(new GetContentListParams()
  360. .setSelfValues({
  361. order: 'publish_at',
  362. })
  363. .setModelId(18)
  364. .setMainBodyColumnId(316)
  365. , 1, 50)).list.map(p => p.toJSON());
  366. });
  367. </script>
  368. <style lang="scss">
  369. @use "sass:list";
  370. @use "sass:math";
  371. @use '@/assets/scss/colors.scss' as *;
  372. .main-card-box {
  373. position: relative;
  374. min-height: 330px;
  375. color: #fff;
  376. margin-right: 24px;
  377. overflow: hidden;
  378. //transform: translateX(-50%);
  379. .content {
  380. position: absolute;
  381. inset: 24px;
  382. z-index: 10;
  383. display: flex;
  384. flex-direction: column;
  385. h4 {
  386. font-family: SourceHanSerifCNBold;
  387. font-size: 1.5rem;
  388. margin: 0;
  389. margin-bottom: 32px;
  390. }
  391. .descs {
  392. display: flex;
  393. flex-direction: row;
  394. flex-wrap: wrap;
  395. .box {
  396. flex: 1 1 50%;
  397. margin-bottom: 32px;
  398. cursor: pointer;
  399. color: #fff;
  400. text-decoration: none;
  401. h5 {
  402. font-size: 1rem;
  403. font-weight: normal;
  404. margin: 0;
  405. }
  406. p {
  407. font-family: Impact;
  408. font-weight: normal;
  409. font-size: 2.8rem;
  410. margin: 0;
  411. }
  412. }
  413. }
  414. }
  415. $background-types: (
  416. type1: (url('@/assets/images/index/BoxPrinting2.png'), url('@/assets/images/index/Box3.jpg')),
  417. type2: (url('@/assets/images/index/BoxPrinting1.png'), url('@/assets/images/index/Box1.png')),
  418. type3: (url('@/assets/images/index/BoxPrinting4.png'), url('@/assets/images/index/Box2.jpg'))
  419. );
  420. @each $typeName, $type in $background-types {
  421. &.#{$typeName} {
  422. &::after {
  423. content: '';
  424. position: absolute;
  425. inset: 0;
  426. background-image: list.nth($type, 2);
  427. z-index: 0;
  428. }
  429. &::before {
  430. content: '';
  431. position: absolute;
  432. bottom: -10px;
  433. right: -10px;
  434. width: 180px;
  435. height: 180px;
  436. background-size: 180px;
  437. background-image: list.nth($type, 1);
  438. z-index: 1;
  439. }
  440. }
  441. }
  442. &.type3 .descs div {
  443. flex-basis: 33%;
  444. margin-bottom: 22px;
  445. }
  446. }
  447. .main-introd {
  448. position: relative;
  449. height: 750px;
  450. }
  451. .main-introd-bg {
  452. position: absolute;
  453. inset: 0;
  454. overflow: hidden;
  455. .a {
  456. position: absolute;
  457. top: 0;
  458. left: 0;
  459. height: 100%;
  460. z-index: -1;
  461. }
  462. .b {
  463. position: absolute;
  464. top: 0;
  465. right: 0;
  466. width: 100%;
  467. z-index: -2;
  468. }
  469. }
  470. .main-grid9 {
  471. .ImageTitleBlock {
  472. flex-basis: 33%;
  473. margin-right: 0;
  474. margin-bottom: 10px;
  475. }
  476. }
  477. .main-time-line {
  478. $height: 500px;
  479. position: relative;
  480. height: $height;
  481. &::before {
  482. content: '';
  483. position: absolute;
  484. top: calc(50% + 30px);
  485. left: 0;
  486. width: 100%;
  487. height: 1px;
  488. background-color: $primary-color;
  489. }
  490. .time-line-scroll {
  491. position: relative;
  492. margin-top: 32px;
  493. margin-bottom: 32px;
  494. display: flex;
  495. flex-direction: row;
  496. align-items: flex-start;
  497. flex-wrap: nowrap;
  498. height: $height;
  499. overflow: hidden;
  500. }
  501. .item {
  502. position: relative;
  503. display: flex;
  504. flex-direction: column-reverse;
  505. align-items: center;
  506. width: 25%;
  507. height: $height;
  508. flex-shrink: 0;
  509. &.top {
  510. transform: translateY(math.div($height, -2));
  511. .time {
  512. &::before {
  513. content: '▲';
  514. }
  515. }
  516. }
  517. &.bottom {
  518. transform: translateY(math.div($height, 2) - 50px);
  519. flex-direction: column;
  520. .time {
  521. margin-bottom: 30px;
  522. &::before {
  523. content: '▼';
  524. }
  525. }
  526. }
  527. .time {
  528. position: relative;
  529. margin: 15px 0;
  530. color: $primary-color;
  531. &::before {
  532. position: absolute;
  533. font-size: 2rem;
  534. top: calc(50% - 1rem + 15px);
  535. left: calc(50% - 1rem);
  536. }
  537. }
  538. .box {
  539. background-color: $box-color;
  540. padding: 10px;
  541. border-radius: 5px;
  542. .title {
  543. font-size: 1.1rem;
  544. color: $text-content-color;
  545. margin-bottom: 8px;
  546. display: -webkit-box;
  547. line-clamp: 2;
  548. -webkit-line-clamp: 2;
  549. -webkit-box-orient: vertical;
  550. overflow: hidden;
  551. text-overflow: ellipsis;
  552. }
  553. .content {
  554. font-size: 0.8rem;
  555. color: $text-content-color;
  556. }
  557. }
  558. }
  559. }
  560. @media (max-width: 750px) {
  561. .main-grid9 {
  562. .ImageTitleBlock {
  563. flex-basis: calc(50% - 10px);
  564. }
  565. }
  566. .main-time-line {
  567. .item {
  568. width: 50%;
  569. }
  570. }
  571. }
  572. @media (max-width: 425px) {
  573. .main-grid9 {
  574. .ImageTitleBlock {
  575. flex-basis: 100%;
  576. }
  577. }
  578. .main-time-line {
  579. .item {
  580. width: 100%;
  581. }
  582. }
  583. .main-card-box {
  584. width: auto;
  585. transform: translateX(20px);
  586. }
  587. }
  588. </style>