Bladeren bron

commit conflicts

abpcoder 1 maand geleden
bovenliggende
commit
d2f24510c1
50 gewijzigde bestanden met toevoegingen van 879 en 103 verwijderingen
  1. 16 0
      src/api/modules/pay.ts
  2. 10 2
      src/api/modules/user.ts
  3. 8 0
      src/common/routes.ts
  4. 19 10
      src/components/ie-button/ie-button.vue
  5. 11 4
      src/components/ie-page/components/vip-popup.vue
  6. 0 1
      src/components/ie-page/ie-page.vue
  7. 27 0
      src/hooks/useEnv.ts
  8. 239 0
      src/hooks/usePay.ts
  9. 33 10
      src/hooks/useTransferPage.ts
  10. 37 1
      src/pages.json
  11. 1 1
      src/pagesMain/pages/index/components/index-guide.vue
  12. 1 1
      src/pagesMain/pages/index/components/index-news.vue
  13. 10 12
      src/pagesMain/pages/index/index.vue
  14. 7 9
      src/pagesMain/pages/me/components/me-info.vue
  15. 1 1
      src/pagesMain/pages/volunteer/volunteer.vue
  16. 1 1
      src/pagesOther/pages/skill/index/index.vue
  17. 31 24
      src/pagesOther/pages/test-center/holland/holland.vue
  18. 32 0
      src/pagesOther/pages/test-center/holland/result.vue
  19. 13 0
      src/pagesOther/pages/test-center/holland/test.vue
  20. 70 0
      src/pagesOther/pages/test-center/index/index.vue
  21. 51 11
      src/pagesOther/pages/test-center/mbti/mbti.vue
  22. 13 0
      src/pagesOther/pages/test-center/mbti/result.vue
  23. 13 0
      src/pagesOther/pages/test-center/mbti/test.vue
  24. BIN
      src/pagesOther/static/image/holland-test.png
  25. BIN
      src/pagesOther/static/image/holland.png
  26. BIN
      src/pagesOther/static/image/mbti.png
  27. 0 0
      src/pagesStudy/components/mp-html/README.md
  28. 0 0
      src/pagesStudy/components/mp-html/changelog.md
  29. 0 0
      src/pagesStudy/components/mp-html/components/mp-html/latex/index.js
  30. 0 0
      src/pagesStudy/components/mp-html/components/mp-html/latex/katex.min.js
  31. 0 0
      src/pagesStudy/components/mp-html/components/mp-html/mp-html.vue
  32. 0 0
      src/pagesStudy/components/mp-html/components/mp-html/node/node.vue
  33. 0 0
      src/pagesStudy/components/mp-html/components/mp-html/parser.js
  34. 0 0
      src/pagesStudy/components/mp-html/package.json
  35. 0 0
      src/pagesStudy/components/mp-html/static/app-plus/mp-html/js/handler.js
  36. 0 0
      src/pagesStudy/components/mp-html/static/app-plus/mp-html/js/uni.webview.min.js
  37. 0 0
      src/pagesStudy/components/mp-html/static/app-plus/mp-html/local.html
  38. 1 0
      src/pagesStudy/components/question-book-item.vue
  39. 1 0
      src/pagesStudy/pages/exam-start/components/question-options.vue
  40. 1 0
      src/pagesStudy/pages/exam-start/components/question-parse.vue
  41. 1 0
      src/pagesStudy/pages/exam-start/components/question-title.vue
  42. 2 1
      src/pagesStudy/pages/targeted-add/targeted-add.vue
  43. 126 0
      src/pagesSystem/pages/pay/pay.vue
  44. BIN
      src/static/image/wechat-pay.png
  45. 1 2
      src/store/appStore.ts
  46. 20 0
      src/store/payStore.ts
  47. 53 9
      src/store/userStore.ts
  48. 2 2
      src/types/index.ts
  49. 26 0
      src/types/pay.ts
  50. 1 1
      src/uni_modules/uv-button/components/uv-button/uv-button.vue

+ 16 - 0
src/api/modules/pay.ts

@@ -0,0 +1,16 @@
+import type { ApiResponse, Pay } from "@/types";
+import flyio from "../flyio";
+// @ts-ignore
+import qs from 'qs';
+
+export function getEcardPrices(params: any) {
+  return flyio.get('/front/ecard/getEcardPrices', params) as Promise<ApiResponse<Pay.EcardPrice[]>>;
+}
+
+export function createOrder(params: any) {
+  return flyio.post('/front/ecard/createOrder?' + qs.stringify(params)) as Promise<ApiResponse<Pay.CreateOrderResult>>;
+}
+
+export function queryOrder(orderId: string) {
+  return flyio.get('/front/ecard/getOrderPayStatus', { orderId }) as Promise<ApiResponse<Pay.OrderPayStatus>>;
+}

+ 10 - 2
src/api/modules/user.ts

@@ -1,6 +1,6 @@
 import flyio from "../flyio";
-import { ApiCaptchaResponse, ApiResponse, DictItem, ConfigItem, ApiResponseList } from "@/types";
-import { ClassItem, ClassListQueryDTO, SchoolItem, SchoolListQueryDTO, SmsRequestDTO, CardInfo } from "@/types/user";
+import type { ApiCaptchaResponse, ApiResponse, DictItem, ConfigItem, ApiResponseList, User } from "@/types";
+import type { ClassItem, ClassListQueryDTO, SchoolItem, SchoolListQueryDTO, SmsRequestDTO, CardInfo } from "@/types/user";
 
 /**
  * 验证会员卡
@@ -32,4 +32,12 @@ export function getSchoolList(params: SchoolListQueryDTO) {
  */
 export function getClassList(params: ClassListQueryDTO) {
   return flyio.get('/front/user/getClassList', params) as Promise<ApiResponse<ClassItem[]>>;
+}
+
+/**
+ * 
+ * @returns 获取用户绑定的卡信息
+ */
+export function getUserBindCard() {
+  return flyio.get('/front/user/getUserBindCard', {}) as Promise<ApiResponse<User.CardInfo>>;
 }

+ 8 - 0
src/common/routes.ts

@@ -129,10 +129,18 @@ export const routes = {
    * holland报告
    */
   pageHolland: '/pagesOther/pages/test-center/holland/holland',
+  /**
+   * holland测评
+   */
+  pageHollandTest: '/pagesOther/pages/test-center/holland/test',
   /**
    * mbti报告
    */
   pageMbti: '/pagesOther/pages/test-center/mbti/mbti',
+  /**
+   * mbti测评
+   */
+  pageMbtiTest: '/pagesOther/pages/test-center/mbti/test',
   /**
    * 登录
    */

+ 19 - 10
src/components/ie-button/ie-button.vue

@@ -1,7 +1,8 @@
 <template>
   <button class="ie-button"
-    :class="['ie-button', `ie-button-${type}`, `ie-button-${size}`, customClass, { 'is-disabled': disabled, 'has-shadow': hasShadow }]"
+    :class="['ie-button', `ie-button-${type}`, `ie-button-${size}`, customClass, { 'is-disabled': disabled, 'is-loading': loading, 'has-shadow': hasShadow }]"
     :disabled="disabled" hover-class="button-hover" :style="getStyle" @click="handleClick">
+    <uv-loading-icon v-if="loading" color="#FFFFFF" size="16"></uv-loading-icon>
     <slot></slot>
   </button>
 </template>
@@ -14,6 +15,7 @@ type Props = {
   customClass: string;
   round: number;
   shadow: boolean;
+  loading?: boolean
 }
 const props = withDefaults(defineProps<Props>(), {
   disabled: false,
@@ -21,7 +23,8 @@ const props = withDefaults(defineProps<Props>(), {
   size: 'large',
   customClass: '',
   round: 999,
-  shadow: true
+  shadow: true,
+  loading: false
 });
 const hasShadow = computed(() => {
   return props.shadow && props.type === 'primary';
@@ -33,6 +36,9 @@ const getStyle = computed(() => {
 });
 const emit = defineEmits(['click']);
 const handleClick = () => {
+  if (props.disabled || props.loading) {
+    return;
+  }
   emit('click');
 }
 </script>
@@ -41,7 +47,7 @@ const handleClick = () => {
   height: fit-content !important;
   line-height: 1;
   font-weight: 800;
-  @apply relative text-center;
+  @apply relative text-center flex items-center justify-center gap-10;
 }
 
 .has-shadow {
@@ -91,14 +97,17 @@ const handleClick = () => {
   box-shadow: none;
 }
 
-.button-hover {
-  // background: linear-gradient(to right, #2a8dde, #007ae5);
+.is-loading {
   @apply opacity-80;
 }
 
-.is-disabled:hover {
-  background: #f7f7f7;
-  color: rgba(0, 0, 0, 0.3);
-  box-shadow: none;
+.button-hover:not(.is-disabled):not(.is-loading) {
+  // background: linear-gradient(to right, #2a8dde, #007ae5);
+  @apply opacity-80;
 }
-</style>
+
+// .is-disabled:hover {
+//   background: #f7f7f7;
+//   color: rgba(0, 0, 0, 0.3);
+//   box-shadow: none;
+// }</style>

+ 11 - 4
src/components/ie-page/components/vip-popup.vue

@@ -8,7 +8,7 @@
           <text>权限</text>
         </view>
         <view class="mt-10 text-32 text-fore-light">开通会员,立即畅享专属权益与服务</view>
-        <!-- <ie-button custom-class="mt-60" @click="handleBuy">升级VIP权限</ie-button> -->
+        <ie-button custom-class="mt-60" @click="handleBuy">升级VIP权限</ie-button>
         <ie-button type="info" custom-class="mt-40" @click="handleActivate">已线下购买,去激活</ie-button>
       </view>
       <ie-image :is-oss="true" src="/study-bg14.png" custom-class="absolute bottom-0 left-0 w-full h-full z-0"
@@ -29,12 +29,19 @@ const close = () => {
 }
 const handleBuy = () => {
   close();
+  setTimeout(() => {
+    transferTo('/pagesSystem/pages/pay/pay', {
+      data: {}
+    });
+  }, 0);
 }
 const handleActivate = () => {
   close();
-  transferTo('/pagesSystem/pages/card-verify/card-verify', {
-    data: {}
-  });
+  setTimeout(() => {
+    transferTo('/pagesSystem/pages/card-verify/card-verify', {
+      data: {}
+    });
+  }, 0);
 }
 defineExpose({
   open,

+ 0 - 1
src/components/ie-page/ie-page.vue

@@ -47,7 +47,6 @@ const vipPopupRef = ref<InstanceType<typeof VipPopup>>();
 
 // 添加安全检查
 const showVipPopup = () => {
-  console.log('showVipPopup called, vipPopupRef.value:', vipPopupRef.value);
   if (vipPopupRef.value) {
     vipPopupRef.value.open();
   } else {

+ 27 - 0
src/hooks/useEnv.ts

@@ -0,0 +1,27 @@
+import { useAppStore } from "@/store/appStore";
+
+export const useEnv = () => {
+  const { sysInfo } = useAppStore();
+  const { uniPlatform } = sysInfo;
+  const platform = computed(() => {
+    if (uniPlatform === 'mp-weixin') {
+      return 'mp';
+    } else if (uniPlatform === 'web') {
+      return window?.platform || 'h5';
+    }
+  });
+  const isH5 = computed(() => platform.value === "h5");
+  const isWap2App = computed(() => platform.value === "wap2app");
+  const isMP = computed(() => platform.value === "mp");
+  const isAndroid = computed(() => sysInfo.platform.toLowerCase() == 'android')
+  const isIOS = computed(() => sysInfo.platform.toLowerCase() == 'ios')
+
+  return {
+    platform,
+    isH5,
+    isWap2App,
+    isMP,
+    isAndroid,
+    isIOS
+  }
+}

+ 239 - 0
src/hooks/usePay.ts

@@ -0,0 +1,239 @@
+import { createOrder, getEcardPrices, queryOrder } from "@/api/modules/pay";
+import { useEnv } from "./useEnv";
+import { usePayStore } from "@/store/payStore";
+import config from "@/config";
+import type { Pay } from "@/types";
+
+export const usePay = () => {
+  const { isH5, isWap2App, isMP } = useEnv();
+  const payStore = usePayStore();
+  const { price } = storeToRefs(payStore);
+  const ready = ref(false);
+  const loading = ref(false);
+  const outTradeNo = ref('');
+  const h5url = ref('');
+  const queryTimes = ref(10);
+  const callback = ref<Pay.OrderPayCallback>({});
+  let webview: PlusWebviewWebviewObject | HTMLIFrameElement | undefined = undefined;
+
+  const payType = computed(() => {
+    if (isH5.value || isWap2App.value) {
+      return undefined;
+    } else if (isMP.value) {
+      return 'jsapi'; // ???
+    }
+  });
+
+  const formatPrice = computed(() => {
+    return price.value / 100;
+  });
+  const getPrice = async () => {
+    const res = await getEcardPrices({});
+    const card = res.data?.at(0);
+    if (card) {
+      payStore.setPrice(card.price)
+      ready.value = true;
+    }
+  }
+  const pay = async () => {
+    loading.value = true;
+    uni.$ie.showLoading('支付中...');
+    try {
+      const { data } = await createOrder({
+        totalFee: price.value,
+        type: payType.value
+      });
+      uni.$ie.hideLoading();
+      console.log(data, 666)
+      // outTradeNo.value = data.outTradeNo;
+      // const no = outTradeNo.value.split('_')[1];
+      // if (no) {
+      //   payStore.setOrderId(Number(no));
+      // }
+      // h5url.value = data.h5url;
+      // if (outTradeNo && h5url) {
+      //   clearResultFrame()
+      //   webview = await createResultFrame(h5url.value);
+      // } else {
+      //   throw new Error('支付地址缺失');
+      // }
+    } catch (error) {
+      loading.value = false;
+      uni.$ie.showToast('下单失败,请稍后再试');
+    }
+  }
+
+  const queryPayStatus = () => {
+    if (payStore.orderId) {
+      return queryOrderStatus(payStore.orderId + '')
+    }
+    return null;
+  }
+
+  const queryOrderStatus = async (orderId: string) => {
+    try {
+      const { data } = await queryOrder(orderId);
+      if (!data) {
+        return null;
+      }
+      return data;
+    } catch (error) {
+      return null;
+    }
+  }
+  const checkOrderStatus = async () => {
+    const no = payStore.orderId;
+    if (no == null) {
+      return;
+    }
+    let queryCount = 0; // 当前查询次数
+    const queryInterval = 3000; // 查询间隔(毫秒)
+    let timerId: ReturnType<typeof setTimeout> | null = null;
+
+    /**
+     * 停止查询
+     */
+    const stopQuery = () => {
+      if (timerId) {
+        clearTimeout(timerId);
+        timerId = null;
+        clearResultFrame();
+      }
+      loading.value = false;
+      uni.$ie.hideLoading();
+    };
+
+    /**
+     * 执行查询
+     */
+    const query = async () => {
+      try {
+        queryCount++;
+        const data = await queryOrderStatus(no + '');
+        console.log(`checkOrderStatus: 第 ${queryCount} 次查询订单状态`);
+        console.log(JSON.stringify(data))
+        if (!data) {
+          console.error('checkOrderStatus: 查询结果为空');
+          stopQuery();
+          callback.value.onFailed?.();
+          return;
+        }
+
+        // 支付成功
+        if (data.isPaySuccess) {
+          console.log('checkOrderStatus: 支付成功');
+          stopQuery();
+          callback.value.onSuccess?.();
+          return;
+        }
+
+        // 支付失败
+        if (data.isPayFailed) {
+          console.log('checkOrderStatus: 支付失败');
+          stopQuery();
+          callback.value.onFailed?.();
+          return;
+        }
+
+        // 未支付状态
+        if (data.isUnPaid) {
+          // 如果已达到最大查询次数,回调失败
+          if (queryCount >= queryTimes.value) {
+            console.log(`checkOrderStatus: 已查询 ${queryTimes.value} 次,仍未支付,回调失败`);
+            stopQuery();
+            callback.value.onFailed?.();
+            return;
+          }
+
+          // 继续查询
+          timerId = setTimeout(() => {
+            query();
+          }, queryInterval);
+        } else {
+          // 未知状态,回调失败
+          console.error('checkOrderStatus: 未知的支付状态', data);
+          stopQuery();
+          callback.value.onFailed?.();
+        }
+      } catch (error) {
+        // 查询过程中发生错误,回调失败
+        console.error('checkOrderStatus: 查询订单状态失败', error);
+        stopQuery();
+        callback.value.onFailed?.();
+      }
+    };
+
+    // 开始第一次查询
+    uni.$ie.showLoading('支付查询中...')
+    query();
+  }
+
+  const createResultFrame = async (url: string) => {
+    if (isH5.value) {
+      await sleep()
+      const iframe = document.createElement('iframe');
+      const style = `width:0px;height:0px;position: absolute;opacity: 0;z-index: -1;outline:none;border:none;`;
+      iframe.setAttribute('style', style);
+      iframe.setAttribute('sandbox', 'allow-scripts allow-top-navigation allow-same-origin');
+      iframe.style.position = 'absolute';
+      iframe.src = url;
+      iframe.onload = () => checkOrderStatus()
+      iframe.onerror = () => console.error('Payment: iframe加载失败')
+      document.body.appendChild(iframe);
+      return iframe
+    } else if (isWap2App.value) {
+      const style: PlusWebviewWebviewStyles = {
+        webviewBGTransparent: true,
+        opacity: 0,
+        render: 'always',
+        zindex: -1,
+        width: '1px',
+        height: '1px',
+        top: '0px',
+        left: '0px',
+        additionalHttpHeaders: {
+          Referer: config.paySiteUrl,
+        }
+      };
+      const wv = plus.webview.create(url, 'wechatPayWebview', style);
+      wv.addEventListener('loaded', (e) => {
+        checkOrderStatus();
+      }, false);
+      wv.show();
+      return wv;
+    }
+  }
+
+  const clearResultFrame = () => {
+    const { isH5, isWap2App } = useEnv();
+    if (webview) {
+      if (isH5.value) {
+        document.body.removeChild(webview as HTMLIFrameElement)
+      } else if (isWap2App.value) {
+        (webview as PlusWebviewWebviewObject).close();
+      }
+      webview = undefined;
+    }
+  }
+
+  const sleep = (time: number = 300) => {
+    return new Promise(resolve => {
+      setTimeout(resolve, time);
+    });
+  }
+
+  const setCallback = (cb: Pay.OrderPayCallback) => {
+    callback.value = cb;
+  }
+
+  return {
+    getPrice,
+    price,
+    formatPrice,
+    ready,
+    pay,
+    loading,
+    setCallback,
+    queryPayStatus
+  }
+}

+ 33 - 10
src/hooks/useTransferPage.ts

@@ -1,5 +1,5 @@
 import { Transfer } from "@/types";
-import { onLoad } from "@dcloudio/uni-app";
+import { onLoad, onUnload } from "@dcloudio/uni-app";
 import { routes } from "@/common/routes";
 export const useTransferPage = <T1 = any, T2 = any>() => {
   const funcMap = {
@@ -12,6 +12,7 @@ export const useTransferPage = <T1 = any, T2 = any>() => {
   let prevData = ref<T1>({} as T1);
   let transferResolver: ((value: unknown) => void) | null = null;
   let eventChannel: UniApp.EventChannel | null = null;
+  const eventId = ref<string | null>(null);
   // onMounted(() => {
   // 使用instance获取getOpenerEventChannel在非页面级组件中会报错
   // const instance = getCurrentInstance().proxy
@@ -24,13 +25,18 @@ export const useTransferPage = <T1 = any, T2 = any>() => {
     data?: T2 | null,
     type?: Transfer.TransferType,
     bigData?: Record<string, any> | null,
-    callback?: Callback
+    callback?: Callback,
+    crossTrigger?: boolean // 当出现A navTo C redirectTo B又需要把参数回传给 A 时,可设置此参数为 true 实现跨页面传递
   }
 
-  onLoad((opt) => {
+  const getEventChannel = () => {
     const pages = getCurrentPages();
     const page = pages[pages.length - 1];
-    eventChannel = (page as any).getOpenerEventChannel();
+    return (page as any).getOpenerEventChannel();
+  }
+
+  onLoad((opt) => {
+    eventChannel = getEventChannel();
     emit('onPageOpen');
     for (const key in opt) {
       if (opt.hasOwnProperty(key) && !['data', 'storageKey'].includes(key)) {
@@ -52,7 +58,9 @@ export const useTransferPage = <T1 = any, T2 = any>() => {
       Object.assign(prevData.value, storageData);
     }
   });
-
+  onUnload(() => {
+    eventId.value = null;
+  });
   function getTransferUrl(url: string, data: any, bigData: any) {
     const urlParams = {
       data: data ? encodeURIComponent(JSON.stringify(data)) : null,
@@ -89,7 +97,7 @@ export const useTransferPage = <T1 = any, T2 = any>() => {
     return queryString ? `${baseUrl}?${queryString}` : baseUrl;
   }
 
-  function transferTo(url: string, { data = null, type = 'navigateTo', bigData = null, callback = null }: TransferOptions = {}) {
+  function transferTo(url: string, { data = null, type = 'navigateTo', bigData = null, callback = null, crossTrigger = false }: TransferOptions = {}) {
     return new Promise((resolve, reject) => {
       transferResolver = resolve;
       if (bigData) {
@@ -98,9 +106,16 @@ export const useTransferPage = <T1 = any, T2 = any>() => {
         // clear big data by force
         uni.removeStorageSync('transferBigData');
       }
-      const nextUrl = getTransferUrl(url, data, bigData);
+      let nextUrl = getTransferUrl(url, data, bigData);
       // 单独处理,函数签名不一样
       if (type === 'navigateTo') {
+        if (crossTrigger) {
+          eventId.value = Date.now().toString();
+          uni.$once(`${eventId.value}_TransferEnd`, (data: any) => {
+            transferResolver?.(data);
+          });
+          nextUrl += `&crossPageEventId=${eventId.value}`;
+        }
         uni.navigateTo({
           url: nextUrl,
           events: {
@@ -113,12 +128,16 @@ export const useTransferPage = <T1 = any, T2 = any>() => {
               }
             }
           },
-          fail: (err) => { 
+          fail: (err) => {
+            eventId.value = null;
             console.log('transferTo fail', err);
           }
         });
       } else {
         const routeFunc = funcMap[type as keyof typeof funcMap];
+        if (prevData.value.crossPageEventId) {
+          nextUrl += `&crossPageEventId=${prevData.value.crossPageEventId}`;
+        }
         routeFunc({
           url: nextUrl,
           fail: (err) => {
@@ -131,8 +150,11 @@ export const useTransferPage = <T1 = any, T2 = any>() => {
 
     });
   }
-  function transferBack(data:any = null, delta = 1) {
+  function transferBack(data: any = null, delta = 1) {
     emit('transferEnd', data);
+    if (prevData.value.crossPageEventId) {
+      uni.$emit(`${prevData.value.crossPageEventId}_TransferEnd`, data);
+    }
     // force clear big data
     uni.removeStorageSync('transferBigData');
     const pages = getCurrentPages();
@@ -169,6 +191,7 @@ export const useTransferPage = <T1 = any, T2 = any>() => {
     prevData,
     transferTo,
     transferBack,
-    routes
+    routes,
+    eventChannel
   }
 };

+ 37 - 1
src/pages.json

@@ -154,7 +154,7 @@
           }
         },
         {
-          "path": "pages/test-center/holland/holland",
+          "path": "pages/test-center/holland/result",
           "style": {
             "navigationBarTitleText": ""
           }
@@ -164,6 +164,36 @@
           "style": {
             "navigationBarTitleText": ""
           }
+        },
+        {
+          "path": "pages/test-center/index/index",
+          "style": {
+            "navigationBarTitleText": ""
+          }
+        },
+        {
+          "path": "pages/test-center/holland/test",
+          "style": {
+            "navigationBarTitleText": ""
+          }
+        },
+        {
+          "path": "pages/test-center/holland/holland",
+          "style": {
+            "navigationBarTitleText": ""
+          }
+        },
+        {
+          "path": "pages/test-center/mbti/test",
+          "style": {
+            "navigationBarTitleText": ""
+          }
+        },
+        {
+          "path": "pages/test-center/mbti/result",
+          "style": {
+            "navigationBarTitleText": ""
+          }
         }
       ]
     },
@@ -248,6 +278,12 @@
           "style": {
             "navigationBarTitleText": ""
           }
+        },
+        {
+          "path": "pages/pay/pay",
+          "style": {
+            "navigationBarTitleText": ""
+          }
         }
       ]
     },

+ 1 - 1
src/pagesMain/pages/index/components/index-guide.vue

@@ -70,7 +70,7 @@ const loadData = () => {
     newsList.value = res.rows;
   });
 }
-onShow(() => {
+onLoad(() => {
   loadData();
 });
 </script>

+ 1 - 1
src/pagesMain/pages/index/components/index-news.vue

@@ -29,7 +29,7 @@ const loadData = async () => {
   newsList.value = rows;
 }
 
-onShow(() => {
+onLoad(() => {
   loadData();
 });
 </script>

+ 10 - 12
src/pagesMain/pages/index/index.vue

@@ -73,8 +73,9 @@ const checkProvinceInfo = () => {
     popupRef.value.open();
   }
 }
-const checkTeacherInfo = async () => {
-  await userStore.checkInfoComplete();
+const checkInfo = async () => {
+  await userStore.checkInfoTeacherComplete();
+  await userStore.checkInfoStudentComplete();
 }
 const reloadUserInfo = async () => {
   if (userStore.isLogin) {
@@ -95,6 +96,13 @@ onPageScroll((e) => {
     scrollTop.value = e.scrollTop;
   }
 });
+onMounted(() => {
+  checkProvinceInfo();
+  if (userStore.isLogin) {
+    checkInfo();
+    userStore.getDirectedSchoolList();
+  }
+});
 onShow(() => {
   setTimeout(() => {
     uni.pageScrollTo({
@@ -102,18 +110,8 @@ onShow(() => {
       duration: 0
     });
   }, 0);
-  setTimeout(() => {
-    checkProvinceInfo();
-    checkTeacherInfo();
-    reloadUserInfo();
-  }, 500);
   isHide.value = false;
 });
-onLoad(() => {
-  if (userStore.isLogin) {
-    userStore.getDirectedSchoolList();
-  }
-});
 </script>
 
 <style lang="scss" scoped></style>

+ 7 - 9
src/pagesMain/pages/me/components/me-info.vue

@@ -48,6 +48,7 @@
 <script lang="ts" setup>
 import { useUserStore } from '@/store/userStore';
 import { useTransferPage } from '@/hooks/useTransferPage';
+import { OPEN_VIP_POPUP } from '@/types/injectionSymbols';
 
 const userStore = useUserStore();
 const { transferTo, routes } = useTransferPage();
@@ -59,6 +60,8 @@ const isStudent = computed(() => userStore.isStudent);
 const isLogin = computed(() => userStore.isLogin);
 const isExperienceVip = computed(() => userStore.isExperienceVip);
 const vipInfo = computed(() => userStore.vipInfo);
+const openVipPopup = inject(OPEN_VIP_POPUP);
+
 const roleDesc = computed(() => {
   if (isLogin.value && !isVip && !userStore.isStudent) {
     return '普通会员';
@@ -78,17 +81,12 @@ const roleDesc = computed(() => {
   return '普通会员';
 });
 
-const handleHeaderClick = async () => {
-  // 不询问直接跳转登录
-  const isLogin = await userStore.checkLogin({ askToLogin: false });
-  if (isLogin) {
-    setTimeout(async () => {
-      await userStore.checkInfoComplete();
-    }, 500)
-  }
+const handleHeaderClick = () => {
+  userStore.checkLogin({ askToLogin: false });
 }
 const handleVip = () => {
-  transferTo('/pagesSystem/pages/card-verify/card-verify')
+  // transferTo('/pagesSystem/pages/card-verify/card-verify')
+  openVipPopup?.();
 }
 
 const handleSettingClick = async () => {

+ 1 - 1
src/pagesMain/pages/volunteer/volunteer.vue

@@ -19,7 +19,7 @@
     <volunteer-menu />
     <ie-gap />
     <volunteer-policy />
-    <volunteer-tier v-if="userStore.isHN" />
+    <volunteer-tier v-if="userStore.isLogin && userStore.isHN" />
     <template #tabbar>
       <ie-tabbar :active="1" />
     </template>

+ 1 - 1
src/pagesOther/pages/skill/index/index.vue

@@ -62,7 +62,7 @@ const handleSelect = async () => {
         selectedMajorId: target.value?.majorId,
         useRedirect: true
     }
-    const picked = await transferTo(routes.universityPicker, {data: option})
+    const picked = await transferTo(routes.universityPicker, {data: option, crossTrigger: true})
     if (!picked) return
     target.value = picked as SelectedUniversityMajor
     uni.$ie.showLoading()

+ 31 - 24
src/pagesOther/pages/test-center/holland/holland.vue

@@ -1,32 +1,39 @@
 <template>
-    <ie-page>
-        <ie-navbar title="霍兰德职业兴趣测评"/>
-        <interest-result :holl-info="hollandData" :series-data="chartData" />
-    </ie-page>
+  <ie-page bg-color="var(--back)">
+    <ie-navbar title="霍兰德职业兴趣测评" />
+    <view class="p-30">
+      <view class="text-28 indent-[2em]">霍兰德职业兴趣测评(self-Directed Search)是由美国职业指导专家霍兰德(John
+        Holland)根据他本人大量的职业咨询经验及其汁液类型理论编制的测评工具。他认为,个人职业兴趣特性与职业之间有一种内在的对应关系,根据系兴趣不同人格可分为以下六种,每个人的性格都是这六个维度的不同程度的组合。
+      </view>
+      <ie-image src="/pagesOther/static/image/holland-test.png" custom-class="w-full h-100 my-40" mode="heightFix" />
+      <view class="text-28 indent-[2em]">
+        职业兴趣测评,定位于高中的职业兴趣特征,从与职业和专业选择密切相关的6种兴趣类型入手,全面深入地了解个人的职业兴趣偏好,并提供应用测评结果指导生涯规划及大学的选则建议。
+      </view>
+    </view>
+    <ie-safe-toolbar :height="130" :shadow="false" bgColor="transparent">
+      <view class="px-40 py-30 flex flex-col gap-20">
+        <uv-button type="primary" shape="circle" @click="handleTest">开始测评</uv-button>
+        <uv-button type="primary" plain shape="circle" @click="handleResult">测评结果</uv-button>
+      </view>
+    </ie-safe-toolbar>
+  </ie-page>
 </template>
 
-<script setup lang="ts">
-import {useTransferPage} from "@/hooks/useTransferPage";
-import {hollDetail} from "@/api/modules/test-center";
-import {HollandEntity} from "@/types/test-center";
-import InterestResult from "@/pagesOther/pages/test-center/holland/components/interest-result.vue";
+<script lang="ts" setup>
+import { useTransferPage } from '@/hooks/useTransferPage';
+const { transferTo, routes } = useTransferPage();
 
-const {prevData} = useTransferPage()
-const hollandData = ref<HollandEntity>({} as HollandEntity)
-const chartData = ref<number[]>([])
+const handleTest = () => {
+  transferTo(routes.pageHollandTest);
+}
 
-onMounted(async () => {
-    if (prevData.value.code) {
-        const {data} = await hollDetail({code: prevData.value.code})
-        const {scorer, scorea, scorei, scores, scoree, scorec} = data
-        hollandData.value = data
-        chartData.value = [scorer, scorea, scorei, scores, scoree, scorec]
+const handleResult = () => {
+  transferTo(routes.testCenterList, {
+    data: {
+      type: 'holland'
     }
-})
-
-onPageScroll(() => {})
+  });
+}
 </script>
 
-<style lang="scss">
-
-</style>
+<style lang="scss" scoped></style>

+ 32 - 0
src/pagesOther/pages/test-center/holland/result.vue

@@ -0,0 +1,32 @@
+<template>
+    <ie-page>
+        <ie-navbar title="霍兰德职业兴趣测评"/>
+        <interest-result :holl-info="hollandData" :series-data="chartData" />
+    </ie-page>
+</template>
+
+<script setup lang="ts">
+import {useTransferPage} from "@/hooks/useTransferPage";
+import {hollDetail} from "@/api/modules/test-center";
+import {HollandEntity} from "@/types/test-center";
+import InterestResult from "@/pagesOther/pages/test-center/holland/components/interest-result.vue";
+
+const {prevData} = useTransferPage()
+const hollandData = ref<HollandEntity>({} as HollandEntity)
+const chartData = ref<number[]>([])
+
+onMounted(async () => {
+    if (prevData.value.code) {
+        const {data} = await hollDetail({code: prevData.value.code})
+        const {scorer, scorea, scorei, scores, scoree, scorec} = data
+        hollandData.value = data
+        chartData.value = [scorer, scorea, scorei, scores, scoree, scorec]
+    }
+})
+
+onPageScroll(() => {})
+</script>
+
+<style lang="scss">
+
+</style>

+ 13 - 0
src/pagesOther/pages/test-center/holland/test.vue

@@ -0,0 +1,13 @@
+<template>
+  <view>
+    
+  </view>
+</template>
+
+<script setup>
+  
+</script>
+
+<style lang="scss">
+
+</style>

+ 70 - 0
src/pagesOther/pages/test-center/index/index.vue

@@ -0,0 +1,70 @@
+<template>
+  <ie-page bg-color="var(--back)">
+    <ie-navbar title="我的测评" />
+    <view class="p-30">
+      <view v-for="item in list" :key="item.type" class="item" :style="{ backgroundColor: item.bgColor }">
+        <view class="flex items-center gap-30 p-30">
+          <ie-image :src="item.image" custom-class="w-150 h-150 flex-shrink-0" />
+          <view>
+            <view class="text-28 font-medium text-fore-title">{{ item.title }}</view>
+            <view class="mt-10 text-24 text-fore-tip">{{ item.description }}</view>
+          </view>
+        </view>
+        <view class="bg-white px-20 py-20 flex items-center justify-between">
+          <view class="flex items-center text-primary text-26 gap-6" @click="handleHistory(item.type)">
+            <uv-icon name="list-dot" size="18" color="var(--primary-color)" />
+            <text>历史记录</text>
+          </view>
+          <view class="flex items-center text-primary text-26 gap-6" @click="handleTest(item.homePage)">
+            <text>点击测评</text>
+            <uv-icon name="arrow-right" size="14" color="var(--primary-color)" />
+          </view>
+        </view>
+      </view>
+    </view>
+  </ie-page>
+</template>
+
+<script lang="ts" setup>
+import IconHolland from '@/pagesOther/static/image/holland.png';
+import IconMbti from '@/pagesOther/static/image/mbti.png';
+import { useTransferPage } from '@/hooks/useTransferPage';
+const { transferTo, routes } = useTransferPage();
+
+const list = [
+  {
+    title: '专业倾向测评',
+    description: 'Holland测评是一种基于职业兴趣的测评工具,根据被测者对六种职业兴趣类型的倾向性,提供对应的职业建议。',
+    type: 'holland',
+    image: IconHolland,
+    bgColor: '#dcedfc',
+    homePage: routes.pageHolland
+  },
+  {
+    title: '职业性格测评',
+    description: 'MBTI测评是一种基于人格类型的测评工具,根据被测者对四种人格维度的偏好程度,判断其人格类型并提供相关建议。',
+    type: 'mbti',
+    image: IconMbti,
+    bgColor: '#dbe2fb',
+    homePage: routes.pageMbti
+  }
+]
+
+const handleHistory = (type: string) => {
+  transferTo(routes.testCenterList, {
+    data: {
+      type
+    }
+  });
+}
+
+const handleTest = (homePage: string) => {
+  transferTo(homePage);
+}
+</script>
+
+<style lang="scss" scoped>
+.item+.item {
+  margin-top: 30rpx;
+}
+</style>

+ 51 - 11
src/pagesOther/pages/test-center/mbti/mbti.vue

@@ -1,20 +1,62 @@
 <template>
-    <ie-page>
-        <ie-navbar title="MBTI职业性格测评"/>
-        <character-result :code="mbtiCode" :scores="mbtiScores"/>
-    </ie-page>
+  <ie-page bg-color="var(--back)">
+    <ie-navbar title="MBTI职业性格测评" />
+    <view class="p-30">
+      <view class="text-28 indent-[2em]">MBTI(Myers-Briggs Type Indicator)是一种全球广泛应用的性格评估工具,根据荣格心理学理论,将人类性格分为16种类型。通过对
+        内外向(I/E)、感知/直觉(S/N)、思维/情感(T/F)、判断/感知(J/P) 四个维度的分析,MBTI 帮助你深刻认识自己的性格倾向和行为模式。
+      </view>
+      <view class="mt-50 mb-10 text-28 font-semibold">
+        为什么需要 MBTI 测评?
+      </view>
+      <view class="text-28 flex flex-col gap-20">
+        <view class="">
+          <text class="">发现你的天赋:</text>
+          <text class="text-fore-tip">了解自己的性格特点和潜在优势,找到适合的学习方法和职业领域。</text>
+        </view>
+        <view class="">
+          <text class="">规划职业未来:</text>
+          <text class="text-fore-tip">在面对专业选择、实习和就业时,更有方向感和信心。</text>
+        </view>
+        <view class="">
+          <text class="">提升沟通能力:</text>
+          <text class="text-fore-tip">更好地理解自己和他人的行为模式,提升人际交往与团队合作能力。</text>
+        </view>
+        <view class="">
+          <text class="">避开盲目选择:</text>
+          <text class="text-fore-tip">不再随波逐流,让你的每一步决策都符合自己的性格特点。</text>
+        </view>
+      </view>
+    </view>
+    <ie-safe-toolbar :height="130" :shadow="false" bgColor="transparent">
+      <view class="px-40 py-30 flex flex-col gap-20">
+        <uv-button type="primary" shape="circle" @click="handleTest">开始测评</uv-button>
+        <uv-button type="primary" plain shape="circle" @click="handleResult">测评结果</uv-button>
+      </view>
+    </ie-safe-toolbar>
+  </ie-page>
 </template>
 
-<script setup lang="ts">
-
+<script lang="ts" setup>
 import {mbtiDetail} from "@/api/modules/test-center";
-import {useTransferPage} from "@/hooks/useTransferPage";
+import { useTransferPage } from '@/hooks/useTransferPage';
 import CharacterResult from "@/pagesOther/pages/test-center/mbti/components/character-result.vue";
 
-const {prevData} = useTransferPage()
+const { prevData, transferTo, routes } = useTransferPage();
 const mbtiCode = ref('')
 const mbtiScores = ref<number[]>([])
 
+const handleTest = () => {
+  transferTo(routes.pageMbtiTest);
+}
+
+const handleResult = () => {
+  transferTo(routes.testCenterList, {
+    data: {
+      type: 'mbti'
+    }
+  });
+}
+
 onMounted(async () => {
     if (prevData.value.code) {
         const {data} = await mbtiDetail({code: prevData.value.code})
@@ -27,6 +69,4 @@ onMounted(async () => {
 onPageScroll(() => {})
 </script>
 
-<style lang="scss">
-
-</style>
+<style lang="scss" scoped></style>

+ 13 - 0
src/pagesOther/pages/test-center/mbti/result.vue

@@ -0,0 +1,13 @@
+<template>
+  <view>
+    
+  </view>
+</template>
+
+<script setup>
+  
+</script>
+
+<style lang="scss">
+
+</style>

+ 13 - 0
src/pagesOther/pages/test-center/mbti/test.vue

@@ -0,0 +1,13 @@
+<template>
+  <view>
+    
+  </view>
+</template>
+
+<script setup>
+  
+</script>
+
+<style lang="scss">
+
+</style>

BIN
src/pagesOther/static/image/holland-test.png


BIN
src/pagesOther/static/image/holland.png


BIN
src/pagesOther/static/image/mbti.png


+ 0 - 0
src/uni_modules/mp-html/README.md → src/pagesStudy/components/mp-html/README.md


+ 0 - 0
src/uni_modules/mp-html/changelog.md → src/pagesStudy/components/mp-html/changelog.md


+ 0 - 0
src/uni_modules/mp-html/components/mp-html/latex/index.js → src/pagesStudy/components/mp-html/components/mp-html/latex/index.js


+ 0 - 0
src/uni_modules/mp-html/components/mp-html/latex/katex.min.js → src/pagesStudy/components/mp-html/components/mp-html/latex/katex.min.js


+ 0 - 0
src/uni_modules/mp-html/components/mp-html/mp-html.vue → src/pagesStudy/components/mp-html/components/mp-html/mp-html.vue


+ 0 - 0
src/uni_modules/mp-html/components/mp-html/node/node.vue → src/pagesStudy/components/mp-html/components/mp-html/node/node.vue


+ 0 - 0
src/uni_modules/mp-html/components/mp-html/parser.js → src/pagesStudy/components/mp-html/components/mp-html/parser.js


+ 0 - 0
src/uni_modules/mp-html/package.json → src/pagesStudy/components/mp-html/package.json


+ 0 - 0
src/uni_modules/mp-html/static/app-plus/mp-html/js/handler.js → src/pagesStudy/components/mp-html/static/app-plus/mp-html/js/handler.js


+ 0 - 0
src/uni_modules/mp-html/static/app-plus/mp-html/js/uni.webview.min.js → src/pagesStudy/components/mp-html/static/app-plus/mp-html/js/uni.webview.min.js


+ 0 - 0
src/uni_modules/mp-html/static/app-plus/mp-html/local.html → src/pagesStudy/components/mp-html/static/app-plus/mp-html/local.html


+ 1 - 0
src/pagesStudy/components/question-book-item.vue

@@ -64,6 +64,7 @@ import EyeIcon from '@/pagesSystem/static/image/icon/icon-eye.png';
 import EyeOffIcon from '@/pagesSystem/static/image/icon/icon-eye-off.png';
 import { EnumQuestionType } from '@/common/enum';
 import type { Study } from '@/types';
+import MpHtml from './mp-html/components/mp-html/mp-html.vue';
 
 
 const props = defineProps({

+ 1 - 0
src/pagesStudy/pages/exam-start/components/question-options.vue

@@ -49,6 +49,7 @@ import { EnumQuestionType, EnumReviewMode } from '@/common/enum';
 import { useExam } from '@/composables/useExam';
 import { Study, Transfer } from '@/types';
 import { EXAM_DATA, EXAM_PAGE_OPTIONS, EXAM_AUTO_SUBMIT } from '@/types/injectionSymbols';
+import MpHtml from '@/pagesStudy/components/mp-html/components/mp-html/mp-html.vue';
 
 const examPageOptions = inject(EXAM_PAGE_OPTIONS) || {} as Transfer.ExamAnalysisPageOptions;
 const examData = inject(EXAM_DATA) || {} as ReturnType<typeof useExam>;

+ 1 - 0
src/pagesStudy/pages/exam-start/components/question-parse.vue

@@ -18,6 +18,7 @@ import { EnumQuestionType, EnumReviewMode } from '@/common/enum';
 import { useExam, decodeHtmlEntities } from '@/composables/useExam';
 import { Study, Transfer } from '@/types';
 import { EXAM_DATA, EXAM_PAGE_OPTIONS } from '@/types/injectionSymbols';
+import MpHtml from '@/pagesStudy/components/mp-html/components/mp-html/mp-html.vue';
 
 const examPageOptions = inject(EXAM_PAGE_OPTIONS) || {} as Transfer.ExamAnalysisPageOptions;
 const examData = inject(EXAM_DATA) || {} as ReturnType<typeof useExam>;

+ 1 - 0
src/pagesStudy/pages/exam-start/components/question-title.vue

@@ -20,6 +20,7 @@ import { EnumPaperType, EnumQuestionType } from '@/common/enum';
 import { useExam } from '@/composables/useExam';
 import { Study, Transfer } from '@/types';
 import { EXAM_DATA, EXAM_PAGE_OPTIONS } from '@/types/injectionSymbols';
+import MpHtml from '@/pagesStudy/components/mp-html/components/mp-html/mp-html.vue';
 
 const examPageOptions = inject(EXAM_PAGE_OPTIONS) || {} as Transfer.ExamAnalysisPageOptions;
 const examData = inject(EXAM_DATA) || {} as ReturnType<typeof useExam>;

+ 2 - 1
src/pagesStudy/pages/targeted-add/targeted-add.vue

@@ -188,8 +188,9 @@ const handleAdd = async () => {
             prevData.value.selectedMajorId === form.value.majorId) {
             return uni.$ie.showToast('请勿选择相同院校专业')
         }
-        // TODO: 如果该页面是从 routes.universityPicker 通过 redirectTo 跳转,则前页面接收不到eventChannel的回传事件
+        // 如果该页面是从 routes.universityPicker 通过 redirectTo 跳转,则前页面接收不到eventChannel的回传事件
         // wx日志:上级页面丢失,无法触发transferEnd
+        // 已通过 transferPage hooks 内部解决
         return transferBack(form.value)
     }
     // 检查数据是否已存在

+ 126 - 0
src/pagesSystem/pages/pay/pay.vue

@@ -0,0 +1,126 @@
+<template>
+  <ie-page bg-color="#F6F8FA">
+    <ie-navbar title="收银台" />
+    <view>
+      <view class="content">
+        <view class="money-info flex flex-col items-center justify-center">
+          <view class="">
+            <text class="unit">¥</text>
+            <text class="money">{{ formatPrice }}</text>
+          </view>
+          <view class="tip">待支付金额</view>
+        </view>
+        <view class="goods-info">
+          <view class="goods-info-item flex items-center justify-between">
+            <text class="tip">商品名称</text>
+            <text>会员卡</text>
+          </view>
+          <view class="goods-info-item flex items-center justify-between">
+            <text class="tip">商品金额</text>
+            <text>¥{{ formatPrice }}</text>
+          </view>
+        </view>
+      </view>
+      <view class="tip mt-[30px] px-[20px]">选择支付方式</view>
+      <view v-if="!isIOS" class="channel-list">
+        <uv-radio-group v-model="payType" placement="column" iconPlacement="right" shape="square" size="24"
+          iconSize="18">
+          <uv-radio :name="payType">
+            <view class="flex items-center" slot="label">
+              <image class="channel-icon" mode="widthFix" src="@/static/image/wechat-pay.png" />
+              <text>微信支付</text>
+            </view>
+          </uv-radio>
+        </uv-radio-group>
+      </view>
+      <view class="btn-wrap">
+        <ie-button type="primary" :loading="loading" @click="handlePay">{{ `确认支付 ¥${formatPrice}` }}</ie-button>
+      </view>
+    </view>
+  </ie-page>
+</template>
+<script lang="ts" setup>
+import { usePay } from '@/hooks/usePay';
+import { useUserStore } from '@/store/userStore';
+
+const userStore = useUserStore();
+const { ready, formatPrice, pay, setCallback, loading } = usePay();
+const isIOS = ref(false);
+const payType = ref('wechat');
+
+const handlePay = () => {
+  loading.value = true
+  setCallback({
+    onSuccess: () => {
+      uni.$ie.showSuccess('支付成功');
+      setTimeout(async () => {
+        userStore.checkInfoStudentComplete();
+      }, 300);
+    },
+    onFailed: () => {
+      uni.$ie.showError('支付失败');
+    }
+  });
+  pay();
+}
+</script>
+<style lang="scss" scoped>
+.content {
+  padding: 0 20px;
+}
+
+.money-info {
+  padding: 30px 0;
+}
+
+.unit {
+  font-size: 20px;
+}
+
+.money {
+  font-size: 48px;
+  font-weight: bold;
+  letter-spacing: 2px;
+}
+
+.tip {
+  color: #7c7c7c;
+  font-size: 15px;
+}
+
+.goods-info {
+  margin-bottom: 26px;
+}
+
+.goods-info-item {
+  &+& {
+    margin-top: 10px;
+  }
+}
+
+.channel-list {
+  margin: 10px 10px 0 10px;
+  border-radius: 12px;
+  background-color: #ffffff;
+  padding: 12px;
+}
+
+.u-radio+.u-radio {
+  margin-top: 10px;
+}
+
+.u-radio:last-child {
+  border-bottom: none !important;
+}
+
+.channel-icon {
+  width: 40px;
+  height: 40px;
+  margin-right: 8px;
+}
+
+.btn-wrap {
+  margin-top: 50px;
+  padding: 0 20px;
+}
+</style>

BIN
src/static/image/wechat-pay.png


+ 1 - 2
src/store/appStore.ts

@@ -15,8 +15,7 @@ export const useAppStore = defineStore('ie-app', {
       activeTabbar: 0,
       statusBarHeight: 0,
       systemInfo: null as UniApp.GetSystemInfoResult | null,
-      appConfig: [] as ConfigItem[],
-      platform: 'web'
+      appConfig: [] as ConfigItem[]
     }
   },
   getters: {

+ 20 - 0
src/store/payStore.ts

@@ -0,0 +1,20 @@
+export const usePayStore = defineStore('ie-pay', {
+  state: () => ({
+    price: 468000,
+    orderId: null as number | null
+  }),
+  actions: {
+    setPrice(price: number) {
+      this.price = price;
+    },
+    setOrderId(id: number | null) {
+      this.orderId = id;
+    }
+  },
+  persist: {
+    storage: {
+      getItem: uni.getStorageSync,
+      setItem: uni.setStorageSync,
+    }
+  }
+});

+ 53 - 9
src/store/userStore.ts

@@ -3,11 +3,13 @@ import { useTransferPage } from '@/hooks/useTransferPage';
 import { getUserInfo, logoutPhysical } from '@/api/modules/login';
 import config from '@/config';
 import type { Study, UserStoreState } from '@/types';
-import { UserInfo, VipCardInfo } from '@/types/user';
+import type { UserInfo, VipCardInfo } from '@/types/user';
 import tools from '@/utils/uni-tool';
 import defaultAvatar from '@/static/personal/avatar_default.png'
-import { CardType, EnumExamType, EnumReviewMode, EnumUserType } from '@/common/enum';
+import { CardType, EnumBindScene, EnumExamType, EnumReviewMode, EnumUserType } from '@/common/enum';
 import { getDirectedSchool, saveDirectedSchool } from '@/api/modules/study';
+import { usePay } from '@/hooks/usePay';
+import { getUserBindCard } from '@/api/modules/user';
 
 const themeColor = '#31A0FC';
 type CheckLoginOptions = {
@@ -183,7 +185,12 @@ export const useUserStore = defineStore('ie-user', {
         resolve(true);
       });
     },
-    checkInfoComplete() {
+
+    /**
+     * 检查老师是否完善信息
+     * @returns 
+     */
+    checkInfoTeacherComplete() {
       return new Promise((resolve, reject) => {
         if (this.needCompleteInfo && (!this.userInfo.location || !this.userInfo.examType || !this.userInfo.endYear)) {
           const { transferTo } = useTransferPage();
@@ -197,6 +204,41 @@ export const useUserStore = defineStore('ie-user', {
         }
       });
     },
+    /**
+     * 检查学生是否需要完善信息
+     * @returns 
+     */
+    checkInfoStudentComplete(): Promise<boolean> {
+      const { queryPayStatus } = usePay();
+      return new Promise(async (resolve, reject) => {
+        const payResult = await queryPayStatus();
+        if (payResult && payResult.isPaySuccess) {
+          const { data } = await getUserBindCard();
+          if (data && data.cardNo && data.password) {
+            const submitInfo = {
+              token: this.accessToken,
+              scene: EnumBindScene.LOGIN_BIND,
+              userInfo: this.userInfo,
+              cardInfo: data,
+              registerInfo: {
+                username: data.cardNo,
+                password: data.password,
+              }
+            };
+            const { transferTo } = useTransferPage();
+            transferTo('/pagesSystem/pages/bind-profile/bind-profile', {
+              data: submitInfo
+            }).then(res => {
+              resolve(res as boolean);
+            }).catch(() => {
+              resolve(false);
+            });
+          }
+        } else {
+          resolve(false);
+        }
+      });
+    },
     async getUserInfo() {
       const res = await getUserInfo();
       const { data, isDefaultModifyPwd, isPasswordExpired, card, org } = res;
@@ -211,6 +253,8 @@ export const useUserStore = defineStore('ie-user', {
       if (org) {
         this.org = org;
       }
+      const { getPrice } = usePay();
+      getPrice();
       return data;
     },
     /**
@@ -265,12 +309,12 @@ export const useUserStore = defineStore('ie-user', {
       this.org = {
         ...config.defaultOrg,
       };
-      // const { transferTo } = useTransferPage();
-      // setTimeout(() => {
-      //   transferTo('/pagesMain/pages/index/index', {
-      //     type: 'reLaunch'
-      //   })
-      // }, 300);
+      const { transferTo, routes } = useTransferPage();
+      setTimeout(() => {
+        transferTo(routes.pageIndex, {
+          type: 'reLaunch'
+        });
+      }, 0);
     },
     deleteAccount() {
       uni.$ie.showLoading('注销中...');

+ 2 - 2
src/types/index.ts

@@ -10,10 +10,11 @@ import * as Voluntary from "./voluntary";
 import * as Dropdown from "./dropdown";
 import * as University from "./university";
 import * as TestCenter from "./test-center";
+import * as Pay from "./pay";
 import { VipCardInfo } from "./user";
 import { EnumExamMode, EnumExamType, EnumReviewMode } from "@/common/enum";
 
-export { Study, User, News, Transfer, System, Major, Career, Tree, Voluntary, Dropdown, University, TestCenter };
+export { Study, User, News, Transfer, System, Major, Career, Tree, Voluntary, Dropdown, University, TestCenter, Pay };
 
 /// 接口响应
 export interface ApiResponse<T> {
@@ -77,7 +78,6 @@ export interface AppStoreState {
   statusBarHeight: number;
   systemInfo: UniApp.GetSystemInfoResult | null;
   appConfig: ConfigItem[];
-  platform: string;
 }
 
 export interface DictStoreState {

+ 26 - 0
src/types/pay.ts

@@ -0,0 +1,26 @@
+export interface EcardPrice {
+  id: number;
+  price: number;
+  location: string;
+  outTime: string;
+  examType: string;
+  createdAt: string;
+  updatedAt: string;
+}
+
+export interface CreateOrderResult {
+  h5url: string;
+  outTradeNo: string;
+}
+
+export interface OrderPayStatus {
+  isUnPaid: boolean;
+  isPayFailed: boolean;
+  isPaySuccess: boolean;
+}
+
+export interface OrderPayCallback {
+  onSuccess?: () => void;
+  onFailed?: () => void;
+  onUnpaid?: () => void;
+}

+ 1 - 1
src/uni_modules/uv-button/components/uv-button/uv-button.vue

@@ -341,7 +341,7 @@ $show-reset-button: 1;
 @import "./nvue.scss";
 /* #endif */
 
-$uv-button-uv-button-height: 40px !default;
+$uv-button-uv-button-height: 44px !default;
 $uv-button-text-font-size: 15px !default;
 $uv-button-loading-text-font-size: 15px !default;
 $uv-button-loading-text-margin-left: 4px !default;