Преглед на файлове

📦 对接一键建村接口

快乐的梦鱼 преди 2 седмици
родител
ревизия
5144a4d38c

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

@@ -457,6 +457,23 @@ export class LightVillageApi extends AppServerRequestModule<DataModel> {
       images: images,
     });
   }
+
+  async createVillage(data: {
+    name: string,
+    longitude: string,
+    latitude: string,
+    area_code: number,
+    /**
+     * 村落类型:
+      95=自然村
+      96=行政村,
+      334=社区
+     */
+    village_type: number,
+  }) {
+    return await this.post<KeyValue>('/village/village/addVillage', '创建村社', data);
+  }
+  
 }
 
 

+ 1 - 0
src/common/components/CommonDialog.vue

@@ -9,6 +9,7 @@
     contentScrollMaxHeight="90vh"
     :contentPadding="0"
     width="auto"
+    title=""
     @update:show="emit('update:show', $event)"
   >
     <template #content>

+ 148 - 19
src/pages/home/light/create-village.vue

@@ -20,34 +20,86 @@
           <Text>请输入家乡名称,然后选择地图位置定位哦</Text>
         </FlexRow>
         <Field v-model="currentVillageName" placeholder="请输入家乡名称,例如:大庆村" />
-        <map
-          id="prevMap"
-          map-id="prevMap"
-          :longitude="currentLonLat.longitude"
-          :latitude="currentLonLat.latitude"
-          :zoom="15"
-          :enable-satellite="enableSatellite"
-          :style="{
-            width: '100%',
-            height: '500px',
-          }"
-          @regionchange="handleRegionchange"
-        >
-        </map>
+        <Field label="类型" placeholder="请选择村社类型">
+          <FlexRow>
+            <RadioGroup v-model="currentVillageType">
+              <Radio :name="95" text="自然村" />
+              <Radio :name="96" text="行政村" />
+              <Radio :name="334" text="社区" />
+            </RadioGroup>
+          </FlexRow>
+        </Field>
+        <FlexCol position="relative">
+          <Image 
+            src="https://xy.wenlvti.net/app_static/images/village/icon_marker2.png" 
+            :width="100"
+            :height="100"
+            :innerStyle="{
+              position: 'absolute',
+              top: '50%',
+              left: '50%',
+              transform: 'translate(-50%, -100%)',
+              pointerEvents: 'none',
+              zIndex: 1000,
+            }"
+          />
+          <map
+            id="prevMap"
+            map-id="prevMap"
+            :longitude="currentLonLat.longitude"
+            :latitude="currentLonLat.latitude"
+            :zoom="15"
+            :enable-satellite="enableSatellite"
+            :style="{
+              width: '100%',
+              height: '500px',
+            }"
+            @regionchange="handleRegionchange"
+          >
+          </map>
+        </FlexCol>
         <FlexRow justify="flex-end">
           <Button :text="'切换' + (enableSatellite ? '地图' : '卫星')" @click="enableSatellite = !enableSatellite" />
         </FlexRow>
         <FlexCol gap="gap.md">
           <Button text="返回上一步" @click="step = 'selectArea'" />
-          <Button text="立即建村" type="primary" @click="handleConfirm" />
+          <Button text="立即建村" type="primary" @click="handleConfirm()" />
         </FlexCol>
       </template>
     </FlexCol>
+
+
+    <CommonDialog v-model:show="showSearchedList" title="您要找的是不是以下村社?" :showDivider="false">
+      <FlexCol gap="gap.md" padding="padding.md" width="600rpx">
+        <BoxMid>
+          <Touchable direction="row" gap="gap.md"center @click="handleConfirm(true)">
+            <Image src="https://xy.wenlvti.net/app_static/images/home/volunteer/IconFollows.png" :radius="20" width="70" height="70" mode="aspectFill" />
+            <Text text="以下没有我的家乡" fontConfig="lightGoldTitle" />
+          </Touchable>
+        </BoxMid>
+        <BoxMid
+          v-for="(item, k) in searchedList"
+          :key="k"
+          justify="flex-start"
+        >
+          <Touchable direction="row" gap="gap.md"center @click="handleChooseSearchedVillage(item as VillageListItem)">
+            <Image :src="item.image" :radius="20" width="70" height="70" mode="aspectFill" />
+            <FlexCol gap="gap.md">
+              <Text :text="item.name" fontConfig="lightGoldTitle" />
+              <Text :text="item.address" fontConfig="secondText" />
+            </FlexCol>
+          </Touchable>
+        </BoxMid>
+      </FlexCol>
+    </CommonDialog>
+
   </CommonTopBanner>
 </template>
 
 <script setup lang="ts">
 import { ref, onMounted } from 'vue';
+import { showError } from '@/common/composeabe/ErrorDisplay';
+import { toast, alert } from '@/components/dialog/CommonRoot';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
 import Cascader, { type CascaderItem } from '@/components/form/Cascader.vue';
@@ -56,8 +108,15 @@ import RegionApi, { type RegionItem } from '@/api/map/RegionApi';
 import Text from '@/components/basic/Text.vue';
 import Button from '@/components/basic/Button.vue';
 import Field from '@/components/form/Field.vue';
-import { showError } from '@/common/composeabe/ErrorDisplay';
-import { toast } from '@/components/dialog/CommonRoot';
+import LightVillageApi, { type VillageListItem } from '@/api/light/LightVillageApi';
+import { backAndCallOnPageBack } from '@/components/utils/PageAction';
+import RadioGroup from '@/components/form/RadioGroup.vue';
+import Radio from '@/components/form/Radio.vue';
+import CommonDialog from '@/common/components/CommonDialog.vue';
+import BoxMid from '@/common/components/box/BoxMid.vue';
+import Touchable from '@/components/feedback/Touchable.vue';
+import Image from '@/components/basic/Image.vue';
+import { getCascaderItemByValue } from '@/components/form/CascaderUtils';
 
 const mapCtx = uni.createMapContext('prevMap');
 
@@ -65,11 +124,28 @@ const step = ref<'selectArea'|'inputInfo'>('selectArea');
 const selectedRegion = ref<RegionItem>();
 const currentLonLat = ref({ longitude: 0, latitude: 0 });
 const currentVillageName = ref('')
+const currentVillageType = ref<number>(95);
+
+const showSearchedList = ref(false);
+const searchedList = ref<VillageListItem[]>([]);
 
 const enableSatellite = ref(false);
 const selectedValue = ref<(string | number)[]>([]);
 const provinceList = ref<CascaderItem[]>([]);
 
+function getReSelectAddress() {
+  const currentItems = getCascaderItemByValue(selectedValue.value, 'value', 'children', provinceList.value);
+  const city = currentItems[1].data as RegionItem;
+  const district = currentItems[2].data as RegionItem;
+  return {
+    city: city?.name,
+    cityCode: city?.areaCode,
+    cityLongitude: city?.longitude,
+    cityLatitude: city?.latitude,
+    district: district.name,
+  };
+}
+
 async function asyncLoadData(item: CascaderItem) {
   const list = await RegionApi.getChildList(Number(item.value));
   return list.map(region => ({
@@ -101,12 +177,65 @@ function handleRegionchange(e: any) {
     };
   }
 }
-function handleConfirm() {
+async function handleConfirm(skipSearch?: boolean) {
   if (!currentVillageName.value) {
     toast('请输入家乡名称');
     return;
   }
-  showError('暂无接口');
+  uni.showLoading();
+  try {
+    //先尝试搜索已有村社 
+    if (!skipSearch) {
+      const searchRes = (await LightVillageApi.getVillageList({
+        areaCode: selectedValue.value[2] as number,
+        keyword: currentVillageName.value.trim() || undefined,
+        page: 1,
+        pageSize: 10
+      })).list;
+      if (searchRes.length > 0) {
+        uni.hideLoading();
+        showSearchedList.value = true;
+        searchedList.value = searchRes;
+        return;
+      }
+    }
+
+    //否则添加村社
+    const res = (await LightVillageApi.createVillage({ 
+      name: currentVillageName.value,
+      longitude: String(currentLonLat.value.longitude),
+      latitude: String(currentLonLat.value.latitude),
+      area_code: selectedValue.value[selectedValue.value.length - 1] as number,
+      village_type: currentVillageType.value,
+    })).data;
+
+    uni.hideLoading();
+
+    await alert({
+      title: '提示',
+      content: '村社创建成功!快去成为志愿者吧',
+    });
+
+    if (res)
+      backAndCallOnPageBack('joinVillage', {
+        id: res.id,
+        createdNew: true,
+        reselectAddress: getReSelectAddress(),
+      });
+
+  } catch (e) {
+    uni.hideLoading();
+    showError(e);
+  }
+}
+
+function handleChooseSearchedVillage(item: VillageListItem) {
+  const res = getReSelectAddress();
+  console.log(res);
+  backAndCallOnPageBack('joinVillage', {
+    id: item.id,
+    reselectAddress: res,
+  });
 }
 
 onMounted(async () => {

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

@@ -30,6 +30,7 @@ const { querys } = useLoadQuerys({
   currentLocation.setCurrentLocationWithCity(querys.city, querys.district);
   currentCity.value = querys.city;
   currentCityCode.value = querys.cityCode;
+  currentDistrict.value = querys.district;
 });
 
 const currentLocation = useGetCurrentLocation({
@@ -51,6 +52,7 @@ function handleJoinFinish() {
 const showCityPopup = ref(false);
 const currentCity = ref('');
 const currentCityCode = ref(0);
+const currentDistrict = ref('');
 
 const lightMap = ref<InstanceType<typeof LightMap>>();
 
@@ -68,6 +70,32 @@ function handleSelectCity(city: RegionItem) {
     })
   }, 2000);
 }
+
+defineExpose({
+  onPageBack(name: string, params: any) {
+    if (name === 'joinVillage') {
+      if (params.createdNew) {
+      }
+      if (params.reselectAddress) {
+        currentLocation.setCurrentLocationWithCity(params.reselectAddress.city, params.reselectAddress.district);
+        currentCity.value = params.reselectAddress.city;
+        currentCityCode.value = params.reselectAddress.cityCode;
+        currentDistrict.value = params.reselectAddress.district;
+        
+        setTimeout(() => {
+          callPrevOnPageBack('changeCurrentCity', { 
+            city: params.reselectAddress.city, 
+            code: params.reselectAddress.cityCode,
+            longitude: Number(params.reselectAddress.cityLongitude),
+            latitude: Number(params.reselectAddress.cityLatitude),
+          })
+        }, 2000);
+      }
+      joinCurrentVillageId.value = params.id;
+      joinDialog.value?.show();
+    }
+  }
+})
 </script>
 
 <template>
@@ -107,7 +135,7 @@ function handleSelectCity(city: RegionItem) {
     full
     :city="currentCity"
     :cityCode="currentCityCode"
-    :district="querys.district"
+    :district="currentDistrict"
     :lonlat="currentLocation.currentLonlat.value"
     :isLightMode="true" 
     @lightVillage="handleLightVillage"

+ 1 - 1
src/pages/home/village/dialogs/JoinDialog.vue

@@ -188,7 +188,7 @@ const addFormDefine : IDynamicFormOptions = {
       additionalProps: {
         placeholder: '请说明认领该村社的原因(选填),可以加快管理员审核速度',
         showWordLimit: true,
-        maxLength: 200,
+        maxLength: 5000,
       } as FieldProps,
     }, */
   ]

+ 3 - 3
src/pages/home/village/introd/card.vue

@@ -117,14 +117,14 @@
       <FlexRow backgroundColor="background.tertiary" radius="radius.md" :padding="[30, 20]">
         <FlexCol center gap="gap.sm" flexBasis="25%">
           <Text text="乡源光" fontConfig="secondText" />
-          <Text :text="villageInfoLoader.content.value?.light" fontConfig="importantTitle" />
+          <Text :text="villageInfoLoader.content.value?.light || 0" fontConfig="importantTitle" />
           <Button type="text" size="mini" text="做任务" @click="navTo('/pages/home/village/task/index')" />
         </FlexCol>
         <Divider type="vertical" />
         <FlexCol center gap="gap.sm" flexBasis="25%">
           <Touchable direction="column" center @click="navTo('/pages/home/village/volunteer/list', { villageId: villageStore.currentVillage?.id ?? undefined })">
             <Text text="乡源人数" fontConfig="contentText" />
-            <Text :text="villageInfoLoader.content.value?.memberCount" fontConfig="importantTitle" />
+            <Text :text="villageInfoLoader.content.value?.memberCount|| 0" fontConfig="importantTitle" />
           </Touchable>
           <WxButton openType="share">
             <Button type="text" size="mini" text="邀请加入" @click="navTo('/pages/home/village/task/index')" />
@@ -135,7 +135,7 @@
           villageId: villageStore.currentVillage?.id ?? undefined,
         })">
           <Text text="关注人数" fontConfig="contentText" />
-          <Text :text="villageInfoLoader.content.value?.followerCount" fontConfig="importantTitle" />
+          <Text :text="villageInfoLoader.content.value?.followerCount|| 0" fontConfig="importantTitle" />
           <Height :size="36" />
         </Touchable>
         <Divider type="vertical" />