Forráskód Böngészése

📦 对接升级管理员选择套餐

快乐的梦鱼 1 hónapja
szülő
commit
c9f402bc46

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

@@ -69,6 +69,7 @@ export class UserInfo extends DataModel<UserInfo> {
   avatar = '';
   username = '';
   regionId = 0;
+  points = 0;
   openId = '';
   fruit = 0;
   isReviewer = false;

+ 62 - 1
src/api/light/OfficialApi.ts

@@ -184,6 +184,46 @@ export class StaffUpgradeOrder extends DataModel<StaffUpgradeOrder> {
   updatetime = '';
 }
 
+export class StaffLevelItem extends DataModel<StaffLevelItem> {
+  constructor() {
+    super(StaffLevelItem, '管理等级套餐');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
+      price: { clientSide: 'number', serverSide: 'string' },
+      timeType: { clientSide: 'number', serverSide: 'number' },
+      days: { clientSide: 'number', serverSide: 'number' },
+      pointsLimit: { clientSide: 'number', serverSide: 'number' },
+      weigh: { clientSide: 'number', serverSide: 'number' },
+      status: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id!: number;
+  /** 套餐名称 */
+  name = '';
+  /** 价格 */
+  price = 0;
+  /** 时间类型: 1=永久, 2=限时 */
+  timeType = 0;
+  /** 天数 (限时时有效) */
+  days = 0;
+  /** 乡源果限额 */
+  pointsLimit = 0;
+  /** 权重 */
+  weigh = 0;
+  /** 状态: 1=启用 */
+  status = 0;
+  /** 时间类型文本 */
+  timeTypeText = '';
+  /** 状态文本 */
+  statusText = '';
+  /** 创建时间 */
+  createtime = '';
+  /** 更新时间 */
+  updatetime = '';
+}
+
 export interface WxPayParams {
   appId: string;
   timeStamp: string;
@@ -326,10 +366,31 @@ export class OfficialApi extends AppServerRequestModule<DataModel> {
   }
 
   /**
+   * 获取个人升级套餐列表
+   */
+  async getStaffList(page: number, pageSize: number) {
+    const res = await this.post<{
+      data: KeyValue[],
+      total: number,
+      per_page: number,
+      current_page: number,
+      last_page: number,
+    }>('/village/growth/staffList', '获取个人升级套餐列表', {
+      page,
+      pageSize,
+    });
+    const data = res.requireData();
+    return {
+      list: transformArrayDataModel<StaffLevelItem>(StaffLevelItem, transformSomeToArray(data.data), '管理等级套餐列表', true),
+      total: data.total,
+    };
+  }
+
+  /**
    * 个人升级村社管理
    * @param staffLevelId 管理等级ID: 1=村社管理, 2=测试
    */
-  async upgradeStaff(villageId: number, staffLevelId = 1, payType: 1 | 3 = 1) {
+  async upgradeStaff(villageId: number, staffLevelId = 1, payType: 1 | 2 | 3 = 1) {
     const res = await this.post<{
       order: KeyValue,
       pay: WxPayParams,

+ 12 - 23
src/pages/home/dig.vue

@@ -94,14 +94,14 @@
       direction="row"
     >
       <FlexCol :flex="1" :gap="10" center>
-        <Text fontConfig="lightGoldTitle">{{ volunteerInfoLoader.content.value?.points || 0 }}</Text>
+        <Text fontConfig="lightGoldTitle">{{ volunteerInfo?.points || 0 }}</Text>
         <Touchable direction="row" align="center" :gap="10" @click="navTo('/pages/dig/about/point')">
           <Text>文化积分</Text>
           <Icon icon="help-filling" color="primary" :size="40" />
         </Touchable>
       </FlexCol>
       <FlexCol :flex="1" :gap="10" center>
-        <Text fontConfig="lightGoldTitle">Lv.{{ volunteerInfoLoader.content.value?.level || 1 }}</Text>
+        <Text fontConfig="lightGoldTitle">Lv.{{ volunteerInfo?.level || 1 }}</Text>
         <Text>等级</Text>
       </FlexCol>
     </BoxMid>
@@ -120,6 +120,7 @@ import { navTo } from '@/components/utils/PageAction';
 import { useAuthStore } from '@/store/auth';
 import { useCollectStore } from '@/store/collect';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import { useVolunteerInfo } from './village/composeabe/VolunteerInfo';
 import { useStorageVar } from '@/components/composeabe/StorageVar';
 import { injectAppConfiguration } from '@/api/system/useAppConfiguration';
 import { confirm } from '@/components/dialog/CommonRoot';
@@ -160,28 +161,16 @@ const bannerLoader = useSimpleDataLoader(async () => {
     url: p.url,
   }));
 });
-const notVolunteerError = ref(false);
+
+const { notVolunteerError, volunteerInfo, getIsVolunteer } = useVolunteerInfo();
+
 const villageListLoader = useSimpleDataLoader(async () => await VillageApi.getClaimedVallageList(), true);
-const volunteerInfoLoader = useSimpleDataLoader(async () => {
-  try {
-    const res = await VillageApi.getVolunteerInfo()
-    notVolunteerError.value = false;
-    return res;
-  } catch (error) {
-    notVolunteerError.value = checkIsNotVolunteerError(error);
-    throw error;
-  }
-}, true);
 const rankListLoader = useSimpleDataLoader(async () => await VillageApi.getVolunteerRanklist(), true);
 
-function checkIsNotVolunteerError(e: unknown) {
-  return (e as RequestApiError).errorMessage.includes('请认领')
-}
-
 watch(() => authStore.isLogged, (newVal) => {
   if (newVal) {
     villageListLoader.reload();
-    volunteerInfoLoader.reload();
+    getIsVolunteer();
     rankListLoader.reload();
   }
 })
@@ -208,7 +197,7 @@ function checkIsVolunteer(item: VillageListItem) {
 }
 
 function clamFinish() {
-  volunteerInfoLoader.reload();
+  getIsVolunteer();
   rankListLoader.reload();
 }
 
@@ -220,8 +209,8 @@ function goSubmitDigPage(item: VillageListItem) {
     name: item.villageName,
     villageId: item.villageId,
     villageVolunteerId: item.villageVolunteerId,
-    points: volunteerInfoLoader.content.value?.points,
-    level: volunteerInfoLoader.content.value?.level,
+    points: volunteerInfo.value?.points,
+    level: volunteerInfo.value?.level,
   })
 }
 function goManagePage(item: VillageListItem) {
@@ -230,8 +219,8 @@ function goManagePage(item: VillageListItem) {
     name: item.villageName,
     villageId: item.villageId,
     villageVolunteerId: item.villageVolunteerId,
-    points: volunteerInfoLoader.content.value?.points,
-    level: volunteerInfoLoader.content.value?.level,
+    points: volunteerInfo.value?.points,
+    level: volunteerInfo.value?.level,
   })
 }
 function goMyDigPage(item: VillageListItem) {

+ 34 - 0
src/pages/home/village/composeabe/VolunteerInfo.ts

@@ -0,0 +1,34 @@
+import VillageApi, { type VolunteerInfo } from "@/api/inhert/VillageApi";
+import type { RequestApiError } from "@imengyu/imengyu-utils";
+import { onMounted, ref } from "vue";
+
+function checkIsNotVolunteerError(e: unknown) {
+  return (e as RequestApiError).errorMessage.includes('请认领')
+}
+
+export function useVolunteerInfo() {
+  const volunteerInfo = ref<null|VolunteerInfo>(null);
+  const notVolunteerError = ref(false);
+
+  const getIsVolunteer = async () => {
+    try {
+      const res = await VillageApi.getVolunteerInfo()
+      notVolunteerError.value = false;
+      volunteerInfo.value = res;
+      return res;
+    } catch (error) {
+      notVolunteerError.value = checkIsNotVolunteerError(error);
+      throw error;
+    }
+  }
+
+  onMounted(async () => {
+    await getIsVolunteer();
+  })
+
+  return {
+    notVolunteerError,
+    volunteerInfo,
+    getIsVolunteer,
+  }
+}

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

@@ -70,6 +70,7 @@
         @selectVillage="onSelectVillage" 
       /> -->
     </template>
+    <LoadingPage v-else />
     <Height :height="200" />
     <Popup 
       v-model:show="showMyFollowPopup" 
@@ -116,6 +117,7 @@ import FrameButton from '@/common/components/FrameButton.vue';
 import JoinDialog from './dialogs/JoinDialog.vue';
 import BubbleTip from '@/components/feedback/BubbleTip.vue';
 import MemoryTimeOut from '@/components/composeabe/MemoryTimeOut';
+import LoadingPage from '@/components/display/loading/LoadingPage.vue';
 
 const topTab = ref<'village' | 'around'>('village');
 const tab = ref('card');

+ 88 - 44
src/pages/home/village/upgrade/my-upgrade-management.vue

@@ -4,41 +4,60 @@
   <CommonTopBanner 
     title="升级成为管理员"
     showNav
+    :customBack="true"
+    @backPressed="handleBack"
   >
     <FlexCol gap="gap.lg" padding="padding.md">
       
-      <!-- <FlexRow justify="flex-end">
-        <Button icon="https://xy.wenlvti.net/app_static/images/home/IconOrder.png" text="我的订单" @click="handleMyOrders()" />
-      </FlexRow>
-      <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 @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>
+      <template v-if="step === 'choose'">
+        <FlexCol padding="padding.md" center>
+          <Text 
+            text="欢迎升级村社管理员,您可以为村社做出管理贡献,请选择升级时长"
+            fontConfig="contentText" :fontSize="30" textAlign="center" 
+          />
+        </FlexCol>
+
+        <BoxMid 
+          v-for="item in loader.content.value" :key="item.id"
+          direction="row" justify="space-between" align="center" gap="gap.md"
+        >
+          <FlexCol width="74%" gap="gap.md">
+            <FlexRow align="center" gap="gap.md">
+              <Text :text="item.name" fontConfig="lightImportantTitle" :fontSize="42" />
+              <Text :text="`(${item.timeTypeText})`" :fontSize="42" :color="item.timeTypeText === '永久' ? 'warning' : 'success'" />
+            </FlexRow>
+            <FlexRow align="center" gap="gap.md">
+              <Tag v-if="item.pointsLimit > 0" :text="`最低积分要求:${item.pointsLimit}`" :type="isPointEnough(item.pointsLimit) ? 'success' : 'danger'" />
+            </FlexRow>
+          </FlexCol>
+          <FlexRow align="center" gap="gap.lg">
+            <Text :text="`¥${item.price}`" fontConfig="lightGoldTitle" :fontSize="40" />
+            <FrameButton primary text="选择" @click="handleChoose(item)" />
+          </FlexRow>
+        </BoxMid>
+
+      </template>
+      <template v-else-if="step === 'pay'">
+
+        <FlexCol padding="padding.md" center>
+          <Text 
+            text="感谢您选择升级管理员,您可以为村社做出管理贡献,请选择您要付款方式" 
+            fontConfig="contentText" :fontSize="30" textAlign="center" 
+          />
+        </FlexCol>
+
+        <BuyFruitInfo :price="choosedLevel?.price || 0" @pay="handleDirectPay(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)" />
+            </BoxMid>
+          </template>
+        </BuyFruitInfo>
+      </template>
     </FlexCol>
 
     <UpgradeManagementSuccessDialog  
@@ -51,23 +70,26 @@
 <script setup lang="ts">
 import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
 import { useRequireLogin } from '@/common/composeabe/RequireLogin';
+import { useVolunteerInfo } from '../composeabe/VolunteerInfo';
+import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import { ref } from 'vue';
-import { navTo, backAndCallOnPageBack } from '@/components/utils/PageAction';
+import { back, 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 OfficialApi from '@/api/light/OfficialApi';
+import OfficialApi, { type StaffLevelItem } 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';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import Tag from '@/components/display/Tag.vue';
 
 const upgradeManagementSuccessDialog = ref<InstanceType<typeof UpgradeManagementSuccessDialog>>();
 
 const { requireLoginAsync } = useRequireLogin();
+const { volunteerInfo } = useVolunteerInfo();
 
 function handlePaySuccess() {
   setTimeout(() => {
@@ -81,22 +103,44 @@ const { querys } = useLoadQuerys({
 
 });
 
-async function handleMyOrders() {
-  if (!await requireLoginAsync('登录后查看我的升级订单哦'))
-    return;
-  navTo('/pages/home/village/upgrade/my-orders', {
-    villageId: querys.value.villageId,
-  });
+const step = ref<'choose' | 'pay'>('choose');
+const choosedLevel = ref<StaffLevelItem|null>(null);
+const loader = useSimpleDataLoader(async () => {
+  return (await OfficialApi.getStaffList(1, 10)).list;
+});
+
+function isPointEnough(pointLimit: number) {
+  return (volunteerInfo.value?.points || 0) >= pointLimit;
 }
 
-async function handleDirectPay(levelType: number, payType: 1 | 3) {
+function handleBack() {
+  if (step.value === 'pay')
+    step.value = 'choose';
+  else 
+    back();
+}
+function handleChoose(level: StaffLevelItem) {
+  console.log(level,volunteerInfo.value );
+  if (level.pointsLimit > 0 && !isPointEnough(level.pointsLimit)) {
+    showError(
+      '您的积分不足,现有 ' + (volunteerInfo.value?.points || 0) + 
+      ' 积分,无法升级。您可以先为村社做贡献(做任务、采编内容等)获取积分哦!');
+    return;
+  }
+
+  step.value = 'pay';
+  choosedLevel.value = level;
+}
+async function handleDirectPay(payType: 1 | 3) {
   if (!requireLoginAsync('登录后为村社升级,做出你的贡献哦'))
     return;
+  if (!choosedLevel.value)
+    throw new Error('请选择升级等级');
   try {
     uni.showLoading({ title: '创建订单中...' });
     const { order: orderInfo, pay: payInfo } = await OfficialApi.upgradeStaff(
       querys.value.villageId,
-      levelType,
+      choosedLevel.value.id,
       payType,
     );
     if (payType === 1) {

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

@@ -59,7 +59,7 @@
           <Touchable direction="row" align="center" :gap="10" @click="navTo('/pages/dig/about/point')">
             <Icon icon="help-filling" color="primary" :size="40" />
           </Touchable>
-          <Text fontConfig="lightGoldTitle">{{ volunteerInfoLoader.content.value?.points || 0 }}</Text>
+          <Text fontConfig="lightGoldTitle">{{ volunteerInfo?.points || 0 }}</Text>
         </FlexRow>
         <Text fontConfig="subText">文化积分</Text>
       </FlexCol>
@@ -71,7 +71,7 @@
     }">
       <CellGroup round>
         <Cell v-if="userInfo" icon="https://xy.wenlvti.net/app_static/images/mine/IconMyArticle.png" title="我的投稿" showArrow touchable @click="navTo('/pages/dig/forms/submits', { 
-          villageVolunteerId: volunteerInfoLoader.content.value?.id || 0 
+          villageVolunteerId: volunteerInfo?.id || 0 
         })" />
         <Cell icon="https://xy.wenlvti.net/app_static/images/mine/IconMyRecord.png" title="福泽记录" showArrow touchable @click="requireLogin(() => navTo('/pages/home/village/bless/my-orders'), '登录后查看我的福泽记录哦')" />
         <Cell icon="https://xy.wenlvti.net/app_static/images/mine/IconMyReward.png" title="兑换记录" showArrow touchable @click="goStoreList" />
@@ -94,6 +94,7 @@ import { useAuthStore } from '@/store/auth';
 import { useRequireLogin } from '@/common/composeabe/RequireLogin';
 import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
 import { useWebPassToken } from '../article/web/webPassToken';
+import { useVolunteerInfo } from '../home/village/composeabe/VolunteerInfo';
 import CellGroup from '@/components/basic/CellGroup.vue';
 import Cell from '@/components/basic/Cell.vue';
 import Image from '@/components/basic/Image.vue';
@@ -118,7 +119,7 @@ const UserHead = 'https://mncdn.wenlvti.net/app_static/xiangyuan/images/user/ava
 
 const authStore = useAuthStore();
 const userInfo = computed(() => authStore.isLogged ? authStore.userInfo : null);
-const volunteerInfoLoader = useSimpleDataLoader(async () => await VillageApi.getVolunteerInfo(), true);
+const { volunteerInfo } = useVolunteerInfo();
 const { requireLogin } = useRequireLogin();
 const { finalUrl: storeOrderFinalUrl } = useWebPassToken('https://xycdn.wenlvti.net/assets/addons/yunexamine/h5/', '/pages/gift/order');
 const { finalUrl: storeFinalUrl } = useWebPassToken('https://xycdn.wenlvti.net/assets/addons/yunexamine/h5/', '/pages/gift/index');