diff --git a/src/pages/Help/index.vue b/src/pages/Help/index.vue
index 195cbdd..29339c1 100644
--- a/src/pages/Help/index.vue
+++ b/src/pages/Help/index.vue
@@ -73,9 +73,18 @@
@@ -109,11 +118,21 @@
@@ -132,6 +151,7 @@ import WindowsIcon from './windows.svg?component'
import AndroidIcon from './android.svg?component'
import request from '@/utils/request'
import { getAllQueryString } from '@/utils/url-utils.ts'
+import { toast } from 'vue-sonner'
import DownloadMethodList from './DownloadMethodList/DownloadMethodList.vue'
import FAQAccordion from './FAQAccordion/index.vue'
@@ -147,13 +167,46 @@ const handleDownload = async (key: string) => {
const platform = platformMap[key] || 'windows'
const ic = getAllQueryString('ic') || sessionStorage.getItem('ic') || 'uSSfg1Y1vt'
- if (platform === 'ios') {
- if (window.OI_SDK && window.OI_SDK.OI) {
- window.OI_SDK.OI.wakeupOrInstall({ data: { platform: 'download', inviteCode: ic } })
+ // 设备环境检测
+ const ua = navigator.userAgent
+ const isAndroidDevice = /Android/i.test(ua)
+ const isIosDevice =
+ /iPhone|iPod|iPad/i.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
+ const isMobileDevice = isAndroidDevice || isIosDevice
+
+ // 1. 安卓下载逻辑
+ if (key === 'android') {
+ if (isAndroidDevice) {
+ isLoadingAndroid.value = true
+ await triggerOpenInstall(ic)
+ isLoadingAndroid.value = false
+ return
+ } else {
+ window.open(
+ 'https://api.hifast.biz/v1/common/client/download/file/Hi%E5%BF%ABVPN-android-1.0.0.apk',
+ '_blank',
+ )
return
}
}
+ // 2. iOS/Mac 下载逻辑
+ if (key === 'ios' || key === 'mac') {
+ if (isMobileDevice) {
+ if (key === 'ios') isLoadingIos.value = true
+ await triggerOpenInstall(ic)
+ isLoadingIos.value = false
+ return
+ } else {
+ window.open(
+ 'https://apps.apple.com/us/app/hi%E5%BF%ABvpn/id6755683167?l=zh-Hans-CN',
+ '_blank',
+ )
+ return
+ }
+ }
+
+ // 3. 原有逻辑 (如 Windows)
try {
const res: any = await request.get('/api/v1/common/client/download', {
invite_code: ic,
@@ -167,7 +220,27 @@ const handleDownload = async (key: string) => {
}
}
+// 提取 OpenInstall 触发逻辑
+const triggerOpenInstall = async (ic: string) => {
+ if (!(window as any).OI_SDK?.isReady && (window as any).OI_SDK_PROMISE) {
+ try {
+ await Promise.race([
+ (window as any).OI_SDK_PROMISE,
+ new Promise((_, reject) => setTimeout(() => reject(new Error('OpenInstall timeout')), 3000)),
+ ])
+ } catch (e) {
+ console.warn('OpenInstall readiness wait failed or timeout:', e)
+ }
+ }
+
+ if ((window as any).OI_SDK && (window as any).OI_SDK.OI) {
+ ;(window as any).OI_SDK.OI.wakeupOrInstall({ data: { platform: 'download', inviteCode: ic } })
+ }
+}
+
const activeIndex = ref(0)
+const isLoadingIos = ref(false)
+const isLoadingAndroid = ref(false)
const otherClients = computed(() => {
return [
diff --git a/src/pages/Home/components/DownloadButton.vue b/src/pages/Home/components/DownloadButton.vue
index 237b76e..0aa81af 100644
--- a/src/pages/Home/components/DownloadButton.vue
+++ b/src/pages/Home/components/DownloadButton.vue
@@ -30,7 +30,7 @@
v-else
:id="mainButton?.id"
:aria-label="mainButton?.label"
- @click="mainButton.key === 'win' ? handleDownload(mainButton.key) : null"
+ @click="handleDownload(mainButton.key)"
class="flex h-full w-full cursor-pointer items-center justify-center transition-transform hover:brightness-110 active:scale-95"
>
@@ -142,10 +142,47 @@ const handleDownload = async (key: string) => {
ios: 'ios',
}
const platform = platformMap[key] || 'windows'
+ const ic = getAllQueryString('ic') || sessionStorage.getItem('ic') || 'uSSfg1Y1vt'
+ // 设备环境检测
+ const ua = navigator.userAgent
+ const isAndroidDevice = /Android/i.test(ua)
+ const isIosDevice =
+ /iPhone|iPod|iPad/i.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
+ const isMobileDevice = isAndroidDevice || isIosDevice
+
+ // 1. 安卓下载逻辑
+ if (key === 'android') {
+ if (isAndroidDevice) {
+ await triggerOpenInstall(ic)
+ return
+ } else {
+ window.open(
+ 'https://api.hifast.biz/v1/common/client/download/file/Hi%E5%BF%ABVPN-android-1.0.0.apk',
+ '_blank',
+ )
+ return
+ }
+ }
+
+ // 2. iOS/Mac 下载逻辑
+ if (key === 'ios' || key === 'mac') {
+ if (isMobileDevice) {
+ await triggerOpenInstall(ic)
+ return
+ } else {
+ window.open(
+ 'https://apps.apple.com/us/app/hi%E5%BF%ABvpn/id6755683167?l=zh-Hans-CN',
+ '_blank',
+ )
+ return
+ }
+ }
+
+ // 3. 原有逻辑 (如 Windows)
try {
const res: any = await request.get('/api/v1/common/client/download', {
- invite_code: getAllQueryString('ic') || sessionStorage.getItem('ic') || 'uSSfg1Y1vt',
+ invite_code: ic,
platform: platform,
})
if (res.url) {
@@ -159,6 +196,24 @@ const handleDownload = async (key: string) => {
}
}
+// 提取 OpenInstall 触发逻辑
+const triggerOpenInstall = async (ic: string) => {
+ if (!(window as any).OI_SDK?.isReady && (window as any).OI_SDK_PROMISE) {
+ try {
+ await Promise.race([
+ (window as any).OI_SDK_PROMISE,
+ new Promise((_, reject) => setTimeout(() => reject(new Error('OpenInstall timeout')), 3000)),
+ ])
+ } catch (e) {
+ console.warn('OpenInstall readiness wait failed or timeout:', e)
+ }
+ }
+
+ if ((window as any).OI_SDK && (window as any).OI_SDK.OI) {
+ ;(window as any).OI_SDK.OI.wakeupOrInstall({ data: { platform: 'download', inviteCode: ic } })
+ }
+}
+
const allDownloadOptions = computed(() => [
{
key: 'win',
diff --git a/src/utils/openinstall.ts b/src/utils/openinstall.ts
index df5c01d..c7fd225 100644
--- a/src/utils/openinstall.ts
+++ b/src/utils/openinstall.ts
@@ -11,8 +11,13 @@ script.charset = 'UTF-8'
script.src = 'https://web.cdn.openinstall.io/openinstall.js'
document.head.appendChild(script)
+let resolveReady: (sdk: OpenInstallSdk) => void
+window.OI_SDK_PROMISE = new Promise((resolve) => {
+ resolveReady = resolve
+})
+
script.addEventListener('load', () => {
- window.OI_SDK = new OpenInstallSdk()
+ window.OI_SDK = new OpenInstallSdk(resolveReady)
})
class OpenInstallSdk {
@@ -20,7 +25,9 @@ class OpenInstallSdk {
public OI: Record // openinstall 实例
- constructor() {
+ public isReady = false
+
+ constructor(private onReadyCallback?: (sdk: OpenInstallSdk) => void) {
this.OI = {}
this.urlQuery = window.OpenInstall.parseUrlParams()
const id = getAllQueryString('id')
@@ -34,47 +41,39 @@ class OpenInstallSdk {
}
async init() {
+ const self = this
try {
this.OI = new window.OpenInstall(
{
appKey: 'alf57p',
onready: function () {
// 初始化成功回调方法。当初始化完成后,会自动进入
- this.schemeWakeup() // 尝试使用scheme打开App(主要用于Android以及iOS的QQ环境中)
+ // 注意:此时的 this 是 OpenInstall 的原始实例对象 (m)
const m = this
+ m.schemeWakeup() // 尝试使用scheme打开App(主要用于Android以及iOS的QQ环境中)
+
const button = document.getElementById('downloadButton_apple')
const button_mac = document.getElementById('downloadButton_mac')
const button1 = document.getElementById('downloadButton_android')
const ic = getAllQueryString('ic') || 'uSSfg1Y1vt'
- if (button) {
- button.onclick = function () {
- if (ic) {
- m.wakeupOrInstall({ data: { platform: 'download', inviteCode: ic } })
- } else {
- m.wakeupOrInstall() // 此方法为scheme、Universal Link唤醒以及引导下载的作用(必须调用且不可额外自行跳转下载)
- }
- return false
+
+ const clickHandler = function () {
+ if (ic) {
+ m.wakeupOrInstall({ data: { platform: 'download', inviteCode: ic } })
+ } else {
+ m.wakeupOrInstall()
}
+ return false
}
- if (button_mac) {
- button_mac.onclick = function () {
- if (ic) {
- m.wakeupOrInstall({ data: { platform: 'download', inviteCode: ic } })
- } else {
- m.wakeupOrInstall() // 此方法为scheme、Universal Link唤醒以及引导下载的作用(必须调用且不可额外自行跳转下载)
- }
- return false
- }
- }
- if (button1) {
- button1.onclick = function () {
- if (ic) {
- m.wakeupOrInstall({ data: { platform: 'download', inviteCode: ic } })
- } else {
- m.wakeupOrInstall() // 此方法为scheme、Universal Link唤醒以及引导下载的作用(必须调用且不可额外自行跳转下载)
- }
- return false
- }
+
+ if (button) button.onclick = clickHandler
+ if (button_mac) button_mac.onclick = clickHandler
+ if (button1) button1.onclick = clickHandler
+
+ // 标记就绪并触发 Promise
+ self.isReady = true
+ if (self.onReadyCallback) {
+ self.onReadyCallback(self)
}
},
},