index.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <template>
  2. <FlexCol :gap="20" :padding="30" :innerStyle="{
  3. marginTop: '-100px',
  4. backgroundImage: 'url(https://mn.wenlvti.net/app_static/xiangyuan/images/home/HomeBackground.jpg)',
  5. backgroundSize: '100% auto',
  6. backgroundRepeat: 'no-repeat',
  7. backgroundPosition: 'top center',
  8. backgroundColor: '#7bda82',
  9. }">
  10. <Height height="100px" />
  11. <FlexCol :gap="4" align="center">
  12. <Image src="https://mncdn.wenlvti.net/app_static/xiangyuan/images/home/ButtonTitle.png" width="430rpx" mode="widthFix" />
  13. <Image src="https://mncdn.wenlvti.net/app_static/xiangyuan/images/home/ButtonSubTitle.png" width="290rpx" mode="widthFix" />
  14. </FlexCol>
  15. <Box icon="https://mncdn.wenlvti.net/app_static/xiangyuan/images/home/icon-pin-distance.png" :padding="false">
  16. <LightMap
  17. @getedCurrentLonlat="getedCurrentLonlat"
  18. />
  19. <FlexRow justify="space-between" :padding="[10, 16]">
  20. <ImageButton src="https://mn.wenlvti.net/app_static/xiangyuan/images/home/ButtonMainAction.png" :width="215" mode="widthFix" @click="goCommonContentList({ modelId: 18, mainBodyColumnId: 361 })" />
  21. <ImageButton src="https://mn.wenlvti.net/app_static/xiangyuan/images/home/ButtonMainLight.png" :width="215" mode="widthFix" @click="navTo('/pages/home/light/submit-map', { latitude: currentLonlat.latitude, longitude: currentLonlat.longitude })" />
  22. <ImageButton src="https://mn.wenlvti.net/app_static/xiangyuan/images/home/ButtonMainSupport.png" :width="215" mode="widthFix" @click="goCommonContentList({ modelId: 18, mainBodyColumnId: 362 })" />
  23. </FlexRow>
  24. </Box>
  25. <Box title="发现 · 周边" icon="https://mncdn.wenlvti.net/app_static/xiangyuan/images/home/icon-compass.png" showMore @moreClicked="goCommonContentList({
  26. modelId: 1,
  27. latitude: currentLonlat.latitude,
  28. longitude: currentLonlat.longitude,
  29. isNearby: true,
  30. })">
  31. <SimplePageContentLoader :loader="recommendedNearbySitesLoader">
  32. <FlexCol :gap="25">
  33. <Touchable
  34. v-for="(item, i) in recommendedNearbySitesLoader.content.value"
  35. :key="i"
  36. justify="space-between"
  37. align="center"
  38. direction="row"
  39. @click="goCommonContentDetail(item.id)"
  40. >
  41. <FlexCol flex="1" :gap="20">
  42. <Text :text="item.title" fontConfig="h5" />
  43. <Text :text="`距离您约 ${item.distance}`" fontConfig="subText" />
  44. </FlexCol>
  45. <Width :width="25" />
  46. <Image
  47. :src="item.image"
  48. :failedImage="AppCofig.defaultImage"
  49. :width="170"
  50. :height="120"
  51. :radius="15"
  52. mode="aspectFill"
  53. />
  54. </Touchable>
  55. </FlexCol>
  56. </SimplePageContentLoader>
  57. </Box>
  58. <Box title="线上史馆展示" icon="https://mncdn.wenlvti.net/app_static/xiangyuan/images/home/icon-ancient-gate.png">
  59. <SimplePageContentLoader :loader="recommendLoader">
  60. <FlexRow justify="space-between" align="center" wrap :gap="25">
  61. <Touchable
  62. v-for="(item, i) in recommendLoader.content.value"
  63. :key="i"
  64. :gap="20"
  65. flex="0 0 48%"
  66. align="center"
  67. direction="column"
  68. @click="goVillageDetails(item)"
  69. >
  70. <Image
  71. :src="item.thumbnail || item.image"
  72. width="100%"
  73. height="200rpx"
  74. :radius="15"
  75. mode="aspectFill"
  76. />
  77. <Text :text="item.title" />
  78. </Touchable>
  79. </FlexRow>
  80. </SimplePageContentLoader>
  81. </Box>
  82. <Box title="精彩推荐" icon="https://mncdn.wenlvti.net/app_static/xiangyuan/images/home/icon-compass.png" showMore @moreClicked="$emit('goDiscover')">
  83. <SimplePageContentLoader :loader="discoverLoader">
  84. <FlexCol :gap="25">
  85. <Touchable
  86. v-for="(item, i) in discoverLoader.content.value"
  87. :key="i"
  88. justify="space-between"
  89. align="center"
  90. direction="row"
  91. @click="goDiscoverDetails(item)"
  92. >
  93. <FlexCol flex="1" :gap="20">
  94. <Text :text="item.title" fontConfig="h5" />
  95. <Text :text="item.desc" fontConfig="subText" />
  96. </FlexCol>
  97. <Width :width="25" />
  98. <Image
  99. :src="item.image"
  100. :failedImage="AppCofig.defaultImage"
  101. :width="170"
  102. :height="120"
  103. :radius="15"
  104. mode="aspectFill"
  105. />
  106. </Touchable>
  107. </FlexCol>
  108. </SimplePageContentLoader>
  109. </Box>
  110. <Loadmore status="nomore" />
  111. <Height :height="150" />
  112. </FlexCol>
  113. </template>
  114. <script setup lang="ts">
  115. import { navTo } from '@/components/utils/PageAction';
  116. import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
  117. import { ref } from 'vue';
  118. import VillageApi from '@/api/inhert/VillageApi';
  119. import VillageInfoApi from '@/api/inhert/VillageInfoApi';
  120. import Box from '@/common/components/parts/Box.vue';
  121. import SimplePageContentLoader from '@/common/components/SimplePageContentLoader.vue';
  122. import AppCofig from '@/common/config/AppCofig';
  123. import Image from '@/components/basic/Image.vue';
  124. import Text from '@/components/basic/Text.vue';
  125. import Loadmore from '@/components/display/loading/Loadmore.vue';
  126. import Touchable from '@/components/feedback/Touchable.vue';
  127. import FlexCol from '@/components/layout/FlexCol.vue';
  128. import FlexRow from '@/components/layout/FlexRow.vue';
  129. import Height from '@/components/layout/space/Height.vue';
  130. import Width from '@/components/layout/space/Width.vue';
  131. import ProvideVar from '@/components/theme/ProvideVar.vue';
  132. import Grid from '@/components/layout/grid/Grid.vue';
  133. import GridItem from '@/components/layout/grid/GridItem.vue';
  134. import LightMap from '../components/LightMap.vue';
  135. import ImageButton from '@/components/basic/ImageButton.vue';
  136. import CommonContent, { GetContentListParams } from '@/api/CommonContent';
  137. import UnmoveableContent from '@/api/inheritor/UnmoveableContent';
  138. import { goCommonContentDetail, goCommonContentList } from '../article/common/CommonContent';
  139. import { toast } from '@/components/utils/DialogAction';
  140. const currentLonlat = ref<{ longitude: number, latitude: number }>({ longitude: 0, latitude: 0 });
  141. const isLightMode = ref(false);
  142. function getedCurrentLonlat(lonlat: { longitude: number, latitude: number }) {
  143. currentLonlat.value = lonlat;
  144. recommendedNearbySitesLoader.loadData();
  145. }
  146. function goVillageDetails(e: any) {
  147. const id = typeof e.markerId == 'number' ? e.markerId : e.id;
  148. uni.setStorageSync('VillageTemp', JSON.stringify(villageLoader.content.value?.find(p => p.id == id)));
  149. setTimeout(() => {
  150. navTo('/pages/home/village/details', { id: id });
  151. }, 200);
  152. }
  153. /** 根据两点经纬度计算直线距离(米),Haversine 公式 */
  154. function getDistanceMeters(
  155. lon1: number, lat1: number,
  156. lon2: number, lat2: number
  157. ): number {
  158. const R = 6371000; // 地球半径 米
  159. const dLat = (lat2 - lat1) * Math.PI / 180;
  160. const dLon = (lon2 - lon1) * Math.PI / 180;
  161. const a =
  162. Math.sin(dLat / 2) * Math.sin(dLat / 2) +
  163. Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
  164. Math.sin(dLon / 2) * Math.sin(dLon / 2);
  165. const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  166. return R * c;
  167. }
  168. function getDistance(longitude: number, latitude: number): string {
  169. const meters = getDistanceMeters(longitude, latitude, currentLonlat.value.longitude, currentLonlat.value.latitude);
  170. if (meters < 100) return '一百米内';
  171. if (meters < 1000) return `${Math.round(meters / 100) * 100}米`;
  172. return `${Math.round(meters / 1000)}km`;
  173. }
  174. const recommendedNearbySitesLoader = useSimpleDataLoader(async () => {
  175. const res = (await CommonContent.getContentList(new GetContentListParams()
  176. .setModelId(UnmoveableContent.modelId)
  177. .setSelfValues({
  178. longitude: currentLonlat.value.longitude,
  179. latitude: currentLonlat.value.latitude,
  180. }), 1, 8, undefined, true)).list;
  181. for (const item of res)
  182. item.distance = getDistance(item.longitude, item.latitude);
  183. return res;
  184. });
  185. const villageLoader = useSimpleDataLoader(async () => {
  186. const res = (await VillageApi.getVallageList(undefined, 1)).map((p, i) => ({
  187. ...p,
  188. id: p.id ?? i,
  189. title: p.villageName,
  190. longitude: Number(p.longitude),
  191. latitude: Number(p.latitude),
  192. width: 30,
  193. height: 30,
  194. iconPath: p.thumbnail || p.image,
  195. }));
  196. return res;
  197. });
  198. const recommendLoader = useSimpleDataLoader(async () => {
  199. //const category = (await CommonContent.getCategoryList(151)).find(p => p.title == '省级');
  200. return (await VillageApi.getVallageList(undefined, 1));
  201. });
  202. const discoverLoader = useSimpleDataLoader(async () => {
  203. return (await VillageInfoApi.getListForDiscover(1, 10)).list.map((item) => {
  204. return {
  205. ...item,
  206. image: (item.thumbnail || item.image) as string,
  207. desc: item.desc || '',
  208. title: item.title,
  209. }
  210. })
  211. });
  212. function goDiscoverDetails(item: any) {
  213. navTo('/pages/home/discover/details', {
  214. id: item.id,
  215. collectModelId: item.collectModuleId,
  216. collectModelInternalName: item.collectModuleInternalName,
  217. });
  218. }
  219. function goList(keywords: string) {
  220. }
  221. </script>