Sfoglia il codice sorgente

📦 首页地址与地图对接新接口

快乐的梦鱼 3 settimane fa
parent
commit
9dba1752c2

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

@@ -366,6 +366,7 @@ export class LightVillageApi extends AppServerRequestModule<DataModel> {
 
   async getVillageList(data?: {
     level?: number, 
+    areaCode?: number,
     region?: number, 
     status?: number, 
     page?: number, 
@@ -382,6 +383,7 @@ export class LightVillageApi extends AppServerRequestModule<DataModel> {
       page: data?.page,
       pageSize: data?.pageSize,
       keyword: data?.keyword,
+      area_code: data?.areaCode,
     });
     return {
       total: res.requireData().total,

+ 121 - 0
src/api/map/RegionApi.ts

@@ -0,0 +1,121 @@
+import { DataModel, transformArrayDataModel, transformDataModel, type KeyValue, type NewDataModel } from '@imengyu/js-request-transform';
+import { AppServerRequestModule } from '../RequestModules';
+import { transformSomeToArray } from '../Utils';
+
+export class RegionItem extends DataModel<RegionItem> {
+  constructor() {
+    super(RegionItem, '地区');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
+      level: { clientSide: 'number', serverSide: 'number' },
+      parentCode: { clientSide: 'number', serverSide: 'number' },
+      areaCode: { clientSide: 'number', serverSide: 'number' },
+      postalCode: { clientSide: 'number', serverSide: 'number' },
+      cityCode: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id!: number;
+  /** 层级 */
+  level = 0;
+  /** 父级行政代码 */
+  parentCode = 0;
+  /** 行政代码 */
+  areaCode = 0;
+  /** 邮政编码 */
+  postalCode = 0;
+  /** 区号 */
+  cityCode = 0;
+  /** 名称 */
+  name = '';
+  /** 简称 */
+  shortName = '';
+  /** 组合名 */
+  mergerName = '';
+  /** 拼音 */
+  pinyin = '';
+  /** 经度 */
+  longitude = '';
+  /** 纬度 */
+  latitude = '';
+}
+
+export class NearRegionItem extends RegionItem {
+  constructor() {
+    super();
+    this._convertTable = {
+      ...this._convertTable,
+      distance: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  /** 距离(公里) */
+  distance = 0;
+}
+
+type PagedRegionResponse = {
+  total: number;
+  per_page?: number;
+  current_page?: number;
+  last_page?: number;
+  data: KeyValue[];
+};
+
+export class RegionApi extends AppServerRequestModule<DataModel> {
+  constructor() {
+    super();
+  }
+
+  /** 获取子级(仅下一级) */
+  async getChildList(parentCode?: number) {
+    const res = await this.post<KeyValue[]>('/area/getChildList', '获取子级', {
+      parent_code: parentCode ?? 0,
+    });
+    return transformArrayDataModel<RegionItem>(
+      RegionItem,
+      transformSomeToArray(res.requireData()),
+      '地区',
+      true,
+    );
+  }
+
+  /** 名称搜索 */
+  async searchByName(keywords: string, options?: {
+    page?: number;
+    pageSize?: number;
+  }) {
+    const res = await this.post<PagedRegionResponse>('/area/searchByName', '名称搜索', {
+      keywords,
+      page: options?.page,
+      pageSize: options?.pageSize,
+    });
+    const data = res.requireData();
+    return {
+      total: data.total ?? 0,
+      list: transformArrayDataModel<RegionItem>(
+        RegionItem,
+        transformSomeToArray(data.data),
+        '地区',
+        true,
+      ),
+    };
+  }
+
+  /** 经纬度附近(最近20条) */
+  async getNearArea(longitude: number, latitude: number, radius: number) {
+    const res = await this.post<KeyValue[]>('/area/getNearArea', '经纬度附近', {
+      longitude,
+      latitude,
+      radius,
+    });
+    return transformArrayDataModel<NearRegionItem>(
+      NearRegionItem,
+      transformSomeToArray(res.requireData()),
+      '附近地区',
+      true,
+    );
+  }
+}
+
+export default new RegionApi();

+ 1 - 0
src/components/form/Cascader.vue

@@ -128,6 +128,7 @@ const props = withDefaults(defineProps<CascaderProps>(), {
   listHeight: 700,
   tabProps: () => ({
     width: 750 - 70,
+    
   }),
   textKey: 'text',
   valueKey: 'value',

+ 1 - 1
src/components/nav/Tabs.vue

@@ -235,7 +235,7 @@ const props = withDefaults(defineProps<TabsProps>(), {
 
 const theme = useTheme();
 
-const tabPaddingHorizontal = computed(() => theme.getVar('TabsItemPaddingHorizontal', 0));
+const tabPaddingHorizontal = computed(() => theme.getVar('TabsItemPaddingHorizontal', 10));
 const themedUnderlayColor = computed(() => theme.resolveThemeColor(props.underlayColor));
 const themedActiveTextColor = computed(() => theme.resolveThemeColor(props.activeTextColor));
 const themedTextColor = computed(() => theme.resolveThemeColor(props.textColor));

+ 8 - 0
src/pages/home/chat/components/ChatMessageContainer.vue

@@ -25,6 +25,13 @@
         :scroll-top="scrollY"
       >
         <FlexCol gap="gap.sm">
+          <FlexRow
+            center
+            gap="gap.sm"
+            :padding="[10,0]"
+          >
+            <Text fontConfig="secondText">内容由AI生成,可能不准确,请仔细甄别</Text>
+          </FlexRow>
           <FlexRow 
             v-if="sessionManager.currentSessionId.value === CHAT_SESSION_LOCAL_ID" 
             align="center"
@@ -63,6 +70,7 @@ import FlexRow from '@/components/layout/FlexRow.vue';
 import Button from '@/components/basic/Button.vue';
 import ActivityIndicator from '@/components/basic/ActivityIndicator.vue';
 import Icon from '@/components/basic/Icon.vue';
+import Text from '@/components/basic/Text.vue';
 
 export interface ChatMessageContainerExpose {
   scrollToBottom: () => void;

+ 1 - 1
src/pages/home/chat/dependent/post/components/agent.vue

@@ -453,7 +453,7 @@ const chatManager = useChat({
     },
     onBuildWelcome: () => {
       return {
-        welcomeMessage: '您好!欢迎使用亮乡源AI伴写助手。我可以与您讨论写作灵感、写作计划、为您智能完成写作任务',
+        welcomeMessage: '您好!欢迎使用亮乡源AI伴写助手。我可以与您讨论写作灵感、写作计划、为您智能完成写作任务。欢迎和我聊天🥰!',
         welcomeActions: [],
       }
     },

+ 3 - 2
src/pages/home/chat/dependent/post/publish.vue

@@ -190,13 +190,14 @@ function publish() {
     images: images.value.map((image) => image.localUrl),
     tags: [
       '亮乡源',
-      `${querys.value.tag || '亮乡源'}话题${querys.value.villageId ? `·${querys.value.villageId}` : ''}·${authStore.userInfo?.id || '0'}`
+      `${querys.value.tag || '亮乡源'}`
     ],
   };
   console.log('data', data, authStore.userInfo);
   (uni as any).shareToOfficialAccount({
     ...data,
-    success: () => {
+    success: (postObj: { postUrl: string }) => {
+      //TODO: 换接口
       LightVillageApi.publishMessage(new PostMessage().setSelfValues({
         title: title.value,
         content: content.value,

+ 43 - 43
src/pages/home/components/CitySelect.vue

@@ -1,60 +1,60 @@
-
-
 <template>
-  <FlexCol 
-    :padding="[0,'space.md']" 
-    backgroundColor="white" 
+  <FlexCol
+    :padding="[0,30]"
+    backgroundColor="white"
     radius="radius.md"
   >
     <NavBar title="选择城市" />
-    <SearchBar 
-      v-model="searchValue"
-      leftIcon=""
-      searchIcon="search"
-      searchState="show"
-      placeholder="搜索城市" 
-      @search="cities.reload()"
-      @clear="cities.reload()"
-    />
-    <IndexList 
-      v-if="cities.content.value"
-      :data="cities.content.value"
-      :groupDataBy="(item) => item.first_letter"
-      :sortGroup="arr => arr.sort()"
-      :innerStyle="{
-        height: '70vh',
-      }"
-      dataDisplayProp="name"
-      @itemClick="handleCityClick"
+    <Cascader
+      v-model="selectedValue"
+      :data="provinceList"
+      textKey="text"
+      valueKey="value"
+      :asyncLoadData="asyncLoadData"
+      :maxSelectLevel="2"
+      @pickEnd="handlePickEnd"
     />
   </FlexCol>
 </template>
 
 <script setup lang="ts">
-import MapApi, { type CityItem } from '@/api/map/MapApi';
-import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
-import SearchBar from '@/components/form/SearchBar.vue';
+import RegionApi, { RegionItem } from '@/api/map/RegionApi';
+import type { CascaderItem } from '@/components/form/Cascader.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
-import { ref } from 'vue';
+import { ref, onMounted } from 'vue';
 import NavBar from '@/components/nav/NavBar.vue';
-import IndexList from '@/components/list/IndexList.vue';
+import Cascader from '@/components/form/Cascader.vue';
+
+const selectedValue = ref<(string | number)[]>([]);
+const provinceList = ref<CascaderItem[]>([]);
+const regionMap = new Map<string | number, RegionItem>();
+
+function regionToCascaderItem(region: RegionItem): CascaderItem {
+  const item: CascaderItem = {
+    text: region.name,
+    value: String(region.areaCode),
+  };
+  regionMap.set(item.value, region);
+  return item;
+}
+
+async function asyncLoadData(item: CascaderItem) {
+  const list = await RegionApi.getChildList(Number(item.value));
+  return list.map(regionToCascaderItem);
+}
 
-const searchValue = ref('');
-const cities = useSimpleDataLoader(async () => {
-  return (await MapApi.loadCityData(true))
-    .flatMap(item => {
-      return (item.children || []).map(child => ({
-        ...child,
-        parentName: item.name,
-      }));
-    })
-    .filter(item => !searchValue.value || item.name.includes(searchValue.value));
-})
+onMounted(async () => {
+  const list = await RegionApi.getChildList(0);
+  provinceList.value = list.map(regionToCascaderItem);
+});
 
 const emit = defineEmits(['selectCity']);
 
-const handleCityClick = (item: CityItem) => {
-  console.log(item);
-  emit('selectCity', item);
+function handlePickEnd() {
+  const lastValue = selectedValue.value[selectedValue.value.length - 1];
+  const region = regionMap.get(String(lastValue));
+  if (region) {
+    emit('selectCity', region);
+  }
 }
 </script>

+ 55 - 52
src/pages/home/components/LightMap.vue

@@ -19,22 +19,6 @@
       @markertap="onMarkerTap" 
     />
 
-    <FlexCol v-if="isEmptyRegion" 
-      gap="gap.xl" 
-      position="absolute" 
-      inset="0" 
-      padding="padding.md"
-      center 
-    > 
-      <FlexCol center gap="gap.xl" padding="padding.md" backgroundColor="white" radius="radius.md">
-        <Text fontConfig="importantTitle" textAlign="center">您选择的地区还未开通亮乡源数据,可联系客服开通</Text>
-        <button open-type="contact" class="remove-button-style">
-          <FlexCol padding="space.md" radius="radius.lg" center backgroundColor="button">
-            <Text fontConfig="primaryTitle">联系客服</Text>
-          </FlexCol>
-        </button>
-      </FlexCol>
-    </FlexCol>
     <FlexCol v-if="mapLoader.error.value" 
       gap="gap.md" 
       position="absolute" 
@@ -66,13 +50,22 @@
           :icon="searchKeyword ? 'close' : 'search'" 
           @click="showSearch" 
         />
-        <SimpleDropDownPicker 
-          v-if="!isEmptyRegion"
-          class="light-map-region-picker" 
-          :modelValue="selectedRegion" 
-          @update:modelValue="onSelectedRegion"
-          :columns="regionLoader.content.value" 
-        />
+        <FlexRow gap="gap.md" align="center">
+          <NButton
+            :text="city + ' ▼'"
+            color="white"
+            type="custom"
+            :radius="30"
+            :padding="[30,10]"
+            @click="emit('changeCity')" 
+          />
+          <SimpleDropDownPicker
+            class="light-map-region-picker" 
+            :modelValue="selectedRegion" 
+            @update:modelValue="onSelectedRegion"
+            :columns="regionLoader.content.value" 
+          />
+        </FlexRow>
         <NButton
           text="定位"
           icon="navigation" 
@@ -97,26 +90,24 @@
 </template>
 
 <script setup lang="ts">
-import { computed, getCurrentInstance, onMounted, ref, watch } from 'vue';
-import { navTo } from '@/components/utils/PageAction';
+import { getCurrentInstance, onMounted, ref, watch } from 'vue';
 import { waitTimeOut } from '@imengyu/imengyu-utils';
+import { toast } from '@/components/dialog/CommonRoot';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import LightVillageApi, { VillageListItem } from '@/api/light/LightVillageApi';
-import AppCofig, { isDev } from '@/common/config/AppCofig';
+import AppCofig from '@/common/config/AppCofig';
 import SimpleDropDownPicker from '@/common/components/SimpleDropDownPicker.vue';
 import NButton from '@/components/basic/Button.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import Text from '@/components/basic/Text.vue';
-import CommonContent from '@/api/CommonContent';
 import Icon from '@/components/basic/Icon.vue';
 import ActivityIndicator from '@/components/basic/ActivityIndicator.vue';
-import type { MapMarker } from '@/types/Map';
-import MapApi from '@/api/map/MapApi';
 import Dialog from '@/components/dialog/Dialog.vue';
 import Field from '@/components/form/Field.vue';
-import { toast } from '@/components/dialog/CommonRoot';
 import FlexRow from '@/components/layout/FlexRow.vue';
 import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
+import RegionApi from '@/api/map/RegionApi';
+import type { MapMarker } from '@/types/Map';
 
 const instance = getCurrentInstance();
 const mapCtx = uni.createMapContext('prevMap', instance);
@@ -138,23 +129,29 @@ const emit = defineEmits([
   'selectVillage',
   'regionChanged',
   'lightVillage',
+  'changeCity',
 ]);
 
 const props = defineProps<{
   lonlat?: { longitude: number, latitude: number } | undefined;
   city?: string;
+  cityCode?: number;
+  district?: string;
   isLightMode?: boolean;
   small?: boolean;
   full?: boolean;
 }>();
 
 const regionLoader = useSimpleDataLoader(async () => {
-  if (!props.city)
+  if (!props.cityCode)
     return [];
-  return (await CommonContent.searchRegion(props.city)).map(p => ({
-    id: p.id,
-    name: p.title,
-  }));
+  return ((await RegionApi.getChildList(props.cityCode))
+    .filter(p => p.areaCode !== props.cityCode && p.name !== '市辖区')
+    .map(p => ({
+      ...p,
+      name: p.name,
+      id: p.areaCode,
+    })));
 }, false);
 const mapLoader = useSimpleDataLoader<MapMarker[]>(async () => {
   mapCtx.removeMarkers({
@@ -165,11 +162,12 @@ const mapLoader = useSimpleDataLoader<MapMarker[]>(async () => {
     return [];
   await waitTimeOut(100);
   const res = (await LightVillageApi.getVillageList({
-    region: selectedRegion.value,
+    areaCode: selectedRegion.value,
     keyword: searchKeyword.value.trim() || undefined,
     page: 1,
     pageSize: 1000
   })).list;
+  //如果为空则尝试获取子级
   hasResItems.value = res.length > 0;
   const list = res.map((p, i) => {
     villageData.set(p.id, p);
@@ -246,12 +244,11 @@ const mapLoader = useSimpleDataLoader<MapMarker[]>(async () => {
       }, 200);
     } else {
       try {
-        const currentRegionName = regionLoader.content.value?.find(p => p.id == selectedRegion.value)?.name;
-        if (currentRegionName) {
-          const res = (await MapApi.simpleGetRegion(currentRegionName)).requireData();
+        const currentRegion = regionLoader.content.value?.find(p => p.id == selectedRegion.value);
+        if (currentRegion) {
           mapCtx.moveToLocation({
-            latitude: Number(res.center.split(',')[1]),
-            longitude: Number(res.center.split(',')[0]),
+            latitude: Number(currentRegion.latitude),
+            longitude: Number(currentRegion.longitude),
           });
         }
       } catch (error) {
@@ -285,10 +282,6 @@ async function asyncAddMarkers(list: MapMarker[]) {
   }
 }
 
-const isEmptyRegion = computed(() => {
-  return !hasResItems.value && ready.value && !searchKeyword.value;
-});
-
 function onMarkerTap(e: any) {
   if (props.isLightMode) {
     emit('update:isLightMode', false);
@@ -309,15 +302,25 @@ function onSelectedRegion(regionId: number) {
   mapLoader.reload();
   emit('regionChanged', regionId);
 }
-function setCurrentRegion(regionName: string) {
-  const region = regionLoader.content.value?.find(p => p.name == regionName)?.id || 
-  regionLoader.content.value?.[0]?.id || undefined;
-  if (region === selectedRegion.value)
+function setCurrentRegion(regionAreaCode: number) {
+  if (regionAreaCode === selectedRegion.value)
     return;
-  selectedRegion.value = region;
+  selectedRegion.value = regionAreaCode;
   emit('regionChanged', selectedRegion.value);
   mapLoader.reload();
 }
+function loadFirstRegion() {
+  //加载第一个地区(排除市辖区)
+  let code : number | undefined = undefined;
+  if (props.district)
+    code = regionLoader.content.value?.find((p) => p.name === props.district)?.areaCode;
+  else
+    code = regionLoader.content.value?.find((p) => p.id !== props.cityCode)?.areaCode;
+
+  if (code)
+    setCurrentRegion(code);
+  console.log('loadFirstRegion', code, props.district);
+}
 
 function showSearch() {
   if (searchKeyword.value) {
@@ -343,9 +346,9 @@ async function searchConfirm() {
   }
 }
 
-watch(() => props.city, async (newVal) => {
+watch(() => props.cityCode, async () => {
   await regionLoader.reload();
-  setCurrentRegion(newVal || '');
+  loadFirstRegion();
 }, { immediate: true });
 
 onMounted(async () => {

+ 12 - 14
src/pages/home/composeabe/GetCurrentLocation.ts

@@ -1,12 +1,13 @@
 import LightVillageApi from "@/api/light/LightVillageApi";
 import MapApi from "@/api/map/MapApi";
+import RegionApi from "@/api/map/RegionApi";
 import TenMapApi from "@/api/map/TenMapApi";
 import { useVillageStore } from "@/store/village";
 import { ref } from "vue";
 
 export function useGetCurrentLocation(
   options: {
-    onCityChanged?: (city: string) => void;
+    onCityChanged?: (city: string, code: number, district?: string) => void;
   }
 ) {
 
@@ -30,8 +31,8 @@ export function useGetCurrentLocation(
           };
           villageStore.setCurrentLonlat(currentLonlat.value);
           const address = (await TenMapApi.geocoder(`${res.latitude},${res.longitude}`));
-          villageStore.setCurrentRegion(address?.address_component?.city || '');
-          onCityChanged?.(address?.address_component?.city || '');
+          const regionRes = (await RegionApi.searchByName(address?.address_component?.city || ''));
+          onCityChanged?.(address?.address_component?.city || '', regionRes.list[0].areaCode, address?.address_component?.district);
           resolve(currentLonlat.value);
         },
         fail: (err) => {
@@ -49,20 +50,17 @@ export function useGetCurrentLocation(
     if (isNaN(currentLonlat.value.longitude) || isNaN(currentLonlat.value.latitude))
       throw new Error('获取模糊定位失败');
     villageStore.setCurrentLonlat(currentLonlat.value);
-    villageStore.setCurrentRegion(res.address_detail.district);
-    onCityChanged?.(res.address_detail.city);
+    const regionRes = (await RegionApi.searchByName(res.address_detail.city || ''));
+    onCityChanged?.(res.address_detail.city, regionRes.list[0].areaCode, res.address_detail.district);
   }
-
-  async function setCurrentLocationWithCity(city: string) {
-    const res = (await MapApi.simpleGetRegion(city)).requireData();
+  async function setCurrentLocationWithCity(city: string, district: string) {
+    villageStore.setCurrentLonlat(currentLonlat.value);  
+    const regionRes = (await RegionApi.searchByName(city || ''));
     currentLonlat.value = {
-      longitude: Number(res.center.split(',')[0]),
-      latitude: Number(res.center.split(',')[1]),
+      longitude: Number(regionRes.list[0].longitude),
+      latitude: Number(regionRes.list[0].latitude),
     };
-    console.log('currentLonlat.value', currentLonlat.value);
-    villageStore.setCurrentLonlat(currentLonlat.value);  
-    villageStore.setCurrentRegion(res.name);
-    onCityChanged?.(city);
+    onCityChanged?.(city, regionRes.list[0].areaCode, district);
   }
 
   return {

+ 44 - 37
src/pages/home/index.vue

@@ -59,10 +59,12 @@
       <LightMap
         small
         :city="currentCity"
+        :cityCode="currentCityCode"
+        :district="currentDistrict"
         :lonlat="currentLocation.currentLonlat.value"
         @getCurrentLonlat="currentLocation.getCurrentExactLocation"
         @selectVillage="handleGoVillageDetails"
-        @regionChanged="currentRegion=$event"
+        @changeCity="handleSelectCity"
       >
         <NoticeBar 
           v-if="currentNoticeContent"
@@ -134,14 +136,10 @@
         </FlexCol>
       </Construction> -->
 
-      <HomeTitle title="乡村排名" showMore @moreClicked="navTo('/pages/home/village/rank/village', {
-        regionId: currentRegion ?? undefined,
-      })" />
+      <HomeTitle title="乡村排名" showMore @moreClicked="navTo('/pages/home/village/rank/village', {})" />
       <VillageRankList :list="villageRankListLoader.content.value ?? []" :jumpToSingle="false" @goDetails="handleGoVillageDetails" />
 
-      <HomeTitle title="志愿者排名" showMore :lightCount="3" @moreClicked="navTo('/pages/home/village/rank/volunteer', {
-        regionId: currentRegion ?? undefined,
-      })" />
+      <HomeTitle title="志愿者排名" showMore :lightCount="3" @moreClicked="navTo('/pages/home/village/rank/volunteer', {})" />
       <VillageUserRankList 
         :list="villageUserRankListLoader.content.value ?? []" 
         scoreSuffix="积分"
@@ -192,7 +190,7 @@
       v-model:show="showCityPopup" 
       closeable
       position="top"
-      size="90vh"
+      size="60vh"
     >
       <CitySelect @selectCity="handleSelectCity" />
     </Popup>
@@ -246,7 +244,7 @@ import LightMap from './components/LightMap.vue';
 import NoticeBar from '@/components/display/NoticeBar.vue';
 import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
 import LightVillageApi, { VillageListItem } from '@/api/light/LightVillageApi';
-import type { CityItem } from '@/api/map/MapApi';
+import type { RegionItem } from '@/api/map/RegionApi';
 import Width from '@/components/layout/space/Width.vue';
 import MasonryGrid from '@/components/layout/masonry/MasonryGrid.vue';
 import MasonryGridItem from '@/components/layout/masonry/MasonryGridItem.vue';
@@ -276,17 +274,22 @@ const introClamTipTimeout = new MemoryTimeOut('IntroClamTip', 1000 * 3600 * 30);
 const postIndexRef = ref<InstanceType<typeof PostIndex>>();
 const appConfiguration = injectAppConfiguration();
 
-const currentRegion = ref<number | null>(null);
 const { value: currentCity } = useStorageVar('currentCityName', '');
+const { value: currentCityCode } = useStorageVar('currentCityCode', 0);
+const { value: currentDistrict } = useStorageVar('currentDistrict', '');
 const currentLocation = useGetCurrentLocation({
-  onCityChanged: (city) => {
+  onCityChanged: (city, code, district) => {
     currentCity.value = city;
+    currentCityCode.value = code;
+    if (!district)
+      console.error('!')
+    currentDistrict.value = district || '';
   },
 });
 const currentNoticeContent = ref('目前厦门市已被点亮一个社区,刚刚小亮贡献了10个光源,让湖里区点亮了一个社区');
 
 const villageRankListLoader = useSimpleDataLoader(async () => {
-  const res = await LightVillageApi.getVillageRankList({ region_id: currentRegion.value ?? undefined, num: 3 });
+  const res = await LightVillageApi.getVillageRankList({ num: 3 });
   return res.map((item, i) => ({
     image: item.image ?? '',
     title: item.name,
@@ -295,7 +298,7 @@ const villageRankListLoader = useSimpleDataLoader(async () => {
   }));
 });
 const villageUserRankListLoader = useSimpleDataLoader(async () => {
-  const res = (await LightVillageApi.getVolunteerRankList({ region_id: currentRegion.value ?? undefined, num: 3 }))
+  const res = (await LightVillageApi.getVolunteerRankList({ num: 3 }))
     .map((item, i) => ({
       id: item.id,
       image: item.image ?? '',
@@ -341,11 +344,6 @@ const recommendLoader = useSimplePageListLoader(20, async (page, pageSize) => {
   return await VillageInfoApi.getListForDiscover(page, pageSize);
 }, true);
 
-watch(currentRegion, async (newVal) => {
-  await villageRankListLoader.reload();
-  await villageUserRankListLoader.reload();
-});
-
 function handleGoRecommendDetails(item: CommonInfoModel) {
   navTo(`/pages/home/discover/details`, { id: item.id });
 }
@@ -356,24 +354,14 @@ async function handleGoVillageDetails(item: VillageListItem) {
   await waitTimeOut(100);
   emit('goVillage')
 }
-async function handleChangedCity(city: string) {
-  currentCity.value = city;
-  try {
-    const res = (await MapApi.simpleGetRegion(city)).requireData();
-    currentLocation.currentLonlat.value = {
-      longitude: Number(res.center.split(',')[0]),
-      latitude: Number(res.center.split(',')[1]),
-    };
-    console.log('currentLocation.currentLonlat.value', currentLocation.currentLonlat.value);
-  } catch (error) {
-    console.error(error);
-    return;
-  }
-}
-function handleSelectCity(city: CityItem) {
+function handleSelectCity(city: RegionItem) {
   currentCity.value = city.name;
+  currentCityCode.value = city.areaCode;
   showCityPopup.value = false;
-  handleChangedCity(city.name);
+  currentLocation.currentLonlat.value = {
+    longitude: Number(city.longitude),
+    latitude: Number(city.latitude),
+  };
 }
 function handleGoAI() {
   requireLogin(async () => {
@@ -386,7 +374,11 @@ async function handleGoPublish() {
   }, '欢迎您发布内容,登录后以便使用更多功能哦!');
 }
 async function handleLightVillage() {
-  requireLogin(async () => navTo('/pages/home/light/submit-map', { city: currentCity.value }), '登录后才能点亮村社哦!');
+  requireLogin(async () => navTo('/pages/home/light/submit-map', { 
+    city: currentCity.value,
+    cityCode: currentCityCode.value,
+    district: currentDistrict.value,
+  }), '登录后才能点亮村社哦!');
 }
 function handleSearch() {
   if (!searchKeywords.value) {
@@ -394,7 +386,8 @@ function handleSearch() {
     return;
   }
   navTo('/pages/home/search/index', { 
-    region: currentRegion.value ?? 0,
+    city: currentCity.value,
+    cityCode: currentCityCode.value,
     searchValue: searchKeywords.value,
   });
 }
@@ -427,6 +420,7 @@ async function loadInfo() {
   }
 }
 
+
 watch(() => authStore.isLogged, async (newVal) => {
   if (newVal) {
     await loadInfo();
@@ -436,7 +430,7 @@ watch(() => authStore.isLogged, async (newVal) => {
 onMounted(async () => {
   try {
     if (currentCity.value) {
-      await currentLocation.setCurrentLocationWithCity(currentCity.value);
+      await currentLocation.setCurrentLocationWithCity(currentCity.value, currentDistrict.value);
     } else {
       await currentLocation.getCurrentFuzzyLocation();
     }
@@ -446,4 +440,17 @@ onMounted(async () => {
   }
   await loadInfo();
 });
+defineExpose({
+  onPageBack: (name: string, data: Record<string, unknown>) => {
+    if (name === 'changeCurrentCity') {
+      currentCity.value = data.city as string;
+      currentCityCode.value = data.code as number;
+      currentDistrict.value = '';
+      currentLocation.currentLonlat.value = {
+        longitude: Number(data.longitude),
+        latitude: Number(data.latitude),
+      };
+    }
+  }
+})
 </script>

+ 55 - 9
src/pages/home/light/submit-map.vue

@@ -2,6 +2,7 @@
 import { ref } from 'vue';
 import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
 import { useGetCurrentLocation } from '../composeabe/GetCurrentLocation';
+import { backAndCallOnPageBack, callPrevOnPageBack } from '@/components/utils/PageAction';
 import AppCofig from '@/common/config/AppCofig';
 import LightMap from '../components/LightMap.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
@@ -12,18 +13,26 @@ import Text from '@/components/basic/Text.vue';
 import Image from '@/components/basic/Image.vue';
 import BoxMid from '@/common/components/box/BoxMid.vue';
 import JoinDialog from '../village/dialogs/JoinDialog.vue';
-import { backAndCallOnPageBack } from '@/components/utils/PageAction';
+import Touchable from '@/components/feedback/Touchable.vue';
+import Icon from '@/components/basic/Icon.vue';
+import CitySelect from '../components/CitySelect.vue';
+import Popup from '@/components/dialog/Popup.vue';
+import type { RegionItem } from '@/api/map/RegionApi';
 
 const { querys } = useLoadQuerys({
   latitude: AppCofig.defaultLonLat[1],
   longitude: AppCofig.defaultLonLat[0],
-  city: ''
+  city: '',
+  cityCode: 0,
+  district: '',
 }, async (querys) => {
-  currentLocation.setCurrentLocationWithCity(querys.city);
+  currentLocation.setCurrentLocationWithCity(querys.city, querys.district);
+  currentCity.value = querys.city;
+  currentCityCode.value = querys.cityCode;
 });
 
 const currentLocation = useGetCurrentLocation({
-  onCityChanged: (city) => {},
+  onCityChanged: () => {},
 });
 
 const joinDialog = ref<InstanceType<typeof JoinDialog>>();
@@ -38,6 +47,24 @@ function handleJoinFinish() {
   joinCurrentVillageId.value = 0;
 }
 
+const showCityPopup = ref(false);
+const currentCity = ref('');
+const currentCityCode = ref(0);
+
+function handleSelectCity(city: RegionItem) {
+  currentLocation.setCurrentLocationWithCity(city.name, '');
+  currentCity.value = city.name;
+  currentCityCode.value = city.areaCode;
+  showCityPopup.value = false;
+  setTimeout(() => {
+    callPrevOnPageBack('changeCurrentCity', { 
+      city, 
+      code: city.areaCode,
+      longitude: Number(city.longitude),
+      latitude: Number(city.latitude),
+    })
+  }, 2000);
+}
 </script>
 
 <template>
@@ -49,10 +76,18 @@ function handleJoinFinish() {
         :innerStyle="{
           width: '500rpx',
         }"
-        center
+        direction="column"
+        align="center"
+        gap="gap.md"
       > 
-        <Image src="https://xy.wenlvti.net/app_static/images/home/BadgeNew.png" :width="220" mode="widthFix" />
-        <Text text="请选择您要点亮的村社" fontConfig="primaryTitle" />
+        <FlexCol center>
+          <Image src="https://xy.wenlvti.net/app_static/images/home/BadgeNew.png" :width="220" mode="widthFix" />
+          <Text text="请选择您要点亮的村社" fontConfig="primaryTitle" />
+        </FlexCol>
+        <Touchable direction="row" align="center" gap="gap.md">
+          <Icon icon="home-filling" size="40" />
+          <Text text="没有我的家乡?点击这里一键建村!" fontConfig="contentText" />
+        </Touchable>
       </BoxMid>
     </FlexRow>
   </FlexCol>
@@ -63,10 +98,21 @@ function handleJoinFinish() {
   />
   <LightMap 
     full
-    :city="querys.city"
+    :city="currentCity"
+    :cityCode="currentCityCode"
+    :district="querys.district"
     :lonlat="currentLocation.currentLonlat.value"
     :isLightMode="true" 
     @lightVillage="handleLightVillage"
     @getCurrentLonlat="currentLocation.getCurrentExactLocation"
-  />
+    @changeCity="showCityPopup=true"
+  />   
+  <Popup 
+    v-model:show="showCityPopup" 
+    closeable
+    position="bottom"
+    size="60vh"
+  >
+    <CitySelect @selectCity="handleSelectCity" />
+  </Popup>
 </template>

+ 2 - 2
src/pages/home/search/index.vue

@@ -46,7 +46,7 @@ import { backAndCallOnPageBack } from '@/components/utils/PageAction';
 
 const { querys } = useLoadQuerys({
   searchValue: '',
-  region: 0,
+  cityCode: 0,
 }, () => {
   listLoader.load();
 });
@@ -54,7 +54,7 @@ const { querys } = useLoadQuerys({
 const listLoader = useSimplePageListLoader(16, async (page, pageSize) => {
   return await LightVillageApi.getVillageList({
     keyword: querys.value.searchValue,
-    region: querys.value.region,
+    //areaCode: querys.value.cityCode,
     page,
     pageSize,
   });

+ 0 - 2
src/pages/home/village/gov/index.vue

@@ -14,8 +14,6 @@ import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
 
 const villageStore = useVillageStore();
 const recommendLoader = useSimpleDataLoader(async () => {
-  if (!villageStore.currentRegion)
-    return [];
 
 
   return [];

+ 0 - 6
src/store/village.ts

@@ -11,7 +11,6 @@ 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[]>([]);
   const shareFromVillageUserId = ref(0);
@@ -38,9 +37,6 @@ export const useVillageStore = defineStore('village', () => {
   function setCurrentLonlat(lonlat: { longitude: number, latitude: number }) {
     currentLonlat.value = lonlat;
   }
-  function setCurrentRegion(region: string) {
-    currentRegion.value = region;
-  }
   async function loadMyFollowVillages() {
     const villages = await FollowVillageApi.getFollowVillageList();
     myFollowVillages.value = villages.list;
@@ -56,7 +52,6 @@ export const useVillageStore = defineStore('village', () => {
   return { 
     currentVillage,
     currentLonlat,
-    currentRegion,
     myFollowVillages,
     myJoinedVillages,
     shareFromVillageUserId,
@@ -64,7 +59,6 @@ export const useVillageStore = defineStore('village', () => {
     reloadVillageInfo,
     setCurrentVillage,
     setCurrentLonlat,
-    setCurrentRegion,
     loadMyFollowVillages,
     loadMyJoinedVillages,
     setShareFromVillageUserId,