提交 50ed2388 作者: 方治民

feat: 优化公共页面 webview 及示例页功能

上级 34d68e9d
......@@ -24,3 +24,4 @@ src/components/FirstUI/**
src/uni_modules/**
*.nvue
*.html
......@@ -7,6 +7,7 @@
**/*.svg
**/*.sh
**/*.html
/public/*
......
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
......
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title>本地网页</title>
<style type="text/css">
.btn {
display: block;
margin: 20px auto;
padding: 5px;
background-color: #007aff;
border: 0;
color: #fff;
height: 40px;
width: 200px;
}
.btn-red {
background-color: #dd524d;
}
.btn-yellow {
background-color: #f0ad4e;
}
.desc {
padding: 10px;
color: #999;
}
.post-message-section {
visibility: hidden;
}
</style>
</head>
<body>
<p class="desc">web-view 组件加载本地 html 示例。点击下列按钮,跳转至其它页面。</p>
<div class="btn-list">
<button class="btn" type="button" data-action="navigateTo">navigateTo</button>
<button class="btn" type="button" data-action="redirectTo">redirectTo</button>
<button class="btn" type="button" data-action="navigateBack">navigateBack</button>
<button class="btn" type="button" data-action="reLaunch">reLaunch</button>
<button class="btn" type="button" data-action="switchTab">switchTab</button>
</div>
<div class="post-message-section">
<p class="desc">网页向应用发送消息,注意:小程序端应用会在此页面后退时接收到消息。</p>
<div class="btn-list">
<button class="btn btn-red" type="button" id="postMessage">postMessage</button>
</div>
</div>
<script type="text/javascript">
let userAgent = navigator.userAgent
if (userAgent.includes('AlipayClient')) {
// 支付宝小程序的 JS-SDK 防止 404 需要动态加载,如果不需要兼容支付宝小程序,则无需引用此 JS 文件。
document.writeln('<script src="https://appx/web-view.min.js"' + '>' + '<' + '/' + 'script>')
} else if (/QQ/i.test(userAgent) && /miniProgram/i.test(userAgent)) {
// QQ 小程序
document.write(
'<script type="text/javascript" src="https://qqq.gtimg.cn/miniprogram/webview_jssdk/qqjssdk-1.0.0.js"' + '>' + '<' + '/' + 'script>',
)
} else if (/miniProgram/i.test(userAgent) && /micromessenger/i.test(userAgent)) {
// 微信小程序 JS-SDK 如果不需要兼容微信小程序,则无需引用此 JS 文件。
document.write('<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.4.0.js"' + '>' + '<' + '/' + 'script>')
} else if (/toutiaomicroapp/i.test(userAgent)) {
// 头条小程序 JS-SDK 如果不需要兼容头条小程序,则无需引用此 JS 文件。
document.write(
'<script type="text/javascript" src="https://s3.pstatp.com/toutiao/tmajssdk/jssdk-1.0.1.js"' + '>' + '<' + '/' + 'script>',
)
} else if (/swan/i.test(userAgent)) {
// 百度小程序 JS-SDK 如果不需要兼容百度小程序,则无需引用此 JS 文件。
document.write(
'<script type="text/javascript" src="https://b.bdstatic.com/searchbox/icms/searchbox/js/swan-2.0.18.js"' + '>' + '<' + '/' + 'script>',
)
} else if (/quickapp/i.test(userAgent)) {
// quickapp
document.write('<script type="text/javascript" src="https://quickapp/jssdk.webview.min.js"' + '>' + '<' + '/' + 'script>')
}
if (!/toutiaomicroapp/i.test(userAgent)) {
document.querySelector('.post-message-section').style.visibility = 'visible'
}
</script>
<!-- uni 的 SDK -->
<!-- 需要把 uni.webview.1.5.6.js 下载到自己的服务器 -->
<script type="text/javascript" src="https://hntq-res.oss-cn-shenzhen.aliyuncs.com/uni.webview.1.5.6.js"></script>
<script type="text/javascript">
// 待触发 `UniAppJSBridgeReady` 事件后,即可调用 uni 的 API。
document.addEventListener('UniAppJSBridgeReady', function () {
uni.postMessage({
data: {
action: 'message',
},
})
uni.getEnv(function (res) {
console.log(`当前环境:${JSON.stringify(res)}`)
})
document.querySelector('.btn-list').addEventListener('click', function (evt) {
const target = evt.target
if (target.tagName === 'BUTTON') {
const action = target.getAttribute('data-action')
switch (action) {
case 'switchTab':
uni.switchTab({
url: '/pages/tabBar/API/API',
})
break
case 'reLaunch':
uni.reLaunch({
url: '/pages/index/index',
})
break
case 'navigateBack':
uni.navigateBack({
delta: 1,
})
break
default:
uni[action]({
url: '/pages/common/about/index',
})
break
}
}
})
document.getElementById('postMessage').addEventListener('click', function () {
uni.postMessage({
data: {
action: 'message',
},
})
})
})
</script>
</body>
</html>
......@@ -7,21 +7,6 @@
"navigationBarTitleText": "Basic APP"
}
},
{
"path": "pages/chat/index",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": true,
"app-plus": {
"titleNView": false,
"bounce": "none",
"pullToRefresh": {
"style": "circle",
"offset": "70px"
}
}
}
},
// === 示例 ===
{
......@@ -48,6 +33,16 @@
"navigationBarTitleText": "Echarts 示例"
}
},
{
"path": "pages/example/chat/index",
"style": {
"navigationStyle": "custom",
"app-plus": {
"titleNView": false,
"bounce": "none"
}
}
},
// ================================ 通用页面分割线 ====================================
// === 关于我们 ===
......
<script setup lang="ts">
import dayjs from 'dayjs'
import Navigate from '@/utils/page/navigate'
import { checkUpgrade } from '@/utils/upgrade'
import { useRuntime } from '@/hooks/app/useRuntime'
const { app } = useRuntime()
const version = computed(() => app.value.version)
const startYear = ref(2005)
const currentYear = ref(dayjs().year())
const { privacy, services, copyright, company } = Navigate.links
......@@ -11,10 +15,14 @@
<view class="wrap">
<view class="logoBox">
<image class="logo" src="/static/logo.png" />
<view class="text-#555 font-bold p-2">{{ $app.name }}</view>
<view class="text-#555 font-bold p-2">{{ $app.description }}</view>
</view>
<fui-footer isFixed>
<template #text>
<view class="mb-2">
<view class="flex-center" v-if="version" @tap="checkUpgrade(true)">V{{ version }}</view>
<view class="text-#ccc text-18 mt-1">发布时间:{{ $app.lastBuildTime }}</view>
</view>
<view class="links">
<text class="underline" @tap="Navigate.webview(services, '服务协议')">服务协议</text>
<text class="underline" @tap="Navigate.webview(privacy, '隐私政策')">隐私政策</text>
......
......@@ -37,9 +37,18 @@
}
// #ifdef APP-PLUS
// 白名单同源检测
if (isSameOrigin(new URI(page.link))) {
// 本地网页或白名单同源检测
if (page.link.startsWith('/hybrid/') || isSameOrigin(new URI(page.link))) {
page.security = true
// 延迟获取 webview 对象,插入 js 文件
setTimeout(() => {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const currentWebview = currentPage.$getAppWebview()
const webview = currentWebview.children()[0]
webview.appendJsFile('_www/static/uni.webview.1.5.6.js')
}, 1000)
} else {
// 对于不安全或不信任的网站地址或可能触发 plus API 的来源,采用手动创建 webview 的方式打开
const { windowHeight, statusBarHeight } = uni.getSystemInfoSync()
......@@ -62,7 +71,7 @@
})
// 安全域白名单,防止 APP 环境的意外注入和非法安全调用 plus API 的问题
const allows = ['yiring.com']
const allows = ['yiring.com', 'uniapp.dcloud.io']
// 在安全域白名单中,但仍然需要排除的链接(通常是代理的网站链接)
const denyLinks = []
function isSameOrigin(url: URI) {
......@@ -75,12 +84,22 @@
return regex.test(url.hostname())
})
}
// 监听来自 webview 的消息
function onPostMessage(e: { detail: { data: any } }) {
console.warn('onPostMessage', e)
}
</script>
<template>
<view class="wrap" v-if="page.link">
<template v-if="page.security">
<web-view :src="page.link" :update-title="!page.title" :webview-styles="page.styles" />
<web-view
:src="page.link"
:update-title="!page.title"
:webview-styles="page.styles"
@message="onPostMessage"
/>
</template>
</view>
</template>
<script lang="ts" setup>
import URI from 'urijs'
const page = reactive({
security: false,
title: '',
......@@ -36,12 +34,8 @@
})
}
// #ifdef APP-PLUS
// 白名单同源检测
if (isSameOrigin(new URI(page.link))) {
page.security = true
} else {
Message.loading()
// #ifdef APP-PLUS
// 对于不安全或不信任的网站地址或可能触发 plus API 的来源,采用手动创建 webview 的方式打开
const webview = plus.webview.create(page.link, 'no-security-webview', {
progress: page.styles.progress,
......@@ -87,31 +81,14 @@
const currentWebview = currentPage.$getAppWebview()
currentWebview.setStyle({ progress: page.styles.progress })
currentWebview.append(webview)
setTimeout(() => {
Message.hideLoading()
}, 1500)
}
// #endif
// #ifndef APP-PLUS
page.security = true
// #endif
setTimeout(() => {
Message.hideLoading()
}, 1500)
})
// 安全域白名单,防止 APP 环境的意外注入和非法安全调用 plus API 的问题
const allows = ['yiring.com']
// 在安全域白名单中,但仍然需要排除的链接(通常是代理的网站链接)
const denyLinks = []
function isSameOrigin(url: URI) {
if (denyLinks.includes(url.href())) {
return false
}
return allows.some((item) => {
const regex = new RegExp(`^(.*\\.)?${item}$`, 'i')
return regex.test(url.hostname())
})
}
</script>
<template>
......
......@@ -5,20 +5,31 @@
const data = reactive({
items: [
{
name: 'Chat AI 示例',
icon: 'emojione-cowboy-hat-face',
page: `/pages/example/chat/index?link=${encodeURIComponent('https://api.coze.cn/open-platform/sdk/chatapp?params=%7B%22chatClientId%22%3A%22Hd1w0dc49QzvaAGlDONiA%22%2C%22chatConfig%22%3A%7B%22bot_id%22%3A%227426295021118488587%22%2C%22user%22%3A%22NmAh7FUtuyO6RPrzYMg4v%22%2C%22conversation_id%22%3A%22cqnuKy6c3lmYTdjF0vslj%22%7D%2C%22componentProps%22%3A%7B%22layout%22%3A%22mobile%22%2C%22lang%22%3A%22zh_CN%22%2C%22uploadable%22%3Atrue%2C%22title%22%3A%22%E6%B9%96%E5%8D%97%E5%A4%A9%E6%B0%94%20AI%20%E6%99%BA%E8%83%BD%E5%8A%A9%E7%90%86%22%2C%22icon%22%3A%22https%3A%2F%2Fhntq-res.oss-cn-shenzhen.aliyuncs.com%2Fhntq-ai-logo.png%22%7D%7D')}`,
animationType: 'slide-in-bottom',
},
{
name: 'Mapbox 地图示例',
icon: 'emojione-globe-showing-europe-africa',
page: '/pages/example/mapbox/index',
},
{
name: 'Webview 示例',
icon: 'logos-internetexplorer',
page: '/pages/example/webview/index',
},
{
name: 'Echarts 示例',
icon: 'logos-chartblocks',
page: '/pages/example/echarts/index',
},
{
name: 'Webview(本地) 示例',
icon: 'logos-internetexplorer',
page: `/pages/common/webview/index?link=${encodeURIComponent('/hybrid/html/local.html')}`,
},
{
name: 'Webview(网络) 示例',
icon: 'logos-internetexplorer',
page: `/pages/common/webview/index?link=${encodeURIComponent('https://uniapp.dcloud.io/static/web-view.html')}`,
},
],
})
</script>
......@@ -34,7 +45,7 @@
marginTop="24"
v-for="(item, idx) in data.items"
:key="idx"
@click="Pages.to(item.page)"
@click="Pages.to(item.page, item.animationType as Pages.AnimationType)"
>
<view class="fui-list__item fui-align__center">
<Icon :class="[`icon-${item.icon}`]" size="48" />
......
......@@ -4,6 +4,22 @@
color: '#FF3333',
},
})
onLoad(() => {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const webview = currentPage.$getAppWebview()
webview.addEventListener('loaded', () => {
webview.appendJsFile('_www/static/uni.webview.1.5.6.js')
webview.evalJS(`
document.addEventListener('UniAppJSBridgeReady', function() {
uni.getEnv(function(res) {
console.log('当前环境:' + JSON.stringify(res));
})
})
`)
})
})
</script>
<template>
......
......@@ -7,9 +7,9 @@
const { exit } = useConcealedExit()
const { app } = useRuntime()
const version = computed(() => app.value.version)
const year = ref(dayjs().year())
const title = ref('Hello World')
const version = computed(() => app.value.version)
onLoad(() => {
// 关闭启动页并检查更新
......@@ -52,16 +52,6 @@
Message.toast(`在手机上运行点击有惊喜~ ╰(*°▽°*)╯`)
// #endif
}
// 打开 AI 对话窗口
function openChat() {
const link =
'https://api.coze.cn/open-platform/sdk/chatapp?params=%7B%22chatClientId%22%3A%22Hd1w0dc49QzvaAGlDONiA%22%2C%22chatConfig%22%3A%7B%22bot_id%22%3A%227426295021118488587%22%2C%22user%22%3A%22NmAh7FUtuyO6RPrzYMg4v%22%2C%22conversation_id%22%3A%22cqnuKy6c3lmYTdjF0vslj%22%7D%2C%22componentProps%22%3A%7B%22layout%22%3A%22mobile%22%2C%22lang%22%3A%22zh_CN%22%2C%22uploadable%22%3Atrue%2C%22title%22%3A%22%E6%B9%96%E5%8D%97%E5%A4%A9%E6%B0%94%20AI%20%E6%99%BA%E8%83%BD%E5%8A%A9%E7%90%86%22%2C%22icon%22%3A%22https%3A%2F%2Fhntq-res.oss-cn-shenzhen.aliyuncs.com%2Fhntq-ai-logo.png%22%7D%7D'
uni.navigateTo({
url: `/pages/chat/index?link=${encodeURIComponent(link)}`,
animationType: 'slide-in-bottom',
})
}
</script>
<template>
......@@ -77,7 +67,7 @@
<view class="flex-center flex-col mb-3">
<text class="title">{{ title }}</text>
<text class="title">{{ $t('app.hello') }}</text>
<Icon icon="emojione-robot-face" size="52" class="mt-3" @click="openChat" />
<Icon icon="emojione-grinning-face" size="48" class="mt-3" />
<fui-divider />
......
export function to(url: string): void {
export type AnimationType =
| 'auto'
| 'none'
| 'slide-in-right'
| 'slide-in-left'
| 'slide-in-top'
| 'slide-in-bottom'
| 'fade-in'
| 'zoom-out'
| 'zoom-fade-out'
| 'pop-in'
export function to(url: string, animationType: AnimationType = 'pop-in'): void {
uni.navigateTo({
url,
animationType,
})
}
......@@ -13,6 +13,8 @@ const ICONS = [
'logos:internetexplorer',
'logos-chartblocks',
'emojione:letter-z',
'emojione:robot-face',
'emojione:cowboy-hat-face',
]
export default defineConfig({
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论