Initial commit

This commit is contained in:
tanxin
2026-10-29 10:08:39 +08:00
commit 4c6243a274
124 changed files with 17359 additions and 0 deletions
+2
View File
@@ -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>
+76
View File
@@ -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>
+45
View File
@@ -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`