快乐的梦鱼 пре 1 месец
родитељ
комит
3294aa9233

+ 61 - 0
src/api/light/LightVillageApi.ts

@@ -66,6 +66,67 @@ export class LightVillageApi extends AppServerRequestModule<DataModel> {
     super();
     super();
   }
   }
 
 
+  /**
+   * 志愿者排行榜
+   * POST /village/volunteer/getRanklist
+   */
+  async getVolunteerRankList(params?: {
+    /** 地区(区域内所有村社) */
+    region_id?: number;
+    /** 数量:显示前几名(默认10) */
+    num?: number;
+    /** 村社ID */
+    village_id?: number;
+  }) {
+    const res = await this.post<{
+      code: number;
+      msg: string;
+      time: string;
+      data: Array<{
+        id: number;
+        name: string;
+        mobile: string;
+        points: number;
+        level: number;
+        type_text?: string;
+        sex_text?: string;
+        status_text?: string;
+      }>;
+    }>('/village/volunteer/getRanklist', '志愿者排行榜', params);
+    return res.requireData().data;
+  }
+
+  /**
+   * 村社排行榜
+   * POST /village/village/getRanklist
+   */
+  async getVillageRankList(params?: {
+    /** 地区(区域内所有村社) */
+    region_id?: number;
+    /** 数量:显示前几名(默认10) */
+    num?: number;
+    /** 点亮状态:0=未点亮,1=已点亮 */
+    is_light?: number;
+  }) {
+    const res = await this.post<{
+      code: number;
+      msg: string;
+      time: string;
+      data: Array<{
+        id: number;
+        name: string;
+        points: number;
+        region: number;
+        status?: string;
+        is_light: number | string;
+        status_text?: string;
+        region_text?: string;
+        is_light_text?: string;
+      }>;
+    }>('/village/village/getRanklist', '村社排行榜', params);
+    return res.requireData().data;
+  }
+
   async getVillageList(level?: number, region?: number, status?: number, page?: number, pageSize?: number) {
   async getVillageList(level?: number, region?: number, status?: number, page?: number, pageSize?: number) {
     const res = await this.get<{
     const res = await this.get<{
       data: any[],
       data: any[],

+ 9 - 7
src/pages/components/LightMap.vue

@@ -51,6 +51,7 @@ import Button from '@/components/basic/Button.vue';
 import { navTo } from '@/components/utils/PageAction';
 import { navTo } from '@/components/utils/PageAction';
 import CommonContent from '@/api/CommonContent';
 import CommonContent from '@/api/CommonContent';
 import { waitTimeOut } from '@imengyu/imengyu-utils';
 import { waitTimeOut } from '@imengyu/imengyu-utils';
+import { useVillageStore } from '@/store/village';
 
 
 const instance = getCurrentInstance();
 const instance = getCurrentInstance();
 const mapCtx = uni.createMapContext('prevMap', instance);
 const mapCtx = uni.createMapContext('prevMap', instance);
@@ -62,7 +63,7 @@ const currentLonlat = ref<{ longitude: number, latitude: number }>({
   latitude: AppCofig.defaultLonLat[1],
   latitude: AppCofig.defaultLonLat[1],
 });
 });
 const villageData = new Map<number, VillageListItem>();
 const villageData = new Map<number, VillageListItem>();
-const emit = defineEmits(['getedCurrentLonlat','update:isLightMode']);
+const emit = defineEmits(['getedCurrentLonlat','update:isLightMode', 'selectVillage']);
 
 
 const props = defineProps<{
 const props = defineProps<{
   startLonlat?: { longitude: number, latitude: number } | undefined;
   startLonlat?: { longitude: number, latitude: number } | undefined;
@@ -71,6 +72,8 @@ const props = defineProps<{
   full?: boolean;
   full?: boolean;
 }>();
 }>();
 
 
+const villageStore = useVillageStore();
+
 const regionLoader = useSimpleDataLoader(async () => {
 const regionLoader = useSimpleDataLoader(async () => {
   return (await CommonContent.getCategoryChildList(5)).map(p => ({
   return (await CommonContent.getCategoryChildList(5)).map(p => ({
     id: p.id,
     id: p.id,
@@ -165,10 +168,7 @@ function onMarkerTap(e: any) {
   }
   }
   const village = villageData.get(e.markerId);
   const village = villageData.get(e.markerId);
   if (village) {
   if (village) {
-    uni.setStorageSync('VillageTemp', JSON.stringify(village));
-    setTimeout(() => {
-      navTo('/pages/home/light/details', { id: village.id });
-    }, 200);
+    emit('selectVillage', village);
   }
   }
 }
 }
 function onSelectedRegion(regionId: number) {
 function onSelectedRegion(regionId: number) {
@@ -176,12 +176,10 @@ function onSelectedRegion(regionId: number) {
   nextNeedAutoFocus.value = true;
   nextNeedAutoFocus.value = true;
   mapLoader.reload();
   mapLoader.reload();
 }
 }
-
 function setCurrentRegion(regionName: string) {
 function setCurrentRegion(regionName: string) {
   selectedRegion.value = regionLoader.content.value?.find(p => p.name == regionName)?.id || undefined;
   selectedRegion.value = regionLoader.content.value?.find(p => p.name == regionName)?.id || undefined;
   mapLoader.reload();
   mapLoader.reload();
 }
 }
-
 function getCurrentLonlat() {
 function getCurrentLonlat() {
   uni.getLocation({
   uni.getLocation({
     type: 'wgs84',
     type: 'wgs84',
@@ -190,8 +188,10 @@ function getCurrentLonlat() {
         longitude: res.longitude,
         longitude: res.longitude,
         latitude: res.latitude,
         latitude: res.latitude,
       };
       };
+      villageStore.setCurrentLonlat(currentLonlat.value);
       const address = await MapApi.regeo(res.latitude, res.longitude);
       const address = await MapApi.regeo(res.latitude, res.longitude);
       currentAddress.value = address.district;
       currentAddress.value = address.district;
+      villageStore.setCurrentRegion(address.district);
       setCurrentRegion(address.district);
       setCurrentRegion(address.district);
       emit('getedCurrentLonlat', currentLonlat.value);
       emit('getedCurrentLonlat', currentLonlat.value);
       mapCtx.moveToLocation({
       mapCtx.moveToLocation({
@@ -252,9 +252,11 @@ onMounted(async () => {
       longitude: Number(res.point.x),
       longitude: Number(res.point.x),
       latitude: Number(res.point.y),
       latitude: Number(res.point.y),
     };
     };
+    villageStore.setCurrentLonlat(currentLonlat.value);
     await waitTimeOut(100);
     await waitTimeOut(100);
     currentAddress.value = res.address_detail.district;
     currentAddress.value = res.address_detail.district;
     setCurrentRegion(res.address_detail.district);
     setCurrentRegion(res.address_detail.district);
+    villageStore.setCurrentRegion(res.address_detail.district);
     emit('getedCurrentLonlat', currentLonlat.value);
     emit('getedCurrentLonlat', currentLonlat.value);
     mapCtx.moveToLocation({
     mapCtx.moveToLocation({
       latitude: currentLonlat.value.latitude,
       latitude: currentLonlat.value.latitude,

+ 26 - 76
src/pages/home/components/VillageMiniMap.vue

@@ -11,9 +11,9 @@
       class="mini-village-map-map"
       class="mini-village-map-map"
       :enable-poi="false"
       :enable-poi="false"
       :markers="markers"
       :markers="markers"
-      :scale="12"
-      :longitude="AppCofig.defaultLonLat[0]"
-      :latitude="AppCofig.defaultLonLat[1]"
+      :scale="15"
+      :longitude="lonlat.longitude"
+      :latitude="lonlat.latitude"
     />
     />
     <NoticeBar 
     <NoticeBar 
       v-if="currentNoticeContent"
       v-if="currentNoticeContent"
@@ -36,92 +36,42 @@
       textColor="#C9211F"
       textColor="#C9211F"
       backgroundColor="#D9492E10"
       backgroundColor="#D9492E10"
     />
     />
-    <Button 
-      :innerStyle="{
-        position: 'absolute',
-        bottom: '20rpx',
-        right: '20rpx',
-        zIndex: 100,
-        backgroundColor: '#ffffff',
-      }" 
-      icon="navigation" 
-      @click="getCurrentLonlat" 
-    >定位</Button>
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { getCurrentInstance, onMounted, ref } from 'vue';
-import { waitTimeOut } from '@imengyu/imengyu-utils';
+import { computed, getCurrentInstance } from 'vue';
 import { useTheme } from '@/components/theme/ThemeDefine';
 import { useTheme } from '@/components/theme/ThemeDefine';
-import LightVillageApi from '@/api/light/LightVillageApi';
-import MapApi from '@/api/map/MapApi';
-import AppCofig from '@/common/config/AppCofig';
-import Button from '@/components/basic/Button.vue';
 import NoticeBar from '@/components/display/NoticeBar.vue';
 import NoticeBar from '@/components/display/NoticeBar.vue';
 import type { MapMarker } from '@/types/Map';
 import type { MapMarker } from '@/types/Map';
-
-const instance = getCurrentInstance();
-const mapCtx = uni.createMapContext('prevMap', instance);
-const currentAddress = ref<string>('');
-const currentLonlat = ref<{ longitude: number, latitude: number }>({ 
-  longitude: AppCofig.defaultLonLat[0], 
-  latitude: AppCofig.defaultLonLat[1],
-});
+import ImagesUrls from '@/common/config/ImagesUrls';
 
 
 const emit = defineEmits(['getedCurrentLonlat']);
 const emit = defineEmits(['getedCurrentLonlat']);
-const props = defineProps<{
+const props = withDefaults(defineProps<{
   markers?: MapMarker[];
   markers?: MapMarker[];
-  startLonlat?: { longitude: number, latitude: number } | undefined;
-}>();
-
-const currentNoticeContent = ref('目前厦门市已被点亮一个社区,刚刚小亮贡献了10个光源,让湖里区点亮了一个社区');
+  currentNoticeContent?: string;
+  lonlat?: { longitude: number, latitude: number } | undefined;
+}>(), {
+  currentNoticeContent: '目前厦门市已被点亮一个社区,刚刚小亮贡献了10个光源,让湖里区点亮了一个社区',
+  lonlat: () => ({
+    longitude: 0,
+    latitude: 0,
+  }),
+});
 
 
 const themeContext = useTheme();
 const themeContext = useTheme();
-
-function getCurrentLonlat() {
-  uni.getLocation({
-    type: 'wgs84',
-    success: async (res) => {
-      currentLonlat.value = {
-        longitude: res.longitude,
-        latitude: res.latitude,
-      };
-      const address = await MapApi.regeo(res.latitude, res.longitude);
-      currentAddress.value = address.district;
-      emit('getedCurrentLonlat', currentLonlat.value);
-      mapCtx.moveToLocation({
-        latitude: currentLonlat.value.latitude,
-        longitude: currentLonlat.value.longitude,
-      });
-    },
-  });
-}
-
-onMounted(async () => {
-  if (props.startLonlat) {
-    currentLonlat.value = props.startLonlat;
-    mapCtx.moveToLocation({
-      latitude: currentLonlat.value.latitude,
-      longitude: currentLonlat.value.longitude,
-    });
-  }
-  if (!props.startLonlat) {
-    const res = await LightVillageApi.getIpAddress();
-    currentLonlat.value = {
-      longitude: Number(res.point.x),
-      latitude: Number(res.point.y),
-    };
-    await waitTimeOut(100);
-    currentAddress.value = res.address_detail.district;
-    emit('getedCurrentLonlat', currentLonlat.value);
-    mapCtx.moveToLocation({
-      latitude: currentLonlat.value.latitude,
-      longitude: currentLonlat.value.longitude,
-    });
-  }
+const markers = computed(() => {
+  return (props.markers || []).concat([
+    {
+      id: 1,
+      longitude: props.lonlat.longitude,
+      latitude: props.lonlat.latitude,
+      width: 30,
+      height: 30,
+      iconPath: ImagesUrls.IconMarker,
+    }
+  ]);
 });
 });
-
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">

+ 3 - 0
src/pages/home/components/VillageMyFollow.vue

@@ -24,6 +24,7 @@
 
 
 <script setup lang="ts">
 <script setup lang="ts">
 import { useSimplePageListLoader } from '@/components/composeabe/loader/SimplePageListLoader';
 import { useSimplePageListLoader } from '@/components/composeabe/loader/SimplePageListLoader';
+import { useVillageStore } from '@/store/village';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import HomeTitle from '@/common/components/parts/HomeTitle.vue';
 import HomeTitle from '@/common/components/parts/HomeTitle.vue';
 import ImageBlock3 from '@/components/display/block/ImageBlock3.vue';
 import ImageBlock3 from '@/components/display/block/ImageBlock3.vue';
@@ -32,6 +33,8 @@ import SimplePageListLoader from '@/components/loader/SimplePageListLoader.vue';
 import type { VillageListItem } from '@/api/inhert/VillageApi';
 import type { VillageListItem } from '@/api/inhert/VillageApi';
 import RequireLogin from '@/common/components/RequireLogin.vue';
 import RequireLogin from '@/common/components/RequireLogin.vue';
 
 
+const villageStore = useVillageStore();
+
 const emit = defineEmits<{
 const emit = defineEmits<{
   (e: 'goDetails', item: VillageListItem): void;
   (e: 'goDetails', item: VillageListItem): void;
 }>();
 }>();

+ 21 - 8
src/pages/home/index.vue

@@ -41,6 +41,7 @@
 
 
     <LightMap
     <LightMap
       small
       small
+      @selectVillage="goDetails"
     >
     >
       <NoticeBar 
       <NoticeBar 
         v-if="currentNoticeContent"
         v-if="currentNoticeContent"
@@ -133,23 +134,24 @@
       round
       round
       size="80vh"
       size="80vh"
     >
     >
-      <VillageMyFollow />
+      <VillageMyFollow @goDetails="goDetails" />
     </Popup>
     </Popup>
   </FlexCol>
   </FlexCol>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { ref, watch } from 'vue';
+import { onMounted, ref, watch } from 'vue';
 import { useTheme } from '@/components/theme/ThemeDefine';
 import { useTheme } from '@/components/theme/ThemeDefine';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import { useStorageVar } from '@/components/composeabe/StorageVar';
 import { useStorageVar } from '@/components/composeabe/StorageVar';
+import { useVillageStore } from '@/store/village';
+import { navTo } from '@/components/utils/PageAction';
 import Image from '@/components/basic/Image.vue';
 import Image from '@/components/basic/Image.vue';
 import Loadmore from '@/components/display/loading/Loadmore.vue';
 import Loadmore from '@/components/display/loading/Loadmore.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
 import Height from '@/components/layout/space/Height.vue';
 import Height from '@/components/layout/space/Height.vue';
 import SearchBar from '@/components/form/SearchBar.vue';
 import SearchBar from '@/components/form/SearchBar.vue';
-import VillageMiniMap from './components/VillageMiniMap.vue';
 import Button from '@/components/basic/Button.vue';
 import Button from '@/components/basic/Button.vue';
 import HomeTitle from '@/common/components/parts/HomeTitle.vue';
 import HomeTitle from '@/common/components/parts/HomeTitle.vue';
 import VillageRankList from './components/VillageRankList.vue';
 import VillageRankList from './components/VillageRankList.vue';
@@ -162,14 +164,16 @@ import Popup from '@/components/dialog/Popup.vue';
 import CitySelect from './components/CitySelect.vue';
 import CitySelect from './components/CitySelect.vue';
 import VillageMyFollow from './components/VillageMyFollow.vue';
 import VillageMyFollow from './components/VillageMyFollow.vue';
 import type { CityItem } from '@/api/map/MapApi';
 import type { CityItem } from '@/api/map/MapApi';
-import { navTo } from '@/components/utils/PageAction';
 import MapApi from '@/api/map/MapApi';
 import MapApi from '@/api/map/MapApi';
-import LightMap from '../components/LightMap.vue';
+import LightMap from './components/LightMap.vue';
 import NoticeBar from '@/components/display/NoticeBar.vue';
 import NoticeBar from '@/components/display/NoticeBar.vue';
 import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
 import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
-import NavBar from '@/components/nav/NavBar.vue';
+import type { VillageListItem } from '@/api/inhert/VillageApi';
+import FollowVillageApi from '@/api/light/FollowVillageApi';
 
 
+const emit = defineEmits(['goVillage']);
 
 
+const villageStore = useVillageStore();
 const themeContext = useTheme();
 const themeContext = useTheme();
 const searchKeywords = ref('');
 const searchKeywords = ref('');
 const showCityPopup = ref(false);
 const showCityPopup = ref(false);
@@ -198,11 +202,20 @@ const recommendLoader = useSimpleDataLoader(async () => {
   }))
   }))
 });
 });
 
 
-
-
+function goDetails(item: VillageListItem) {
+  showMyFollowPopup.value = false;
+  villageStore.setCurrentVillage(item);
+  emit('goVillage')
+}
 function handleSelectCity(city: CityItem) {
 function handleSelectCity(city: CityItem) {
   currentCity.value = city.name;
   currentCity.value = city.name;
   showCityPopup.value = false;
   showCityPopup.value = false;
 }
 }
 
 
+onMounted(async () => {
+  const res = await FollowVillageApi.getFollowVillageList({ page: 1, pageSize: 200 });
+  villageStore.setMyFollowVillages(res.list);
+  if (res.list.length > 0)
+    villageStore.setCurrentVillage(res.list[0]);
+});
 </script>
 </script>

+ 1 - 1
src/pages/home/light/submit-map.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
 import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
 import AppCofig from '@/common/config/AppCofig';
 import AppCofig from '@/common/config/AppCofig';
-import LightMap from '@/pages/components/LightMap.vue';
+import LightMap from '../components/LightMap.vue';
 
 
 const { querys } = useLoadQuerys({
 const { querys } = useLoadQuerys({
   latitude: AppCofig.defaultLonLat[1],
   latitude: AppCofig.defaultLonLat[1],

+ 36 - 4
src/pages/home/village/index.vue

@@ -1,5 +1,15 @@
 <template>
 <template>
-  <FlexCol gap="gap.md">
+  <FlexCol position="absolute" :left="0" :top="0">
+    <StatusBarSpace />
+    <Button 
+      @click="showMyFollowPopup = true"
+      icon="https://xy.wenlvti.net/app_static/images/home/IconSwitch.png"
+      :text="villageStore.currentVillage?.villageName || '未选择村庄'"
+      type="custom"
+      color="transparent"
+    />
+  </FlexCol>
+  <FlexCol v-if="villageStore.currentVillage" gap="gap.md">
     <FlexRow center :padding="[0,30]" gap="gap.md">
     <FlexRow center :padding="[0,30]" gap="gap.md">
       <HomeLargeTitle title="村社名片" :active="tab === 'card'" @click="tab = 'card'" />
       <HomeLargeTitle title="村社名片" :active="tab === 'card'" @click="tab = 'card'" />
       <HomeLargeTitle title="乡源树" :active="tab === 'tree'" @click="tab = 'tree'">
       <HomeLargeTitle title="乡源树" :active="tab === 'tree'" @click="tab = 'tree'">
@@ -10,21 +20,43 @@
     </FlexRow>
     </FlexRow>
     <Card v-if="tab === 'card'" />
     <Card v-if="tab === 'card'" />
     <Tree v-if="tab === 'tree'" />
     <Tree v-if="tab === 'tree'" />
-    <Loadmore status="nomore" />
+    <Popup 
+      v-model:show="showMyFollowPopup" 
+      closeable
+      position="bottom"
+      round
+      size="80vh"
+    >
+      <VillageMyFollow @goDetails="onSelectVillage" />
+    </Popup>
     <Height :height="150" />
     <Height :height="150" />
   </FlexCol>
   </FlexCol>
+  <Empty v-else description="请选择村庄" />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import Loadmore from '@/components/display/loading/Loadmore.vue';
+import { ref } from 'vue';
+import { useVillageStore } from '@/store/village';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import Height from '@/components/layout/space/Height.vue';
 import Height from '@/components/layout/space/Height.vue';
 import Image from '@/components/basic/Image.vue';
 import Image from '@/components/basic/Image.vue';
 import HomeLargeTitle from '@/common/components/parts/HomeLargeTitle.vue';
 import HomeLargeTitle from '@/common/components/parts/HomeLargeTitle.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
-import { ref } from 'vue';
 import Card from './introd/card.vue';
 import Card from './introd/card.vue';
 import Tree from './introd/tree.vue';
 import Tree from './introd/tree.vue';
+import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
+import Button from '@/components/basic/Button.vue';
+import Popup from '@/components/dialog/Popup.vue';
+import VillageMyFollow from '../components/VillageMyFollow.vue';
+import Empty from '@/components/feedback/Empty.vue';
+import type { VillageListItem } from '@/api/inhert/VillageApi';
 
 
 const tab = ref('card');
 const tab = ref('card');
+const villageStore = useVillageStore();
+const showMyFollowPopup = ref(false);
+
+function onSelectVillage(village: VillageListItem) {
+  villageStore.setCurrentVillage(village);
+  showMyFollowPopup.value = false;
+}
 </script>
 </script>

+ 72 - 21
src/pages/home/village/introd/card.vue

@@ -65,22 +65,29 @@
         <Icon name="https://xy.wenlvti.net/app_static/images/village/IconMap.png" size="fontSize.md" />
         <Icon name="https://xy.wenlvti.net/app_static/images/village/IconMap.png" size="fontSize.md" />
         <Text :text="villageInfoLoader.content.value?.address" fontConfig="contentText" />
         <Text :text="villageInfoLoader.content.value?.address" fontConfig="contentText" />
       </FlexRow>
       </FlexRow>
-      <VillageMiniMap />
+      <VillageMiniMap 
+        v-if="villageInfoLoader.content.value"
+        :lonlat="{ 
+          longitude: villageInfoLoader.content.value.longitude, 
+          latitude: villageInfoLoader.content.value.latitude 
+        }" 
+      />
 
 
       <FlexRow center gap="gap.lg">
       <FlexRow center gap="gap.lg">
         <Button 
         <Button 
           icon="https://xy.wenlvti.net/app_static/images/village/IconJoin.png" 
           icon="https://xy.wenlvti.net/app_static/images/village/IconJoin.png" 
           radius="radius.lgr"
           radius="radius.lgr"
           size="large"
           size="large"
-          :innerStyle="{ flexBasis: '20%' }"
+          :innerStyle="{ flexBasis: '25%' }"
+          @click="isFollowed ? onUnFollow() : onFollow()"
         >
         >
-          关注
+          {{ isFollowed ? '已关注' : '关注' }}
         </Button>
         </Button>
         <Button 
         <Button 
           icon="https://xy.wenlvti.net/app_static/images/village/IconFollow.png" 
           icon="https://xy.wenlvti.net/app_static/images/village/IconFollow.png" 
           size="large"
           size="large"
           radius="radius.lgr"
           radius="radius.lgr"
-          :innerStyle="{ flexBasis: '20%' }"
+          :innerStyle="{ flexBasis: '25%' }"
         >
         >
           加入
           加入
         </Button>
         </Button>
@@ -197,7 +204,7 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { ref } from 'vue';
+import { computed, ref, watch } from 'vue';
 import HomeTitle from '@/common/components/parts/HomeTitle.vue';
 import HomeTitle from '@/common/components/parts/HomeTitle.vue';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import Button from '@/components/basic/Button.vue';
 import Button from '@/components/basic/Button.vue';
@@ -219,31 +226,75 @@ import GridItem from '@/components/layout/grid/GridItem.vue';
 import MasonryGrid from '@/components/layout/masonry/MasonryGrid.vue';
 import MasonryGrid from '@/components/layout/masonry/MasonryGrid.vue';
 import MasonryGridItem from '@/components/layout/masonry/MasonryGridItem.vue';
 import MasonryGridItem from '@/components/layout/masonry/MasonryGridItem.vue';
 import IndexCommonImageItem from '@/common/components/parts/IndexCommonImageItem.vue';
 import IndexCommonImageItem from '@/common/components/parts/IndexCommonImageItem.vue';
+import { useVillageStore } from '@/store/village';
+import FollowVillageApi from '@/api/light/FollowVillageApi';
+import { confirm, toast } from '@/components/utils/DialogAction';
 
 
+const villageStore = useVillageStore();
 const villageInfoLoader = useSimpleDataLoader(async () => {
 const villageInfoLoader = useSimpleDataLoader(async () => {
+  const village = villageStore.currentVillage;
   return {
   return {
-    title: '高点社区',
-    desc: '',
-    address: '福建省厦门市同安区高浦社区',
-    applyCount: 100,
-    levelText: '一级',
-    rankText: '1',
-    light: 0,
-    memberCount: 0,
-    followerCount: 0,
-    images: [
-      'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest1.jpg',
-      'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest2.jpg',
-      'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest3.jpg',
-      'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest4.jpg',
-      'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest5.jpg',
-    ],
+    title: village?.villageName || '',
+    desc: village?.desc || '',
+    address: village?.address,
+    applyCount: village?.applyCount || 0,
+    levelText: village?.levelText as string || '',
+    rankText: village?.rankText as string || '',
+    light: village?.light as number || 0,
+    memberCount: village?.memberCount as number || 0,
+    followerCount: village?.followerCount as number || 0,
+    images: village?.images || [],
+    longitude: village?.longitude as number || 0,
+    latitude: village?.latitude as number || 0,
   };
   };
 });
 });
 
 
+watch(() => villageStore.currentVillage, () => {
+  villageInfoLoader.reload();
+  recommendLoader.reload();
+
+});
+
 const rankActiveTag = ref('乡源果');
 const rankActiveTag = ref('乡源果');
 const listActiveTag = ref('广场');
 const listActiveTag = ref('广场');
 
 
+const isFollowed = computed(() => {
+  return villageStore.myFollowVillages.some(p => p.id === villageStore.currentVillage?.id);
+});
+const isJoined = computed(() => {
+  return false;
+});
+
+async function onFollow() {
+  if (!villageStore.currentVillage) return;
+  try {
+    await FollowVillageApi.followVillage(villageStore.currentVillage.id);
+    villageStore.myFollowVillages.push(villageStore.currentVillage);
+    toast('关注成功');
+  } catch {
+    toast('关注失败');
+  }
+}
+function onUnFollow() {
+  if (!villageStore.currentVillage) return;
+  confirm({
+    title: '取消关注',
+    content: '确定取消关注该村庄吗?',
+    confirmText: '取消关注',
+    cancelText: '取消',
+  }).then(async (res) => {
+    if (res) {
+      try { 
+        await FollowVillageApi.unfollowVillage(villageStore.currentVillage!.id);
+        villageStore.myFollowVillages = villageStore.myFollowVillages.filter(p => p.id !== villageStore.currentVillage!.id);
+        toast('取消关注成功');
+      } catch {
+        toast('取消关注失败');
+      }
+    }
+  });
+}
+
 const recommendLoader = useSimpleDataLoader(async () => {
 const recommendLoader = useSimpleDataLoader(async () => {
   return [
   return [
     {
     {

+ 44 - 0
src/pages/home/village/recommed/around.vue

@@ -0,0 +1,44 @@
+<template>
+  <FlexCol>
+    <HomeTitle title="周边村社" />
+    <MasonryGrid>
+      <MasonryGridItem
+        v-for="(item, i) in recommendLoader.content.value"
+        :key="i"
+        :width="340"
+      >
+        <IndexCommonImageItem
+          :image="item.image"
+          :title="item.title"
+          :desc="item.desc"
+          :userName="item.userName"
+          :likes="item.likes"
+          :isLike="item.isLike"
+        />
+      </MasonryGridItem>
+    </MasonryGrid>
+  </FlexCol>
+</template>
+
+<script setup lang="ts">  
+import FlexCol from '@/components/layout/FlexCol.vue';
+import HomeTitle from '@/common/components/parts/HomeTitle.vue';
+import MasonryGrid from '@/components/layout/masonry/MasonryGrid.vue';
+import MasonryGridItem from '@/components/layout/masonry/MasonryGridItem.vue';
+import IndexCommonImageItem from '@/common/components/parts/IndexCommonImageItem.vue';
+import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import { useVillageStore } from '@/store/village';
+import LightVillageApi from '@/api/light/LightVillageApi';
+import CommonContent from '@/api/CommonContent';
+
+const villageStore = useVillageStore();
+const recommendLoader = useSimpleDataLoader(async () => {
+  if (!villageStore.currentRegion)
+    return [];
+  const regionId = await CommonContent.getCategoryList(villageStore.currentRegion);
+  const res = await LightVillageApi.getVillageList(1, villageStore.currentRegion, undefined, 1, 20);
+
+
+  return [];
+});
+</script>

+ 1 - 1
src/pages/index.vue

@@ -14,7 +14,7 @@
         textColor="whweixinite"
         textColor="whweixinite"
         align="left"
         align="left"
       />
       />
-      <HomeIndex v-if="tabIndex === 0" />
+      <HomeIndex v-if="tabIndex === 0" @goVillage="tabIndex = 1" />
       <VillageIndex v-else-if="tabIndex === 1" />
       <VillageIndex v-else-if="tabIndex === 1" />
       <DigIndex v-else-if="tabIndex === 2" />
       <DigIndex v-else-if="tabIndex === 2" />
       <DiscoverIndex v-else-if="tabIndex === 3" />
       <DiscoverIndex v-else-if="tabIndex === 3" />

+ 45 - 0
src/store/village.ts

@@ -0,0 +1,45 @@
+import { ref } from 'vue'
+import { defineStore } from 'pinia'
+import VillageApi, { VillageListItem } from '@/api/inhert/VillageApi';
+
+/** 
+ * 村庄各页面共享 数据
+ */
+export const useVillageStore = defineStore('village', () => {
+  
+  const currentVillage = ref<VillageListItem | null>(null);
+  const currentLonlat = ref<{ longitude: number, latitude: number } | null>(null);
+  const currentRegion = ref<string | null>(null);
+  const myFollowVillages = ref<VillageListItem[]>([]);
+  const myJoinedVillages = ref<VillageListItem[]>([]);
+
+  function setCurrentVillage(village: VillageListItem) {
+    currentVillage.value = village;
+    console.log('setCurrentVillage', village);
+  }
+  function setCurrentLonlat(lonlat: { longitude: number, latitude: number }) {
+    currentLonlat.value = lonlat;
+  }
+  function setCurrentRegion(region: string) {
+    currentRegion.value = region;
+  }
+  function setMyFollowVillages(villages: VillageListItem[]) {
+    myFollowVillages.value = villages;
+  }
+  function setMyJoinedVillages(villages: VillageListItem[]) {
+    myJoinedVillages.value = villages;
+  }
+
+  return { 
+    currentVillage,
+    currentLonlat,
+    currentRegion,
+    myFollowVillages,
+    myJoinedVillages,
+    setCurrentVillage,
+    setCurrentLonlat,
+    setCurrentRegion,
+    setMyFollowVillages,
+    setMyJoinedVillages,
+  }
+})