index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  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. <IndexStats />
  38. </div>
  39. </section>
  40. <!-- 保护区发展历程 -->
  41. <section class="main-section main-background main-background-type2">
  42. <div class="content">
  43. <div class="title">
  44. <h2>保护区发展历程</h2>
  45. </div>
  46. <SimplePageContentLoader :loader="recordData">
  47. <div class="main-time-line">
  48. <ScrollRect scroll="horizontal" containerClass="time-line-scroll">
  49. <div
  50. v-for="(item, index) in recordData.content.value"
  51. :key="index"
  52. class="item"
  53. :class="index % 2 === 0 ? 'top' : 'bottom'"
  54. >
  55. <div class="time">
  56. {{ DataDateUtils.formatDate(item.publishAt, 'YYYY-MM') }}
  57. </div>
  58. <div class="box">
  59. <div class="title">{{ item.title }}</div>
  60. <div class="content">{{ item.desc }}</div>
  61. </div>
  62. </div>
  63. </ScrollRect>
  64. </div>
  65. </SimplePageContentLoader>
  66. </div>
  67. </section>
  68. <!-- 精选推荐非遗 -->
  69. <section class="main-section main-background main-background-type1">
  70. <div class="content">
  71. <div class="title">
  72. <h2>精选非遗</h2>
  73. </div>
  74. <SimplePageContentLoader :loader="recommend2Data">
  75. <div class="main-grid9 d-flex flex-row flex-wrap justify-content-between">
  76. <NuxtLink
  77. v-for="(item, index) in recommend2Data.content.value"
  78. :key="index"
  79. :to="{ path: '/details/intangible', query: { id: item.id } }"
  80. >
  81. <ImageTitleBlock
  82. :image="item.thumbnail || item.image"
  83. :title="item.title"
  84. :desc="item.desc"
  85. />
  86. </NuxtLink>
  87. </div>
  88. </SimplePageContentLoader>
  89. </div>
  90. </section>
  91. <!-- 精选推荐文物 -->
  92. <section class="main-section main-background main-background-type1">
  93. <div class="content">
  94. <div class="title">
  95. <h2>文物古迹</h2>
  96. </div>
  97. <SimplePageContentLoader :loader="recommend1Data">
  98. <div class="main-grid9 d-flex flex-row flex-wrap justify-content-between">
  99. <NuxtLink
  100. v-for="(item, index) in recommend1Data.content.value"
  101. :key="index"
  102. :to="{ path: '/details/artifact', query: { id: item.id } }"
  103. >
  104. <ImageTitleBlock
  105. :image="item.thumbnail || item.image"
  106. :title="item.title"
  107. :desc="item.desc"
  108. />
  109. </NuxtLink>
  110. </div>
  111. </SimplePageContentLoader>
  112. </div>
  113. </section>
  114. <!-- 文旅推荐项目 -->
  115. <section class="main-section main-background main-background-type1">
  116. <div class="content">
  117. <div class="title">
  118. <h2>文旅推荐</h2>
  119. </div>
  120. <SimplePageContentLoader :loader="recommend3Data">
  121. <Carousel ref="carousel6Ref" v-bind="carousel2Config">
  122. <Slide
  123. v-for="(item, index) in recommend3Data.content.value"
  124. :key="index"
  125. >
  126. <NuxtLink :to="{ path: '/news/detail', query: { id: item.id } }">
  127. <ImageTitleBlock
  128. :image="item.thumbnail || item.image"
  129. :title="item.title"
  130. :desc="item.desc"
  131. />
  132. </NuxtLink>
  133. </Slide>
  134. </Carousel>
  135. </SimplePageContentLoader>
  136. <div class="simple-carousel2-left-right">
  137. <div @click="carousel6Ref?.prev()">←</div>
  138. <div @click="carousel6Ref?.next()">→</div>
  139. </div>
  140. </div>
  141. </section>
  142. <!-- 最新资讯动态 -->
  143. <section class="main-section main-background main-background-type1">
  144. <div class="content">
  145. <div class="title left-right">
  146. <h2>最新资讯动态</h2>
  147. <div class="small-more">
  148. <NuxtLink :to="{ path: '/news' }">
  149. <span>更多动态信息</span>
  150. <img src="@/assets/images/index/ButtonMore.png" alt="更多" />
  151. </NuxtLink>
  152. </div>
  153. </div>
  154. <SimplePageContentLoader :loader="newsData">
  155. <div class="main-grid9 d-flex flex-row flex-wrap justify-content-between">
  156. <NuxtLink
  157. v-for="(item, index) in newsData.content.value"
  158. :key="index"
  159. :to="{ path: '/news/detail', query: { id: item.id } }"
  160. >
  161. <ImageTitleBlock
  162. :image="item.thumbnail || item.image"
  163. :title="item.title"
  164. :desc="item.typeText"
  165. />
  166. </NuxtLink>
  167. </div>
  168. </SimplePageContentLoader>
  169. </div>
  170. </section>
  171. <!-- 介绍 -->
  172. <section class="main-introd main-section main-introd-bg">
  173. <div class="row h-100">
  174. <div class="col-lg-6 col-md-6 col-sm-12">
  175. </div>
  176. <div class="col-lg-6 col-md-6 col-sm-12 p-5 d-flex flex-column justify-content-center">
  177. <h2>闽南文化生态保护区(厦门市)概况</h2>
  178. <p class="mt-4">
  179. <SimpleRemoveRichHtml :content="overviewData.content.value || ''" />
  180. </p>
  181. <div class="d-flex flex-row justify-content-end p-4">
  182. <NuxtLink class="simple-link" style="z-index:100" :to="{ path: '/about/' }">详情 →</NuxtLink>
  183. </div>
  184. </div>
  185. </div>
  186. </section>
  187. </div>
  188. </template>
  189. <script setup lang="ts">
  190. import { Carousel, Slide, Pagination, Navigation } from 'vue3-carousel'
  191. import { ref } from 'vue';
  192. import { useRouter } from 'vue-router';
  193. import { useSSrSimpleDataLoader } from '@/composeable/SimpleDataLoader';
  194. import { NO_CONTENT_STRING } from '@/common/ConstStrings';
  195. import { DataDateUtils } from '@imengyu/js-request-transform';
  196. import { ScrollRect } from '@imengyu/vue-scroll-rect';
  197. import ImageTitleBlock from '@/components/parts/ImageTitleBlock.vue';
  198. import SimplePageContentLoader from '@/components/content/SimplePageContentLoader.vue';
  199. import IndexContent from '@/api/introduction/IndexContent';
  200. import NewsIndexContent from '@/api/news/NewsIndexContent';
  201. import UnmoveableContent from '@/api/inheritor/UnmoveableContent';
  202. import ProjectsContent from '@/api/inheritor/ProjectsContent';
  203. import CommonContent, { GetColumListParams, GetContentListParams, type GetContentListItem } from '@/api/CommonContent';
  204. const carouselConfig = {
  205. itemsToShow: 1,
  206. wrapAround: true,
  207. autoPlay: 5000,
  208. }
  209. const carousel2Config = {
  210. itemsToShow: 'auto',
  211. mouseWheel: true,
  212. wrapAround: true
  213. }
  214. const carousel6Ref = ref<any>(null);
  215. const bannerData = await useSSrSimpleDataLoader('banner', async () => {
  216. return (await IndexContent.getBanner()).map(p => p.toJSON());
  217. });
  218. const overviewData = await useSSrSimpleDataLoader('overview', async () => {
  219. return (await IndexContent.getColumList(new GetColumListParams().setSelfValues({
  220. modelId: 17,
  221. mainBodyColumnId: 234,
  222. }))).list[0]?.overview || NO_CONTENT_STRING
  223. });
  224. const recommend1Data = await useSSrSimpleDataLoader('recommend1', async () => {
  225. return (await UnmoveableContent.getContentList(new GetContentListParams(), 1, 9)).list.map(p => p.toJSON());
  226. });
  227. const recommend2Data = await useSSrSimpleDataLoader('recommend2', async () => {
  228. return (await ProjectsContent.getContentList(new GetContentListParams().setMainBodyColumnId([]), 1, 9)).list.map(p => p.toJSON());
  229. });
  230. const recommend3Data = await useSSrSimpleDataLoader('recommend3', async () => {
  231. return (await CommonContent.getContentList(new GetContentListParams()
  232. .setModelId(17)
  233. .setMainBodyColumnId(273)
  234. , 1, 8)).list.map(p => p.toJSON());
  235. });
  236. const newsData = await useSSrSimpleDataLoader('news', async () => {
  237. return (await NewsIndexContent.getContentList(new GetContentListParams()
  238. .setMainBodyColumnId([ 228/* , 298, 299 */ ])
  239. , 1, 9)).list.map(p => p.toJSON());
  240. });
  241. const recordData = await useSSrSimpleDataLoader('record', async () => {
  242. return (await CommonContent.getContentList(new GetContentListParams()
  243. .setSelfValues({
  244. order: 'publish_at',
  245. })
  246. .setModelId(18)
  247. .setMainBodyColumnId(316)
  248. , 1, 50)).list.map(p => p.toJSON());
  249. });
  250. </script>
  251. <style lang="scss">
  252. @use "sass:list";
  253. @use "sass:math";
  254. @use '@/assets/scss/colors.scss' as *;
  255. .main-introd {
  256. position: relative;
  257. height: 750px;
  258. }
  259. .main-introd-bg {
  260. background-image: url('@/assets/images/index/Introd.jpg');
  261. background-size: cover;
  262. background-position: center top;
  263. }
  264. .main-grid9 {
  265. gap: 10px;
  266. a {
  267. display: flex;
  268. max-width: 100%;
  269. flex: 1 1 31%;
  270. }
  271. .ImageTitleBlock {
  272. flex-basis: 100%;
  273. margin-right: 0;
  274. }
  275. }
  276. .main-time-line {
  277. $height: 500px;
  278. position: relative;
  279. height: $height;
  280. &::before {
  281. content: '';
  282. position: absolute;
  283. top: calc(50% + 30px);
  284. left: 0;
  285. width: 100%;
  286. height: 1px;
  287. background-color: $primary-color;
  288. }
  289. .time-line-scroll {
  290. position: relative;
  291. margin-top: 32px;
  292. margin-bottom: 32px;
  293. display: flex;
  294. flex-direction: row;
  295. align-items: flex-start;
  296. flex-wrap: nowrap;
  297. height: $height;
  298. overflow: hidden;
  299. }
  300. .item {
  301. position: relative;
  302. display: flex;
  303. flex-direction: column-reverse;
  304. align-items: center;
  305. width: 25%;
  306. height: $height;
  307. flex-shrink: 0;
  308. &.top {
  309. transform: translateY(math.div($height, -2));
  310. .time {
  311. &::before {
  312. content: '▲';
  313. }
  314. }
  315. }
  316. &.bottom {
  317. transform: translateY(math.div($height, 2) - 50px);
  318. flex-direction: column;
  319. .time {
  320. margin-bottom: 30px;
  321. &::before {
  322. content: '▼';
  323. }
  324. }
  325. }
  326. .time {
  327. position: relative;
  328. margin: 15px 0;
  329. color: $primary-color;
  330. &::before {
  331. position: absolute;
  332. font-size: 2rem;
  333. top: calc(50% - 1rem + 15px);
  334. left: calc(50% - 1rem);
  335. }
  336. }
  337. .box {
  338. background-color: $box-color;
  339. padding: 10px;
  340. border-radius: 5px;
  341. .title {
  342. font-size: 1.1rem;
  343. color: $text-content-color;
  344. margin-bottom: 8px;
  345. display: -webkit-box;
  346. line-clamp: 2;
  347. -webkit-line-clamp: 2;
  348. -webkit-box-orient: vertical;
  349. overflow: hidden;
  350. text-overflow: ellipsis;
  351. }
  352. .content {
  353. font-size: 0.8rem;
  354. color: $text-content-color;
  355. }
  356. }
  357. }
  358. }
  359. @media (max-width: 750px) {
  360. .main-grid9 {
  361. .ImageTitleBlock {
  362. flex-basis: calc(50% - 10px);
  363. }
  364. }
  365. .main-time-line {
  366. .item {
  367. width: 50%;
  368. }
  369. }
  370. }
  371. @media (max-width: 425px) {
  372. .main-grid9 {
  373. .ImageTitleBlock {
  374. flex-basis: 100%;
  375. }
  376. }
  377. .main-introd-bg {
  378. .a {
  379. display: none;
  380. }
  381. .b {
  382. position: absolute;
  383. top: 0;
  384. left: 0;
  385. width: 100%;
  386. z-index: -2;
  387. }
  388. }
  389. .main-time-line {
  390. .item {
  391. width: 100%;
  392. }
  393. }
  394. }
  395. </style>