index.vue 18 KB

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