优化组件/更新

This commit is contained in:
gyq
2025-12-03 10:13:55 +08:00
parent 92f9776a35
commit 09b6e36a52
261 changed files with 22080 additions and 7238 deletions

View File

@@ -2,7 +2,7 @@
<view id="_root" :class="(selectable?'_select ':'')+'_root'" :style="containerStyle">
<slot v-if="!nodes[0]" />
<!-- #ifndef APP-PLUS-NVUE -->
<node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu]" name="span" />
<node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]" name="span" />
<!-- #endif -->
<!-- #ifdef APP-PLUS-NVUE -->
<web-view ref="web" src="/uni_modules/mp-html/static/app-plus/mp-html/local.html" :style="'margin-top:-2px;height:' + height + 'px'" @onPostMessage="_onMessage" />
@@ -12,7 +12,7 @@
<script>
/**
* mp-html v2.3.0
* mp-html v2.5.1
* @description 富文本组件
* @tutorial https://github.com/jin-yufeng/mp-html
* @property {String} container-style 容器的样式
@@ -128,7 +128,6 @@ export default {
},
beforeDestroy () {
this._hook('onDetached')
clearInterval(this._timer)
},
methods: {
/**
@@ -284,6 +283,28 @@ export default {
// #endif
},
/**
* @description 设置媒体播放速率
* @param {Number} rate 播放速率
*/
setPlaybackRate (rate) {
this.playbackRate = rate
for (let i = (this._videos || []).length; i--;) {
this._videos[i].playbackRate(rate)
}
// #ifdef APP-PLUS
const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].playbackRate=' + rate
// #ifndef APP-PLUS-NVUE
let page = this.$parent
while (!page.$scope) page = page.$parent
page.$scope.$getAppWebview().evalJS(command)
// #endif
// #ifdef APP-PLUS-NVUE
this.$refs.web.evalJs(command)
// #endif
// #endif
},
/**
* @description 设置内容
* @param {String} content html 内容
@@ -308,19 +329,32 @@ export default {
this.$emit('load')
})
// 等待图片加载完毕
let height
clearInterval(this._timer)
this._timer = setInterval(() => {
this.getRect().then(rect => {
if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
// 设置懒加载,每 350ms 获取高度,不变则认为加载完毕
let height = 0
const callback = rect => {
if (!rect || !rect.height) rect = {}
// 350ms 总高度无变化就触发 ready 事件
if (rect.height === height) {
this.$emit('ready', rect)
clearInterval(this._timer)
} else {
height = rect.height
setTimeout(() => {
this.getRect().then(callback).catch(callback)
}, 350)
}
height = rect.height
}).catch(() => { })
}, 350)
}
this.getRect().then(callback).catch(callback)
} else {
// 未设置懒加载,等待所有图片加载完毕
if (!this.imgList._unloadimgs) {
this.getRect().then(rect => {
this.$emit('ready', rect)
}).catch(() => {
this.$emit('ready', {})
})
}
}
// #endif
},
@@ -340,7 +374,7 @@ export default {
* @description 设置内容
*/
_set (nodes, append) {
this.$refs.web.evalJs('setContent(' + JSON.stringify(nodes) + ',' + JSON.stringify([this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''), this.errorImg, this.loadingImg, this.pauseVideo, this.scrollTable, this.selectable]) + ',' + append + ')')
this.$refs.web.evalJs('setContent(' + JSON.stringify(nodes).replace(/%22/g, '') + ',' + JSON.stringify([this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''), this.errorImg, this.loadingImg, this.pauseVideo, this.scrollTable, this.selectable]) + ',' + append + ')')
},
/**
@@ -366,7 +400,9 @@ export default {
case 'onReady':
this.getRect().then(res => {
this.$emit('ready', res)
}).catch(() => { })
}).catch(() => {
this.$emit('ready', {})
})
break
// 总高度发生变化
case 'onHeightChange':

View File

@@ -3,20 +3,33 @@
<block v-for="(n, i) in childs" v-bind:key="i">
<!-- 图片 -->
<!-- 占位图 -->
<image v-if="n.name==='img'&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
<image v-if="n.name==='img'&&!n.t&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
<!-- 显示图片 -->
<!-- #ifdef H5 || (APP-PLUS && VUE2) -->
<img v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #endif -->
<!-- #ifndef H5 || APP-PLUS -->
<image v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #ifndef H5 || (APP-PLUS && VUE2) -->
<!-- 表格中的图片使用 rich-text 防止大小不正确 -->
<rich-text v-if="n.name==='img'&&n.t" :style="'display:'+n.t" :nodes="[{attrs:{style:n.attrs.style||'',src:n.attrs.src},name:'img'}]" :data-i="i" @tap.stop="imgTap" />
<!-- #endif -->
<!-- #ifdef APP-HARMONY -->
<image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+ctrl[i]+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':(n.m||'scaleToFill'))" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #endif -->
<!-- #ifndef H5 || APP-PLUS || MP-KUAISHOU -->
<image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="!n.h?'widthFix':(!n.w?'heightFix':(n.m||'scaleToFill'))" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #endif -->
<!-- #ifdef MP-KUAISHOU -->
<image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src" :lazy-load="opts[0]" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap"></image>
<!-- #endif -->
<!-- #ifdef APP-PLUS && VUE3 -->
<image v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':(n.m||''))" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #endif -->
<!-- 文本 -->
<!-- #ifndef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
<text v-else-if="n.text" :user-select="n.us" decode>{{n.text}}</text>
<!-- #ifdef MP-WEIXIN -->
<text v-else-if="n.text" :user-select="opts[4]=='force'&&isiOS" decode>{{n.text}}</text>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN || MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
<text v-else-if="n.text" decode>{{n.text}}</text>
<!-- #endif -->
<text v-else-if="n.name==='br'">\n</text>
<!-- 链接 -->
@@ -25,7 +38,7 @@
</view>
<!-- 视频 -->
<!-- #ifdef APP-PLUS -->
<view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" @vplay.stop="play" />
<view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" :data-i="i" @vplay.stop="play" />
<!-- #endif -->
<!-- #ifndef APP-PLUS -->
<video v-else-if="n.name==='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :object-fit="n.attrs['object-fit']" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
@@ -57,10 +70,10 @@
<!-- 富文本 -->
<!-- #ifdef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
<rich-text v-else-if="!n.c&&!handler.isInline(n.name, n.attrs.style)" :id="n.attrs.id" :style="n.f" :nodes="[n]" />
<rich-text v-else-if="!n.c&&!handler.isInline(n.name, n.attrs.style)" :id="n.attrs.id" :style="n.f" :user-select="opts[4]" :nodes="[n]" />
<!-- #endif -->
<!-- #ifndef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
<rich-text v-else-if="!n.c" :id="n.attrs.id" :style="n.f+';display:inline'" :preview="false" :nodes="[n]" />
<rich-text v-else-if="!n.c" :id="n.attrs.id" :style="'display:inline;'+n.f" :preview="false" :selectable="opts[4]" :user-select="opts[4]" :nodes="[n]" />
<!-- #endif -->
<!-- 继续递归 -->
<view v-else-if="n.c===2" :id="n.attrs.id" :class="'_block _'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style">
@@ -113,7 +126,10 @@ export default {
},
data () {
return {
ctrl: {}
ctrl: {},
// #ifdef MP-WEIXIN
isiOS: uni.getSystemInfoSync().system.includes('iOS')
// #endif
}
},
props: {
@@ -129,7 +145,7 @@ export default {
},
components: {
// #ifndef H5 && VUE3
// #ifndef ((H5 || APP-PLUS) && VUE3) || APP-HARMONY
node
// #endif
},
@@ -167,14 +183,22 @@ export default {
},
methods:{
// #ifdef MP-WEIXIN
toJSON () { },
toJSON () { return this },
// #endif
/**
* @description 播放视频事件
* @param {Event} e
*/
play (e) {
this.root.$emit('play')
const i = e.currentTarget.dataset.i
const node = this.childs[i]
this.root.$emit('play', {
source: node.name,
attrs: {
...node.attrs,
src: node.src[this.ctrl[i] || 0]
}
})
// #ifndef APP-PLUS
if (this.root.pauseVideo) {
let flag = false
@@ -194,6 +218,9 @@ export default {
// #endif
)
ctx.id = id
if (this.root.playbackRate) {
ctx.playbackRate(this.root.playbackRate)
}
this.root._videos.push(ctx)
}
}
@@ -214,7 +241,14 @@ export default {
// #ifdef H5 || APP-PLUS
node.attrs.src = node.attrs.src || node.attrs['data-src']
// #endif
// #ifndef APP-HARMONY
this.root.$emit('imgtap', node.attrs)
// #endif
// #ifdef APP-HARMONY
this.root.$emit('imgtap', {
...node.attrs
})
// #endif
// 自动预览图片
if (this.root.previewImg) {
uni.previewImage({
@@ -279,6 +313,25 @@ export default {
// 加载完毕,取消加载中占位图
this.$set(this.ctrl, i, 1)
}
this.checkReady()
},
/**
* @description 检查是否所有图片加载完毕
*/
checkReady () {
if (this.root && !this.root.lazyLoad) {
this.root._unloadimgs -= 1
if (!this.root._unloadimgs) {
setTimeout(() => {
this.root.getRect().then(rect => {
this.root.$emit('ready', rect)
}).catch(() => {
this.root.$emit('ready', {})
})
}, 350)
}
}
},
/**
@@ -355,6 +408,7 @@ export default {
if (this.opts[2]) {
this.$set(this.ctrl, i, -1)
}
this.checkReady()
}
if (this.root) {
this.root.$emit('error', {

View File

@@ -71,16 +71,24 @@ const config = {
viewbox: 'viewBox',
attributename: 'attributeName',
repeatcount: 'repeatCount',
repeatdur: 'repeatDur'
repeatdur: 'repeatDur',
foreignobject: 'foreignObject'
}
}
const tagSelector={}
const {
windowWidth,
let windowWidth, system
// #ifdef MP-WEIXIN
if (uni.canIUse('getWindowInfo')) {
windowWidth = uni.getWindowInfo().windowWidth
system = uni.getDeviceInfo().system
} else {
// #endif
const systemInfo = uni.getSystemInfoSync()
windowWidth = systemInfo.windowWidth
// #ifdef MP-WEIXIN
system
// #endif
} = uni.getSystemInfoSync()
system = systemInfo.system
}
// #endif
const blankChar = makeMap(' ,\r,\n,\t,\f')
let idIndex = 0
@@ -138,6 +146,26 @@ function decodeEntity (str, amp) {
return str
}
/**
* @description 合并多个块级标签,加快长内容渲染
* @param {Array} nodes 要合并的标签数组
*/
function mergeNodes (nodes) {
let i = nodes.length - 1
for (let j = i; j >= -1; j--) {
if (j === -1 || nodes[j].c || !nodes[j].name || (nodes[j].name !== 'div' && nodes[j].name !== 'p' && nodes[j].name[0] !== 'h') || (nodes[j].attrs.style || '').includes('inline')) {
if (i - j >= 5) {
nodes.splice(j + 1, i - j, {
name: 'div',
attrs: {},
children: nodes.slice(j + 1, i + 1)
})
}
i = j - 1
}
}
}
/**
* @description html 解析器
* @param {Object} vm 组件实例
@@ -146,6 +174,7 @@ function Parser (vm) {
this.options = vm || {}
this.tagStyle = Object.assign({}, config.tagStyle, this.options.tagStyle)
this.imgList = vm.imgList || []
this.imgList._unloadimgs = 0
this.plugins = vm.plugins || []
this.attrs = Object.create(null)
this.stack = []
@@ -170,6 +199,9 @@ Parser.prototype.parse = function (content) {
while (this.stack.length) {
this.popNode()
}
if (this.nodes.length > 50) {
mergeNodes(this.nodes)
}
return this.nodes
}
@@ -214,9 +246,15 @@ Parser.prototype.getUrl = function (url) {
} else if (domain) {
// 否则补充整个域名
url = domain + url
}
} else if (domain && !url.includes('data:') && !url.includes('://')) {
url = domain + '/' + url
} /* #ifdef APP-PLUS */ else {
url = plus.io.convertLocalFileSystemURL(url)
} /* #endif */
} else if (!url.includes('data:') && !url.includes('://')) {
if (domain) {
url = domain + '/' + url
} /* #ifdef APP-PLUS */ else {
url = plus.io.convertLocalFileSystemURL(url)
} /* #endif */
}
return url
}
@@ -291,6 +329,7 @@ Parser.prototype.onTagName = function (name) {
this.tagName = this.xml ? name : name.toLowerCase()
if (this.tagName === 'svg') {
this.xml = (this.xml || 0) + 1 // svg 标签内大小写敏感
config.ignoreTags.style = undefined // svg 标签内 style 可用
}
}
@@ -301,6 +340,12 @@ Parser.prototype.onTagName = function (name) {
*/
Parser.prototype.onAttrName = function (name) {
name = this.xml ? name : name.toLowerCase()
// #ifdef (VUE3 && (H5 || APP-PLUS)) || APP-PLUS-NVUE
if (name.includes('?') || name.includes(';')) {
this.attrName = undefined
return
}
// #endif
if (name.substr(0, 5) === 'data-') {
if (name === 'data-src' && !this.attrs.src) {
// data-src 自动转为 src
@@ -427,7 +472,7 @@ Parser.prototype.onOpenTag = function (selfClose) {
node.webp = 'T'
}
// data url 图片如果没有设置 original-src 默认为不可预览的小图片
if (attrs.src.includes('data:') && !attrs['original-src']) {
if (attrs.src.includes('data:') && this.options.previewImg !== 'all' && !attrs['original-src']) {
attrs.ignore = 'T'
}
if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) {
@@ -435,11 +480,18 @@ Parser.prototype.onOpenTag = function (selfClose) {
const item = this.stack[i]
if (item.name === 'a') {
node.a = item.attrs
break
}
if (item.name === 'table' && !node.webp && !attrs.src.includes('cloud://')) {
if (!styleObj.display || styleObj.display.includes('inline')) {
node.t = 'inline-block'
} else {
node.t = styleObj.display
}
styleObj.display = undefined
}
// #ifndef H5 || APP-PLUS
const style = item.attrs.style || ''
if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || !styleObj.width.includes('%'))) {
if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || parseInt(styleObj.width) > 100)) {
styleObj.width = '100% !important'
styleObj.height = ''
for (let j = i + 1; j < this.stack.length; j++) {
@@ -483,6 +535,9 @@ Parser.prototype.onOpenTag = function (selfClose) {
}
// #endif
this.imgList.push(src)
if (!node.t) {
this.imgList._unloadimgs += 1
}
// #ifdef H5 || APP-PLUS
if (this.options.lazyLoad) {
attrs['data-src'] = attrs.src
@@ -511,6 +566,13 @@ Parser.prototype.onOpenTag = function (selfClose) {
if (!isNaN(parseInt(styleObj.height)) && (!styleObj.height.includes('%') || (parent && (parent.attrs.style || '').includes('height')))) {
node.h = 'T'
}
if (node.w && node.h && styleObj['object-fit']) {
if (styleObj['object-fit'] === 'contain') {
node.m = 'aspectFit'
} else if (styleObj['object-fit'] === 'cover') {
node.m = 'aspectFill'
}
}
} else if (node.name === 'svg') {
siblings.push(node)
this.stack.push(node)
@@ -523,6 +585,11 @@ Parser.prototype.onOpenTag = function (selfClose) {
}
}
attrs.style = attrs.style.substr(1) || undefined
// #ifdef (MP-WEIXIN || MP-QQ) && VUE3
if (!attrs.style) {
delete attrs.style
}
// #endif
} else {
if ((node.name === 'pre' || ((attrs.style || '').includes('white-space') && attrs.style.includes('pre'))) && this.pre !== 2) {
this.pre = node.pre = 1
@@ -556,8 +623,8 @@ Parser.prototype.onCloseTag = function (name) {
siblings.push({
name,
attrs: {
class: tagSelector[name],
style: this.tagStyle[name]
class: tagSelector[name] || '',
style: this.tagStyle[name] || ''
}
})
}
@@ -632,11 +699,19 @@ Parser.prototype.popNode = function () {
return
}
const name = config.svgDict[node.name] || node.name
if (name === 'foreignObject') {
for (const child of (node.children || [])) {
if (child.attrs && !child.attrs.xmlns) {
child.attrs.xmlns = 'http://www.w3.org/1999/xhtml'
break
}
}
}
src += '<' + name
for (const item in node.attrs) {
const val = node.attrs[item]
if (val) {
src += ` ${config.svgDict[item] || item}="${val}"`
src += ` ${config.svgDict[item] || item}="${val.replace(/"/g, '')}"`
}
}
if (!node.children) {
@@ -658,6 +733,7 @@ Parser.prototype.popNode = function () {
node.children = undefined
// #endif
this.xml = false
config.ignoreTags.style = true
return
}
@@ -777,6 +853,8 @@ Parser.prototype.popNode = function () {
let padding = parseFloat(attrs.cellpadding)
let spacing = parseFloat(attrs.cellspacing)
const border = parseFloat(attrs.border)
const bordercolor = styleObj['border-color']
const borderstyle = styleObj['border-style']
if (node.c) {
// padding 和 spacing 默认 2
if (isNaN(padding)) {
@@ -787,11 +865,15 @@ Parser.prototype.popNode = function () {
}
}
if (border) {
attrs.style += ';border:' + border + 'px solid gray'
attrs.style += `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}`
}
if (node.flag && node.c) {
// 有 colspan 或 rowspan 且含有链接的表格通过 grid 布局实现
styleObj.display = 'grid'
if (styleObj['border-collapse'] === 'collapse') {
styleObj['border-collapse'] = undefined
spacing = 0
}
if (spacing) {
styleObj['grid-gap'] = spacing + 'px'
styleObj.padding = spacing + 'px'
@@ -809,6 +891,23 @@ Parser.prototype.popNode = function () {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].name === 'tr') {
trList.push(nodes[i])
} else if (nodes[i].name === 'colgroup') {
let colI = 1
for (const col of (nodes[i].children || [])) {
if (col.name === 'col') {
const style = col.attrs.style || ''
const start = style.indexOf('width') ? style.indexOf(';width') : 0
// 提取出宽度
if (start !== -1) {
let end = style.indexOf(';', start + 6)
if (end === -1) {
end = style.length
}
width[colI] = style.substring(start ? start + 7 : 6, end)
}
colI += 1
}
}
} else {
traversal(nodes[i].children || [])
}
@@ -825,7 +924,7 @@ Parser.prototype.popNode = function () {
col++
}
let style = td.attrs.style || ''
const start = style.indexOf('width') ? style.indexOf(';width') : 0
let start = style.indexOf('width') ? style.indexOf(';width') : 0
// 提取出 td 的宽度
if (start !== -1) {
let end = style.indexOf(';', start + 6)
@@ -837,7 +936,30 @@ Parser.prototype.popNode = function () {
}
style = style.substr(0, start) + style.substr(end)
}
style += (border ? `;border:${border}px solid gray` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '')
// 设置竖直对齐
style += ';display:flex'
start = style.indexOf('vertical-align')
if (start !== -1) {
const val = style.substr(start + 15, 10)
if (val.includes('middle')) {
style += ';align-items:center'
} else if (val.includes('bottom')) {
style += ';align-items:flex-end'
}
} else {
style += ';align-items:center'
}
// 设置水平对齐
start = style.indexOf('text-align')
if (start !== -1) {
const val = style.substr(start + 11, 10)
if (val.includes('center')) {
style += ';justify-content: center'
} else if (val.includes('right')) {
style += ';justify-content: right'
}
}
style = (border ? `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '') + ';' + style
// 处理列合并
if (td.attrs.colspan) {
style += `;grid-column-start:${col};grid-column-end:${col + parseInt(td.attrs.colspan)}`
@@ -890,7 +1012,7 @@ Parser.prototype.popNode = function () {
const td = nodes[i]
if (td.name === 'th' || td.name === 'td') {
if (border) {
td.attrs.style = `border:${border}px solid gray;${td.attrs.style || ''}`
td.attrs.style = `border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'};${td.attrs.style || ''}`
}
if (padding) {
td.attrs.style = `padding:${padding}px;${td.attrs.style || ''}`
@@ -912,11 +1034,26 @@ Parser.prototype.popNode = function () {
node.children = [table]
attrs = table.attrs
}
} else if ((node.name === 'tbody' || node.name === 'tr') && node.flag && node.c) {
node.flag = undefined;
(function traversal (nodes) {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].name === 'td') {
// 颜色样式设置给单元格避免丢失
for (const style of ['color', 'background', 'background-color']) {
if (styleObj[style]) {
nodes[i].attrs.style = style + ':' + styleObj[style] + ';' + (nodes[i].attrs.style || '')
}
}
} else {
traversal(nodes[i].children || [])
}
}
})(children)
} else if ((node.name === 'td' || node.name === 'th') && (attrs.colspan || attrs.rowspan)) {
for (let i = this.stack.length; i--;) {
if (this.stack[i].name === 'table') {
if (this.stack[i].name === 'table' || this.stack[i].name === 'tbody' || this.stack[i].name === 'tr') {
this.stack[i].flag = 1 // 指示含有合并单元格
break
}
}
} else if (node.name === 'ruby') {
@@ -941,18 +1078,20 @@ Parser.prototype.popNode = function () {
}
}
} else if (node.c) {
node.c = 2
for (let i = node.children.length; i--;) {
const child = node.children[i]
// #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
if (child.name && (config.inlineTags[child.name] || (child.attrs.style || '').includes('inline'))) {
child.c = 1
(function traversal (node) {
node.c = 2
for (let i = node.children.length; i--;) {
const child = node.children[i]
// #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
if (child.name && (config.inlineTags[child.name] || ((child.attrs.style || '').includes('inline') && child.children)) && !child.c) {
traversal(child)
}
// #endif
if (!child.c || child.name === 'table') {
node.c = 1
}
}
// #endif
if (!child.c || child.name === 'table') {
node.c = 1
}
}
})(node)
}
if ((styleObj.display || '').includes('flex') && !node.c) {
@@ -977,22 +1116,8 @@ Parser.prototype.popNode = function () {
node.f = ';max-width:100%'
}
// 优化长内容加载速度
if (children.length >= 50 && node.c && !(styleObj.display || '').includes('flex')) {
let i = children.length - 1
for (let j = i; j >= -1; j--) {
// 合并多个块级标签
if (j === -1 || children[j].c || !children[j].name || (children[j].name !== 'div' && children[j].name !== 'p' && children[j].name[0] !== 'h') || (children[j].attrs.style || '').includes('inline')) {
if (i - j >= 5) {
children.splice(j + 1, i - j, {
name: 'div',
attrs: {},
children: node.children.slice(j + 1, i + 1)
})
}
i = j - 1
}
}
mergeNodes(children)
}
// #endif
@@ -1012,8 +1137,10 @@ Parser.prototype.popNode = function () {
}
attrs.style = attrs.style.substr(1) || undefined
// #ifdef (MP-WEIXIN || MP-QQ) && VUE3
if (!attrs.style) {
delete attrs.style
for (const key in attrs) {
if (!attrs[key]) {
delete attrs[key]
}
}
// #endif
}
@@ -1040,7 +1167,15 @@ Parser.prototype.onText = function (text) {
}
}
// 去除含有换行符的空串
if (trim === ' ' && flag) return
if (trim === ' ') {
if (flag) return
// #ifdef VUE3
else {
const parent = this.stack[this.stack.length - 1]
if (parent && parent.name[0] === 't') return
}
// #endif
}
text = trim
}
const node = Object.create(null)
@@ -1051,9 +1186,8 @@ Parser.prototype.onText = function (text) {
node.text = decodeEntity(text)
if (this.hook(node)) {
// #ifdef MP-WEIXIN
if (this.options.selectable === 'force' && system.includes('iOS')) {
if (this.options.selectable === 'force' && system.includes('iOS') && !uni.canIUse('rich-text.user-select')) {
this.expose()
node.us = 'T'
}
// #endif
const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes