HomeView.vue 17 KB

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