Kaynağa Gözat

🎨 优化首页提交页点亮页细节

快乐的梦鱼 1 ay önce
ebeveyn
işleme
af3d59f12f

+ 6 - 0
src/common/components/FrameButton.vue

@@ -44,4 +44,10 @@ const padding = computed(() => {
 });
 
 const emit = defineEmits(['click']);
+
+defineOptions({
+  options: {
+    virtualHost: true,
+  },
+})
 </script>

+ 6 - 0
src/common/components/PrimaryButton.vue

@@ -31,4 +31,10 @@ const props = withDefaults(defineProps<{
 }); 
 
 const emit = defineEmits(['click']);
+
+defineOptions({
+  options: {
+    virtualHost: true,
+  },
+})
 </script>

+ 15 - 6
src/common/components/RequireLogin.vue

@@ -1,20 +1,29 @@
 <template>
   <slot v-if="isLogged" />
-  <FlexCol v-else center :height="300" :gap="30">
-    <Icon name="smile-filling" :size="80" />
-    <Text :text="unLoginMessage" textAlign="center" fontConfig="subText" />
-    <Button :innerStyle="{width: '50%'}" type="primary" @click="goLogin">去登录</Button>
-  </FlexCol>
+  <BackgroundBox 
+    v-else center :height="300" :gap="30"
+    backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxMid.png"
+    :backgroundCutBorder="32"
+    :backgroundCutBorderSize="36"
+    :padding="[45,35]"
+  >
+    <FlexCol center :gap="20">
+      <Icon name="smile-filling" :size="80" />
+      <Text :text="unLoginMessage" textAlign="center" fontConfig="contentSpeicalText" />
+      <PrimaryButton :innerStyle="{width: '50%'}" @click="goLogin" text="去登录" />
+    </FlexCol>
+  </BackgroundBox>
 </template>
 
 <script setup lang="ts">
-import Button from '@/components/basic/Button.vue';
 import Icon from '@/components/basic/Icon.vue';
 import Text from '@/components/basic/Text.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import { navTo } from '@/components/utils/PageAction';
 import { useAuthStore } from '@/store/auth';
 import { computed, watch } from 'vue';
+import PrimaryButton from './PrimaryButton.vue';
+import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
 
 const authStore = useAuthStore();
 const isLogged = computed(() => authStore.isLogged);

+ 3 - 1
src/common/components/parts/IndexCommonImageItem.vue

@@ -6,13 +6,14 @@
     :width="340"
     :imageWidth="340"
     :imageRadius="15"
+    :defaultImage="defaultImage"
     :titleProps="{ fontSize: 24, color: 'gray', lines: 2 }"
     :descProps="{ fontSize: 24, color: 'gray', lines: 2 }"
     backgroundColor="transparent"
     @click="$emit('click')"
   >
     <template #footer> 
-      <FlexRow justify="space-between" align="center" :padding="[10,0]" :margin="[10,0,0,0]">
+      <FlexRow v-if="userName || likes" justify="space-between" align="center" :padding="[10,0]" :margin="[10,0,0,0]">
         <FlexRow align="center" :gap="10">
           <Avatar :url="image" :size="40" />
           <Text :text="userName" :fontSize="24" color="gray" />
@@ -40,6 +41,7 @@ const props = defineProps<{
   userName?: string;
   likes?: number;
   isLike?: boolean;
+  defaultImage?: string;
 }>()
 
 const emit = defineEmits([ 'click' ]);

+ 7 - 0
src/components/basic/BackgroundImageButton.vue

@@ -18,6 +18,7 @@ import type { TouchableFlexProps } from '../feedback/Touchable';
 
 const props = withDefaults(defineProps<BackgroundBoxProps & TouchableFlexProps>(), {
   activeOpacity: 0.7,
+  touchable: true,
 });
 
 const { makeStyle } = useBackgroundBoxStyleMaker(props);
@@ -30,4 +31,10 @@ const style = computed(() => {
 
 const emit = defineEmits(['click']);
 
+
+defineOptions({
+  options: {
+    virtualHost: true,
+  },
+})
 </script>

+ 11 - 4
src/components/canvas/MiniRender.ts

@@ -711,8 +711,8 @@ export namespace MiniRender {
         height: number,
         backgroundColor: string,
       },
-      public onInit: () => void,
-      public updateFn: (dtMs: number) => void,
+      public onInit: (this: Scene) => void,
+      public updateFn: (this: Scene, dtMs: number) => void,
     ) {
       this.canvas = options.canvas;
       this.width = options.width;
@@ -725,7 +725,7 @@ export namespace MiniRender {
       console.log(`[Canvas] init canvas ${this.width}x${this.height}`);
       
       await this.canvas.initCanvas(this.width, this.height);
-      this.onInit();
+      this.onInit.call(this);
     }
 
     public clear(): void {
@@ -747,7 +747,7 @@ export namespace MiniRender {
     public tick(ts: number): void {
       const dt = this.lastTs === null ? 16 : Math.max(0, ts - this.lastTs);
       this.lastTs = ts;
-      this.updateFn(dt);
+      this.updateFn.call(this, dt);
       this.root.update(dt);
       this.renderOnce();
     }
@@ -774,6 +774,13 @@ export namespace MiniRender {
       this.root.removeAll();
     }
 
+    public precentXToPixel(precent: number): number {
+      return this.width * precent;
+    }
+    public precentYToPixel(precent: number): number {
+      return this.height * precent;
+    }
+
     private getEventPosition(e: any): { x: number; y: number } {
       if (e?.detail?.x !== undefined) return { x: e.detail.x, y: e.detail.y };
       if (e?.offsetX !== undefined) return { x: e.offsetX, y: e.offsetY };

+ 5 - 0
src/components/display/block/ImageBlock2.vue

@@ -13,6 +13,7 @@
       :height="imageHeight"
       :radius="imageRadius"
       :mode="imageHeight ? 'aspectFill' : 'widthFix'"
+      :defaultImage="defaultImage"
     />
     <slot name="desc">
       <FlexCol :padding="15">
@@ -77,6 +78,10 @@ export interface ImageBlock2Props extends Partial<FlexProps> {
    */
   imageRadius?: string | number;
   /**
+   * 图片的默认路径。
+   */
+  defaultImage?: string;
+  /**
    * 图片下方显示标题。
    */
   title?: string;

+ 3 - 23
src/pages/dig/details.vue

@@ -63,29 +63,8 @@
       </FlexCol>
       <XBarSpace />
     </FlexCol>
-    <FlexCol v-else padding="padding.md">
-      <FlexCol 
-        center 
-        gap="gap.lg" 
-        border="primary" 
-        backgroundColor="background.tertiary"
-        radius="radius.md" 
-        padding="padding.md"
-      > 
-        <Image 
-          src="https://xy.wenlvti.net/app_static/images/village/PlaceholderVolunteer.png" 
-          mode="widthFix" 
-          :width="200" 
-          :height="200" 
-        />
-        <Text>您还不是当前村社的志愿者</Text>
-        <Text>欢迎注册,加入志愿者队伍,点亮村落</Text>
-        <FlexRow gap="gap.md">
-          <Button type="primary" @click="goJoin">去点亮村落</Button>
-          <Button @click="back()">返回</Button>
-        </FlexRow>
-      </FlexCol>
-    </FlexCol>
+    <NotVolTip v-else @goJoin="goJoin">
+    </NotVolTip>
   </CommonTopBanner>
 </template>
 
@@ -113,6 +92,7 @@ import Button from '@/components/basic/Button.vue';
 import { CollectableModulesIdMap } from './forms/forms';
 import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
 import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
+import NotVolTip from '../home/components/NotVolTip.vue';
 
 const { querys } = useLoadQuerys({ 
   name: '',

+ 5 - 19
src/pages/dig/forms/list-ordinary.vue

@@ -10,7 +10,7 @@
             <SearchBar
               v-model="searchText"
               placeholder="搜一搜" 
-              :innerStyle="{ width: '280rpx' }"
+              :innerStyle="{ width: '200rpx' }"
               @search="search"
             />
             <picker @change="handleCatalogChange" :value="currentCatalogIndex" :range="currentCatalogList" range-key="name">
@@ -27,25 +27,10 @@
             @click="newData"
           />
         </FlexRow>
-        <FlexCol 
+        <NotVolTip 
           v-if="!isJoined" 
-          center 
-          gap="gap.lg" 
-          border="primary" 
-          backgroundColor="background.tertiary"
-          radius="radius.md" 
-          padding="padding.md"
-        > 
-          <Image 
-            src="https://xy.wenlvti.net/app_static/images/village/PlaceholderVolunteer.png" 
-            mode="widthFix" 
-            :width="200" 
-            :height="200" 
-          />
-          <Text>您还不是当前村社的志愿者</Text>
-          <Text>欢迎注册,加入志愿者队伍,点亮村落</Text>
-          <Button type="primary" @click="goJoin">去点亮村落</Button>
-        </FlexCol>
+          @goJoin="goJoin"
+        /> 
         <Height :height="5" />
         <SimplePageListLoader :loader="listLoader" :noEmpty="true">
           <FlexCol :gap="20">
@@ -127,6 +112,7 @@ import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
 import BackgroundImageButton from '@/components/basic/BackgroundImageButton.vue';
 import Width from '@/components/layout/space/Width.vue';
 import PrimaryButton from '@/common/components/PrimaryButton.vue';
+import NotVolTip from '@/pages/home/components/NotVolTip.vue';
 
 const subTitle = ref('');
 const searchText = ref('');

+ 1 - 1
src/pages/home/components/LightMap.vue

@@ -143,7 +143,7 @@ const mapLoader = useSimpleDataLoader<MapMarker[]>(async () => {
   if (!selectedRegion.value)
     return [];
   await waitTimeOut(200);
-  const res = (await LightVillageApi.getVillageList(undefined, selectedRegion.value, undefined, 1, 1000)).map((p, i) => {
+  const res = (await LightVillageApi.getVillageList(undefined, selectedRegion.value, undefined, 1, 1000)).list.map((p, i) => {
     villageData.set(p.id, p);
     const maker : MapMarker = {
       id: p.id ?? i,

+ 34 - 0
src/pages/home/components/NotVolTip.vue

@@ -0,0 +1,34 @@
+<template>
+  <BackgroundBox 
+    backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxMid.png"
+    :backgroundCutBorder="32"
+    :backgroundCutBorderSize="36"
+    :padding="[40,20]"
+    center 
+    gap="gap.lg" 
+    border="primary" 
+    backgroundColor="background.tertiary"
+    radius="radius.md" 
+  > 
+    <Image 
+      src="https://xy.wenlvti.net/app_static/images/village/PlaceholderVolunteer.png" 
+      mode="widthFix" 
+      :width="200" 
+      :height="200" 
+    />
+    <Text fontConfig="lightGoldTitle">您还不是当前村社的志愿者</Text>
+    <Text fontConfig="contentSpeicalText">欢迎注册,加入志愿者队伍,点亮村落</Text>
+    <PrimaryButton type="primary" text="去点亮村落" @click="emit('goJoin')" />
+  </BackgroundBox>
+</template>
+
+<script setup lang="ts">
+import Image from '@/components/basic/Image.vue';
+import Text from '@/components/basic/Text.vue';
+import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
+import PrimaryButton from '@/common/components/PrimaryButton.vue';
+
+const emit = defineEmits<{
+  (e: 'goJoin'): void;
+}>();
+</script>

+ 15 - 3
src/pages/home/dig.vue

@@ -28,7 +28,9 @@
             fontConfig="contentSpeicalText" 
           />
           <FlexRow justify="space-around" gap="gap.md">
-            <FrameButton primary text="去认领村庄" @click="navTo('/pages/home/light/submit-map')" width="220rpx" />
+            <FrameButton primary text="去认领村庄" @click="navTo('/pages/home/light/submit-map', {
+              city: currentCity,
+            })" width="220rpx" />
           </FlexRow>
         </FlexCol>
         <SimplePageContentLoader
@@ -105,7 +107,14 @@
 
       <Height :height="20" />
       <HomeTitle v-if="authStore.isLogged" title="我的贡献" />
-      <FlexRow v-if="authStore.isLogged" backgroundColor="white" radius="radius.md" :padding="[40,20]">
+      <BackgroundBox 
+        v-if="authStore.isLogged" 
+        backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxMid.png"
+        :backgroundCutBorder="32"
+        :backgroundCutBorderSize="36"
+        :padding="[40,20]"
+        direction="row"
+      >
         <FlexCol :flex="1" :gap="10" center>
           <Text fontConfig="lightGoldTitle">{{ volunteerInfoLoader.content.value?.points || 0 }}</Text>
           <Touchable direction="row" align="center" :gap="10" @click="navTo('/pages/dig/about/point')">
@@ -117,7 +126,7 @@
           <Text fontConfig="lightGoldTitle">Lv.{{ volunteerInfoLoader.content.value?.level || 1 }}</Text>
           <Text>等级</Text>
         </FlexCol>
-      </FlexRow>
+      </BackgroundBox>
     </FlexCol>
 
     <CommonDialog
@@ -165,10 +174,13 @@ import CommonDivider from '@/common/components/CommonDivider.vue';
 import CommonDialog from '@/common/components/CommonDialog.vue';
 import Touchable from '@/components/feedback/Touchable.vue';
 import Icon from '@/components/basic/Icon.vue';
+import { useStorageVar } from '@/components/composeabe/StorageVar';
+import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
 
 const showOnlinePreviewDialog = ref(false);
 const authStore = useAuthStore();
 const collectStore = useCollectStore();
+const { value: currentCity } = useStorageVar('currentCityName', '');
 
 const notVolunteerError = ref(false);
 const villageListLoader = useSimpleDataLoader(async () => await VillageApi.getClaimedVallageList(), true);

+ 15 - 17
src/pages/home/light/form/volunteer.ts

@@ -29,7 +29,7 @@ export function getVolunteerForm(options: {
           { 
             label: '登录账号', name: 'username', type: 'text',
             additionalProps: { 
-              placeholder: '请输入登录账号',
+              placeholder: '您可以设置一个登录账号方便登录,英文和数字组合',
             },
             rules: [{ required: true, message: '请输入登录账号' }],
             show: { callback: () => options.isNew.value },
@@ -39,7 +39,7 @@ export function getVolunteerForm(options: {
             name: 'password',
             type: 'text',
             additionalProps: { 
-              placeholder: '请输入密码',
+              placeholder: '请设置一个登录密码',
               type: 'password',
             } as FieldProps,
             rules: [{ required: true, message: '请输入密码' }],
@@ -50,7 +50,7 @@ export function getVolunteerForm(options: {
             name: 'passwordRepeat',
             type: 'text',
             additionalProps: { 
-              placeholder: '请再输入一次密码',
+              placeholder: '请再输入一次登录密码',
               type: 'password',
             } as FieldProps,
             rules: [
@@ -75,13 +75,13 @@ export function getVolunteerForm(options: {
         },
         children: [
           {
-            label: '真实名称', name: 'name', type: 'text',
-            additionalProps: { placeholder: '请输入真实名称' },
-            rules: [{ required: true, message: '请输入真实名称' }],
+            label: '称', name: 'name', type: 'text',
+            additionalProps: { placeholder: '请输入称' },
+            rules: [{ required: true, message: '请输入称' }],
           },
           {
-            label: '手机号', name: 'mobile', type: 'text',
-            additionalProps: { placeholder: '请输入手机号' },
+            label: '联系方式', name: 'mobile', type: 'text',
+            additionalProps: { placeholder: '请输入联系方式,如手机号' },
             rules: [{ required: true, message: '请输入手机号' }],
           },
           { 
@@ -112,6 +112,7 @@ export function getVolunteerForm(options: {
           },
           {
             label: '类型', name: 'type', type: 'radio-value',
+            defaultValue: 'volunteer',
             additionalProps: {
               options: [
                 { text: '志愿者', value: 'volunteer' },
@@ -121,7 +122,8 @@ export function getVolunteerForm(options: {
             rules: [{ required: true, message: '请选择类型' }],
           },
           {
-            label: '类型', name: 'residentStatus', type: 'radio-value',
+            label: '身份', name: 'residentStatus', type: 'radio-value',
+            defaultValue: 2,
             additionalProps: {
               options: [
                 { text: '在村劳作', value: 1 },
@@ -133,19 +135,15 @@ export function getVolunteerForm(options: {
             rules: [{ required: true, message: '请选择类型' }],
           },
           { 
-            label: '工作单位', name: 'unit', type: 'text', additionalProps: { placeholder: '请输入手机号' } ,
-            rules: [{ required: true, message: '请输入手机号' }],
+            label: '工作单位', name: 'unit', type: 'text', additionalProps: { placeholder: '请输入工作单位' } ,
+            rules: [{ required: true, message: '请输入工作单位' }],
             show: { callback: () => options.formRef.value?.getValueByPath('type') === 'staff' },
           },
           { 
-            label: '职位', name: 'job', type: 'text', additionalProps: { placeholder: '请输入手机号' } ,
-            rules: [{ required: true, message: '请输入手机号' }],
+            label: '职位', name: 'job', type: 'text', additionalProps: { placeholder: '请输入职位' } ,
+            rules: [{ required: true, message: '请输入职位' }],
             show: { callback: () => options.formRef.value?.getValueByPath('type') === 'staff' },
           },
-          { 
-            label: '手机号', name: 'mobile', type: 'text', additionalProps: { placeholder: '请输入手机号' } ,
-            rules: [{ required: true, message: '请输入手机号' }],
-          },
           { label: '现居地址', name: 'address', type: 'text', additionalProps: { placeholder: '请输入现居地址' } },
           { 
             label: '个人介绍', 

+ 32 - 7
src/pages/home/light/submit.vue

@@ -5,7 +5,22 @@
     }">
       <FlexCol :gap="20" :padding="30">
         <!--注册-->
-        <FlexCol v-if="step === 'register'">
+        <FlexCol v-if="step === 'register'" :gap="20">
+          <BackgroundBox 
+            backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxMid.png"
+            :backgroundCutBorder="32"
+            :backgroundCutBorderSize="36"
+            :padding="[45,35]"
+          >
+            <FlexCol center :gap="20">
+              <Icon name="smile-filling" :size="80" />
+              <Text 
+                text="感谢您为本村做出贡献,点亮村庄!不过在这之前您需要注册基本信息成为志愿者,这可以帮助您更好的为村做出贡献,领取奖励等。请完善以下信息" 
+                textAlign="center" 
+                fontConfig="contentSpeicalText" 
+              />
+            </FlexCol>
+          </BackgroundBox>
           <BackgroundBox
             backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxMid.png"
             :backgroundCutBorder="32"
@@ -22,11 +37,18 @@
           <PrimaryButton text="提交" @click="registerSubmit" :loading="registerFormLoading" />
         </FlexCol>
         <!--认领-->
-        <FlexCol v-else-if="step === 'add'">
-          <Alert 
-            type="info"
-            message="您已经是志愿者,请完善以下信息认领当前村社"
-          />
+        <FlexCol v-else-if="step === 'add'" :gap="20">
+          <BackgroundBox 
+            backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxMid.png"
+            :backgroundCutBorder="32"
+            :backgroundCutBorderSize="36"
+            :padding="[45,35]"
+          >
+            <FlexCol center :gap="20">
+              <Icon name="smile-filling" :size="80" />
+              <Text text="您已经是志愿者,请完善以下信息认领当前村社" textAlign="center" fontConfig="contentSpeicalText" />
+            </FlexCol>
+          </BackgroundBox>
           <BackgroundBox
             backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxMid.png"
             :backgroundCutBorder="32"
@@ -160,6 +182,9 @@ onMounted(async () => {
     isNew: ref(true),
     formRef: registerFormRef,
   });
+  
+  registerFormModel.value.type = 'volunteer';
+  registerFormModel.value.residentStatus = 1;
   registerFormModel.value.regionId = querys.value.regionId;
   registerFormModel.value.unit = querys.value.unit;
 });
@@ -250,7 +275,7 @@ async function addSubmit() {
 }
 
 async function suscribePassMessage() {
-  const TEMPLATE_ID = 'iNdqAKNyltLso9nFMvzrlcgCMGMALveIfZWfI2HYAQQ';
+  const TEMPLATE_ID = '6CrLGzIl1yUxATluKrzhpG246pMrpwiPLZsF-5CKG1k';
   return new Promise<boolean>((resolve, reject) => {
     uni.requestSubscribeMessage({
       tmplIds: [TEMPLATE_ID],

+ 17 - 12
src/pages/home/village/components/VillageTree.vue

@@ -22,6 +22,7 @@ import { RandomUtils } from '@imengyu/imengyu-utils';
 import { getCurrentInstance, onBeforeUnmount, onMounted } from 'vue';
 
 let currentText : MiniRender.Text | null = null;
+let treeMain : MiniRender.Sprite | null = null;
 const todoTreeText = '现在没有树的动画,树是静态的。';
 
 const emit = defineEmits<{
@@ -38,7 +39,7 @@ const render = new MiniRender.Scene(
     height: HEIGHT,
     backgroundColor: '#000',
   }, 
-  async () => {
+  async function() {
     const bg = new MiniRender.Sprite();
     bg.src = 'https://xy.wenlvti.net/app_static/images/village/TreeTestBg.jpg';
     bg.x = 0;
@@ -49,20 +50,20 @@ const render = new MiniRender.Scene(
     const text = new MiniRender.Text(todoTreeText, {
       fontSize: 20,
       color: '#000',
-      align: 'left',
+      align: 'center',
       wrap: 'wrap',
-      maxWidth: 300,
+      maxWidth: this.precentXToPixel(0.8),
     });
-    text.x = 70;
-    text.y = 50;
+    text.x = this.precentXToPixel(0.2);
+    text.y = this.precentYToPixel(0.12);
     currentText = text;
 
-    const treeMain = new MiniRender.Sprite();
+    treeMain = new MiniRender.Sprite();
     treeMain.src = 'https://xy.wenlvti.net/app_static/images/home/tree/Level4.png';
-    treeMain.x = 135;
-    treeMain.y = 55;
-    treeMain.width = 180;
-    treeMain.height = 200;
+    treeMain.width = this.precentXToPixel(0.60);
+    treeMain.height = this.precentYToPixel(0.85);
+    treeMain.x = (this.width - treeMain.width) / 2;
+    treeMain.y = (this.height - treeMain.height);
     treeMain.interactive = true;
 
     render.root
@@ -76,6 +77,9 @@ const render = new MiniRender.Scene(
 );
 
 function createFruit() {
+  if (!treeMain)
+    return;
+  
   const fruit = new MiniRender.AnimateSprite({
     framerate: 7,
     images: ['https://xy.wenlvti.net/app_static/images/home/tree/AnimFruit.png'],
@@ -94,8 +98,9 @@ function createFruit() {
     currentAnimation: 'walk',
   });
   fruit.play();
-  fruit.x = RandomUtils.genRandom(120, 260);
-  fruit.y = RandomUtils.genRandom(90, 120);
+  //在树的区域内创建水果
+  fruit.x = RandomUtils.genRandom(treeMain.x + treeMain.width / 2, treeMain.x + treeMain.width / 2);
+  fruit.y = RandomUtils.genRandom(treeMain.y + treeMain.height / 2, treeMain.y + treeMain.height / 2);
   fruit.width = 30;
   fruit.height = 30;
   fruit.interactive = true;

+ 63 - 31
src/pages/home/village/index.vue

@@ -1,16 +1,26 @@
 <template>
   <CommonRoot>
-    <FlexCol v-if="showSwitch" position="absolute" :left="0" :top="0">
+    <FlexCol v-if="showSwitch" position="absolute" :left="0" :top="0" :right="0" backgroundColor="rgba(255,255,255,0.5)">
       <StatusBarSpace />
-      <Button
-        @click="showMyFollowPopup = true"
-        icon="https://xy.wenlvti.net/app_static/images/home/IconSwitch.png"
-        :text="villageStore.currentVillage?.name || '未选择村庄'"
-        type="custom"
-        color="transparent"
-      />
+      <FlexRow gap="gap.md">
+        <Button
+          icon="https://xy.wenlvti.net/app_static/images/home/IconSwitch.png"
+          :text="villageStore.currentVillage?.name || '未选择村庄'"
+          :textColor="topTab === 'village' ? 'text.titleLight' : 'text.content'"
+          color="transparent"
+          type="custom" 
+          @click="showMyFollowPopup = true"
+        />
+        <Button 
+          type="custom" 
+          color="transparent" 
+          :textColor="topTab === 'around' ? 'text.titleLight' : 'text.content'"
+          text="周边村社" 
+          @click="topTab = 'around'" 
+        />
+      </FlexRow>
     </FlexCol>
-    <FlexCol v-if="villageStore.currentVillage" gap="gap.md">
+    <FlexCol v-if="topTab === 'village' && villageStore.currentVillage" gap="gap.md">
       <FlexCol v-if="!isLight" center :padding="[60,0]" gap="gap.md">
         <Image src="https://xy.wenlvti.net/app_static/images/home/BadgeNew.png" :width="320" mode="widthFix" />
         <Text text="本村暂未点亮" fontConfig="primaryTitle" />
@@ -34,25 +44,28 @@
         <Card v-if="tab === 'card'" @goTree="tab = 'tree'" />
         <Tree v-if="tab === 'tree'" />
       </template>
-      <Popup 
-        v-model:show="showMyFollowPopup" 
-        closeable
-        position="bottom"
-        round
-        size="80vh"
-      >
-        <VillageMyFollow @goDetails="onSelectVillage" />
-      </Popup>
       <Height :height="150" />
     </FlexCol>
-    <Around v-else />
+    <Around 
+      v-else 
+      @selectVillage="onSelectVillage" 
+    />
     <Height :height="200" />
+    <Popup 
+      v-model:show="showMyFollowPopup" 
+      closeable
+      position="bottom"
+      round
+      size="80vh"
+    >
+      <VillageMyFollow @goDetails="onSelectVillage" />
+    </Popup>
 
   </CommonRoot>
 </template>
 
 <script setup lang="ts">
-import { computed, onMounted, ref } from 'vue';
+import { computed, onMounted, ref, watch } from 'vue';
 import { useVillageStore } from '@/store/village';
 import { useFollow } from './composeabe/Follow';
 import { navTo } from '@/components/utils/PageAction';
@@ -74,7 +87,9 @@ import HomeLargeTitle from '@/common/components/parts/HomeLargeTitle.vue';
 import Around from './recommed/around.vue';
 import FrameButton from '@/common/components/FrameButton.vue';
 import { isDevEnv } from '@/common/config/AppCofig';
+import { waitTimeOut } from '@imengyu/imengyu-utils';
 
+const topTab = ref<'village' | 'around'>('village');
 const tab = ref('card');
 const villageStore = useVillageStore();
 const showMyFollowPopup = ref(false);
@@ -90,17 +105,6 @@ const isLight = computed(() => {
   return villageStore.currentVillage?.isLight ?? false;
 });
 
-onMounted(() => {
-  if (!props.showSwitch) {
-    uni.setNavigationBarTitle({
-      title: villageStore.currentVillage?.name || '未选择村庄',
-    });
-  }
-  if (isDevEnv) {
-    //tab.value = 'tree';
-  }
-});
-
 function handleGoApply() {
   navTo('/pages/home/light/submit', {
     villageId: villageStore.currentVillage?.id ?? undefined,
@@ -110,7 +114,35 @@ function handleGoApply() {
 }
 
 function onSelectVillage(village: VillageListItem) {
+  topTab.value = 'village';
   villageStore.setCurrentVillage(village);
   showMyFollowPopup.value = false;
 }
+
+watch(() => villageStore.currentVillage, (newVal) => {
+  if (!newVal) {
+    if (topTab.value === 'village')
+     topTab.value = 'around';
+  } else {
+    if (topTab.value === 'around')
+      topTab.value = 'village';
+  }
+});
+
+onMounted(async () => {
+  if (!props.showSwitch) {
+    uni.setNavigationBarTitle({
+      title: villageStore.currentVillage?.name || '未选择村庄',
+    });
+  }
+  if (isDevEnv) {
+    tab.value = 'tree';
+  }
+  await waitTimeOut(1000);
+  if (villageStore.currentVillage) {
+    topTab.value = 'village';
+  } else {
+    topTab.value = 'around';
+  }
+});
 </script>

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

@@ -45,11 +45,19 @@
         justify="space-between" 
       >
         <FlexCol gap="gap.md"> 
-          <Text :text="`${villageInfoLoader.content.value?.applyCount} 个乡源果可申请`" fontConfig="secondText" />  
-          <Text :text="`存储空间内存:${villageInfoLoader.content.value?.sizeText}`" fontConfig="secondText" />
+          <Text :text="`${villageInfoLoader.content.value?.applyCount || 0} 个乡源果可申请`" fontConfig="secondText" />  
+          <Text :text="`存储空间内存:${villageInfoLoader.content.value?.sizeText || 0}`" fontConfig="secondText" />
         </FlexCol>
         <FlexCol gap="gap.md">
-          <Button icon="https://xy.wenlvti.net/app_static/images/village/IconUser.png" radius="radius.lgr" :padding="[10, 30]" backgroundColor="white" @click="($refs.applyGoodsDialog as any).show()">升级村社</Button>
+          <Button 
+            icon="https://xy.wenlvti.net/app_static/images/village/IconUser.png" 
+            radius="radius.lgr" 
+            :padding="[10, 30]" 
+            backgroundColor="white" 
+            @click="($refs.applyGoodsDialog as any).show()"
+          >
+            升级村社
+          </Button>
         </FlexCol>
       </FlexRow>