home.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. <template>
  2. <view class="home-container page-home d-flex flex-col bg-base">
  3. <Image
  4. innerClass="position-absolute"
  5. width="100%"
  6. src="https://mncdn.wenlvti.net/app_static/minnan/images/home/BackgroundBanner5.jpg"
  7. mode="widthFix"
  8. />
  9. <view class="content d-flex flex-col wing-l">
  10. <!-- 分栏 -->
  11. <view class="shadow-base radius-l bg-base p-3">
  12. <view
  13. class="main-banner-box mb-25"
  14. @click="navTo('home/introduction')"
  15. >
  16. <text class="title">闽南文化生态保护区(厦门市)</text>
  17. <text>世界闽南文化交流中心</text>
  18. <view class="more">
  19. <text>查看详情</text>
  20. </view>
  21. <Image
  22. innerClass="footer"
  23. src="https://mncdn.wenlvti.net/app_static/minnan/images/home/MainBanner2.png"
  24. mode="widthFix"
  25. />
  26. </view>
  27. <HomeButton
  28. title="探索闽南文化"
  29. icon="https://mncdn.wenlvti.net/app_static/minnan/images/home/IconMap.png"
  30. bg="https://mncdn.wenlvti.net/app_static/minnan/images/home/ButtonMapBg.png"
  31. large
  32. @click="navTo('/pages/introduction/explore')"
  33. />
  34. <view class="position-relative d-flex flex-row flex-wrap justify-between mt-25 row-gap-sss">
  35. <HomeButton
  36. title="热点新鲜事"
  37. icon="https://mncdn.wenlvti.net/app_static/minnan/images/home/IconDoc.png"
  38. @click="navTo('/pages/introduction/news')"
  39. />
  40. <HomeButton
  41. title="保护与传承"
  42. icon="https://mncdn.wenlvti.net/app_static/minnan/images/home/IconIch.png"
  43. @click="navTo('/pages/introduction/inhert')"
  44. />
  45. <HomeButton
  46. title="闽南走透透"
  47. icon="https://mncdn.wenlvti.net/app_static/minnan/images/home/IconArtifact.png"
  48. @click="navTo('/pages/introduction/recommend-news')"
  49. />
  50. <HomeButton
  51. title="文旅探世界"
  52. icon="https://mncdn.wenlvti.net/app_static/minnan/images/home/IconDiscover.png"
  53. @click="navTo('/pages/introduction/travel')"
  54. />
  55. </view>
  56. <view class="position-relative d-flex flex-row flex-wrap justify-between mt-3">
  57. <Box1AudioPlay
  58. class="w-100"
  59. :title="indexAudioPlayer.currentTitle.value"
  60. :image="indexAudioPlayer.currentItem?.value?.image"
  61. :playState="indexAudioPlayer.isPlaying.value"
  62. :playTime="indexAudioPlayer.timeString.value"
  63. @playPauseClick="indexAudioPlayer.playpause"
  64. @nextClick="indexAudioPlayer.next"
  65. @prevClick="indexAudioPlayer.prev"
  66. @click="handleGoAudioList"
  67. />
  68. </view>
  69. </view>
  70. <!-- 数据统计 -->
  71. <SimplePageContentLoader :loader="statsLoader">
  72. <view v-if="statsLoader.content.value" class="d-flex flex-col justify-center mt-3 pt-3 pb-3 bg-light-page radius-base">
  73. <view class="d-flex flex-col">
  74. <StatsText
  75. :title="statsLoader.content.value[0].title"
  76. :data="statsLoader.content.value[0].datas"
  77. :type="statsLoader.content.value[0].type"
  78. />
  79. <view class="p-2">
  80. <HorizontalScrollText :text="statsText1" :fontSize="26" color="text.second" :outerStyle="{ height: '40rpx' }" />
  81. </view>
  82. </view>
  83. <view class="border-top-light-primary pt-2 mt-3"></view>
  84. <view class="d-flex flex-col">
  85. <StatsText
  86. :title="statsLoader.content.value[1].title"
  87. :data="statsLoader.content.value[1].datas"
  88. :type="statsLoader.content.value[1].type"
  89. />
  90. <view class="p-2">
  91. <HorizontalScrollText :text="statsText2" :fontSize="26" color="text.second" :outerStyle="{ height: '40rpx' }" />
  92. </view>
  93. </view>
  94. </view>
  95. <view v-if="statsLoader.content.value" class="d-flex flex-col justify-center pt-3 pb-3 bg-light-page radius-base">
  96. <StatsText
  97. :title="statsLoader.content.value[2].title"
  98. :data="statsLoader.content.value[2].datas"
  99. :type="statsLoader.content.value[2].type"
  100. />
  101. <view class="border-top-light-primary pt-2 mt-3"></view>
  102. <StatsText
  103. :title="statsLoader.content.value[3].title"
  104. :data="statsLoader.content.value[3].datas"
  105. :type="statsLoader.content.value[3].type"
  106. />
  107. <view class="border-top-light-primary pt-2 mt-3"></view>
  108. <StatsText
  109. :title="statsLoader.content.value[4].title"
  110. :data="statsLoader.content.value[4].datas"
  111. :type="statsLoader.content.value[4].type"
  112. />
  113. </view>
  114. </SimplePageContentLoader>
  115. <!-- 文化地图 -->
  116. <HomeTitle title="文化地图" />
  117. <view class="position-relative radius-l overflow-hidden">
  118. <map
  119. id="map"
  120. class="w-100 height-400"
  121. :markers="mapLoader.content.value || []"
  122. :enable-zoom="false"
  123. :enable-scroll="false"
  124. :scale="15"
  125. @click="navTo('inhert/map/index', { tab: mapTab })"
  126. />
  127. <scroll-view class="map-tags position-absolute" :scroll-x="true">
  128. <view class="tag-bar d-flex flex-row flex-nowrap">
  129. <view :class="mapTab == 1 ? 'active' : ''" @click="mapTab=1">
  130. <text class="iconfont icon-read" />
  131. 非遗项目
  132. </view>
  133. <view :class="mapTab == 2 ? 'active' : ''" @click="mapTab=2">
  134. <text class="iconfont icon-task-trip" />
  135. 非遗传习所
  136. </view>
  137. <view :class="mapTab == 3 ? 'active' : ''" @click="mapTab=3">
  138. <text class="iconfont icon-task-buliding" />
  139. 文物古迹
  140. </view>
  141. <view :class="mapTab == 4 ? 'active' : ''" @click="mapTab=4">
  142. <text class="iconfont icon-place" />
  143. 传统村落
  144. </view>
  145. <view :class="mapTab == 5 ? 'active' : ''" @click="mapTab=5">
  146. <text class="iconfont icon-task-environment-3" />
  147. 闽南文化景区
  148. </view>
  149. </view>
  150. </scroll-view>
  151. </view>
  152. <!-- 精彩推荐 -->
  153. <HomeTitle title="精彩推荐" />
  154. <SimplePageContentLoader :loader="recommendLoader">
  155. <view class="d-flex flex-row justify-between flex-wrap">
  156. <view
  157. v-for="(tab, k) in recommendLoader.content.value"
  158. :key="k"
  159. class="grid4-item position-relative mb-3"
  160. @click="handleGoDetails(tab)"
  161. >
  162. <text
  163. class="tag bg-mask-white color-primary radius-l p-1 position-absolute size-s text-lines-1"
  164. >
  165. {{ tab.title }}
  166. </text>
  167. <Image
  168. width="100%"
  169. :height="250"
  170. :radius="15"
  171. :src="tab.thumbnail || tab.image || AppCofig.defaultImage"
  172. mode="aspectFit"
  173. />
  174. </view>
  175. </view>
  176. </SimplePageContentLoader>
  177. </view>
  178. </view>
  179. <Tabbar :current="0" />
  180. </template>
  181. <script setup lang="ts">
  182. import { ref, watch } from 'vue';
  183. import { onShareTimeline, onShareAppMessage } from '@dcloudio/uni-app';
  184. import { navTo } from '@/components/utils/PageAction';
  185. import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
  186. import { useSimpleListAudioPlayer } from '@/common/composeabe/SimpleAudioPlayer';
  187. import CommonContent, { GetContentListParams } from '@/api/CommonContent';
  188. import UnmoveableContent from '@/api/inheritor/UnmoveableContent';
  189. import SeminarContent from '@/api/inheritor/SeminarContent';
  190. import ProjectsContent from '@/api/inheritor/ProjectsContent';
  191. import AppCofig from '@/common/config/AppCofig';
  192. import VillageApi from '@/api/inhert/VillageApi';
  193. import ScenicSpotContent from '@/api/fusion/ScenicSpotContent';
  194. import IndexContent from '@/api/introduction/IndexContent';
  195. import StatsText, { type StatsTextItem } from './parts/StatsText.vue';
  196. import HomeTitle from '@/pages/parts/HomeTitle.vue';
  197. import Tabbar from '@/common/components/tabs/Tabbar.vue';
  198. import Box1AudioPlay from '@/pages/parts/Box1AudioPlay.vue';
  199. import SimplePageContentLoader from "@/common/components/SimplePageContentLoader.vue";
  200. import HorizontalScrollText from '@/components/typography/HorizontalScrollText.vue';
  201. import Image from '@/components/basic/Image.vue';
  202. import HomeButton from './parts/HomeButton.vue';
  203. import { navHomePageMiniCommonListGo } from './article/common/CommonContent';
  204. const mapCtx = uni.createMapContext('map');
  205. const mapTab = ref(1);
  206. const mapLoader = useSimpleDataLoader(async () => {
  207. let list ;
  208. switch (mapTab.value) {
  209. default:
  210. case 1:
  211. list = (await ProjectsContent.getContentList(new GetContentListParams(), 1, 6)).list
  212. break;
  213. case 2:
  214. list = (await SeminarContent.getContentList(new GetContentListParams(), 1, 6)).list
  215. break;
  216. case 3:
  217. list = (await UnmoveableContent.getContentList(new GetContentListParams(), 1, 6)).list
  218. break;
  219. case 4:
  220. list = (await VillageApi.getVallageList()).slice(1, 10)
  221. break;
  222. case 5:
  223. list = (await ScenicSpotContent.getContentList(new GetContentListParams(), 1, 6)).list
  224. break;
  225. }
  226. const res = list.map((p) => {
  227. return {
  228. ...p,
  229. id: p.id,
  230. longitude: Number(p.longitude),
  231. latitude: Number(p.latitude),
  232. iconPath: p.thumbnail,
  233. width: 40,
  234. height: 40,
  235. };
  236. });
  237. mapCtx.includePoints({
  238. points: res.map(p => {
  239. if (!p.longitude || !p.latitude) {
  240. p.longitude = AppCofig.defaultLonLat[0];
  241. p.latitude = AppCofig.defaultLonLat[1];
  242. }
  243. return {
  244. latitude: p.latitude,
  245. longitude: p.longitude,
  246. }
  247. }),
  248. padding: [20, 20, 20, 20],
  249. });
  250. return res;
  251. }, true, undefined, true);
  252. watch(mapTab, () => mapLoader.loadData(undefined, true));
  253. const indexAudioPlayer = useSimpleListAudioPlayer(async () => {
  254. return (await CommonContent.getContentList(new GetContentListParams()
  255. .setModelId(5)
  256. .setMainBodyColumnId(313)
  257. , 1, 6)).list.map((p) => {
  258. return {
  259. id: p.id,
  260. title: p.title,
  261. image: p.thumbnail || p.image,
  262. src: p.audio as string,
  263. }
  264. });
  265. })
  266. function handleGoAudioList() {
  267. navTo('/pages/inhert/language/list')
  268. }
  269. const recommendLoader = useSimpleDataLoader(async () => {
  270. const list = [];
  271. list.push(...(await ProjectsContent.getContentList(new GetContentListParams(), 1, 6)).list.map((p) => {
  272. p.itemType = 'intangible';
  273. return p;
  274. }));
  275. list.push(...(await CommonContent.getContentList(new GetContentListParams()
  276. .setModelId(1)
  277. , 1, 6)).list.map((p) => {
  278. p.itemType = p.type == GetContentListParams.TYPE_VIDEO ? 'video' : 'artifact';
  279. return p;
  280. }));
  281. return list;
  282. });
  283. const statsText1 = ref('');
  284. const statsText2 = ref('');
  285. const statsLoader = useSimpleDataLoader(async () => {
  286. const data = (await IndexContent.getStats());
  287. let sumInheritor = 0;
  288. let sumProject = 0;
  289. const topLevelProject = data.ichData.find((p: any) => p.level_text == '人类非遗')?.total || 0;
  290. const secondLevelProject = data.ichData.find((p: any) => p.level_text == '国家级')?.total || 0;
  291. const thirdLevelProject = data.ichData.find((p: any) => p.level_text == '省级')?.total || 0;
  292. const forthLevelProject = data.ichData.find((p: any) => p.level_text == '市级')?.total || 0;
  293. const topLevelInheritor = data.inheritorData.find((p: any) => p.level_text == '国家级')?.total || 0;
  294. const secondLevelInheritor = data.inheritorData.find((p: any) => p.level_text == '省级')?.total || 0;
  295. const thirdLevelInheritor = data.inheritorData.find((p: any) => p.level_text == '市级')?.total || 0;
  296. const projects = (data.ichData as any[]).filter((p: any) => [ '人类非遗', '国家级', '省级', '市级' ].includes(p.level_text)).map((item: any) => {
  297. if (item.level_text != '人类非遗')
  298. sumProject += item.total;
  299. return {
  300. title: item.level_text,
  301. value: item.total,
  302. titleSuffix: '项',
  303. //subText: '\u00a0',
  304. type: 'forth',
  305. onClick: () => navTo('/pages/inhert/intangible/list', { tab: 0, level: item.level }),
  306. } as StatsTextItem
  307. });
  308. //projects.shift();
  309. //projects[0].subText = `人类非遗 ${topLevelProject}`;
  310. const inheritors = data.inheritorData.filter((p: any) => [ '国家级', '省级', '市级' ].includes(p.title)).map((item: any) => {
  311. sumInheritor += item.total;
  312. return {
  313. title: item.title,
  314. value: item.total,
  315. titleSuffix: '人',
  316. type: 'normal',
  317. onClick: () => navTo('/pages/inhert/inheritor/list', { level: item.level }),
  318. }
  319. });
  320. statsText1.value = `目前厦门市非遗项目市级以上共有 ${sumProject} 项,其中:国家级 ${secondLevelProject} 项(含 ${topLevelProject} 项为人类非遗)、省级 ${thirdLevelProject} 项、市级 ${forthLevelProject} 项。`;
  321. statsText2.value = `目前厦门市非遗传承人市级以上共有 ${sumInheritor} 人,其中:国家级 ${topLevelInheritor} 人、省级 ${secondLevelInheritor} 人、市级 ${thirdLevelInheritor} 人。`;
  322. return [
  323. {
  324. title: '非遗项目',
  325. datas: projects
  326. },
  327. {
  328. title: '非遗传承人',
  329. datas: inheritors
  330. },
  331. {
  332. title: '非遗传习所',
  333. datas: data.ichCenter.map((item: any) => {
  334. return {
  335. title: item.title,
  336. value: item.total,
  337. titleSuffix: '处',
  338. type: 'normal',
  339. onClick: () => navTo('/pages/inhert/seminar/list', { region: item.id }),
  340. }
  341. }),
  342. },
  343. {
  344. title: '重要相关历史风貌区',
  345. datas: data.historyData.map((item: any) => {
  346. return {
  347. title: item.title,
  348. value: item.total,
  349. titleSuffix: '处',
  350. onClick: () => {
  351. switch (item.title) {
  352. case '世界文化遗产':
  353. navHomePageMiniCommonListGo({
  354. title: '世界文化遗产',
  355. modelId: 17,
  356. mainBodyColumnId: 310
  357. });
  358. break;
  359. case '传统村落':
  360. navTo('/pages/inhert/village/list');
  361. break;
  362. case '重点区域':
  363. navHomePageMiniCommonListGo({
  364. title: '重点区域',
  365. modelId: 17,
  366. mainBodyColumnId: 283
  367. });
  368. break;
  369. }
  370. },
  371. }
  372. }),
  373. },
  374. {
  375. title: '闽南文化重要相关文物古迹',
  376. type: 'none',
  377. datas: data.minnanCr.map((item: any) => {
  378. return {
  379. title: item.title,
  380. value: item.total,
  381. titleSuffix: '处',
  382. onClick: () => navTo('/pages/inhert/artifact/list', {
  383. level: item.level
  384. }),
  385. }
  386. }),
  387. },
  388. ]
  389. });
  390. function handleGoDetails(item: any) {
  391. switch (item.itemType) {
  392. case 'artifact':
  393. navTo('/pages/inhert/artifact/details', { id: item.id });
  394. break;
  395. case 'intangible':
  396. navTo('/pages/inhert/intangible/details', { id: item.id });
  397. break;
  398. case 'video':
  399. navTo('/pages/video/details', { id: item.id, modelId: item.modelId, mainBodyColumnId: item.mainBodyColumnId });
  400. break;
  401. default:
  402. navTo('/pages/article/details', { id: item.id, modelId: item.modelId, mainBodyColumnId: item.mainBodyColumnId });
  403. break;
  404. }
  405. }
  406. onShareTimeline(() => {
  407. return {};
  408. })
  409. onShareAppMessage(() => {
  410. return {};
  411. })
  412. </script>
  413. <style lang="scss">
  414. .page-home {
  415. .content {
  416. margin-top: 470rpx;
  417. }
  418. .map-tags {
  419. left: 0;
  420. top: 0;
  421. padding: 15rpx 0;
  422. font-size: 25rpx;
  423. .tag-bar {
  424. padding: 0 20rpx;
  425. view {
  426. display: flex;
  427. flex-direction: row;
  428. align-items: center;
  429. flex-shrink: 0;
  430. border-radius: 40rpx;
  431. padding: 10rpx 15rpx;
  432. background-color: #f7f3e8;
  433. color: #d9492e;
  434. margin-right: 10rpx;
  435. .iconfont {
  436. margin-right: 8rpx;
  437. }
  438. &.active {
  439. background-color: #d9492e;
  440. color: #f7f3e8;
  441. }
  442. }
  443. }
  444. }
  445. .grid4-item {
  446. width: 320rpx;
  447. .tag {
  448. top: 2rpx;
  449. right: 2rpx;
  450. z-index: 20;
  451. }
  452. }
  453. .main-banner-box {
  454. position: relative;
  455. display: flex;
  456. flex-direction: column;
  457. overflow: hidden;
  458. border-radius: 15rpx;
  459. background: linear-gradient(180deg, #E5CDAB 0%, #F0E3D6 100%), #F7F3E8;
  460. padding: 20rpx;
  461. font-family: "SongtiSCBlack";
  462. color: #432A04;
  463. .title {
  464. font-size: 40rpx;
  465. }
  466. text {
  467. font-size: 35rpx;
  468. margin-top: 10rpx;
  469. }
  470. .more {
  471. margin-top: 30rpx;
  472. padding: 10rpx 20rpx;
  473. width: 150rpx;
  474. background-image: url('https://mncdn.wenlvti.net/app_static/minnan/images/home/MainBanner.png');
  475. background-size: 100% auto;
  476. background-repeat: no-repeat;
  477. text {
  478. font-family: initial;
  479. font-size: 30rpx;
  480. }
  481. }
  482. .footer {
  483. position: absolute;
  484. right: 0;
  485. bottom: 0;
  486. width: 370rpx;
  487. z-index: 2;
  488. height: auto;
  489. }
  490. }
  491. }
  492. </style>