ソースを参照

feat: Apple登录接口联调

victor.zhou 4 ヶ月 前
コミット
0404719233

+ 2 - 0
README.md

@@ -7,6 +7,8 @@ https://www.yuque.com/kolo7/ly4p08/uuwa89hedxi4qtds?singleDoc# 《google Pay》
 包名:com.ipeaking.recipemuse
 
 
+[iOS内购(IAP)自动续订订阅类型总结](https://juejin.cn/post/6844903924193853453)
+
 
 ```bash
 # 克隆项目

+ 4 - 9
src/hooks/useLogin.ts

@@ -1,28 +1,23 @@
 import { appleLogin, googleLogin } from "@/api";
 import { setToken } from "@/utils/auth";
+import nativeHelper, { eventTypeEnum } from "@/utils/nativeHelper";
 
 // 触发原生登录的函数
 export function triggerNativeLogin() {
   // 在 React Native 应用中使用 react-native-webview 的 WebView 组件时,该库会自动在 WebView 加载的网页中注入 window.ReactNativeWebView 对象
   // @ts-ignore
-  if (window.ReactNativeWebView) {
-    // 向原生应用发送登录请求
-    // @ts-ignore
-    console.log('h5: 发送登录请求给原生')
-    window.ReactNativeWebView.postMessage('NATIVE_LOGIN_REQUEST');
-  } else {
-    console.warn('不在 React Native 环境中');
-  }
+  nativeHelper.postMessage({ type: eventTypeEnum.NATIVE_LOGIN_REQUEST });
 }
 
 
 
 // 示例:在按钮点击时触发登录
 // document.getElementById('loginButton').addEventListener('click', triggerNativeLogin);
-export function appleLoginHandler(data) {
+export function appleLoginHandler(data: any) {
   const params = {
     code: data.authorizationCode,
     id_token: data.identityToken,
+    user: data.user,
   };
   // console.log('appleLoginHandler params', params);
   return appleLogin(params)

+ 5 - 1
src/main.ts

@@ -11,6 +11,8 @@ import HowToCook from './views/HowToCook.vue'
 import SauceDesign from './views/SauceDesign.vue'
 import FortuneCooking from './views/FortuneCooking.vue'
 import SettingsDemo from './views/SettingsDemo.vue'
+import Profile from './views/Profile.vue'
+import ManageSubscription from './views/ManageSubscription.vue'
 import { autoRefreshEnvSettings } from './utils/envWatcher'
 import './style.css'
 
@@ -29,7 +31,9 @@ const routes = [
     { path: '/how-to-cook', component: HowToCook },
     { path: '/sauce-design', component: SauceDesign },
     { path: '/fortune-cooking', component: FortuneCooking },
-    { path: '/settings-demo', component: SettingsDemo }
+    { path: '/settings-demo', component: SettingsDemo },
+    { path: '/profile', component: Profile },
+    { path: '/manage-subscription', component: ManageSubscription }
 ]
 
 const router = createRouter({

+ 26 - 0
src/utils/nativeHelper.ts

@@ -0,0 +1,26 @@
+export enum eventTypeEnum {
+  // 发起原生登录
+  'NATIVE_LOGIN_REQUEST' = 'NATIVE_LOGIN_REQUEST',
+  'H5_LOGIN_SUCCESS_IOS' = 'H5_LOGIN_SUCCESS_IOS',
+  'H5_LOGIN_SUCCESS_ANDROID' = 'H5_LOGIN_SUCCESS_ANDROID'
+}
+
+interface IMessageData {
+  type: eventTypeEnum
+  data?: any
+  message?: string,
+}
+
+const nativeHelper = {
+  postMessage: (data: IMessageData) => {
+    if (window.ReactNativeWebView) {
+      // 向原生应用发送登录请求
+      // @ts-ignore
+      window.ReactNativeWebView.postMessage(JSON.stringify(data));
+    } else {
+      console.warn('不在 React Native 环境中');
+    }
+  }
+}
+
+export default nativeHelper

+ 9 - 6
src/views/Home.vue

@@ -634,6 +634,7 @@ import { showToast } from 'vant'
 import { getExpiresIn, setExpiresIn, setToken, setUserInfo } from '@/utils/auth'
 import { setOptions, destroy as idleTimerDestroy } from '@/utils/idleTimer'
 import { getProfile, refreshToken } from '@/api'
+import { eventTypeEnum } from '@/utils/nativeHelper'
 
 const { t } = useI18n()
 
@@ -722,9 +723,9 @@ const tastePresets = [
 const eventHandler = (event: any) => {
     try {
       const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
-      
+      debugger
       const type = data?.type
-      if (['LOGIN_SUCCESS_ANDROID', 'LOGIN_SUCCESS_IOS'].includes(type)) {
+      if ([eventTypeEnum.H5_LOGIN_SUCCESS_IOS, eventTypeEnum.H5_LOGIN_SUCCESS_ANDROID].includes(type)) {
         loginHandler(data)
       }
       
@@ -737,15 +738,17 @@ let idleTimer: any
 const loginHandler = (data: any) => {
     const type = data?.type
     console.log('原生登录成功, h5收到请求数', data.data);
+    debugger
     let api
-    if (type === 'LOGIN_SUCCESS_ANDROID') {
-      
+    if (type === eventTypeEnum.H5_LOGIN_SUCCESS_ANDROID) {
       api = androidLoginHandler
-      } else if (type === 'LOGIN_SUCCESS_IOS') {
+    } else if (type === eventTypeEnum.H5_LOGIN_SUCCESS_IOS) {
       api = appleLoginHandler
     }
     if (api) {
-      api(data.data).then(res => getTokenHandler(res, true)).then()
+      api(data.data).then(res => getTokenHandler(res, true)).catch(err => {
+        console.error('登录失败:', err)
+      })
     }
 }
 

+ 145 - 0
src/views/ManageSubscription.vue

@@ -0,0 +1,145 @@
+<template>
+    <div class="min-h-screen bg-yellow-400 px-2 md:px-4 py-6">
+        <!-- 全局导航 -->
+        <GlobalNavigation />
+
+        <div class="max-w-2xl mx-auto">
+            <!-- 页面标题区域 -->
+            <div class="mb-6">
+                <div class="flex items-center justify-between mb-4">
+                    <h1 class="text-3xl md:text-4xl font-bold text-dark-800">Complete Your Subscription</h1>
+                    <div class="w-[168px] bg-blue-600 text-white px-4 py-2 rounded-full border-2 border-[#0A0910]">
+                        <p class="font-bold text-sm md:text-base">Premium Plan</p>
+                        <p class="text-xs md:text-sm">• $9.99/month</p>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 订阅摘要卡片 -->
+            <div class="mb-6">
+                <div class="bg-white border-2 border-[#0A0910] rounded-lg p-6 md:p-8">
+                    <h2 class="text-2xl font-bold text-blue-700 mb-6">Subscription Summary</h2>
+                    
+                    <div class="space-y-4">
+                        <!-- Plan -->
+                        <div class="flex justify-between items-center">
+                            <span class="text-gray-600">Plan:</span>
+                            <span class="text-lg font-bold text-dark-800">Premium Plan</span>
+                        </div>
+                        
+                        <!-- Price -->
+                        <div class="flex justify-between items-center">
+                            <span class="text-gray-600">Price:</span>
+                            <span class="text-lg font-bold text-dark-800">$9.99/month</span>
+                        </div>
+                        
+                        <!-- Billing Cycle -->
+                        <div class="flex justify-between items-center">
+                            <span class="text-gray-600">Billing Cycle:</span>
+                            <span class="text-lg font-bold text-dark-800">Monthly</span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 选择支付方式 -->
+            <div class="mb-6">
+                <h2 class="text-2xl font-bold text-dark-800 mb-4">Choose Payment Method</h2>
+                
+                <div class="flex gap-4 mb-6">
+                    <!-- Google Pay -->
+                    <button
+                        @click="selectPaymentMethod('google')"
+                        :class="[
+                            'bg-white border-2 rounded-lg p-6 transition-all duration-200',
+                            selectedPayment === 'google' 
+                                ? 'border-blue-600 shadow-lg' 
+                                : 'border-[#0A0910] hover:border-blue-400'
+                        ]"
+                    >
+                        <div class="flex items-center justify-center h-16">
+                            <div class="flex items-center gap-3">
+                                <svg class="w-12 h-12" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+                                    <path d="M24 9.5C20.65 9.5 17.45 10.65 14.9 12.7L18.3 16.1C19.95 14.85 21.95 14.15 24 14.15C28.55 14.15 32.35 17.4 33.25 21.75H38.95C37.95 14.45 31.65 9.5 24 9.5Z" fill="#EA4335"/>
+                                    <path d="M9.05 24C9.05 26.05 9.55 28 10.45 29.7L5.8 33.1C4.1 30.35 3 27.25 3 24C3 20.75 4.1 17.65 5.8 14.9L10.45 18.3C9.55 20 9.05 21.95 9.05 24Z" fill="#FBBC05"/>
+                                    <path d="M24 38.5C20.65 38.5 17.45 37.35 14.9 35.3L10.45 38.7C13.2 41.4 16.95 43 21 43.5V43.5C28.3 42.5 34.3 37.5 36.75 30.75H33.25C32.35 35.1 28.55 38.5 24 38.5Z" fill="#34A853"/>
+                                    <path d="M45 24C45 22.85 44.9 21.7 44.7 20.6H24V27.65H35.4C34.7 30.55 32.85 32.95 30.35 34.45L33.85 37.2C37.95 33.45 40.2 28.15 40.2 24C40.2 22.85 40.1 21.7 39.9 20.6H45V24Z" fill="#4285F4"/>
+                                </svg>
+                                <span class="text-xl font-bold text-dark-800">Google Pay</span>
+                            </div>
+                        </div>
+                    </button>
+
+                    <!-- Apple Pay -->
+                    <button
+                        @click="selectPaymentMethod('apple')"
+                        :class="[
+                            'border-2 rounded-lg p-6 transition-all duration-200',
+                            selectedPayment === 'apple' 
+                                ? 'bg-black border-blue-600 shadow-lg' 
+                                : 'bg-black border-[#0A0910] hover:border-blue-400'
+                        ]"
+                    >
+                        <div class="flex items-center justify-center h-16">
+                            <div class="flex items-center gap-3">
+                                <svg class="w-10 h-10" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+                                    <path d="M17.05 20.28C16.03 21.23 14.96 21.08 13.93 20.63C12.84 20.17 11.85 20.15 10.7 20.63C9.28 21.25 8.52 21.07 7.62 20.28C2.02 14.58 2.83 5.97 9.26 5.67C10.76 5.75 11.79 6.52 12.65 6.58C13.95 6.31 15.21 5.52 16.61 5.63C18.31 5.78 19.6 6.45 20.45 7.65C16.77 9.87 17.63 14.72 20.95 16.11C20.27 17.89 19.4 19.65 17.04 20.29L17.05 20.28ZM12.53 5.62C12.38 3.06 14.42 0.93 16.87 0.75C17.2 3.66 14.18 5.89 12.53 5.62Z" fill="white"/>
+                                </svg>
+                                <span class="text-xl font-bold text-white">Apple Pay</span>
+                            </div>
+                        </div>
+                    </button>
+                </div>
+            </div>
+
+            <!-- 完成订阅按钮和条款 -->
+            <div class="mb-6">
+                <button
+                    @click="handleCompleteSubscription"
+                    :disabled="!selectedPayment"
+                    class="w-full bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white px-6 py-4 rounded-lg font-bold text-lg border-2 border-[#0A0910] transition-all duration-300 transform hover:scale-105 disabled:scale-100 disabled:cursor-not-allowed mb-4"
+                >
+                    Complete Subscription
+                </button>
+                
+                <p class="text-center text-sm text-gray-600">
+                    By subscribing, you agree to our 
+                    <a href="#" class="text-blue-600 hover:text-blue-700 underline">Terms of Service</a>
+                    and 
+                    <a href="#" class="text-blue-600 hover:text-blue-700 underline">Privacy Policy</a>.
+                </p>
+            </div>
+        </div>
+
+        <!-- 底部 -->
+        <GlobalFooter />
+    </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import GlobalNavigation from '@/components/GlobalNavigation.vue'
+import GlobalFooter from '@/components/GlobalFooter.vue'
+import { showToast } from 'vant'
+import 'vant/es/toast/style'
+
+const selectedPayment = ref<'google' | 'apple' | ''>('')
+
+const selectPaymentMethod = (method: 'google' | 'apple') => {
+    selectedPayment.value = method
+}
+
+const handleCompleteSubscription = () => {
+    if (!selectedPayment.value) {
+        showToast('请选择支付方式')
+        return
+    }
+    
+    const paymentName = selectedPayment.value === 'google' ? 'Google Pay' : 'Apple Pay'
+    showToast(`正在使用 ${paymentName} 完成订阅...`)
+}
+</script>
+
+<style scoped>
+/* 保持与项目整体风格一致 */
+</style>

+ 115 - 0
src/views/Profile.vue

@@ -0,0 +1,115 @@
+<template>
+    <div class="min-h-screen bg-yellow-400 px-2 md:px-4 py-6">
+        <!-- 全局导航 -->
+        <GlobalNavigation />
+
+        <div class="max-w-4xl mx-auto">
+            <!-- 页面标题 -->
+            <div class="mb-6">
+                <h1 class="text-3xl md:text-4xl font-bold text-dark-800 mb-2">Account Information</h1>
+            </div>
+
+            <!-- 账户信息卡片 -->
+            <div class="mb-6">
+                <div class="bg-white border-2 border-[#0A0910] rounded-lg p-6 md:p-8">
+                    <!-- 用户头像和基本信息 -->
+                    <div class="flex items-center gap-4 mb-6">
+                        <div class="w-20 h-20 bg-blue-200 rounded-full flex items-center justify-center border-2 border-[#0A0910]">
+                            <svg class="w-10 h-10 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
+                                <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
+                            </svg>
+                        </div>
+                        <div class="flex-1">
+                            <h2 class="text-2xl font-bold text-dark-800 mb-1">John Doe</h2>
+                            <p class="text-gray-600">john@example.com</p>
+                        </div>
+                    </div>
+
+                    <!-- 账户创建时间 -->
+                    <div class="mb-4">
+                        <p class="text-sm text-gray-500 mb-1">Account Created</p>
+                        <p class="text-base font-medium text-dark-800">January 15, 2024</p>
+                    </div>
+
+                    <!-- 最后登录时间 -->
+                    <div>
+                        <p class="text-sm text-gray-500 mb-1">Last Login</p>
+                        <p class="text-base font-medium text-dark-800">Just now</p>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 订阅详情卡片 -->
+            <div class="mb-6">
+                <h2 class="text-2xl font-bold text-dark-800 mb-4">Subscription Details</h2>
+                <div class="bg-white border-2 border-[#0A0910] rounded-lg p-6 md:p-8">
+                    <!-- 订阅状态 -->
+                    <div class="bg-blue-50 border-2 border-blue-300 rounded-lg p-4 mb-6">
+                        <div class="flex items-center justify-between mb-3">
+                            <h3 class="text-lg font-bold text-blue-800">7-Day Free Trial</h3>
+                            <span class="bg-green-100 text-green-800 text-sm font-medium px-3 py-1 rounded-full border border-green-400">
+                                Active
+                            </span>
+                        </div>
+                        <div class="space-y-2">
+                            <div class="flex justify-between items-center">
+                                <span class="text-sm text-gray-600">Days Remaining:</span>
+                                <span class="text-base font-bold text-dark-800">4 days</span>
+                            </div>
+                            <div class="flex justify-between items-center">
+                                <span class="text-sm text-gray-600">Expires on:</span>
+                                <span class="text-base font-bold text-dark-800">November 8, 2025</span>
+                            </div>
+                        </div>
+                    </div>
+
+                    <!-- 当前功能 -->
+                    <div class="mb-6">
+                        <h4 class="text-base font-bold text-dark-800 mb-3">Current Features:</h4>
+                        <div class="space-y-2">
+                            <div class="flex items-center gap-2">
+                                <span class="text-green-500 text-xl">✓</span>
+                                <span class="text-dark-800">Full access to all features</span>
+                            </div>
+                            <div class="flex items-center gap-2">
+                                <span class="text-green-500 text-xl">✓</span>
+                                <span class="text-dark-800">Unlimited content</span>
+                            </div>
+                            <div class="flex items-center gap-2">
+                                <span class="text-green-500 text-xl">✓</span>
+                                <span class="text-dark-800">Basic support</span>
+                            </div>
+                        </div>
+                    </div>
+
+                    <!-- 管理订阅按钮 -->
+                    <button 
+                        @click="handleManageSubscription"
+                        class="w-full bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg font-bold text-base border-2 border-[#0A0910] transition-all duration-300 transform hover:scale-105"
+                    >
+                        Manage Subscription
+                    </button>
+                </div>
+            </div>
+        </div>
+
+        <!-- 底部 -->
+        <GlobalFooter />
+    </div>
+</template>
+
+<script setup lang="ts">
+import { useRouter } from 'vue-router'
+import GlobalNavigation from '@/components/GlobalNavigation.vue'
+import GlobalFooter from '@/components/GlobalFooter.vue'
+
+const router = useRouter()
+
+const handleManageSubscription = () => {
+    router.push('/manage-subscription')
+}
+</script>
+
+<style scoped>
+/* 保持与项目整体风格一致 */
+</style>

BIN
原型页面/.~需求分析.docx


BIN
原型页面/需求分析.docx