Fix image loading and improve PWA install guidance.
Allow /images/ as public static assets and show install hints on all pages including mobile and HTTP deployments. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,26 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRoute } from 'vitepress'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
const route = useRoute()
|
||||
const showAndroidInstall = ref(false)
|
||||
const showIOSHint = ref(false)
|
||||
type InstallMode = 'native' | 'ios' | 'android-menu' | 'desktop' | 'https-required'
|
||||
|
||||
const showInstall = ref(false)
|
||||
const dismissed = ref(false)
|
||||
const installMode = ref<InstallMode | null>(null)
|
||||
const isMobile = ref(false)
|
||||
let deferredPrompt: BeforeInstallPromptEvent | null = null
|
||||
let fallbackTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
interface BeforeInstallPromptEvent extends Event {
|
||||
prompt: () => Promise<void>
|
||||
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>
|
||||
}
|
||||
|
||||
const isDocPage = computed(() => route.path !== '/')
|
||||
const visible = computed(() => !dismissed.value && showInstall.value && installMode.value !== null)
|
||||
|
||||
const visible = computed(() => {
|
||||
if (dismissed.value) return false
|
||||
if (isMobile.value && isDocPage.value) return false
|
||||
return showAndroidInstall.value || showIOSHint.value
|
||||
})
|
||||
function needsHttpsForInstall() {
|
||||
return (
|
||||
!window.isSecureContext &&
|
||||
!['localhost', '127.0.0.1'].includes(location.hostname)
|
||||
)
|
||||
}
|
||||
|
||||
function onBeforeInstallPrompt(event: Event) {
|
||||
event.preventDefault()
|
||||
deferredPrompt = event as BeforeInstallPromptEvent
|
||||
installMode.value = 'native'
|
||||
showInstall.value = true
|
||||
if (fallbackTimer) {
|
||||
clearTimeout(fallbackTimer)
|
||||
fallbackTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
isMobile.value = window.matchMedia('(max-width: 768px)').matches
|
||||
@@ -38,15 +51,29 @@ onMounted(() => {
|
||||
|
||||
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
|
||||
if (isIOS) {
|
||||
showIOSHint.value = true
|
||||
installMode.value = 'ios'
|
||||
showInstall.value = true
|
||||
return
|
||||
}
|
||||
|
||||
window.addEventListener('beforeinstallprompt', (event) => {
|
||||
event.preventDefault()
|
||||
deferredPrompt = event as BeforeInstallPromptEvent
|
||||
showAndroidInstall.value = true
|
||||
})
|
||||
if (needsHttpsForInstall()) {
|
||||
installMode.value = 'https-required'
|
||||
showInstall.value = true
|
||||
return
|
||||
}
|
||||
|
||||
window.addEventListener('beforeinstallprompt', onBeforeInstallPrompt)
|
||||
|
||||
fallbackTimer = setTimeout(() => {
|
||||
if (deferredPrompt || installMode.value) return
|
||||
installMode.value = isMobile.value ? 'android-menu' : 'desktop'
|
||||
showInstall.value = true
|
||||
}, 2500)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('beforeinstallprompt', onBeforeInstallPrompt)
|
||||
if (fallbackTimer) clearTimeout(fallbackTimer)
|
||||
})
|
||||
|
||||
async function installApp() {
|
||||
@@ -54,7 +81,7 @@ async function installApp() {
|
||||
await deferredPrompt.prompt()
|
||||
await deferredPrompt.userChoice
|
||||
deferredPrompt = null
|
||||
showAndroidInstall.value = false
|
||||
showInstall.value = false
|
||||
}
|
||||
|
||||
function dismiss() {
|
||||
@@ -66,15 +93,24 @@ function dismiss() {
|
||||
<template>
|
||||
<div v-if="visible" class="install-app">
|
||||
<div class="install-app__content">
|
||||
<p v-if="showAndroidInstall" class="install-app__text">
|
||||
<p v-if="installMode === 'native'" class="install-app__text">
|
||||
可将「道德经」安装到桌面,像 App 一样使用。
|
||||
</p>
|
||||
<p v-else class="install-app__text">
|
||||
<p v-else-if="installMode === 'ios'" class="install-app__text">
|
||||
iPhone:Safari 底部分享 →「添加到主屏幕」,阅读更方便。
|
||||
</p>
|
||||
<p v-else-if="installMode === 'android-menu'" class="install-app__text">
|
||||
Android:Chrome 右上角菜单 →「安装应用」或「添加到主屏幕」。
|
||||
</p>
|
||||
<p v-else-if="installMode === 'desktop'" class="install-app__text">
|
||||
电脑:地址栏右侧点击「安装」图标,或浏览器菜单 →「安装道德经」。
|
||||
</p>
|
||||
<p v-else class="install-app__text">
|
||||
安装应用需通过 HTTPS 域名访问(内网 http://IP 地址只能添加快捷方式,无法真正安装)。
|
||||
</p>
|
||||
<div class="install-app__actions">
|
||||
<button
|
||||
v-if="showAndroidInstall"
|
||||
v-if="installMode === 'native'"
|
||||
type="button"
|
||||
class="install-app__primary"
|
||||
@click="installApp"
|
||||
|
||||
Reference in New Issue
Block a user