Files
taimed-international-app/uni_modules/yingbing-video/components/modules/iframe.vue

498 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view
:class="'iframe' + dataId"
class="find_iframe"
:data-sandbox="sandbox"
:data-allow="allow"
:data-allowfullscreen="allowfullscreen"
:data-frameborder="frameborder"
:data-loadingShow="loadingShow"
:data-console="console"
:data-iframeStyle="iframeStyleString"
:data-iframeClass="iframeClassString"
:data-crossOrigin="crossOrigin"
:data-setDataName="setDataName"
:data-overrideUrlLoadingOptions="overrideUrlLoadingOptionsString"
:ready="ready" :change:ready="ComIframe.readyWatcher"
:isDestroy="isDestroy" :change:isDestroy="ComIframe.destroyWatcher"
:src="src" :change:src="ComIframe.srcWatcher"
:sandbox="sandbox" :change:sandbox="ComIframe.sandboxWatcher"
:allow="allow" :change:allow="ComIframe.allowWatcher"
:allowfullscreen="allowfullscreen" :change:allowfullscreen="ComIframe.allowfullscreenWatcher"
:frameborder="frameborder" :change:frameborder="ComIframe.frameborderWatcher"
:iframeStyle="iframeStyle" :change:iframeStyle="ComIframe.iframeStyleWatcher"
:iframeClass="iframeClass" :change:iframeClass="ComIframe.iframeClassWatcher"
:jump="jump" :change:jump="ComIframe.jumpWatcher"
:assignUrl="assignUrl" :change:assignUrl="ComIframe.assignUrlWatcher"
:loadingStatus="loadingStatus" :change:loadingStatus="ComIframe.loadingWatcher"
:evalJS="evalJSString" :change:evalJS="ComIframe.evalJSWatcher"
:evalCSS="evalCSSString" :change:evalCSS="ComIframe.evalCSSWatcher"
:setData="setDataValue" :change:setData="ComIframe.setDataWatcher"
:overrideUrlLoading="overrideUrlLoadingOptionsString" :change:overrideUrlLoading="ComIframe.overrideUrlLoadingWatcher">
</view>
</template>
<script>
export default {
props: {
dataId: {
type: String,
default () {
return new Date().getTime().toString() + Math.round(Math.random() * 10000)
}
},
src: {//链接
type: String,
default: ''
},
sandbox: {//沙盒模式 原生属性
type: String,
default: 'allow-same-origin allow-scripts allow-forms allow-top-navigation-by-user-activation allow-popups allow-modals'
},
allow: {//允许一些操作 原生属性
type: String,
// default: 'autoplay; fullscreen; encrypted-media; picture-in-picture'
default: ''
},
allowfullscreen: {//是否允许全屏 原生属性
type: Boolean,
default: true
},
//跨域属性 anonymous-它有一个默认值。它定义了将在不传递凭据信息的情况下发送CORS请求 use-credentials-将发送带有凭据、cookie 和证书的 cross-origin 请求
crossOrigin: {
type: String,
default: ''
},
frameborder: {//iframe边框 原生属性
type: String,
default: '0'
},
iframeClass: {//iframe样式
type: [String, Object],
default: ''
},
iframeStyle: {//iframe样式
type: [String, Object],
default: ''
},
loadingShow: {//展示loading
type: Boolean,
default: true
},
console: {//控制console
type: String,
default: 'log warn error'
},
overrideUrlLoadingOptions: {//仅支持app
type: Object,
default: () => {
return {
effect: '',
mode: '',
match: '',
exclude: ''
}
}
}
},
computed: {
overrideUrlLoadingOptionsString () {
return JSON.stringify(this.overrideUrlLoadingOptions)
},
iframeStyleString () {
return JSON.stringify(this.iframeStyle)
},
iframeClassString () {
return JSON.stringify(this.iframeClass)
}
},
data () {
return {
ready: '',
jump: 0,
isDestroy: false,
evalJSString: '',//js字符串
evalCSSString: '',//注入css字符串
assignUrl: '',//跳转链接
loadingStatus: -1,//加载进度提控制
evalJSTask: new Map(),//注入js临时队列表
setDataName: '',
setDataValue: ''
}
},
mounted () {
this.$nextTick(function () {
setTimeout(() => {
this.ready = this.dataId
}, 100)
})
},
methods: {
//返回上一页
back () {
this.jump = 0
this.$nextTick(function () {
this.jump = -1
})
},
//调用内部iframe跳转链接
assign (url) {
this.assignUrl = url
},
//显示进度条加载
showLoading () {
this.loadingStatus = -1
this.$nextTick(function () {
this.loadingStatus = 1
})
},
//关闭进度条加载
hideLoading () {
this.loadingStatus = -1
this.$nextTick(function () {
this.loadingStatus = 0
})
},
//销毁
destroy () {
this.isDestroy = false
this.$nextTick(() => {
this.isDestroy = true
})
},
//注入js
evalJS (str) {
if ( this.evalJSString ) {//如果又正在进行注入的js任务
const id = new Date().getTime().toString() + Math.round(Math.random() * 10000)
this.evalJSTask.set(id, str)//加入等待队列
} else {
this.evalJSString = str
}
},
//注入css
evalCSS (str) {
this.evalCSSString = ''
this.$nextTick(() => {
this.evalCSSString = str
})
},
setData (key, value) {
this.setDataName = key
this.setDataValue = ''
this.$nextTick(() => {
this.setDataValue = value
})
},
loadstart (e) {
this.$emit('loadstart', e)
},
loaded (e) {
this.$emit('loaded', e)
},
callError (e) {
this.$emit('error', e)
},
backError () {
this.$emit('backerror')
},
callDestroy () {
this.$emit('destroyed')
},
overrideUrlLoading (e) {
this.$emit('overrideurlloading', e)
},
message (e) {
this.$emit('message', e)
},
callEvalJS (e) {//注入js回调
this.evalJSString = ''
if ( this.evalJSTask.size ) {//如果等待队列有值并且当前没有正在执行的注入js任务
const firstEntrie = this.evalJSTask.entries().next().value//获取等待队列中的第一个
this.evalJS(firstEntrie[1])//执行任务
this.evalJSTask.delete(firstEntrie[0])//删除等待队列
}
if ( e.status == 'error' ) this.callError(e.message)
}
}
}
</script>
<!-- #ifdef APP-VUE || H5 -->
<script lang="renderjs" type="module" module="ComIframe">
export default {
data () {
return {
dom: null,
iframe: null,
loading: null,
iframeSrc: '',
backTimer: null
}
},
mounted () {
window.addEventListener('message', this.messageListener);
},
beforeDestroy () {
this.destoryIframe()
},
methods: {
messageListener (e) {
this.callMethod('message', {origin: e.origin, data: e.data})
},
destoryIframe () {
window.removeEventListener('message', this.messageListener)
if ( this.loading ) {
this.loading.remove()
this.loading = null
}
if ( this.iframe ) {
this.iframe.remove()
this.iframe = null
}
this.callMethod('callDestroy')
},
showLoadingRender () {
if ( this.loading && this.getData('loadingShow') ) {
this.loading.classList.remove('browser-loading-hide')
this.loading.classList.add('browser-loading-show')
}
},
hideLoadingRender () {
if ( this.loading && this.getData('loadingShow') ) {
this.loading.classList.remove('browser-loading-show')
this.loading.classList.add('browser-loading-hide')
}
},
clearBackTimer () {
if ( this.backTimer ) {
window.clearTimeout(this.backTimer)
this.backTimer = null
}
},
initIframe () {
this.showLoadingRender()
this.callMethod('loadstart', {href: this.iframeSrc})
this.iframe = document.createElement('IFRAME')
this.iframe.setAttribute('class', 'find_iframe_iframe')
this.iframe.setAttribute('frameborder', this.getData('frameborder'))
this.iframe.setAttribute('allow', this.getData('allow'))
this.iframe.setAttribute('allowfullscreen', this.getData('allowfullscreen'))
this.iframe.setAttribute('sandbox', this.getData('sandbox'))
this.iframe.setAttribute('crossOrigin', this.getData('crossOrigin'))
if ( this.getData('iframeStyle') ) this.iframeStyleWatcher(JSON.parse(this.getData('iframeStyle')))//初始化style
if ( this.getData('iframeClass') ) this.iframeClassWatcher(JSON.parse(this.getData('iframeClass')))//初始化style
this.iframe.src = this.iframeSrc
this.iframe.onload = () => {
this.hideLoadingRender()
const iframeWindow = this.iframe.contentWindow
iframeWindow.onbeforeunload = (e) => {
this.clearBackTimer()
this.callMethod('loadstart', e)
this.showLoadingRender()
}
const history = iframeWindow.history
const iframeDocument = this.iframe.contentDocument || iframeWindow.document;
const title = iframeDocument.title
const href = iframeWindow.location.href
const head = iframeDocument.head;
const links = head.querySelectorAll('link[rel="icon"], link[rel="shortcut icon"]');//尝试从网页代码中获取favicon.ico
const favicon = links.length > 0 ? links[0].href : href + (href.substring(href.length -1) == '/' ? '' : '/') + 'favicon.ico'//没有获取到默认使用网址拼接favicon.ico
this.callMethod('loaded', {title, href, favicon, history})
}
this.iframe.onerror = (e) => {
this.hideLoadingRender()
this.callMethod('callError', e)
}
this.dom.appendChild(this.iframe)
//控制iframe内部的console
const consoles = this.getData('console').split(' ')
Object.keys(this.iframe.contentWindow.console).forEach(key => {
if ( consoles.indexOf(key) == -1 ) this.iframe.contentWindow.console[key] = function () {}
})
const options = JSON.parse(this.getData('overrideUrlLoadingOptions'))
if ( options && options.mode ) this.overrideUrlLoadingWatcher(this.getData('overrideUrlLoadingOptions'))
},
readyWatcher (newVal) {
if ( newVal ) {
this.dom = document.querySelector('.iframe' + newVal)
this.loading = document.createElement('DIV')
this.loading.setAttribute('class', 'browser-loading-line')
this.dom.appendChild(this.loading)
}
if ( this.iframeSrc && this.dom ) this.initIframe()
},
destroyWatcher (newVal) {
if ( newVal ) this.destoryIframe()
},
srcWatcher (newVal, oldVal) {
this.iframeSrc = newVal
if ( !this.dom ) return
if ( newVal != oldVal ) {
if ( oldVal ) this.destoryIframe()
if ( newVal ) this.initIframe()
}
},
sandboxWatcher (newVal) {
this.iframe && this.iframe.setAttribute('sandbox', newVal)
},
allowWatcher (newVal) {
this.iframe && this.iframe.setAttribute('allow', allow)
},
allowfullscreenWatcher (newVal) {
this.iframe && this.iframe.setAttribute('allowfullscreen', newVal)
},
frameborderWatcher (newVal) {
this.iframe && this.iframe.setAttribute('frameborder', newVal)
},
iframeStyleWatcher (newVal) {
if ( typeof newVal == "string" ) this.iframe && this.iframe.setAttribute('style', newVal)
if ( typeof newVal == "object" ) {
let style = ''
Object.keys(newVal).forEach(key => {
style += key + ':' + newVal[key] + ';'
})
this.iframe && this.iframe.setAttribute('style', style)
}
},
iframeClassWatcher (newVal) {
if ( typeof newVal == "string" ) this.iframe && this.iframe.setAttribute('class', 'find_iframe_iframe ' + newVal)
if ( typeof newVal == "object" ) {
let className = ''
Object.keys(newVal).forEach(key => {
className += newVal[key] ? (key + ' ') : ''
})
this.iframe && this.iframe.setAttribute('class', 'find_iframe_iframe ' + className)
}
},
jumpWatcher (newVal) {
if ( newVal < 0 && this.iframe ) {
this.iframe.contentWindow.history.back()
this.backTimer = window.setTimeout(() => {
this.callMethod('backError')
}, 300)
}
},
assignUrlWatcher (newVal) {
const iframeWindow = this.iframe?.contentWindow
iframeWindow && iframeWindow.window.location.assign(newVal);
},
loadingWatcher (newVal) {
if ( newVal == 0 ) this.hideLoading()
if ( newVal == 1 ) this.showLoading()
},
evalJSWatcher (newVal) {
if ( newVal ) {
try{
const iframeDocument = this.iframe.contentDocument || iframeWindow.document;
const script = iframeDocument.createElement('SCRIPT')
script.innerHTML = newVal
iframeDocument.head.appendChild(script)//插入script标签
this.callMethod('callEvalJS', {status: 'success'})//通知逻辑层成功执行回调
iframeDocument.head.removeChild(script)//执行完script标签后需要移除
}catch(e){
this.callMethod('callEvalJS', {status: 'error', message: JSON.stringify(e)})
}
}
},
evalCSSWatcher (newVal) {
if ( newVal ) {
try{
const iframeDocument = this.iframe.contentDocument || iframeWindow.document;
const style = iframeDocument.createElement('STYLE')
style.innerHTML = newVal
iframeDocument.head.appendChild(style)
}catch(e){
this.callMethod('callError', e)
}
}
},
//给网页值传参
setDataWatcher (newVal) {
if ( newVal ) {
try{
const iframeWindow = this.iframe.contentWindow
const key = this.getData('setDataName')
if ( key ) iframeWindow[key] = newVal
}catch(e){
this.callMethod('callEvalJS', {status: 'error', message: JSON.stringify(e)})
}
}
},
//拦截监听
overrideUrlLoadingWatcher (newVal) {
// #ifdef APP-VUE
const wv = plus.webview.currentWebview()
const options = JSON.parse(newVal)
wv.overrideUrlLoading(options, (e) => {
this.callMethod('overrideUrlLoading', e)
return true;
});
// #endif
},
getData (name) {
const value = this.dom.getAttribute('data-' + name)
if ( ['true', 'false'].includes(value) ) return value == 'false' ? false : true
else if ( /^\d+$/.test(value) ) return Number(value)
else return value
},
callMethod (name, args) {
// #ifndef H5
this.$ownerInstance.callMethod(name, args)
// #endif
// #ifdef H5
this[name](args)
// #endif
}
}
}
</script>
<!-- #endif -->
<style>
@keyframes loading-show {
from {
width: 0;
}
to {
width: 99%;
}
}
@keyframes loading-hide {
from {
width: 0;
opacity: 1;
}
to {
width: 100%;
opacity: 0;
}
}
.find_iframe {
position: relative;
width: 100%;
height: 100%;
}
/deep/ .find_iframe_iframe {
width: 100%;
height: 100%;
}
/deep/ .browser-loading-line {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 3px;
border-radius: 3px;
background-color: #4cd964;
z-index: 1;
touch-action: none;
pointer-events: none;
}
/deep/ .browser-loading-show {
animation: loading-show 5s ease both;
}
/deep/ .browser-loading-hide {
animation: loading-hide .5s linear both;
}
</style>