Просмотр исходного кода

📦 浇水施肥拾果按钮对接接口

快乐的梦鱼 1 месяц назад
Родитель
Сommit
c4a417c9f4

+ 3 - 2
src/api/BaseAppServerRequestModule.ts

@@ -160,6 +160,7 @@ export class BaseAppServerRequestModule<T extends DataModel> extends RequestCore
           let errType : RequestApiErrorType = 'unknow';
           let errType : RequestApiErrorType = 'unknow';
           let errString = '';
           let errString = '';
           let errCodeStr = '';
           let errCodeStr = '';
+          let errData = json.data;
 
 
           if (typeof json.message === 'string') 
           if (typeof json.message === 'string') 
             errString = json.message;
             errString = json.message;
@@ -195,8 +196,8 @@ export class BaseAppServerRequestModule<T extends DataModel> extends RequestCore
             errString,
             errString,
             errCodeStr,
             errCodeStr,
             response.status,
             response.status,
-            null,
-            null,
+            errData,
+            json,
             response.headers,
             response.headers,
             apiInfo
             apiInfo
           );
           );

+ 67 - 0
src/api/light/TreeApi.ts

@@ -1,6 +1,7 @@
 import { DataModel, transformArrayDataModel, transformDataModel, type KeyValue, type NewDataModel } from '@imengyu/js-request-transform';
 import { DataModel, transformArrayDataModel, transformDataModel, type KeyValue, type NewDataModel } from '@imengyu/js-request-transform';
 import { AppServerRequestModule } from '../RequestModules';
 import { AppServerRequestModule } from '../RequestModules';
 import { transformSomeToArray } from '../Utils';
 import { transformSomeToArray } from '../Utils';
+import type { RequestApiResult } from '@imengyu/imengyu-utils';
 
 
 /** 流水/日志通用字段 */
 /** 流水/日志通用字段 */
 abstract class GrowthLogBase extends DataModel<GrowthLogBase> {
 abstract class GrowthLogBase extends DataModel<GrowthLogBase> {
@@ -193,6 +194,18 @@ export class UpgradePackageItem extends DataModel<UpgradePackageItem> {
   deletetime = '' as string | null;
   deletetime = '' as string | null;
 }
 }
 
 
+export class DropFruitResult extends DataModel<DropFruitResult> {
+  constructor() {
+    super(DropFruitResult, '产果结果');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      fruitRemain: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  fruitRemain = 0;
+}
+
 export class UpgradeOrderItem extends DataModel<UpgradeOrderItem> {
 export class UpgradeOrderItem extends DataModel<UpgradeOrderItem> {
   constructor() {
   constructor() {
     super(UpgradeOrderItem, '升级订单');
     super(UpgradeOrderItem, '升级订单');
@@ -516,6 +529,60 @@ export class TreeApi extends AppServerRequestModule<DataModel> {
     return transformDataModel<UpgradePackageItem>(UpgradePackageItem, res.requireData());
     return transformDataModel<UpgradePackageItem>(UpgradePackageItem, res.requireData());
   }
   }
 
 
+  private getStringDataOrMessage(res: RequestApiResult<any>) {
+    return typeof res.data === 'string' ? res.data : res.message;
+  }
+
+  /** 浇水(每日限次,仅可关注或认领的村社) */
+  async water(villageId: number) {
+    const res = await this.post<null>('/village/growth/water', '浇水', {
+      village_id: villageId,
+    });
+    return this.getStringDataOrMessage(res);
+  }
+
+  /** 施肥(每日限次,仅可关注或认领的村社) */
+  async fertilize(villageId: number) {
+    const res = await this.post<null>('/village/growth/fertilize', '施肥', {
+      village_id: villageId,
+    });
+    return this.getStringDataOrMessage(res);
+  }
+
+  /** 拾取果子 */
+  async pick(villageId: number) {
+    const res = await this.post<null>('/village/growth/pick', '拾取果子', {
+      village_id: villageId,
+    });
+    return this.getStringDataOrMessage(res);
+  }
+
+  /** 乡源树产果(随机产果,返回可拾取数量) */
+  async dropFruit(villageId: number) {
+    const res = await this.post<KeyValue>('/village/growth/dropFruit', '乡源树产果', {
+      village_id: villageId,
+    });
+    return transformDataModel<DropFruitResult>(DropFruitResult, res.requireData());
+  }
+
+  /** 下单赐福套餐 */
+  async createBlessOrder(villageId: number, blessId: number) {
+    const res = await this.post<KeyValue>('/village/growth/bless', '下单赐福', {
+      village_id: villageId,
+      bless_id: blessId,
+    });
+    return transformDataModel<BlessOrderItem>(BlessOrderItem, res.requireData());
+  }
+
+  /**
+   * 发放赐福奖励(支付成功后调用;文档未列参数,按订单 id 发放)
+   * @param id 赐福订单 ID(`createBlessOrder` 返回的 id)
+   */
+  async grantBlessReward(id: number) {
+    const res = await this.post<null>('/village/growth/grantBlessReward', '发放赐福奖励', { id });
+    return res.requireData();
+  }
+
   /** 升级订单列表 */
   /** 升级订单列表 */
   async getUpgradeOrderList(options?: {
   async getUpgradeOrderList(options?: {
     page?: number;
     page?: number;

+ 5 - 0
src/common/components/parts/HomeTitle.vue

@@ -1,6 +1,7 @@
 <template>
 <template>
   <SubTitle 
   <SubTitle 
     :title="title" 
     :title="title" 
+    :moreText="moreText"
     :showMore="showMore" 
     :showMore="showMore" 
     :padding="[25, 0, 20, 0]"
     :padding="[25, 0, 20, 0]"
     @moreClicked="emit('moreClicked')"
     @moreClicked="emit('moreClicked')"
@@ -44,6 +45,10 @@ const props = defineProps({
     type: Boolean,
     type: Boolean,
     default: false,
     default: false,
   },
   },
+  moreText: {
+    type: String,
+    default: '更多',
+  },
   lightCount: {
   lightCount: {
     type: Number,
     type: Number,
     default: 2,
     default: 2,

+ 1 - 1
src/common/composeabe/ErrorDisplay.ts

@@ -5,7 +5,7 @@
  * @param title 弹窗标题
  * @param title 弹窗标题
  * @param callback 弹窗确认回调
  * @param callback 弹窗确认回调
  */
  */
-export function showError(e: any, title = '糟糕,出错了', callback?: () => void) {
+export function showError(e: any, title = '提示', callback?: () => void) {
   console.log('showError', e);
   console.log('showError', e);
   let message = '';
   let message = '';
   if (e?.errMsg) 
   if (e?.errMsg) 

+ 12 - 1
src/common/composeabe/RequireLogin.ts

@@ -30,6 +30,17 @@ export function useReqireLogin() {
       } else {
       } else {
         cb();
         cb();
       }
       }
-    }
+    },
+    async requireLoginAsync(message: string = '登录后查看') {
+      if (!authStore.isLogged) {
+        await confirm({ 
+          title: '提示', 
+          content: message,
+          confirmText: '去登录' 
+        });
+        return false;
+      }
+      return true;
+    },
   }
   }
 }
 }

+ 7 - 1
src/components/loader/SimplePageListLoader.vue

@@ -6,7 +6,7 @@
     :status="loader.status.value" 
     :status="loader.status.value" 
   />
   />
   <slot v-else-if="loader.status.value == 'empty'" name="empty">
   <slot v-else-if="loader.status.value == 'empty'" name="empty">
-    <Empty description="暂无数据" />
+    <Empty :description="emptyView?.text || '暂无数据'" />
   </slot>
   </slot>
   <Loadmore 
   <Loadmore 
     v-else-if="loader.status.value == 'error'"
     v-else-if="loader.status.value == 'error'"
@@ -27,6 +27,12 @@ const props = defineProps({
     type: Object as PropType<ISimplePageListLoader<any, any>>,
     type: Object as PropType<ISimplePageListLoader<any, any>>,
     default: null,
     default: null,
   },
   },
+  emptyView: {
+    type: Object as PropType<{
+      text: string;
+    }>,
+    default: null,
+  },
 })
 })
 
 
 function handleRetry() {
 function handleRetry() {

+ 8 - 0
src/pages.json

@@ -112,6 +112,14 @@
       }
       }
     },
     },
     {
     {
+      "path": "pages/home/village/bless/my-orders",
+      "style": {
+        "navigationBarTitleText": "我的订单",
+        "navigationStyle": "custom",
+        "enablePullDownRefresh": true
+      }
+    },
+    {
       "path": "pages/home/village/gov/index",
       "path": "pages/home/village/gov/index",
       "style": {
       "style": {
         "navigationBarTitleText": "政贤连心",
         "navigationBarTitleText": "政贤连心",

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

@@ -293,7 +293,7 @@ onMounted(async () => {
       clear: false,
       clear: false,
     });
     });
   });
   });
-  await regionLoader.reload();
+  //await regionLoader.reload();
 });
 });
 </script>
 </script>
 
 

+ 80 - 0
src/pages/home/village/bless/my-orders.vue

@@ -0,0 +1,80 @@
+<template>
+  <CommonTopBanner title="我的赐福订单">
+    <SimplePageListLoader :loader="listLoader" :emptyView="{ text: '冷冷清清,等你来添光加彩' }">
+      <FlexCol gap="gap.md">
+        <BackgroundBox
+          v-for="item in listLoader.list.value"
+          :key="item.id" 
+          direction="row"
+          backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxMid.png"
+          :backgroundCutBorder="30"
+          :backgroundCutBorderSize="30"
+          :padding="[16, 30]"
+        >
+          <Touchable
+            :gap="20"
+            :padding="[15,20]"
+            touchable
+            flex="1"
+            direction="row"
+            justify="space-between"
+            align="center"
+          >
+            <FlexCol>
+              <Text fontConfig="lightImportantTitle">{{ item.blessName }}</Text>
+              <Text :size="23" :text="`${item.villageName} ${item.addLight}乡源光/${item.addFruit}乡源果`" />
+            </FlexCol>
+            <FlexCol>
+              <FlexRow align="center" gap="gap.sm">
+                <Text :size="23" fontFamily="SongtiSCBlack">¥</Text>
+                <Text :size="40" fontFamily="SongtiSCBlack">{{ item.amount }}</Text>
+              </FlexRow>
+              <Text :size="23">{{ item.createtime }}</Text>
+            </FlexCol>
+          </Touchable>
+        </BackgroundBox>
+      </FlexCol>
+    </SimplePageListLoader>
+  </CommonTopBanner>
+</template>
+
+<script setup lang="ts">
+import FlexCol from '@/components/layout/FlexCol.vue';
+import TreeApi from '@/api/light/TreeApi';
+import SimplePageListLoader from '@/components/loader/SimplePageListLoader.vue';
+import { useSimplePageListLoader } from '@/components/composeabe/loader/SimplePageListLoader';
+import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
+import Touchable from '@/components/feedback/Touchable.vue';
+import Text from '@/components/basic/Text.vue';
+import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
+import { useAuthStore } from '@/store/auth';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
+
+const { querys } = useLoadQuerys({
+  villageId: 0,
+}, () => {
+  listLoader.load();
+});
+const authStore = useAuthStore();
+
+const listLoader = useSimplePageListLoader(10, async (page, pageSize) => {
+  if (!querys.value.villageId || !authStore.userId) {
+    return {
+      list: [],
+      total: 0,
+    };
+  }
+  const res = await TreeApi.getBlessOrders({
+    page,
+    pageSize,
+    villageId: querys.value.villageId,
+    userId: authStore.userId,
+  });
+  return {
+    list: res.list,
+    total: res.total,
+  };
+});
+
+</script>

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

@@ -109,6 +109,18 @@ const render = new MiniRender.Scene(
   }
   }
 );
 );
 
 
+function playStateAnimation(state: 'collect' | 'water' | 'fertilize' | 'task' | 'bless' | 'exchange') {
+  //TODO: 播放动画
+}
+
+defineExpose({
+  reload: () => {
+    render.root.removeAll();
+    render.init();
+  },
+  playStateAnimation,
+});
+
 onBeforeUnmount(() => {
 onBeforeUnmount(() => {
   render.dispose();
   render.dispose();
 });
 });

+ 33 - 40
src/pages/home/village/dialogs/ApplyGoodsDialog.vue

@@ -1,5 +1,5 @@
 <template>
 <template>
-  <CommonDialog v-model:show="show" title="申请村社">
+  <CommonDialog v-model:show="show" title="升级村社">
     <FlexCol gap="gap.lg">
     <FlexCol gap="gap.lg">
       <FlexRow wrap justify="space-around" gap="gap.md">
       <FlexRow wrap justify="space-around" gap="gap.md">
         <BackgroundBox 
         <BackgroundBox 
@@ -48,52 +48,45 @@ import Text from '@/components/basic/Text.vue';
 import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
 import BackgroundBox from '@/components/display/block/BackgroundBox.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 { ref } from 'vue';
+import TreeApi, { type UpgradePackageItem } from '@/api/light/TreeApi';
+import { computed, ref } from 'vue';
 
 
 const show = ref(false);
 const show = ref(false);
-
-const infoGrids = ref([
-  {
-    label: '认证等级',
-    desc: '初级·年费',
-    value: '1000',
-    unit: '元/年',
-  },
-  {
-    label: '管理员',
-    desc: '乡源果',
-    value: '399',
-    unit: '个',
-  },
-  {
-    label: '认证等级',
-    desc: '中级·年费',
-    value: '1000',
-    unit: '元/年',
-  },
-  {
-    label: '管理员',
-    desc: '乡源果',
-    value: '399',
-    unit: '个',
-  },
-  {
-    label: '认证等级',
-    desc: '高级·年费',
-    value: '1000',
-    unit: '元/年',
+const props = defineProps({
+  villageId: {
+    type: Number,
+    required: true,
   },
   },
-  {
-    label: '管理员',
-    desc: '乡源果',
-    value: '399',
-    unit: '个',
-  },
-]);
+});
+
+const upgradeList = ref<UpgradePackageItem[]>([]);
+
+const infoGrids = computed(() =>
+  upgradeList.value.map((item) => [
+    {
+      label: '认证等级',
+      desc: `${item.name}·年费`,
+      value: String(item.price),
+      unit: '元/年',
+    },
+    {
+      label: '管理员',
+      desc: '管理员上限',
+      value: String(item.managerLimit),
+      unit: '人',
+    },
+  ]).flat(),
+);
+
+async function loadUpgradeList() {
+  const res = await TreeApi.getUpgradeList();
+  upgradeList.value = res.list;
+}
 
 
 defineExpose({
 defineExpose({
   show: () => {
   show: () => {
     show.value = true;
     show.value = true;
+    loadUpgradeList();
   },
   },
 });
 });
 </script>
 </script>

+ 3 - 1
src/pages/home/village/dialogs/BlessBuyDialog.vue

@@ -54,7 +54,7 @@
       </BackgroundBox>
       </BackgroundBox>
         
         
       <Height :height="5" />
       <Height :height="5" />
-      <PrimaryButton text="立即赐福" @click="show = false" width="520rpx" />
+      <PrimaryButton text="立即赐福" @click="show = false;emit('buyBless')" width="520rpx" />
       <Height :height="5" />
       <Height :height="5" />
       <Text 
       <Text 
         textAlign="center" 
         textAlign="center" 
@@ -85,6 +85,8 @@ const show = ref(false);
 const props = defineProps<{
 const props = defineProps<{
   currentBless?: BlessPackageItem;
   currentBless?: BlessPackageItem;
 }>();
 }>();
+const emit = defineEmits(['buyBless']);
+
 const infoGrids = computed(() => [
 const infoGrids = computed(() => [
   {
   {
     label: '村社加乡源光',
     label: '村社加乡源光',

+ 10 - 2
src/pages/home/village/introd/card.vue

@@ -212,8 +212,16 @@
     @publishsuccess="onPublishSuccess"
     @publishsuccess="onPublishSuccess"
   />
   />
 
 
-  <JoinDialog ref="joinDialog" :villageId="villageStore.currentVillage?.id ?? 0" />
-  <ApplyGoodsDialog ref="applyGoodsDialog" />
+  <JoinDialog
+    ref="joinDialog" 
+    v-if="villageStore.currentVillage"
+    :villageId="villageStore.currentVillage.id" 
+  />
+  <ApplyGoodsDialog 
+    ref="applyGoodsDialog" 
+    v-if="villageStore.currentVillage"
+    :villageId="villageStore.currentVillage.id"
+  />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">

+ 127 - 8
src/pages/home/village/introd/tree.vue

@@ -3,7 +3,9 @@
     <FlexCol :margin="[10,0,0,0]">
     <FlexCol :margin="[10,0,0,0]">
       <Text textAlign="center" text="一人添果,全村增光;乡源树茂,故土名扬" fontConfig="primaryTitle" fontSize="35rpx"  />
       <Text textAlign="center" text="一人添果,全村增光;乡源树茂,故土名扬" fontConfig="primaryTitle" fontSize="35rpx"  />
     </FlexCol>
     </FlexCol>
-    <VillageTree />
+    <VillageTree 
+      ref="villageTreeRef"
+    />
     <FlexCol :padding="30">
     <FlexCol :padding="30">
       <FlexCol>
       <FlexCol>
         <FlexRow center>
         <FlexRow center>
@@ -36,15 +38,15 @@
 
 
       <Height height="space.xl" />
       <Height height="space.xl" />
       <FlexRow justify="space-around" :padding="[0, 30]">
       <FlexRow justify="space-around" :padding="[0, 30]">
-        <Touchable center direction="column" flexBasis="22%">
+        <Touchable center direction="column" flexBasis="22%" @click="handlePick">
           <Image src="https://xy.wenlvti.net/app_static/images/village/IconCollect.png" :width="130" mode="widthFix" />
           <Image src="https://xy.wenlvti.net/app_static/images/village/IconCollect.png" :width="130" mode="widthFix" />
           <Text text="拾果" fontConfig="contentText" />
           <Text text="拾果" fontConfig="contentText" />
         </Touchable>
         </Touchable>
-        <Touchable center direction="column" flexBasis="22%">
+        <Touchable center direction="column" flexBasis="22%" @click="handleFertilize">
           <Image src="https://xy.wenlvti.net/app_static/images/village/IconFertilization.png" :width="130" mode="widthFix" />
           <Image src="https://xy.wenlvti.net/app_static/images/village/IconFertilization.png" :width="130" mode="widthFix" />
           <Text text="施肥" fontConfig="contentText" />
           <Text text="施肥" fontConfig="contentText" />
         </Touchable>
         </Touchable>
-        <Touchable center direction="column" flexBasis="22%">
+        <Touchable center direction="column" flexBasis="22%" @click="handleWater">
           <Image src="https://xy.wenlvti.net/app_static/images/village/IconWatering.png" :width="130" mode="widthFix" />
           <Image src="https://xy.wenlvti.net/app_static/images/village/IconWatering.png" :width="130" mode="widthFix" />
           <Text text="浇水" fontConfig="contentText" textAlign="center" />
           <Text text="浇水" fontConfig="contentText" textAlign="center" />
         </Touchable>
         </Touchable>
@@ -71,7 +73,10 @@
               :size="80"
               :size="80"
               radius="50%"
               radius="50%"
             />
             />
-            <Text :text="item.content" fontConfig="contentText" :innerStyle="{ flex: 1 }" />
+            <FlexCol flex="1">
+              <Text :text="item.nickname" fontConfig="contentText" :innerStyle="{ flex: 1 }" />
+              <Text :text="item.content" fontConfig="contentText" :innerStyle="{ flex: 1 }" />
+            </FlexCol>
             <BackgroundBox
             <BackgroundBox
               backgroundImage="https://xy.wenlvti.net/app_static/images/village/TagNormal.png"
               backgroundImage="https://xy.wenlvti.net/app_static/images/village/TagNormal.png"
               :backgroundCutBorder="[10, 10, 10, 10]"
               :backgroundCutBorder="[10, 10, 10, 10]"
@@ -84,7 +89,12 @@
         </FlexCol>
         </FlexCol>
       </SimplePageContentLoader>
       </SimplePageContentLoader>
 
 
-      <HomeTitle title="乡源赐福" />
+      <HomeTitle 
+        title="乡源赐福" 
+        showMore 
+        moreText="我的订单" 
+        @moreClicked="handleGoBlessOrders" 
+      />
       <FlexRow wrap>
       <FlexRow wrap>
         <Touchable 
         <Touchable 
           v-for="(item, index) in blessingInfoLoader.content.value"
           v-for="(item, index) in blessingInfoLoader.content.value"
@@ -116,13 +126,20 @@
     </FlexCol>
     </FlexCol>
   </FlexCol>
   </FlexCol>
 
 
-  <BlessBuyDialog ref="blessBuyDialogRef" :currentBless="currentBless" />
+  <BlessBuyDialog 
+    ref="blessBuyDialogRef" 
+    :currentBless="currentBless" 
+    @buyBless="handleBuyBlessConfirm"
+  />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
 import { ref, watch } from 'vue';
 import { ref, watch } from 'vue';
 import { useVillageStore } from '@/store/village';
 import { useVillageStore } from '@/store/village';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import { useReqireLogin } from '@/common/composeabe/RequireLogin';
+import { showError } from '@/common/composeabe/ErrorDisplay';
+import { navTo } from '@/components/utils/PageAction';
 import HomeTitle from '@/common/components/parts/HomeTitle.vue';
 import HomeTitle from '@/common/components/parts/HomeTitle.vue';
 import Text from '@/components/basic/Text.vue';
 import Text from '@/components/basic/Text.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
@@ -138,13 +155,16 @@ import Progress from '@/components/display/Progress.vue';
 import TreeApi, { type BlessPackageItem, type GrowthLogFeedItem } from '@/api/light/TreeApi';
 import TreeApi, { type BlessPackageItem, type GrowthLogFeedItem } from '@/api/light/TreeApi';
 import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
 import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
 import BlessBuyDialog from '../dialogs/BlessBuyDialog.vue';
 import BlessBuyDialog from '../dialogs/BlessBuyDialog.vue';
+import { RequestApiError } from '@imengyu/imengyu-utils';
 
 
 const GROWTH_FEED_COUNT = 6;
 const GROWTH_FEED_COUNT = 6;
 const DEFAULT_AVATAR = 'https://xy.wenlvti.net/app_static/images/village/PlaceholderVolunteer.png';
 const DEFAULT_AVATAR = 'https://xy.wenlvti.net/app_static/images/village/PlaceholderVolunteer.png';
 
 
 const villageStore = useVillageStore();
 const villageStore = useVillageStore();
+const { requireLoginAsync } = useReqireLogin();
 
 
 const blessBuyDialogRef = ref<InstanceType<typeof BlessBuyDialog>>();
 const blessBuyDialogRef = ref<InstanceType<typeof BlessBuyDialog>>();
+const villageTreeRef = ref<InstanceType<typeof VillageTree>>();
 const currentBless = ref<BlessPackageItem>();
 const currentBless = ref<BlessPackageItem>();
 
 
 const infoLoader = useSimpleDataLoader(async () => {
 const infoLoader = useSimpleDataLoader(async () => {
@@ -178,12 +198,24 @@ const infoLoader = useSimpleDataLoader(async () => {
     return {
     return {
       id: `${logType}-${item.id}`,
       id: `${logType}-${item.id}`,
       head: DEFAULT_AVATAR,
       head: DEFAULT_AVATAR,
+      nickname: item.nickname,
       content,
       content,
       levelText,
       levelText,
     };
     };
   }
   }
   const { list } = await TreeApi.getRandomGrowthLogFeed(GROWTH_FEED_COUNT, { villageId });
   const { list } = await TreeApi.getRandomGrowthLogFeed(GROWTH_FEED_COUNT, { villageId });
-  return list.map(mapGrowthFeedToInfoItem);
+  const res = list.map(mapGrowthFeedToInfoItem);
+  if (res.length > 0)
+    return res;
+  return [
+    {
+      id: 'default',
+      head: DEFAULT_AVATAR,
+      content: '冷冷清清,等你来添光加彩',
+      nickname: '',
+      levelText: '第一条',
+    }
+  ];
 });
 });
 
 
 watch(() => villageStore.currentVillage?.id, () => {
 watch(() => villageStore.currentVillage?.id, () => {
@@ -198,10 +230,97 @@ function handleBuyBless(bless: BlessPackageItem) {
   currentBless.value = bless;
   currentBless.value = bless;
   blessBuyDialogRef.value?.show();
   blessBuyDialogRef.value?.show();
 }
 }
+async function handleGoBlessOrders() {
+  if (!await requireLoginAsync('登录后查看我的赐福订单'))
+    return;
+  navTo('/pages/home/village/bless/my-orders', { 
+    villageId: villageStore.currentVillage?.id 
+  });
+}
+async function handleBuyBlessConfirm() {
+  if (!currentBless.value || !villageStore.currentVillage?.id) 
+    return;
+  if (!await requireLoginAsync('登录后为村社赐福,留下你的大名吧'))
+    return;
+  try {
+    uni.showLoading({
+      title: '请稍后...',
+    });
+    const order = await TreeApi.createBlessOrder(villageStore.currentVillage?.id, currentBless.value.id);
+    if (order) {
+      console.log(order);
+    }
+  } catch (error) {
+    showError(error);
+  } finally {
+    uni.hideLoading();
+  }
+}
 function handleGoBless() {
 function handleGoBless() {
   uni.pageScrollTo({
   uni.pageScrollTo({
     scrollTop: 1000,
     scrollTop: 1000,
     duration: 300,
     duration: 300,
   });
   });
 }
 }
+
+function handleFertilize() {
+  handlePickOrWaterOrFertilize('fertilize');
+}
+function handlePick() {
+  handlePickOrWaterOrFertilize('pick');
+}
+function handleWater() {
+  handlePickOrWaterOrFertilize('water');
+}
+async function handlePickOrWaterOrFertilize(action: 'pick' | 'water' | 'fertilize') {
+  if (!villageStore.currentVillage?.id) 
+    return;
+  switch (action) {
+    case 'pick':
+      if (!await requireLoginAsync('登录后就可以为乡源树拾果了'))
+        return;
+      break;
+    case 'water':
+      if (!await requireLoginAsync('登录后就可以为乡源树浇水了'))
+        return;
+      break;
+    case 'fertilize':
+      if (!await requireLoginAsync('登录后就可以为乡源树施肥了'))
+        return;
+      break;
+  }
+  try {
+    uni.showLoading({
+      title: '请稍后...',
+    });
+    let res = '';
+    switch (action) {
+      case 'pick':
+        res = await TreeApi.pick(villageStore.currentVillage.id);
+        villageTreeRef.value?.playStateAnimation('collect');
+        break;
+      case 'water':
+        res = await TreeApi.water(villageStore.currentVillage.id);
+        villageTreeRef.value?.playStateAnimation('water');
+        break;
+      case 'fertilize':
+        res = await TreeApi.fertilize(villageStore.currentVillage.id);
+        villageTreeRef.value?.playStateAnimation('fertilize');
+        break;
+    }
+    uni.hideLoading();
+    uni.showToast({
+      title: res,
+    });
+  } catch (e) {
+    uni.hideLoading();
+    console.log(e);
+    
+    if (e instanceof RequestApiError && typeof e.data === 'string') {
+      showError(e.data);
+      return;
+    }
+    showError(e);
+  }
+}
 </script>
 </script>