浏览代码

📦 乡源果支付功能

快乐的梦鱼 3 天之前
父节点
当前提交
738c9468bb

+ 2 - 1
.claude/settings.local.json

@@ -3,7 +3,8 @@
     "allow": [
       "mcp__chrome-devtools__new_page",
       "mcp__chrome-devtools__take_snapshot",
-      "mcp__chrome-devtools__navigate_page"
+      "mcp__chrome-devtools__navigate_page",
+      "Bash(npx vue-tsc *)"
     ]
   }
 }

+ 1 - 1
src/api/auth/UserApi.ts

@@ -117,7 +117,7 @@ export class UserApi extends AppServerRequestModule<DataModel> {
     return (await this.post('/content/main_body_user/changepwd', '更新密码', data))
   }
   async getUserInfo() {
-    return (await this.post('/content/main_body_user/getMainBodyUser', '获取用户信息', {}, undefined, UserInfo)).data as UserInfo;
+    return (await this.post('/user/userData', '获取用户信息', {}, undefined, UserInfo)).data as UserInfo;
   }  
   async updateUserInfo(data: {
     nickname?: string,

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

@@ -363,6 +363,112 @@ export class UpgradeOrderItem extends DataModel<UpgradeOrderItem> {
   deletetime = '' as string | null;
 }
 
+export class FruitDepositItem extends DataModel<FruitDepositItem> {
+  constructor() {
+    super(FruitDepositItem, '充值套餐');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
+      price: { clientSide: 'number', serverSide: 'number' },
+      addFruit: { clientSide: 'number', serverSide: 'number' },
+      sort: { clientSide: 'number', serverSide: 'number' },
+      status: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id!: number;
+  name = '';
+  /** 金额 */
+  price = 0;
+  /** 乡源果数量 */
+  addFruit = 0;
+  sort = 0;
+  /** 状态: 0=禁用, 1=启用 */
+  status = 0;
+  statusText = '';
+  createtime = '' as string | null;
+  updatetime = '' as string | null;
+  deletetime = '' as string | null;
+}
+
+export class FruitDepositOrderItem extends DataModel<FruitDepositOrderItem> {
+  constructor() {
+    super(FruitDepositOrderItem, '充值乡源果订单');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
+      userId: { clientSide: 'number', serverSide: 'number' },
+      orderType: { clientSide: 'number', serverSide: 'number' },
+      staffLevelId: { clientSide: 'number', serverSide: 'number' },
+      villageId: { clientSide: 'number', serverSide: 'number' },
+      amount: { clientSide: 'number', serverSide: 'number' },
+      days: { clientSide: 'number', serverSide: 'number' },
+      status: { clientSide: 'number', serverSide: 'number' },
+      paytime: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id!: number;
+  orderNo = '';
+  userId = null as number | null;
+  /** 订单类型: 3=乡源果充值 */
+  orderType = 3;
+  staffLevelId = 0;
+  villageId = null as number | null;
+  amount = 0;
+  days = 0;
+  /** 状态: 0=待支付, 1=已支付 */
+  status = 0;
+  paytime = 0;
+  createtime = '' as string | null;
+  updatetime = '' as string | null;
+  orderTypeText = '';
+  payTypeText = '';
+  statusText = '';
+  paytimeText = '';
+  rewardStatusText = '';
+}
+
+export class FruitDepositOrderConfirm extends DataModel<FruitDepositOrderConfirm> {
+  constructor() {
+    super(FruitDepositOrderConfirm, '充值乡源果下单确认');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      order: {
+        clientSide: 'object',
+        clientSideChildDataModel: FruitDepositOrderItem,
+        serverSide: 'undefined',
+        clientSideRequired: true,
+      },
+      pay: {
+        clientSide: 'object',
+        clientSideChildDataModel: {
+          convertTable: {
+            appId: { clientSide: 'string', clientSideRequired: true },
+            timeStamp: { clientSide: 'string', clientSideRequired: true },
+            nonceStr: { clientSide: 'string', clientSideRequired: true },
+            package: { clientSide: 'string', clientSideRequired: true },
+            signType: { clientSide: 'string', clientSideRequired: true },
+            paySign: { clientSide: 'string', clientSideRequired: true },
+          },
+        },
+        serverSide: 'undefined',
+        clientSideRequired: true,
+      },
+    };
+  }
+
+  order!: FruitDepositOrderItem;
+  pay!: {
+    appId: string;
+    timeStamp: string;
+    nonceStr: string;
+    package: string;
+    signType: string;
+    paySign: string;
+  };
+}
+
 export class UpgradeOrderConfirm extends DataModel<UpgradeOrderConfirm> {
   constructor() {
     super(UpgradeOrderConfirm, '升级订单确认');
@@ -826,6 +932,37 @@ export class TreeApi extends AppServerRequestModule<DataModel> {
     });
     return this.getStringDataOrMessage(res);
   }
+
+  /** 充值套餐列表 */
+  async getFruitDepositList(options?: {
+    page?: number;
+    pageSize?: number;
+    keywords?: string;
+  }) {
+    const res = await this.post<PagedGrowthResponse>(
+      '/village/growth/fruitDepositList',
+      '充值套餐列表',
+      {
+        page: options?.page,
+        pageSize: options?.pageSize,
+        keywords: options?.keywords,
+      },
+    );
+    return this.parsePagedList<FruitDepositItem>(
+      FruitDepositItem,
+      res.requireData(),
+      '充值套餐',
+    );
+  }
+
+  /** 充值乡源果下单 */
+  async placeOrder(fruitDepositId: number, villageId?: number) {
+    const res = await this.post<KeyValue>('/village/growth/placeOrder', '充值乡源果下单', {
+      fruit_deposit_id: fruitDepositId,
+      village_id: villageId,
+    });
+    return transformDataModel<FruitDepositOrderConfirm>(FruitDepositOrderConfirm, res.requireData());
+  }
 }
 
 export default new TreeApi();

+ 14 - 0
src/pages.json

@@ -102,6 +102,20 @@
       }
     },
     {
+      "path": "pages/home/village/bless/recharge",
+      "style": {
+        "navigationBarTitleText": "充值乡源果",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/home/village/bless/pay-select",
+      "style": {
+        "navigationBarTitleText": "赐福支付",
+        "navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/home/village/upgrade/upgrade-village",
       "style": {
         "navigationBarTitleText": "升级村社",

+ 102 - 0
src/pages/home/village/bless/pay-select.vue

@@ -0,0 +1,102 @@
+
+
+<template>
+  <CommonTopBanner 
+    title="赐福支付"
+    showNav
+  >
+    <FlexCol gap="gap.lg" padding="padding.md">
+      <FlexRow center>
+        <Image src="https://xy.wenlvti.net/app_static/images/village/IconBlessing.png" :width="100" :height="100" />
+      </FlexRow>
+      <FlexCol padding="padding.md" center>
+        <Text 
+          text="请选择您要付款方式" 
+          fontConfig="contentText" :fontSize="30" textAlign="center" 
+        />
+      </FlexCol>
+      <BuyFruitInfo :price="querys.blessPackagePrice" @pay="handleDirectPay(3)">
+        <template #prepend>
+          <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
+            <FlexCol width="74%">
+              <FlexRow align="center" gap="gap.md">
+                <Icon icon="wechat" size="36" />
+                <Text text="在线支付" fontConfig="lightImportantTitle" :fontSize="42" />
+              </FlexRow>
+              <Text text="推荐使用微信线支付方式,方便快捷,立即生效" fontConfig="lightGoldTitle" :fontSize="30" />
+            </FlexCol>
+            <FrameButton primary text="选择" @click="handleDirectPay(1)" />
+          </BoxMid>
+        </template>
+      </BuyFruitInfo>
+    </FlexCol>
+  </CommonTopBanner>
+</template>
+
+<script setup lang="ts">
+import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
+import { useRequireLogin } from '@/common/composeabe/RequireLogin';
+import { ref } from 'vue';
+import { navTo, backAndCallOnPageBack } from '@/components/utils/PageAction';
+import { showError } from '@/common/composeabe/ErrorDisplay';
+import BoxMid from '@/common/components/box/BoxMid.vue';
+import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
+import FrameButton from '@/common/components/FrameButton.vue';
+import Text from '@/components/basic/Text.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import Image from '@/components/basic/Image.vue';
+import DirectPayDialog from './dialogs/DirectPayDialog.vue';
+import TreeApi from '@/api/light/TreeApi';
+import Icon from '@/components/basic/Icon.vue';
+import BuyFruitInfo from '../upgrade/components/BuyFruitInfo.vue';
+import { assertNotNull } from '@imengyu/imengyu-utils';
+
+const { requireLoginAsync } = useRequireLogin();
+
+const directPayDialog = ref<InstanceType<typeof DirectPayDialog>>();
+
+function handlePaySuccess() {
+  setTimeout(() => {
+    backAndCallOnPageBack('blessPaySuccessRefresh', {});
+  }, 100);
+}
+
+const { querys } = useLoadQuerys({
+  villageId: 0,
+  blessPackageId: 0,
+  blessPackagePrice: 0,
+});
+
+async function handleDirectPay(payType: 1 | 3) {
+  if (!requireLoginAsync('登录后为村社升级,做出你的贡献哦'))
+    return;
+  try {
+    uni.showLoading({ title: '创建订单中...' });
+    const payInfo = await TreeApi.createBlessOrder(querys.value.villageId, querys.value.blessPackageId, payType);
+    if (payType === 1) {
+      assertNotNull(payInfo, '支付信息不存在');
+      await new Promise<void>((resolve, reject) => {
+        uni.requestPayment({
+          provider: 'wxpay',
+          appId: payInfo.pay.appId,
+          timeStamp: payInfo.pay.timeStamp,
+          nonceStr: payInfo.pay.nonceStr,
+          package: payInfo.pay.package,
+          signType: payInfo.pay.signType,
+          paySign: payInfo.pay.paySign,
+          success: () => resolve(),
+          fail: reject,
+        });
+      });
+      handlePaySuccess();
+    } else if (payType === 3) {
+      handlePaySuccess();
+    }
+  } catch (e) {
+    showError(e);
+  } finally {
+    uni.hideLoading();
+  }
+}
+</script>

+ 95 - 0
src/pages/home/village/bless/recharge.vue

@@ -0,0 +1,95 @@
+
+
+<template>
+  <CommonTopBanner 
+    title="充值乡源果"
+    showNav
+  >
+    <FlexCol gap="gap.lg" padding="padding.md">
+      <FlexRow center>
+        <Image src="https://xy.wenlvti.net/app_static/images/village/IconBlessing.png" :width="100" :height="100" />
+      </FlexRow>
+      <FlexCol padding="padding.md" center>
+        <Text 
+          text="请选择您要付款方式" 
+          fontConfig="contentText" :fontSize="30" textAlign="center" 
+        />
+      </FlexCol>
+
+      <FlexCol gap="gap.md">
+        <BoxMid v-for="item in listLoader.content.value" direction="row" justify="space-between" align="center" gap="gap.md">
+          <FlexCol width="74%">
+            <Text :text="item.name" fontConfig="lightImportantTitle" :fontSize="42" />
+            <Text :text="`+ ${item.addFruit} 乡源果`" fontConfig="lightGoldTitle" :fontSize="30" />
+          </FlexCol>
+          <FlexRow align="center" gap="gap.lg">
+            <Text :text="`¥${item.price}`" fontConfig="lightGoldTitle" :fontSize="42" />
+            <FrameButton primary text="选择" @click="handlePay(item.id)" />
+          </FlexRow>
+        </BoxMid>
+      </FlexCol>
+    </FlexCol>
+  </CommonTopBanner>
+</template>
+
+<script setup lang="ts">
+import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
+import { useRequireLogin } from '@/common/composeabe/RequireLogin';
+import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import { toast } from '@/components/dialog/CommonRoot';
+import { backAndCallOnPageBack, navTo } from '@/components/utils/PageAction';
+import { showError } from '@/common/composeabe/ErrorDisplay';
+import BoxMid from '@/common/components/box/BoxMid.vue';
+import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
+import FrameButton from '@/common/components/FrameButton.vue';
+import Text from '@/components/basic/Text.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import Image from '@/components/basic/Image.vue';
+import TreeApi from '@/api/light/TreeApi';
+
+const { requireLoginAsync } = useRequireLogin();
+const { querys } = useLoadQuerys({
+  villageId: 0,
+});
+
+const listLoader = useSimpleDataLoader(async () => {
+  return (await TreeApi.getFruitDepositList({ page: 1, pageSize: 30 })).list;
+});
+
+async function handlePay(id: number) {
+  if (!requireLoginAsync('登录后为村社升级,做出你的贡献哦'))
+    return;
+  try {
+    uni.showLoading({ title: '创建订单中...' });
+    const payInfo = await TreeApi.placeOrder(
+      id,
+      querys.value.villageId,
+    );
+    if (payInfo && payInfo.pay) {
+      uni.requestPayment({
+        provider: 'wxpay',
+        appId: payInfo.pay.appId,
+        timeStamp: payInfo.pay.timeStamp,
+        nonceStr: payInfo.pay.nonceStr,
+        package: payInfo.pay.package,
+        signType: payInfo.pay.signType,
+        paySign: payInfo.pay.paySign,
+        success: () => {
+          toast('支付成功');
+          setTimeout(() => 
+            backAndCallOnPageBack('paySuccessAndRefresh', {})
+          , 1000);
+        },
+        fail: (err) => {
+          showError(`支付失败: ${err.errMsg}`);
+        },
+      });
+    }
+  } catch (e) {
+    showError(e);
+  } finally {
+    uni.hideLoading();
+  }
+}
+</script>

+ 11 - 5
src/pages/home/village/dialogs/BlessBuyDialog.vue

@@ -52,26 +52,30 @@
       <Height :height="5" />
       <PrimaryButton text="立即赐福" @click="show = false;emit('buyBless')" width="520rpx" />
       <Height :height="5" />
-      <Text 
-        textAlign="center" 
-        text="注:倍率仅作用于用户乡源果,村社乡源光不翻倍。赐福到期后,倍率自动恢复为1倍"
-        fontConfig="contentText"
-      />
+      <SimplePageContentLoader :loader="infoLoader">
+        <FlexCol gap="gap.md" padding="padding.md">
+          <Parse :content="infoLoader.content?.value?.content || ''" />
+        </FlexCol>
+      </SimplePageContentLoader>
     </FlexCol>
   </CommonDialog>
 </template>
 
 <script setup lang="ts">
+import CommonContent from '@/api/CommonContent';
 import type { BlessPackageItem } from '@/api/light/TreeApi';
 import BoxMid from '@/common/components/box/BoxMid.vue';
 import CommonDialog from '@/common/components/CommonDialog.vue';
 import PrimaryButton from '@/common/components/PrimaryButton.vue';
 import Image from '@/components/basic/Image.vue';
 import Text from '@/components/basic/Text.vue';
+import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
+import Parse from '@/components/display/parse/Parse.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
 import Height from '@/components/layout/space/Height.vue';
+import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
 import { DateUtils } from '@imengyu/imengyu-utils';
 import { computed, ref } from 'vue';
 
@@ -120,6 +124,8 @@ const effectiveDate = computed(() => {
   return DateUtils.formatDate(DateUtils.dateAddDays(new Date(), props.currentBless?.days || 0), 'YYYY-MM-DD');
 });
 
+const infoLoader = useSimpleDataLoader(async () => await CommonContent.getContentDetail(7027, undefined, 18));
+
 defineExpose({
   show: () => {
     show.value = true;

+ 5 - 33
src/pages/home/village/introd/tree.vue

@@ -174,7 +174,6 @@ const authStore = useAuthStore();
 const { requireLoginAsync } = useRequireLogin();
 
 const blessBuyDialogRef = ref<InstanceType<typeof BlessBuyDialog>>();
-const blessSuccessDialogRef = ref<InstanceType<typeof BlessSuccessDialog>>();
 const villageTreeRef = ref<InstanceType<typeof VillageTree>>();
 const currentBless = ref<BlessPackageItem>();
 
@@ -266,38 +265,11 @@ async function handleGoBlessOrders() {
   });
 }
 async function handleBuyBlessConfirm() {
-  if (!currentBless.value || !villageStore.currentVillage?.id) 
-    return;
-  if (!await requireLoginAsync('登录后为村社赐福,留下你的大名吧'))
-    return;
-  try {
-    uni.showLoading({
-      title: '请稍后...',
-    });
-    const payInfo = await TreeApi.createBlessOrder(villageStore.currentVillage?.id, currentBless.value.id, 1);
-    if (payInfo) {  
-      uni.requestPayment({
-        provider: 'wxpay',
-        appId: payInfo.pay.appId,
-        timeStamp: payInfo.pay.timeStamp,
-        nonceStr: payInfo.pay.nonceStr,
-        package: payInfo.pay.package,
-        signType: payInfo.pay.signType,
-        paySign: payInfo.pay.paySign,
-        success: () => {
-          blessSuccessDialogRef.value?.show();
-          handleBlessPaySuccessRefresh();
-        },
-        fail: (err) => {
-          showError(err);
-        },
-      });
-    }
-  } catch (error) {
-    showError(error);
-  } finally {
-    uni.hideLoading();
-  }
+  navTo('/pages/home/village/bless/pay-select', { 
+    villageId: villageStore.currentVillage?.id,
+    blessPackageId: currentBless.value?.id,
+    blessPackagePrice: currentBless.value?.amount,
+  });
 }
 function handleGoBless() {
   uni.pageScrollTo({

+ 45 - 0
src/pages/home/village/upgrade/components/BuyFruitInfo.vue

@@ -0,0 +1,45 @@
+<template>
+  <FlexCol gap="gap.md">
+    <slot name="prepend" />
+    <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
+      <FlexCol width="74%">
+        <FlexRow align="center" gap="gap.md">
+          <Icon icon="https://xy.wenlvti.net/app_static/images/village/IconFruit.png" size="40" />
+          <Text text="乡源果支付" fontConfig="lightImportantTitle" :fontSize="42" />
+        </FlexRow>
+        <Text :text="`余额 ${authStore.userInfo?.fruit || '0'} 乡源果` + (price ? ` 需要 ${Math.ceil(price * 100)} 乡源果` : '')" fontConfig="lightGoldTitle" :fontSize="30" />
+      </FlexCol>
+      <FrameButton primary text="选择" @click="emit('pay')" />
+    </BoxMid>      
+    <SimplePageContentLoader :loader="infoLoader">
+      <FlexCol gap="gap.md" padding="padding.md">
+        <IconTextBlock icon="prompt" :title="infoLoader.content?.value?.title" />
+        <Parse :content="infoLoader.content?.value?.content || ''" />
+      </FlexCol>
+    </SimplePageContentLoader>
+    <slot name="extra" />
+  </FlexCol>
+</template>
+
+<script setup lang="ts">
+import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import BoxMid from '@/common/components/box/BoxMid.vue';
+import Text from '@/components/basic/Text.vue';
+import Icon from '@/components/basic/Icon.vue';
+import FrameButton from '@/common/components/FrameButton.vue';
+import CommonContent from '@/api/CommonContent';
+import Parse from '@/components/display/parse/Parse.vue';
+import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import { useAuthStore } from '@/store/auth';
+import IconTextBlock from '@/components/display/block/IconTextBlock.vue';
+
+const emit = defineEmits(['pay']);
+const props = defineProps<{
+  price?: number;
+}>()
+  
+const authStore = useAuthStore();
+const infoLoader = useSimpleDataLoader(async () => await CommonContent.getContentDetail(7023, undefined, 18));
+</script>

+ 1 - 0
src/pages/home/village/upgrade/components/UpgradeSelect.vue

@@ -76,6 +76,7 @@ function handleSelect(item: UpgradePackageItem) {
   navTo('/pages/home/village/upgrade/select', {
     villageId: props.villageId,
     upgradePackageId: item.id,
+    upgradePackagePrice: item.price,
   });
 }
 

+ 20 - 26
src/pages/home/village/upgrade/my-upgrade-management.vue

@@ -21,33 +21,26 @@
         />
       </FlexCol>
 
-      <FlexCol gap="gap.md">
-        <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
-          <FlexCol width="74%">
-            <Text text="在线支付" fontConfig="lightImportantTitle" :fontSize="42" />
-            <Text text="推荐使用微信线支付方式,方便快捷,立即生效" fontConfig="lightGoldTitle" :fontSize="30" />
-          </FlexCol>
-          <FrameButton primary text="选择" @click="handleDirectPay(1, 1)" />
-        </BoxMid>
-        <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
-          <FlexCol width="74%">
-            <FlexRow align="center" gap="gap.md">
-              <Icon icon="https://xy.wenlvti.net/app_static/images/village/IconFruit.png" size="40" />
-              <Text text="乡源果支付" fontConfig="lightImportantTitle" :fontSize="42" />
-            </FlexRow>
-            <Text :text="`余额 ${0} 乡源果`" fontConfig="lightGoldTitle" :fontSize="30" />
-          </FlexCol>
-          <FrameButton primary text="选择" @click="handleDirectPay(1, 3)" />
-        </BoxMid>
-        <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
-          <FlexCol width="74%">
-            <Text text="测试" fontConfig="lightImportantTitle" :fontSize="42" />
-            <Text text="¥ 0.01" fontConfig="lightGoldTitle" :fontSize="30" />
-          </FlexCol>
-          <FrameButton primary text="选择" @click="handleDirectPay(2, 1)" />
-        </BoxMid>
-      </FlexCol>
+      <BuyFruitInfo @pay="handleDirectPay(1, 3)">
+        <template #prepend>
+          <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
+            <FlexCol width="74%">
+              <Text text="在线支付" fontConfig="lightImportantTitle" :fontSize="42" />
+              <Text text="推荐使用微信线支付方式,方便快捷,立即生效" fontConfig="lightGoldTitle" :fontSize="30" />
+            </FlexCol>
+            <FrameButton primary text="选择" @click="handleDirectPay(1, 1)" />
+          </BoxMid>
+          <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
+            <FlexCol width="74%">
+              <Text text="测试" fontConfig="lightImportantTitle" :fontSize="42" />
+              <Text text="¥ 0.01" fontConfig="lightGoldTitle" :fontSize="30" />
+            </FlexCol>
+            <FrameButton primary text="选择" @click="handleDirectPay(2, 1)" />
+          </BoxMid>
+        </template>
+      </BuyFruitInfo>
     </FlexCol>
+
     <UpgradeManagementSuccessDialog  
       ref="upgradeManagementSuccessDialog"
       @success="handlePaySuccess"
@@ -70,6 +63,7 @@ import OfficialApi from '@/api/light/OfficialApi';
 import UpgradeManagementSuccessDialog from './dialogs/UpgradeManagementSuccess.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
 import Icon from '@/components/basic/Icon.vue';
+import BuyFruitInfo from './components/BuyFruitInfo.vue';
 
 const upgradeManagementSuccessDialog = ref<InstanceType<typeof UpgradeManagementSuccessDialog>>();
 

+ 29 - 36
src/pages/home/village/upgrade/select.vue

@@ -20,41 +20,33 @@
         />
       </FlexCol>
 
-      <FlexCol gap="gap.md">
-        <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
-          <FlexCol width="74%">
-            <FlexRow align="center" gap="gap.md">
-              <Icon icon="wechat" size="36" />
-              <Text text="在线支付" fontConfig="lightImportantTitle" :fontSize="42" />
-            </FlexRow>
-            <Text text="推荐使用微信线支付方式,方便快捷,立即生效" fontConfig="lightGoldTitle" :fontSize="30" />
-          </FlexCol>
-          <FrameButton primary text="选择" @click="handleDirectPay(1)" />
-        </BoxMid>
-        <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
-          <FlexCol width="74%">
-            <FlexRow align="center" gap="gap.md">
-              <Icon icon="https://xy.wenlvti.net/app_static/images/village/IconFruit.png" size="40" />
-              <Text text="乡源果支付" fontConfig="lightImportantTitle" :fontSize="42" />
-            </FlexRow>
-            <Text :text="`余额 ${0} 乡源果`" fontConfig="lightGoldTitle" :fontSize="30" />
-          </FlexCol>
-          <FrameButton primary text="选择" @click="handleDirectPay(3)" />
-        </BoxMid>
-        <BoxMid
-          v-if="false"
-          direction="row"
-          justify="space-between"
-          align="center"
-          gap="gap.md"
-        >
-          <FlexCol width="74%">
-            <Text text="对公打款" fontConfig="lightImportantTitle" :fontSize="42" />
-            <Text text="对公打款,需要提供对公账户信息。适用于村社政府做出贡献" fontConfig="lightGoldTitle" :fontSize="30" />
-          </FlexCol>
-          <FrameButton primary text="选择" @click="handlePublicPay" />
-        </BoxMid>
-      </FlexCol>
+      <BuyFruitInfo :price="querys.upgradePackagePrice" @pay="handleDirectPay(3)">
+        <template #prepend>
+          <BoxMid direction="row" justify="space-between" align="center" gap="gap.md">
+            <FlexCol width="74%">
+              <FlexRow align="center" gap="gap.md">
+                <Icon icon="wechat" size="36" />
+                <Text text="在线支付" fontConfig="lightImportantTitle" :fontSize="42" />
+              </FlexRow>
+              <Text text="推荐使用微信线支付方式,方便快捷,立即生效" fontConfig="lightGoldTitle" :fontSize="30" />
+            </FlexCol>
+            <FrameButton primary text="选择" @click="handleDirectPay(1)" />
+          </BoxMid>
+          <BoxMid
+            v-if="false"
+            direction="row"
+            justify="space-between"
+            align="center"
+            gap="gap.md"
+          >
+            <FlexCol width="74%">
+              <Text text="对公打款" fontConfig="lightImportantTitle" :fontSize="42" />
+              <Text text="对公打款,需要提供对公账户信息。适用于村社政府做出贡献" fontConfig="lightGoldTitle" :fontSize="30" />
+            </FlexCol>
+            <FrameButton primary text="选择" @click="handlePublicPay" />
+          </BoxMid>
+        </template>
+      </BuyFruitInfo>
     </FlexCol>
 
     <DirectPayDialog
@@ -79,8 +71,8 @@ import FlexRow from '@/components/layout/FlexRow.vue';
 import Button from '@/components/basic/Button.vue';
 import Image from '@/components/basic/Image.vue';
 import DirectPayDialog from './dialogs/DirectPayDialog.vue';
+import BuyFruitInfo from './components/BuyFruitInfo.vue';
 import TreeApi from '@/api/light/TreeApi';
-import Touchable from '@/components/feedback/Touchable.vue';
 import Icon from '@/components/basic/Icon.vue';
 
 const { requireLoginAsync } = useRequireLogin();
@@ -96,6 +88,7 @@ function handlePaySuccess() {
 const { querys } = useLoadQuerys({
   villageId: 0,
   upgradePackageId: 0,
+  upgradePackagePrice: 0,
 }, () => {
 
 });

+ 8 - 3
src/pages/user/index.vue

@@ -47,7 +47,8 @@
     >
       <FlexRow :flex="1" :gap="10" center>
         <Text>我的乡源果: </Text>
-        <Text fontConfig="lightGoldTitle">{{ volunteerInfoLoader.content.value?.fruit || 0 }}</Text>
+        <Text fontConfig="lightGoldTitle">{{ userInfo?.fruit || 0 }}</Text>
+        <FrameButton size="small" text="充值" @click="navTo('/pages/home/village/bless/recharge')" />
       </FlexRow>
       <FlexRow :flex="1" :gap="10" center>
         <Touchable direction="row" align="center" :gap="10" @click="navTo('/pages/dig/about/point')">
@@ -80,7 +81,7 @@
 </template>
 
 <script setup lang="ts">
-import { computed } from 'vue';
+import { computed, onMounted } from 'vue';
 import { navTo } from '@/components/utils/PageAction';
 import { confirm } from '@/components/dialog/CommonRoot';
 import { useAuthStore } from '@/store/auth';
@@ -105,6 +106,7 @@ import ProvideVar from '@/components/theme/ProvideVar.vue';
 import Grid from '@/components/layout/grid/Grid.vue';
 import GridItem from '@/components/layout/grid/GridItem.vue';
 import BoxMid from '@/common/components/box/BoxMid.vue';
+import FrameButton from '@/common/components/FrameButton.vue';
 
 const UserHead = 'https://mncdn.wenlvti.net/app_static/xiangyuan/images/user/avatar.png';
 
@@ -138,5 +140,8 @@ function goStore() {
   }), '登录后查看我的哦');
 }
 
-
+onMounted(() => {
+  if (authStore.isLogged)
+    authStore.refreshUserInfo();
+});
 </script>

+ 3 - 2
src/store/auth.ts

@@ -122,8 +122,9 @@ export const useAuthStore = defineStore('auth', {
         loginType: this.loginType,
       }
     },
-    refreshUserInfo() {
-      //TODO: 刷新用户信息
+    async refreshUserInfo() {
+      this.userInfo = await UserApi.getUserInfo();
+      this.saveLoginState();
     },
   },
   getters: {