Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
## 1.0.0(2026-05-07)
|
||||
- 支持 uni-link 组件
|
||||
@@ -0,0 +1,362 @@
|
||||
<template>
|
||||
<text style="text-decoration-line: underline" @click="openURL">
|
||||
<slot></slot>
|
||||
</text>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 链接
|
||||
* @description 链接组件
|
||||
* @uniPlatform {
|
||||
* "app": {
|
||||
* "android": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.21"
|
||||
* },
|
||||
* "ios": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.11"
|
||||
* },
|
||||
* "harmony": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.07"
|
||||
* }
|
||||
* },
|
||||
* "mp": {
|
||||
* "weixin": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* },
|
||||
* "alipay": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.31"
|
||||
* }
|
||||
* },
|
||||
* "web": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
// #ifdef APP
|
||||
import { openSchema } from '@/uni_modules/uts-openSchema'
|
||||
// #endif
|
||||
|
||||
defineOptions({
|
||||
name: 'UniLink',
|
||||
styleIsolation: 'app'
|
||||
})
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
* @description 链接地址,点击组件时要打开的 URL
|
||||
* @default ""
|
||||
* @uniPlatform {
|
||||
* "app": {
|
||||
* "android": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.21"
|
||||
* },
|
||||
* "ios": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.11"
|
||||
* },
|
||||
* "harmony": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.07"
|
||||
* }
|
||||
* },
|
||||
* "mp": {
|
||||
* "weixin": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* },
|
||||
* "alipay": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.31"
|
||||
* }
|
||||
* },
|
||||
* "web": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
href?: string
|
||||
/**
|
||||
* @description 打开方式。"_self" 在当前应用内置 webview 中打开;"_blank" 在外部浏览器中打开(小程序端会复制链接提示用户外部打开)
|
||||
* @default "_self"
|
||||
* @uniPlatform {
|
||||
* "app": {
|
||||
* "android": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.21"
|
||||
* },
|
||||
* "ios": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.11"
|
||||
* },
|
||||
* "harmony": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.07"
|
||||
* }
|
||||
* },
|
||||
* "mp": {
|
||||
* "weixin": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* },
|
||||
* "alipay": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.31"
|
||||
* }
|
||||
* },
|
||||
* "web": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
target?: '_blank' | '_self'
|
||||
/**
|
||||
* @description `target="_blank"`失败时,可以通过本属性控制失败回退策略。"turn-self"使用_self方式打开;"copy-link-modal"复制链接并弹框提醒;"none"不回退,触发失败回调
|
||||
* @default "turn-self"
|
||||
* @uniPlatform {
|
||||
* "app": {
|
||||
* "android": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.21"
|
||||
* },
|
||||
* "ios": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.11"
|
||||
* },
|
||||
* "harmony": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.07"
|
||||
* }
|
||||
* },
|
||||
* "mp": {
|
||||
* "weixin": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* },
|
||||
* "alipay": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.31"
|
||||
* }
|
||||
* },
|
||||
* "web": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
blankFallback: 'turn-self' | 'copy-link-modal' | 'none'
|
||||
}
|
||||
|
||||
type LocaleType = 'zh-Hans' | 'zh-Hant' | 'en' | 'fr' | 'la'
|
||||
|
||||
type LocaleMessages = {
|
||||
emptyHref: string
|
||||
popupBlocked: string
|
||||
internalOpenFailed: string
|
||||
copiedOpenInBrowserPrefix: string
|
||||
copyFailedOpenInBrowser: string
|
||||
}
|
||||
|
||||
const LINK_WEBVIEW_PAGE_PATH = '/uni_modules/uni-link-x/pages/link-webview'
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
href: '',
|
||||
target: '_self',
|
||||
blankFallback: 'turn-self'
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
error: [message: string]
|
||||
}>()
|
||||
|
||||
const appLanguage = (uni.getAppBaseInfo().appLanguage ?? 'en').toLowerCase()
|
||||
|
||||
function resolveLocaleType() : LocaleType {
|
||||
if (
|
||||
appLanguage.indexOf('zh-hant') == 0 ||
|
||||
appLanguage.indexOf('zh-tw') == 0 ||
|
||||
appLanguage.indexOf('zh-hk') == 0 ||
|
||||
appLanguage.indexOf('zh-mo') == 0
|
||||
) {
|
||||
return 'zh-Hant'
|
||||
}
|
||||
if (appLanguage.indexOf('zh') == 0) {
|
||||
return 'zh-Hans'
|
||||
}
|
||||
if (appLanguage.indexOf('fr') == 0) {
|
||||
return 'fr'
|
||||
}
|
||||
if (appLanguage.indexOf('la') == 0 || appLanguage.indexOf('lat') == 0) {
|
||||
return 'la'
|
||||
}
|
||||
return 'en'
|
||||
}
|
||||
|
||||
function getLocaleMessages() : LocaleMessages {
|
||||
const localeType = resolveLocaleType()
|
||||
|
||||
if (localeType == 'zh-Hans') {
|
||||
return {
|
||||
emptyHref: '链接地址不能为空',
|
||||
popupBlocked: '浏览器拦截了新窗口',
|
||||
internalOpenFailed: '应用中未找到uni-link组件下的link-webview页面',
|
||||
copiedOpenInBrowserPrefix: '已复制链接,请使用外部浏览器打开:',
|
||||
copyFailedOpenInBrowser: '复制链接失败,请手动复制链接并使用外部浏览器打开'
|
||||
}
|
||||
}
|
||||
|
||||
if (localeType == 'zh-Hant') {
|
||||
return {
|
||||
emptyHref: '連結地址不能為空',
|
||||
popupBlocked: '瀏覽器封鎖了新視窗',
|
||||
internalOpenFailed: '應用中未找到 uni-link 組件下的 link-webview 頁面',
|
||||
copiedOpenInBrowserPrefix: '已複製連結,請使用外部瀏覽器開啟:',
|
||||
copyFailedOpenInBrowser: '複製連結失敗,請手動複製連結並使用外部瀏覽器開啟'
|
||||
}
|
||||
}
|
||||
|
||||
if (localeType == 'fr') {
|
||||
return {
|
||||
emptyHref: 'L’URL ne peut pas être vide',
|
||||
popupBlocked: 'Le navigateur a bloqué la nouvelle fenêtre',
|
||||
internalOpenFailed: 'Échec de l\'ouverture de la page @uni_modules/uni-link-x/pages/link-webview',
|
||||
copiedOpenInBrowserPrefix: 'Le lien a été copié. Veuillez l’ouvrir dans un navigateur externe :',
|
||||
copyFailedOpenInBrowser: 'Échec de la copie du lien. Veuillez le copier manuellement et l’ouvrir dans un navigateur externe.'
|
||||
}
|
||||
}
|
||||
|
||||
if (localeType == 'la') {
|
||||
return {
|
||||
emptyHref: 'Inscriptio nexus vacua esse non potest',
|
||||
popupBlocked: 'Navigator fenestram novam prohibuit',
|
||||
internalOpenFailed: 'Pagina aperiri non potuit @uni_modules/uni-link-x/pages/link-webview',
|
||||
copiedOpenInBrowserPrefix: 'Nexus descriptus est. Quaeso eum in navigatro externo aperi:',
|
||||
copyFailedOpenInBrowser: 'Descriptio nexus defecit. Quaeso nexum manu describe et in navigatro externo aperi.'
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
emptyHref: 'The link URL cannot be empty',
|
||||
popupBlocked: 'The browser blocked the new window',
|
||||
internalOpenFailed: 'Failed to open page @uni_modules/uni-link-x/pages/link-webview',
|
||||
copiedOpenInBrowserPrefix: 'The link has been copied. Please open it in an external browser:',
|
||||
copyFailedOpenInBrowser: 'Failed to copy the link. Please copy it manually and open it in an external browser.'
|
||||
}
|
||||
}
|
||||
|
||||
function showPrompt(content : string) {
|
||||
uni.showModal({
|
||||
content,
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
|
||||
function copyLinkAndPrompt(url : string) {
|
||||
const messages = getLocaleMessages()
|
||||
uni.setClipboardData({
|
||||
data: url,
|
||||
showToast: false,
|
||||
success: () => {
|
||||
showPrompt(messages.copiedOpenInBrowserPrefix + '\n' + url)
|
||||
},
|
||||
fail: () => {
|
||||
showPrompt(messages.copyFailedOpenInBrowser)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function reportOpenError(message : string) {
|
||||
emit('error', message)
|
||||
}
|
||||
|
||||
function openInBuiltInWebview(url : string) {
|
||||
uni.navigateTo({
|
||||
url: `${LINK_WEBVIEW_PAGE_PATH}?url=${encodeURIComponent(url)}`,
|
||||
success: () => {},
|
||||
fail: () => {
|
||||
// 异常情况下可能pages.json中漏配了本组件下的link-webview.uvue页面
|
||||
reportOpenError(getLocaleMessages().internalOpenFailed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function openURL() {
|
||||
const url = props.href.trim()
|
||||
|
||||
if (url.length == 0) {
|
||||
reportOpenError(getLocaleMessages().emptyHref)
|
||||
return
|
||||
}
|
||||
|
||||
if (props.target == '_self') {
|
||||
openInBuiltInWebview(url)
|
||||
}
|
||||
else{
|
||||
// _blank方式
|
||||
// #ifdef WEB
|
||||
const targetWindow = window.open(url, '_blank')
|
||||
if (targetWindow != null) {
|
||||
return
|
||||
}
|
||||
else if (props.blankFallback=="turn-self"){
|
||||
openInBuiltInWebview(url)
|
||||
return
|
||||
}
|
||||
else if(props.blankFallback=="copy-link-modal"){
|
||||
copyLinkAndPrompt(url)
|
||||
return
|
||||
}
|
||||
reportOpenError(getLocaleMessages().popupBlocked)
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
if (props.blankFallback=="turn-self"){
|
||||
openInBuiltInWebview(url)
|
||||
}
|
||||
else if(props.blankFallback=="copy-link-modal"){
|
||||
copyLinkAndPrompt(url)
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP
|
||||
try {
|
||||
openSchema(url)
|
||||
} catch (error) {
|
||||
// 这里常见的情况是iOS设备在真机运行时缺少xcode环境编译openSchema失败
|
||||
if (props.blankFallback=="turn-self"){
|
||||
openInBuiltInWebview(url)
|
||||
}
|
||||
else if(props.blankFallback=="copy-link-modal"){
|
||||
copyLinkAndPrompt(url)
|
||||
}
|
||||
else{
|
||||
reportOpenError((error as Error).message) // 报错后发送错误事件,这里的错误原因未知,无法自行提前国际化
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"id": "uni-link-x",
|
||||
"displayName": "uni-link 链接",
|
||||
"version": "1.0.0",
|
||||
"description": "Link 链接组件,可自定义链接的打开方式。",
|
||||
"isExt": true,
|
||||
"extUrl": "https://ext.dcloud.net.cn/plugin?id=27898",
|
||||
"keywords": [
|
||||
"uni-ui",
|
||||
"uniui",
|
||||
"link",
|
||||
"链接",
|
||||
"uni-app-x"
|
||||
],
|
||||
"engines": {
|
||||
"HBuilderX": "",
|
||||
"uni-app": "",
|
||||
"uni-app-x": "^5.07"
|
||||
},
|
||||
"repository": "https://gitcode.com/dcloud/uni-ui-x",
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"type": "component-vue",
|
||||
"darkmode": "x",
|
||||
"i18n": "x",
|
||||
"widescreen": "x",
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"uts-openSchema"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "√",
|
||||
"aliyun": "√",
|
||||
"alipay": "x"
|
||||
},
|
||||
"client": {
|
||||
"uni-app-x": {
|
||||
"web": {
|
||||
"safari": "√",
|
||||
"chrome": "√"
|
||||
},
|
||||
"app": {
|
||||
"android": "√",
|
||||
"ios": "√",
|
||||
"harmony": "√"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "√"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<web-view style="flex: 1;" :src="currentUrl" @error="onWebViewError"></web-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const currentUrl = ref<string>('')
|
||||
|
||||
onLoad((options : UTSJSONObject | null) => {
|
||||
if (options == null) {
|
||||
return
|
||||
}
|
||||
const rawUrl = options['url']
|
||||
if (rawUrl == null) {
|
||||
return
|
||||
}
|
||||
const nextUrl = rawUrl as string
|
||||
currentUrl.value = decodeURIComponent(nextUrl) ?? nextUrl
|
||||
})
|
||||
|
||||
function onWebViewError(event : UniWebViewErrorEvent) {
|
||||
const detail = event.detail
|
||||
const errMsg = detail.errMsg ?? ''
|
||||
uni.showModal({
|
||||
content: `Error: ${errMsg}`,
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
打开web链接组件。点击后通过内置或外部浏览器打开链接,加载 `href` 属性中配置的 URL。
|
||||
|
||||
- `target="_self"` 时,使用 `uni_modules/uni-link-x/pages/link-webview` 页面内的 `web-view` 打开链接。
|
||||
* 注意小程序需要将域名加入应用的域名白名单。
|
||||
- `target="_blank"` 时,
|
||||
* APP平台调用手机浏览器打开链接
|
||||
* Web调用 `window.open(url, '_blank')`
|
||||
* 小程序平台无法打开手机浏览器,此时会走到失败回退逻辑中
|
||||
|
||||
`target="_blank"`失败时,可以通过组件属性`blank-fallback`来控制失败后的回退处理策略,`blank-fallback`属性有3个值:
|
||||
|
||||
1. turn-self :使用_self方式打开。此值为默认值。也就是 `target="_blank"` 时,小程序下默认转到内置浏览器打开。
|
||||
2. copy-link-modal :复制链接并弹框提醒。会复制URL到剪贴板中,并弹框提示用户在外部浏览器中打开。
|
||||
3. none :不回退。会触发组件的error事件。
|
||||
|
||||
- 开发者也可以通过条件编译组合使用,比如APP平台使用`_black`,小程序平台使用`_self`
|
||||
|
||||
常见失败原因:
|
||||
- 安装本组件时未在项目的pages.json中注册组件的内置页面。(HBuilderX导入本组件到项目下时会弹框提醒注册内置页面)
|
||||
- 小程序平台未将域名加入应用的域名白名单。
|
||||
- iOS运行基座未包括openSchema插件,仅在无xcode时运行会触发。打包其实不会发生此错误。
|
||||
- app平台,有些手机系统会弹框询问用户是否允许app打开浏览器,如果用户点了拒绝,这种情况没有失败回调。但有种判断方式是app的onHide生命周期是否触发,如果没有触发,那么意味着并没有启动系统浏览器。
|
||||
|
||||
### 基本用法
|
||||
|
||||
```html
|
||||
<uni-link href="https://uniapp.dcloud.net.cn">uni-app x 文档
|
||||
</uni-link>
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
| --- | --- | --- |
|
||||
| error | 打开失败时触发 | `message: string` |
|
||||
|
||||
### 依赖
|
||||
|
||||
`APP` 平台依赖 `uts-openSchema`。`uni-link` 已在 `uni_modules/uni-link-x/package.json` 中声明该依赖。
|
||||
|
||||
组件目录下有 `pages_init.json`,会向应用的 pages.json 中注册组件中自带的内置浏览器页面。如在HBuilderX中弹框询问是否将组件中的页面注册到项目pages.json中时,请选择同意。
|
||||
|
||||
### 示例页面
|
||||
|
||||
`/pages/uni-ui/link/link`
|
||||
Reference in New Issue
Block a user