Initial commit
This commit is contained in:
2
wechat-mini-program/node_modules/mp-html/plugins/README.md
generated
vendored
Normal file
2
wechat-mini-program/node_modules/mp-html/plugins/README.md
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# 插件
|
||||
本目录下是一些扩展插件,可以按照需要选用以实现更加丰富的功能
|
||||
25
wechat-mini-program/node_modules/mp-html/plugins/audio/README.md
generated
vendored
Normal file
25
wechat-mini-program/node_modules/mp-html/plugins/audio/README.md
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# audio
|
||||
功能:音乐播放器
|
||||
大小:*≈4KB*
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | √ | √ | √ | √(nvue 不支持) |
|
||||
|
||||
百度小程序原生包在此 [问题](https://smartprogram.baidu.com/forum/topic/show/125787) 未解决前无法使用
|
||||
|
||||
说明:
|
||||
在大多数小程序平台,*audio* 标签已被废弃或无法使用,本插件可以代替 *audio* 标签播放音乐,并实现以下优化:
|
||||
1. *pause-video* 属性也可以应用于音频,即播放一个音视频时可以自动暂停其他正在播放的音视频
|
||||
2. 增加了一个可以拖动的进度条
|
||||
3. 组件大小可以根据页面宽度自动调整
|
||||
4. 支持 *autoplay* 属性
|
||||
5. 播放被后台打断时,页面显示后自动继续播放
|
||||
|
||||
基础库要求:
|
||||
支付宝 *1.23.4+* ,其余平台满足最低要求即可
|
||||
第 *5* 条仅微信 *2.2.3+* 、*QQ*、百度支持
|
||||
|
||||
?> 如果希望页面上使用本组件,组件的路径为 *path/to/mp-html/audio/audio*
|
||||
属性和事件基本同 *audio* 组件,组件实例上提供了 *setSrc*、*play*、*seek*、*pause*、*stop* 方法可供控制播放状态
|
||||
11
wechat-mini-program/node_modules/mp-html/plugins/audio/build.js
generated
vendored
Normal file
11
wechat-mini-program/node_modules/mp-html/plugins/audio/build.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
module.exports = {
|
||||
usingComponents: {
|
||||
'my-audio': '../audio/audio'
|
||||
},
|
||||
handler (file) {
|
||||
// 删去原来的 audio 标签
|
||||
if (file.basename === 'node.wxml' || file.basename === 'node.vue') {
|
||||
file.contents = Buffer.from(file.contents.toString().replace(/<audio[\s\S]+?>/, ''))
|
||||
}
|
||||
}
|
||||
}
|
||||
7
wechat-mini-program/node_modules/mp-html/plugins/audio/context.js
generated
vendored
Normal file
7
wechat-mini-program/node_modules/mp-html/plugins/audio/context.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
const ctx = {}
|
||||
|
||||
module.exports = {
|
||||
get: id => ctx[id],
|
||||
set: (id, vm) => { ctx[id] = vm },
|
||||
remove: id => { ctx[id] = undefined }
|
||||
}
|
||||
34
wechat-mini-program/node_modules/mp-html/plugins/audio/index.js
generated
vendored
Normal file
34
wechat-mini-program/node_modules/mp-html/plugins/audio/index.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @fileoverview audio 插件
|
||||
*/
|
||||
const context = require('./context')
|
||||
let index = 0
|
||||
|
||||
function Audio (vm) {
|
||||
this.vm = vm
|
||||
}
|
||||
|
||||
Audio.prototype.onUpdate = function () {
|
||||
this.audios = []
|
||||
}
|
||||
|
||||
Audio.prototype.onParse = function (node) {
|
||||
if (node.name === 'audio') {
|
||||
if (!node.attrs.id) {
|
||||
node.attrs.id = 'a' + index++
|
||||
}
|
||||
this.audios.push(node.attrs.id)
|
||||
}
|
||||
}
|
||||
|
||||
Audio.prototype.onLoad = function () {
|
||||
setTimeout(() => {
|
||||
for (let i = 0; i < this.audios.length; i++) {
|
||||
const ctx = context.get(this.audios[i])
|
||||
ctx.id = this.audios[i]
|
||||
this.vm._videos.push(ctx)
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
module.exports = Audio
|
||||
189
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/audio.js
generated
vendored
Normal file
189
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/audio.js
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @fileoverview audio 组件
|
||||
*/
|
||||
const context = require('./context')
|
||||
|
||||
Component({
|
||||
data: {
|
||||
time: '00:00'
|
||||
},
|
||||
properties: {
|
||||
name: String, // 音乐名
|
||||
author: String, // 作者
|
||||
poster: String, // 海报图片地址
|
||||
autoplay: Boolean, // 是否自动播放
|
||||
controls: Boolean, // 是否显示控件
|
||||
loop: Boolean, // 是否循环播放
|
||||
src: { // 源地址
|
||||
type: String,
|
||||
observer (src) {
|
||||
this.setSrc(src)
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
// 创建内部 context
|
||||
this._ctx = wx.createInnerAudioContext()
|
||||
this._ctx.onError(err => {
|
||||
this.setData({
|
||||
error: true
|
||||
})
|
||||
this.triggerEvent('error', err)
|
||||
})
|
||||
this._ctx.onTimeUpdate(() => {
|
||||
const time = this._ctx.currentTime
|
||||
const min = parseInt(time / 60)
|
||||
const sec = Math.ceil(time % 60)
|
||||
const data = {}
|
||||
data.time = (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec)
|
||||
// 不在拖动状态下需要更新进度条
|
||||
if (!this.lastTime) {
|
||||
data.value = time / this._ctx.duration * 100
|
||||
}
|
||||
this.setData(data)
|
||||
})
|
||||
this._ctx.onEnded(() => {
|
||||
if (!this.properties.loop) {
|
||||
this.setData({
|
||||
playing: false
|
||||
})
|
||||
}
|
||||
})
|
||||
// #ifndef ALIPAY
|
||||
},
|
||||
attached () {
|
||||
context.set(this.id, this)
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
context.set(this.properties.id, this)
|
||||
this.setSrc(this.properties.src)
|
||||
// #endif
|
||||
},
|
||||
// #ifdef MP-ALIPAY
|
||||
didUpdate (e) {
|
||||
if (e.src !== this.properties.src) {
|
||||
this.setSrc(this.properties.src)
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
detached () {
|
||||
this._ctx.destroy()
|
||||
// #ifndef MP-ALIPAY
|
||||
context.remove(this.id)
|
||||
// #endif
|
||||
// #ifdef MP_ALIPAY
|
||||
context.remove(this.properties.id)
|
||||
// #endif
|
||||
},
|
||||
// #ifndef ALIPAY | TOUTIAO
|
||||
pageLifetimes: {
|
||||
show () {
|
||||
// 播放被后台打断时,页面显示后自动继续播放
|
||||
if (this.data.playing && this._ctx.paused) {
|
||||
this._ctx.play()
|
||||
}
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
methods: {
|
||||
/**
|
||||
* @description 设置源
|
||||
* @param {string} src 源地址
|
||||
*/
|
||||
setSrc (src) {
|
||||
this._ctx.autoplay = this.properties.autoplay
|
||||
this._ctx.loop = this.properties.loop
|
||||
this._ctx.src = src
|
||||
if (this.properties.autoplay && !this.data.playing) {
|
||||
this.setData({
|
||||
playing: true
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 播放音乐
|
||||
*/
|
||||
play () {
|
||||
this._ctx.play()
|
||||
this.setData({
|
||||
playing: true
|
||||
})
|
||||
this.triggerEvent('play'
|
||||
// #ifdef MP-ALIPAY
|
||||
, {
|
||||
target: {
|
||||
id: this.props.id
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
)
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 暂停音乐
|
||||
*/
|
||||
pause () {
|
||||
this._ctx.pause()
|
||||
this.setData({
|
||||
playing: false
|
||||
})
|
||||
this.triggerEvent('pause')
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 设置播放速率
|
||||
* @param {Number} rate 播放速率
|
||||
*/
|
||||
playbackRate (rate) {
|
||||
this._ctx.playbackRate = rate
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 停止音乐
|
||||
*/
|
||||
stop () {
|
||||
this._ctx.stop()
|
||||
this.setData({
|
||||
playing: false,
|
||||
time: '00:00'
|
||||
})
|
||||
this.triggerEvent('stop')
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 控制进度
|
||||
* @param {number} sec 秒数
|
||||
*/
|
||||
seek (sec) {
|
||||
this._ctx.seek(sec)
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 移动进度条
|
||||
* @param {event} e
|
||||
* @private
|
||||
*/
|
||||
_seeking (e) {
|
||||
// 避免过于频繁 setData
|
||||
if (e.timeStamp - this.lastTime < 200) return
|
||||
const time = Math.round(e.detail.value / 100 * this._ctx.duration)
|
||||
const min = parseInt(time / 60)
|
||||
const sec = time % 60
|
||||
this.setData({
|
||||
time: (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec)
|
||||
})
|
||||
this.lastTime = e.timeStamp
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 进度条移动完毕
|
||||
* @param {event} e
|
||||
* @private
|
||||
*/
|
||||
_seeked (e) {
|
||||
this._ctx.seek(e.detail.value / 100 * this._ctx.duration)
|
||||
this.lastTime = undefined
|
||||
}
|
||||
}
|
||||
})
|
||||
3
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/audio.json
generated
vendored
Normal file
3
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/audio.json
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
17
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/audio.wxml
generated
vendored
Normal file
17
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/audio.wxml
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<view wx:if="{{controls}}" class="_contain">
|
||||
<!-- 海报和按钮 -->
|
||||
<view class="_poster" style="background-image:url('{{poster}}')">
|
||||
<view class="_button" bindtap="{{playing?'pause':'play'}}">
|
||||
<view class="{{playing?'_pause':'_play'}}" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 曲名和作者 -->
|
||||
<view class="_title">
|
||||
<view class="_name">{{name||'未知音频'}}</view>
|
||||
<view class="_author">{{author||'未知作者'}}</view>
|
||||
</view>
|
||||
<!-- 进度条 -->
|
||||
<slider class="_slider" activeColor="#585959" block-size="12" disabled="{{error}}" value="{{value}}" bindchanging="_seeking" bindchange="_seeked" />
|
||||
<!--播放时间-->
|
||||
<view class="_time">{{time}}</view>
|
||||
</view>
|
||||
127
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/audio.wxss
generated
vendored
Normal file
127
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/audio.wxss
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
/* 顶层容器 */
|
||||
._contain {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: 290px;
|
||||
background-color: #fcfcfc;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* 播放、暂停按钮 */
|
||||
._button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
background-color: rgb(0, 0, 0, 0.2);
|
||||
border: 1px solid white;
|
||||
border-radius: 50%;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
._play {
|
||||
margin-left: 2px;
|
||||
border-top: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
border-left: 8px solid white;
|
||||
}
|
||||
|
||||
._pause {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* 海报 */
|
||||
._poster {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
background-color: #e6e6e6;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
/* 标题栏 */
|
||||
._title {
|
||||
flex: 1;
|
||||
margin: 4px 0 0 14px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
._author {
|
||||
width: 45px;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
._name {
|
||||
width: 140px;
|
||||
font-size: 15px;
|
||||
line-height: 39px;
|
||||
}
|
||||
|
||||
._author,
|
||||
._name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
._slider {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
bottom: 8px;
|
||||
width: 140px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 播放时间 */
|
||||
._time {
|
||||
margin: 7px 14px 0 0;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
/* 响应式布局,大屏幕用更大的尺寸 */
|
||||
@media (min-width: 400px) {
|
||||
._contain {
|
||||
width: 380px;
|
||||
}
|
||||
|
||||
._button {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
._poster {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
._author {
|
||||
width: 60px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
._name {
|
||||
width: 180px;
|
||||
font-size: 19px;
|
||||
line-height: 55px;
|
||||
}
|
||||
|
||||
._slider {
|
||||
right: 20px;
|
||||
bottom: 10px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
._time {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
3
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/build.js
generated
vendored
Normal file
3
wechat-mini-program/node_modules/mp-html/plugins/audio/miniprogram/build.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
template: '<my-audio wx:if="{{n.name==\'audio\'}}" id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" author="{{n.attrs.author}}" controls="{{n.attrs.controls}}" autoplay="{{n.attrs.autoplay}}" loop="{{n.attrs.loop}}" name="{{n.attrs.name}}" poster="{{n.attrs.poster}}" src="{{n.src[ctrl[i]||0]}}" data-i="{{i}}" data-source="audio" bindplay="play" binderror="mediaError" />'
|
||||
}
|
||||
269
wechat-mini-program/node_modules/mp-html/plugins/audio/uni-app/audio.vue
generated
vendored
Normal file
269
wechat-mini-program/node_modules/mp-html/plugins/audio/uni-app/audio.vue
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<view v-if="controls" @click="onClick" class="_contain">
|
||||
<!-- 海报和按钮 -->
|
||||
<view class="_poster" :style="'background-image:url('+poster+')'">
|
||||
<view class="_button" @tap="_buttonTap">
|
||||
<view :class="playing?'_pause':'_play'" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 曲名和作者 -->
|
||||
<view class="_title">
|
||||
<view class="_name">{{name||'未知音频'}}</view>
|
||||
<view class="_author">{{author||'未知作者'}}</view>
|
||||
</view>
|
||||
<!-- 进度条 -->
|
||||
<slider class="_slider" activeColor="#585959" block-size="12" handle-size="12" :disabled="error" :value="value" @changing="_seeking" @change="_seeked" />
|
||||
<!--播放时间-->
|
||||
<view class="_time">{{time||'00:00'}}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* @fileoverview audio 组件
|
||||
*/
|
||||
import context from './context'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
error: false,
|
||||
playing: false,
|
||||
time: '00:00',
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
props: {
|
||||
aid: String,
|
||||
name: String, // 音乐名
|
||||
author: String, // 作者
|
||||
poster: String, // 海报图片地址
|
||||
autoplay: [Boolean, String], // 是否自动播放
|
||||
controls: [Boolean, String], // 是否显示控件
|
||||
loop: [Boolean, String], // 是否循环播放
|
||||
src: String // 源地址
|
||||
},
|
||||
watch: {
|
||||
src (src) {
|
||||
this.setSrc(src)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this._ctx = uni.createInnerAudioContext()
|
||||
this._ctx.onError((err) => {
|
||||
this.error = true
|
||||
this.$emit('error', err)
|
||||
})
|
||||
this._ctx.onTimeUpdate(() => {
|
||||
const time = this._ctx.currentTime
|
||||
const min = parseInt(time / 60)
|
||||
const sec = Math.ceil(time % 60)
|
||||
this.time = (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec)
|
||||
if (!this.lastTime) {
|
||||
this.value = time / this._ctx.duration * 100 // 不在拖动状态下
|
||||
}
|
||||
})
|
||||
this._ctx.onEnded(() => {
|
||||
if (!this.loop) {
|
||||
this.playing = false
|
||||
}
|
||||
})
|
||||
context.set(this.aid, this)
|
||||
this.setSrc(this.src)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this._ctx.destroy()
|
||||
context.remove(this.aid)
|
||||
},
|
||||
onPageShow () {
|
||||
if (this.playing && this._ctx.paused) {
|
||||
this._ctx.play()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 设置源
|
||||
setSrc (src) {
|
||||
this._ctx.autoplay = this.autoplay
|
||||
this._ctx.loop = this.loop
|
||||
this._ctx.src = src
|
||||
if (this.autoplay && !this.playing) {
|
||||
this.playing = true
|
||||
}
|
||||
},
|
||||
// 播放
|
||||
play () {
|
||||
this._ctx.play()
|
||||
this.playing = true
|
||||
this.$emit('play', {
|
||||
target: {
|
||||
id: this.aid
|
||||
}
|
||||
})
|
||||
},
|
||||
// 暂停
|
||||
pause () {
|
||||
this._ctx.pause()
|
||||
this.playing = false
|
||||
this.$emit('pause')
|
||||
},
|
||||
// 设置播放速率
|
||||
playbackRate (rate) {
|
||||
this._ctx.playbackRate = rate
|
||||
},
|
||||
// 移动进度条
|
||||
seek (sec) {
|
||||
this._ctx.seek(sec)
|
||||
},
|
||||
// 内部方法
|
||||
_buttonTap () {
|
||||
if (this.playing) this.pause()
|
||||
else this.play()
|
||||
},
|
||||
_seeking (e) {
|
||||
// 避免过于频繁 setData
|
||||
if (e.timeStamp - this.lastTime < 200) return
|
||||
const time = Math.round(e.detail.value / 100 * this._ctx.duration)
|
||||
const min = parseInt(time / 60)
|
||||
const sec = time % 60
|
||||
this.time = (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec)
|
||||
this.lastTime = e.timeStamp
|
||||
},
|
||||
_seeked (e) {
|
||||
this.seek(e.detail.value / 100 * this._ctx.duration)
|
||||
this.lastTime = undefined
|
||||
},
|
||||
onClick(e) {
|
||||
this.$emit('onClick', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 顶层容器 */
|
||||
._contain {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: 290px;
|
||||
background-color: #fcfcfc;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* 播放、暂停按钮 */
|
||||
._button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
background-color: rgb(0, 0, 0, 0.2);
|
||||
border: 1px solid white;
|
||||
border-radius: 50%;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
._play {
|
||||
margin-left: 2px;
|
||||
border-top: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
border-left: 8px solid white;
|
||||
}
|
||||
|
||||
._pause {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* 海报 */
|
||||
._poster {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
background-color: #e6e6e6;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
/* 标题栏 */
|
||||
._title {
|
||||
flex: 1;
|
||||
margin: 4px 0 0 14px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
._author {
|
||||
width: 45px;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
._name {
|
||||
width: 140px;
|
||||
font-size: 15px;
|
||||
line-height: 39px;
|
||||
}
|
||||
|
||||
._author,
|
||||
._name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
._slider {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
bottom: 8px;
|
||||
width: 140px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 播放时间 */
|
||||
._time {
|
||||
margin: 7px 14px 0 0;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
/* 响应式布局,大屏幕用更大的尺寸 */
|
||||
@media (min-width: 400px) {
|
||||
._contain {
|
||||
width: 380px;
|
||||
}
|
||||
|
||||
._button {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
._poster {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
._author {
|
||||
width: 60px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
._name {
|
||||
width: 180px;
|
||||
font-size: 19px;
|
||||
line-height: 55px;
|
||||
}
|
||||
|
||||
._slider {
|
||||
right: 20px;
|
||||
bottom: 10px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
._time {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
3
wechat-mini-program/node_modules/mp-html/plugins/audio/uni-app/build.js
generated
vendored
Normal file
3
wechat-mini-program/node_modules/mp-html/plugins/audio/uni-app/build.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
template: '<my-audio v-if="n.name==\'audio\'" :class="n.attrs.class" :style="n.attrs.style" :aid="n.attrs.id" :author="n.attrs.author" :controls="n.attrs.controls" :autoplay="n.attrs.autoplay" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" data-source="audio" @play="play" @error="mediaError" />'
|
||||
}
|
||||
30
wechat-mini-program/node_modules/mp-html/plugins/card/README.md
generated
vendored
Normal file
30
wechat-mini-program/node_modules/mp-html/plugins/card/README.md
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# card
|
||||
功能:商品(联络人)信息卡
|
||||
大小:*≈7KB*
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | √ | √ | √ | √(nvue 不支持) |
|
||||
|
||||
### 效果图
|
||||

|
||||
|
||||
### 参数列表
|
||||
|参数名|是否必须|类型|说明|
|
||||
|:---- |:---|:----- |----- |
|
||||
|src|是|String|图片Url|
|
||||
|title|是|String|标题|
|
||||
|desc|是|String|描述|
|
||||
|url|是|String|跳转url|
|
||||
|color|是|String|文字颜色|
|
||||
|bgcolor|是|String|卡片背景颜色|
|
||||
|border|是|String|卡片边框颜色|
|
||||
|
||||
### 说明:
|
||||
1. 可以显示商品信息卡片/联络人信息卡片
|
||||
|
||||
### 基础库要求:
|
||||
满足最低要求即可
|
||||
|
||||
?> 如果希望页面上使用本组件,组件的路径为 *path/to/mp-html/card/card*
|
||||
14
wechat-mini-program/node_modules/mp-html/plugins/card/build.js
generated
vendored
Normal file
14
wechat-mini-program/node_modules/mp-html/plugins/card/build.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
usingComponents: {
|
||||
'my-card': '../card/card'
|
||||
},
|
||||
handler (file) {
|
||||
if (file.isBuffer()) {
|
||||
let content = file.contents.toString()
|
||||
if (file.path.includes('parser.js')) {
|
||||
content = content.replace(/trustTags\s*:\s*makeMap\('/, "trustTags: makeMap('card,").replace(/voidTags\s*:\s*makeMap\('/, "voidTags: makeMap('card,")
|
||||
}
|
||||
file.contents = Buffer.from(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
7
wechat-mini-program/node_modules/mp-html/plugins/card/index.js
generated
vendored
Normal file
7
wechat-mini-program/node_modules/mp-html/plugins/card/index.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @fileoverview Card 插件
|
||||
*/
|
||||
function Card (vm) {
|
||||
}
|
||||
|
||||
module.exports = Card
|
||||
3
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/build.js
generated
vendored
Normal file
3
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/build.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
template: '<my-card wx:if="{{n.name==\'card\'}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" mode="{{opts[5]}}" src="{{n.attrs.src}}" title="{{n.attrs.title}}" desc="{{n.attrs.desc}}" url="{{n.attrs.url}}" color="{{n.attrs.color}}" bgcolor="{{n.attrs.bgcolor}}" border="{{n.attrs.border}}" name="{{n.attrs.name}}" data-i="{{i}}" data-source="card" />'
|
||||
}
|
||||
26
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/card.js
generated
vendored
Normal file
26
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/card.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @fileoverview card 组件
|
||||
*/
|
||||
Component({
|
||||
properties: {
|
||||
mode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
src: String,
|
||||
title: String,
|
||||
desc: String,
|
||||
url: String,
|
||||
color: String,
|
||||
bgcolor: String,
|
||||
border: String
|
||||
},
|
||||
data: {},
|
||||
methods: {
|
||||
onClick (e) {
|
||||
if (this.properties.url && this.properties.url.trim().length > 6 && !this.properties.mode) {
|
||||
wx.navigateTo({ url: this.properties.url })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
3
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/card.json
generated
vendored
Normal file
3
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/card.json
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
9
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/card.wxml
generated
vendored
Normal file
9
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/card.wxml
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<view class="card" bindtap="onClick" style="background-color:{{bgcolor||'#a4d0ff'}};border:{{border||'1px solid #FFF'}};color:{{color||'#000'}}">
|
||||
<image class="card-img" mode="aspectFill" src="{{src}}" />
|
||||
<view class="text-wrap text-wrap-width" wx:if="{{!!desc}}">
|
||||
<view class="title one-t">{{title}}</view>
|
||||
<view class="desc one-t">{{desc}}</view>
|
||||
</view>
|
||||
<view wx:else class="text-wrap-width title more-t">{{title}}</view>
|
||||
<image class="card-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAABCFBMVEUAAAC/v7+qqqqZmZmLi6KJnZ2ImZmHlpaGlKGMjJmSkp6Li5eQkJuKlZ+Pj5mOjpeJkpuNjZ6IkJmHj5eLi5uKkpmGjZqJj5uLi5eIjpmIjZiKj5qKj5mJjpiHjJqJjZaGj5iKj5eIjJmKjpqHi5eGjZeIi5eHjZiIi5eHjJiIjpaHjJeIjZiGjJeIjZiGjJaGjJeIi5aGjJeHi5eHi5aHjJeHjJaHjJeGi5eHjJaHjJeGi5aGi5aHjJeHjJaGjJeGjJeHjJaHi5eGi5eHjJaHjJeGjJaGjJeHi5aHjJeHjJeHi5aGjJeHi5aGjJaHi5eGjJaHi5eHjJaGi5eGi5aHjJeGi5aGi5apAvjmAAAAV3RSTlMABAYKCw0PERMUFRYXGBkbHB0eICEjJiksLS8wMjQ1ODk7PD9ATFZXWFlaW1xdXl+Hi6msu7/Dx8vMzs/R0tTV19na3N3f4uTn6evs7e7v8PHy9PX7/P18cCTXAAABEklEQVRo3u2YWU5CQRQFn4qCM4LzhIoDAorzrIgCigiCimf/O/Gj3UIlmJxaQFXSea/T90aRMcYYY4zpG0ZPu9cZMnAi6SsLBjqS9LnJBcqSpC53Sjs/kqSPNaxwGAqtFbrQXKILjQW68DpPF17m6EI9TRdqM3TheZouVCbpQnkcK5RC4T5BF27jdOFqhC5cDtOFixhdOB+iC2cDdOEoggttLrDbk6QW5/+WJB1T/r1e+FAHWT/2q/35scsiF/w3cdZ/R13Y+8H/MMb6Hycgfz74n6ZYfzXJ+mspyF8I/vos68cep0X4eV2EB4SD4H9bZP3vy+yTtL3KjrGddXgQ34BXCVvwMmT7P69zjDHGGGP6gF83lHISOctsKQAAAABJRU5ErkJggg=="></image>
|
||||
</view>
|
||||
55
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/card.wxss
generated
vendored
Normal file
55
wechat-mini-program/node_modules/mp-html/plugins/card/miniprogram/card.wxss
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
.one-t {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
transition: all linear 0.2s;
|
||||
}
|
||||
.more-t {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
transition: all linear 0.2s;
|
||||
}
|
||||
.card {
|
||||
width: 80%;
|
||||
margin: 10rpx auto;
|
||||
max-width: 700rpx;
|
||||
max-height: 140rpx;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0 20rpx 10rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
.card-img {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 12rpx;
|
||||
flex: 0 0 96rpx;
|
||||
}
|
||||
.card-icon {
|
||||
width: 30rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
.card .text-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.card .text-wrap-width {
|
||||
width: 72%;
|
||||
}
|
||||
.card .title {
|
||||
font-weight: bold;
|
||||
font-size: 34rpx;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
.card .desc {
|
||||
font-size: 27rpx;
|
||||
line-height: 37rpx;
|
||||
}
|
||||
3
wechat-mini-program/node_modules/mp-html/plugins/card/uni-app/build.js
generated
vendored
Normal file
3
wechat-mini-program/node_modules/mp-html/plugins/card/uni-app/build.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
template: '<my-card v-if="n.name==\'card\'" :class="n.attrs.class" :style="n.attrs.style" :mode="opts[5]" :src="n.attrs.src" :title="n.attrs.title" :desc="n.attrs.desc" :url="n.attrs.url" :color="n.attrs.color" :bgcolor="n.attrs.bgcolor" :border="n.attrs.border" :name="n.attrs.name" :data-i="i" data-source="card" />'
|
||||
}
|
||||
122
wechat-mini-program/node_modules/mp-html/plugins/card/uni-app/card.vue
generated
vendored
Normal file
122
wechat-mini-program/node_modules/mp-html/plugins/card/uni-app/card.vue
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<view class="card" @click="onClick" :style="[customStyle]" :data-i="$attrs['data-i']">
|
||||
<image class="card-img" mode="aspectFill" :src="src" />
|
||||
<view class="text-wrap text-wrap-width" v-if="!!desc">
|
||||
<view class="title one-t">{{title}}</view>
|
||||
<view class="desc one-t">{{desc}}</view>
|
||||
</view>
|
||||
<view v-else class="text-wrap-width title more-t">{{title}}</view>
|
||||
<image class="card-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAABCFBMVEUAAAC/v7+qqqqZmZmLi6KJnZ2ImZmHlpaGlKGMjJmSkp6Li5eQkJuKlZ+Pj5mOjpeJkpuNjZ6IkJmHj5eLi5uKkpmGjZqJj5uLi5eIjpmIjZiKj5qKj5mJjpiHjJqJjZaGj5iKj5eIjJmKjpqHi5eGjZeIi5eHjZiIi5eHjJiIjpaHjJeIjZiGjJeIjZiGjJaGjJeIi5aGjJeHi5eHi5aHjJeHjJaHjJeGi5eHjJaHjJeGi5aGi5aHjJeHjJaGjJeGjJeHjJaHi5eGi5eHjJaHjJeGjJaGjJeHi5aHjJeHjJeHi5aGjJeHi5aGjJaHi5eGjJaHi5eHjJaGi5eGi5aHjJeGi5aGi5apAvjmAAAAV3RSTlMABAYKCw0PERMUFRYXGBkbHB0eICEjJiksLS8wMjQ1ODk7PD9ATFZXWFlaW1xdXl+Hi6msu7/Dx8vMzs/R0tTV19na3N3f4uTn6evs7e7v8PHy9PX7/P18cCTXAAABEklEQVRo3u2YWU5CQRQFn4qCM4LzhIoDAorzrIgCigiCimf/O/Gj3UIlmJxaQFXSea/T90aRMcYYY4zpG0ZPu9cZMnAi6SsLBjqS9LnJBcqSpC53Sjs/kqSPNaxwGAqtFbrQXKILjQW68DpPF17m6EI9TRdqM3TheZouVCbpQnkcK5RC4T5BF27jdOFqhC5cDtOFixhdOB+iC2cDdOEoggttLrDbk6QW5/+WJB1T/r1e+FAHWT/2q/35scsiF/w3cdZ/R13Y+8H/MMb6Hycgfz74n6ZYfzXJ+mspyF8I/vos68cep0X4eV2EB4SD4H9bZP3vy+yTtL3KjrGddXgQ34BXCVvwMmT7P69zjDHGGGP6gF83lHISOctsKQAAAABJRU5ErkJggg=="></image>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
mode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
src: String,
|
||||
title: String,
|
||||
desc: String,
|
||||
url: String,
|
||||
color: String,
|
||||
bgcolor: String,
|
||||
border: String
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
customStyle () {
|
||||
return {
|
||||
'background-color': this.bgColor || '#a4d0ff',
|
||||
border: this.border || '1px solid #FFF',
|
||||
color: this.color || '#000'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick (e) {
|
||||
if (this.url && this.url.trim().length > 6 && !this.mode) {
|
||||
uni.navigateTo({ url: this.url })
|
||||
}
|
||||
this.$emit('click', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.one-t {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
transition: all linear 0.2s;
|
||||
}
|
||||
|
||||
.more-t {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break:break-all;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
transition: all linear 0.2s;
|
||||
}
|
||||
|
||||
.card {
|
||||
|
||||
width: 80%;
|
||||
margin: 10rpx auto;
|
||||
max-width: 700rpx;
|
||||
max-height: 140rpx;
|
||||
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
padding: 20rpx 0 20rpx 10rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
&-img {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 12rpx;
|
||||
flex: 0 0 96rpx;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
width: 30rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
|
||||
.text-wrap {
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
&-width {
|
||||
width: 72%;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 34rpx;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 27rpx;
|
||||
line-height: 37rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
137
wechat-mini-program/node_modules/mp-html/plugins/editable/README.md
generated
vendored
Normal file
137
wechat-mini-program/node_modules/mp-html/plugins/editable/README.md
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
# editable
|
||||
功能:富文本编辑
|
||||
下表列出了本插件与原生 *editor* 组件的功能差异,可按需选用
|
||||
|
||||
| 组件 | 优点 | 缺点 |
|
||||
|:---:|:---:|:---:|
|
||||
| 原生 *editor* | 底层通过 *contenteditable* 实现,编辑流畅 | 支持标签少(不支持音视频、表格以及 *section* 等常用标签)、部分小程序平台不支持或低版本不兼容 |
|
||||
| 本插件 | 支持标签全面、支持平台全面 | 编辑灵活性不够强 |
|
||||
|
||||
大小:*≈17.5KB*
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | √ | √ | √ | √(nvue 不支持) |
|
||||
|
||||
说明:
|
||||
引入本插件后,会给组件添加以下属性:
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
|:---:|:---:|:---:|:---:|
|
||||
| editable | Boolean | false | 是否开启内容编辑 |
|
||||
| placeholder | String | 请输入 | 输入框为空时占位符(`2.1.0+`) |
|
||||
|
||||
添加以下事件:
|
||||
|
||||
| 事件名 | 触发时机 | 用途 |
|
||||
|:---:|:---:|:---:|
|
||||
| remove(`2.2.0+`) | 删除图片/视频/音频标签时 | 删除已上传的线上文件 |
|
||||
|
||||
支持以下操作:
|
||||
|
||||
| 类型 | 操作 |
|
||||
|:---:|:---:|
|
||||
| 文本 | 修改 |
|
||||
| 图片 | 更换链接、调整宽度、设置成超链接(`2.0.4+`)、设置预览图链接、禁用预览、删除 |
|
||||
| 链接 | 更换链接、删除 |
|
||||
| 音视频 | 设置封面、设置循环播放、设置自动播放(`2.2.0+`)、删除 |
|
||||
| 普通标签 | 设置字体大小、斜体、粗体、下划线(`2.0.4+`)、居中、缩进、删除 |
|
||||
|
||||
> `2.2.1` 版本起所有标签支持上下移动操作,但仅限同级标签间移动,即在有同级标签且非第一个(或最后一个)时可以上移(或下移)
|
||||
|
||||
> 在支付宝小程序中使用时需要在页面样式中添加 *page { position: relative; }* 避免 *tooltip* 错位
|
||||
|
||||
> 菜单项可以通过编辑 *plugins/editable/config.js* 进行修改,仅可以删减或调整顺序,添加或更名无效
|
||||
|
||||
组件实例上提供了以下方法(*editable* 属性为 *true* 时才可以调用):
|
||||
|
||||
| 名称 | 功能 |
|
||||
|:---:|:---:|
|
||||
| undo | 撤销一个操作 |
|
||||
| redo | 重做一个操作 |
|
||||
| insertHtml | 在光标处插入指定 html 内容(`2.1.0+`) |
|
||||
| insertImg | 在光标处插入一张图片 |
|
||||
| insertTable(rows, cols) | 在光标处插入一个 rows 行 cols 列的表格(`2.1.3+`) |
|
||||
| insertVideo | 在光标处插入一个视频 |
|
||||
| insertAudio | 在光标处插入一个音频 |
|
||||
| insertLink | 在光标处插入一个链接 |
|
||||
| insertText | 在光标处插入一段文本 |
|
||||
| clear | 清空内容 |
|
||||
| getContent | 获取编辑后的 html 内容 |
|
||||
|
||||
> 考虑到不同场景下希望获取链接的方法不同,需要在初始时给组件设置一个 *getSrc* 方法(否则插入图片、音视频、链接或修改链接等操作无法使用),每次组件内需要链接时会调用此方法,开发者可在此方法中自行决定如何获取链接,返回 **线上地址** 即可(具体用法见下方示例)
|
||||
|
||||
编辑完成后,通过 *getContent* 方法获取编辑后的 *html*,最后将 *editable* 属性设置为 *false* 即可正常渲染
|
||||
|
||||
> 点击保存按钮时,部分平台 *tap* 事件早于 *blur* 事件触发,直接获取内容可能导致无法获取当前编辑的文本内容,因此建议设置一个小的延时后获取(可参考下方示例,[详细](https://github.com/jin-yufeng/mp-html/issues/368))
|
||||
|
||||
示例:
|
||||
```javascript
|
||||
Page({
|
||||
onLoad () {
|
||||
// ctx 为组件实例,获取方法见上
|
||||
/**
|
||||
* @description 设置获取链接的方法
|
||||
* @param {String} type 链接的类型(img/video/audio/link)
|
||||
* @param {String} value 修改链接时,这里会传入旧值
|
||||
* @returns {Promise} 返回线上地址(2.2.0 版本起设置了 domain 属性时,可以缺省主域名)
|
||||
* type 为 audio/video 时,可以返回一个源地址数组
|
||||
* 2.1.3 版本起 type 为 audio 时,可以返回一个 object,包含 src、name、author、poster 等字段
|
||||
* 2.2.0 版本起 type 为 img 时,可以返回一个源地址数组,表示插入多张图片(修改链接时仅限一张)
|
||||
*/
|
||||
this.ctx.getSrc = (type, value) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 以图片为例
|
||||
if (type == 'img') {
|
||||
wx.chooseImage({
|
||||
count: value === undefined ? 9 : 1, // 2.2.0 版本起插入图片时支持多张(修改图片链接时仅限一张)
|
||||
success: res => {
|
||||
wx.showLoading({
|
||||
title: '上传中'
|
||||
});
|
||||
(async ()=>{
|
||||
const arr = []
|
||||
for (let item of res.tempFilePaths) {
|
||||
// 依次上传
|
||||
const src = await upload(item)
|
||||
arr.push(src)
|
||||
}
|
||||
return arr
|
||||
})().then(res => {
|
||||
wx.hideLoading()
|
||||
resolve(res)
|
||||
})
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
finishEdit () {
|
||||
setTimeout(() => {
|
||||
var html = ctx.getContent() // 获取编辑好的 html
|
||||
// 上传 html
|
||||
wx.request({
|
||||
url: 'xxx',
|
||||
data: {
|
||||
html
|
||||
},
|
||||
success: () => {
|
||||
this.setData({
|
||||
editable: false // 结束编辑
|
||||
})
|
||||
}
|
||||
})
|
||||
}, 50)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**示例项目**:
|
||||
微信小程序点击 [代码片段](https://developers.weixin.qq.com/s/GFbJKum77eBy) 即可在微信开发者工具中导入;*uni-app* 下载 [示例项目](https://mp-html.oss-cn-hangzhou.aliyuncs.com/editable.zip) 在 *HBuilder X* 中打开即可体验;注意示例项目中不一定包含最新版本,仅供参考使用方法
|
||||
|
||||
注意事项:
|
||||
1. 不要在 *editable* 属性被设置为 *true* 前通过 *setContent* 方法(用 *content* 属性)设置内容,否则在切换为 *true* 后会变成空白
|
||||
2. *editable* 属性为 *true* 时不支持在 *scroll-view* 中使用,否则提示框的位置可能不正确
|
||||
15
wechat-mini-program/node_modules/mp-html/plugins/editable/config.js
generated
vendored
Normal file
15
wechat-mini-program/node_modules/mp-html/plugins/editable/config.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// 以下项目可以删减或更换顺序,但不能添加或更改名字
|
||||
module.exports = {
|
||||
// 普通标签的菜单项
|
||||
node: ['大小', '颜色', '斜体', '粗体', '下划线', '居中', '缩进', '上移', '下移', '删除'],
|
||||
// 可以设置的文字颜色,此项可以添加 css 颜色
|
||||
color: ['red', 'yellow', 'blue', 'green', 'gray', 'white', 'black'],
|
||||
// 图片的菜单项
|
||||
img: ['换图', '宽度', '超链接', '预览图', '禁用预览', '上移', '下移', '删除'],
|
||||
// 链接的菜单项
|
||||
link: ['更换链接', '上移', '下移', '删除'],
|
||||
// 音视频的菜单项
|
||||
media: ['封面', '循环', '自动播放', '上移', '下移', '删除'],
|
||||
// 卡片的菜单项
|
||||
card: ['上移', '下移', '删除']
|
||||
}
|
||||
813
wechat-mini-program/node_modules/mp-html/plugins/editable/miniprogram/build.js
generated
vendored
Normal file
813
wechat-mini-program/node_modules/mp-html/plugins/editable/miniprogram/build.js
generated
vendored
Normal file
@@ -0,0 +1,813 @@
|
||||
const path = require('path')
|
||||
/* global getTop */
|
||||
module.exports = {
|
||||
style: `/* #ifndef MP-ALIPAY */
|
||||
._address,
|
||||
._article,
|
||||
._aside,
|
||||
._body,
|
||||
._caption,
|
||||
._center,
|
||||
._cite,
|
||||
._footer,
|
||||
._header,
|
||||
._html,
|
||||
._nav,
|
||||
._pre,
|
||||
._section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* #endif */`,
|
||||
methods: {
|
||||
/**
|
||||
* @description 开始编辑文本
|
||||
* @param {Event} e
|
||||
*/
|
||||
editStart (e) {
|
||||
if (this.properties.opts[5]) {
|
||||
const i = e.currentTarget.dataset.i
|
||||
if (!this.data.ctrl['e' + i] && this.properties.opts[5] !== 'simple') {
|
||||
// 显示虚线框
|
||||
this.setData({
|
||||
['ctrl.e' + i]: 1
|
||||
})
|
||||
// 点击其他地方则取消虚线框
|
||||
setTimeout(() => {
|
||||
this.root._mask.push(() => {
|
||||
this.setData({
|
||||
['ctrl.e' + i]: 0
|
||||
})
|
||||
})
|
||||
}, 50)
|
||||
this.root._edit = this
|
||||
this.i = i
|
||||
this.cursor = this.getNode(i).text.length
|
||||
} else {
|
||||
if (this.properties.opts[5] === 'simple') {
|
||||
this.root._edit = this
|
||||
this.i = i
|
||||
this.cursor = this.getNode(i).text.length
|
||||
}
|
||||
this.root._mask.pop()
|
||||
this.root._maskTap()
|
||||
// 将 text 转为 textarea
|
||||
this.setData({
|
||||
['ctrl.e' + i]: 2
|
||||
})
|
||||
// 延时对焦,避免高度错误
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
['ctrl.e' + i]: 3
|
||||
})
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description 输入文本
|
||||
* @param {Event} e
|
||||
*/
|
||||
editInput (e) {
|
||||
const i = e.target.dataset.i
|
||||
// 替换连续空格
|
||||
const value = e.detail.value.replace(/ {2,}/, $ => {
|
||||
let res = '\xa0'
|
||||
for (let i = 1; i < $.length; i++) {
|
||||
res += '\xa0'
|
||||
}
|
||||
return res
|
||||
})
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].text', this.getNode(i).text, value) // 记录编辑历史
|
||||
this.cursor = e.detail.cursor
|
||||
},
|
||||
/**
|
||||
* @description 完成编辑文本
|
||||
* @param {Event} e
|
||||
*/
|
||||
editEnd (e) {
|
||||
const i = e.target.dataset.i
|
||||
// 更新到视图
|
||||
this.setData({
|
||||
['ctrl.e' + i]: 0
|
||||
})
|
||||
this.root.setData({
|
||||
['nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].text']: e.detail.value.replace(/ {2}/g, '\xa0 ')
|
||||
})
|
||||
if (e.detail.cursor !== undefined) {
|
||||
this.cursor = e.detail.cursor
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description 插入一个标签
|
||||
* @param {Object} node 要插入的标签
|
||||
*/
|
||||
insert (node) {
|
||||
setTimeout(() => {
|
||||
const arr = this.i.split('_')
|
||||
const i = parseInt(arr.pop())
|
||||
let path = arr.join('_')
|
||||
const children = path ? this.getNode(path).children : this.properties.childs
|
||||
const childs = children.slice(0)
|
||||
if (!childs[i]) {
|
||||
childs.push(node)
|
||||
} else if (childs[i].text) {
|
||||
// 在文本中插入
|
||||
const text = childs[i].text
|
||||
if (node.type === 'text') {
|
||||
if (this.cursor) {
|
||||
childs[i].text = text.substring(0, this.cursor) + node.text + text.substring(this.cursor)
|
||||
} else {
|
||||
childs[i].text += node.text
|
||||
}
|
||||
} else {
|
||||
const list = []
|
||||
if (this.cursor) {
|
||||
list.push({
|
||||
type: 'text',
|
||||
text: text.substring(0, this.cursor)
|
||||
})
|
||||
}
|
||||
list.push(node)
|
||||
if (this.cursor < text.length) {
|
||||
list.push({
|
||||
type: 'text',
|
||||
text: text.substring(this.cursor)
|
||||
})
|
||||
}
|
||||
childs.splice(i, 1, ...list)
|
||||
}
|
||||
} else {
|
||||
childs.splice(i + 1, 0, node)
|
||||
}
|
||||
path = this.properties.opts[7] + path
|
||||
if (path[path.length - 1] === '_') {
|
||||
path = path.slice(0, -1)
|
||||
}
|
||||
this.root._editVal('nodes' + (path ? '[' + path.replace(/_/g, '].children[') + '].children' : ''), children, childs, true)
|
||||
this.i = arr.join('_') + '_' + (i + 1)
|
||||
}, 200)
|
||||
},
|
||||
/**
|
||||
* @description 移除第 i 个标签
|
||||
* @param {Number} i
|
||||
*/
|
||||
remove (i) {
|
||||
const arr = i.split('_')
|
||||
const j = arr.pop()
|
||||
let path = arr.join('_')
|
||||
const children = path ? this.getNode(path).children : this.properties.childs
|
||||
const childs = children.slice(0)
|
||||
const delEle = childs.splice(j, 1)[0]
|
||||
if (delEle.name === 'img' || delEle.name === 'video' || delEle.name === 'audio') {
|
||||
let src = delEle.attrs.src
|
||||
if (delEle.src) {
|
||||
src = delEle.src.length === 1 ? delEle.src[0] : delEle.src
|
||||
}
|
||||
this.root.triggerEvent('remove', {
|
||||
type: delEle.name,
|
||||
src
|
||||
})
|
||||
}
|
||||
this.root._edit = undefined
|
||||
this.root._maskTap()
|
||||
path = this.properties.opts[7] + path
|
||||
if (path[path.length - 1] === '_') {
|
||||
path = path.slice(0, -1)
|
||||
}
|
||||
this.root._editVal('nodes' + (path ? '[' + path.replace(/_/g, '].children[') + '].children' : ''), children, childs, true)
|
||||
},
|
||||
/**
|
||||
* @description 标签被点击
|
||||
* @param {Event} e
|
||||
*/
|
||||
nodeTap (e) {
|
||||
if (this.properties.opts[5]) {
|
||||
const i = e.currentTarget.dataset.i
|
||||
if (this.root._table) {
|
||||
const node = this.getNode(i)
|
||||
if (node.name === 'table') {
|
||||
this.root._table = undefined
|
||||
this.root._remove_table = () => {
|
||||
this.remove(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.root._lock) return
|
||||
// 阻止上层出现点击态
|
||||
this.root._lock = true
|
||||
setTimeout(() => {
|
||||
this.root._lock = false
|
||||
}, 50)
|
||||
const node = this.getNode(i)
|
||||
if (node.name === 'td' || node.name === 'th') {
|
||||
this.root._table = true
|
||||
}
|
||||
if (this.data.ctrl['e' + this.i] === 3) return
|
||||
this.root._maskTap()
|
||||
this.root._edit = this
|
||||
if (this.properties.opts[5] === 'simple') return
|
||||
const arr = i.split('_')
|
||||
const j = parseInt(arr.pop())
|
||||
let path = arr.join('_')
|
||||
const siblings = path ? this.getNode(path).children : this.properties.childs
|
||||
// 显示实线框
|
||||
this.setData({
|
||||
['ctrl.e' + i]: 1
|
||||
})
|
||||
this.root._mask.push(() => {
|
||||
this.setData({
|
||||
['ctrl.e' + i]: 0
|
||||
})
|
||||
})
|
||||
if (node.children.length === 1 && node.children[0].type === 'text') {
|
||||
const ii = i + '_0'
|
||||
if (!this.data.ctrl['e' + ii]) {
|
||||
this.setData({
|
||||
['ctrl.e' + ii]: 1
|
||||
})
|
||||
this.root._mask.push(() => {
|
||||
this.setData({
|
||||
['ctrl.e' + ii]: 0
|
||||
})
|
||||
})
|
||||
this.cursor = node.children[0].text.length
|
||||
}
|
||||
this.i = ii
|
||||
} else if (!(this.i || '').includes(i)) {
|
||||
this.i = i + '_'
|
||||
}
|
||||
const items = this.root._getItem(node, j !== 0, j !== siblings.length - 1)
|
||||
this.root._tooltip({
|
||||
top: getTop(e),
|
||||
items,
|
||||
success: tapIndex => {
|
||||
if (items[tapIndex] === '大小') {
|
||||
// 改变字体大小
|
||||
const style = node.attrs.style || ''
|
||||
let value = style.match(/;font-size:([0-9]+)px/)
|
||||
if (value) {
|
||||
value = parseInt(value[1])
|
||||
} else {
|
||||
value = 16
|
||||
}
|
||||
this.root._slider({
|
||||
min: 10,
|
||||
max: 30,
|
||||
value,
|
||||
top: getTop(e),
|
||||
changing: val => {
|
||||
if (Math.abs(val - value) > 2) {
|
||||
// 字号变换超过 2 时更新到视图
|
||||
this.changeStyle('font-size', i, val + 'px', value + 'px')
|
||||
value = e.detail.value
|
||||
}
|
||||
},
|
||||
change: val => {
|
||||
if (val !== value) {
|
||||
this.changeStyle('font-size', i, val + 'px', value + 'px')
|
||||
}
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.style', style, this.getNode(i).attrs.style)
|
||||
}
|
||||
})
|
||||
} else if (items[tapIndex] === '颜色') {
|
||||
// 改变文字颜色
|
||||
const items = this.root._getItem('color')
|
||||
this.root._color({
|
||||
top: getTop(e),
|
||||
items,
|
||||
success: tapIndex => {
|
||||
const style = node.attrs.style || ''
|
||||
const value = style.match(/;color:([^;]+)/)
|
||||
this.changeStyle('color', i, items[tapIndex], value ? value[1] : undefined)
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.style', style, this.getNode(i).attrs.style)
|
||||
}
|
||||
})
|
||||
} else if (items[tapIndex] === '上移' || items[tapIndex] === '下移') {
|
||||
const arr = siblings.slice(0)
|
||||
const item = arr[j]
|
||||
if (items[tapIndex] === '上移') {
|
||||
arr[j] = arr[j - 1]
|
||||
arr[j - 1] = item
|
||||
} else {
|
||||
arr[j] = arr[j + 1]
|
||||
arr[j + 1] = item
|
||||
}
|
||||
path = this.properties.opts[7] + path
|
||||
if (path[path.length - 1] === '_') {
|
||||
path = path.slice(0, -1)
|
||||
}
|
||||
this.root._editVal('nodes' + (path ? '[' + path.replace(/_/g, '].children[') + '].children' : ''), siblings, arr, true)
|
||||
} else if (items[tapIndex] === '删除') {
|
||||
if ((node.name === 'td' || node.name === 'th') && this.root._remove_table) {
|
||||
this.root._remove_table()
|
||||
this.root._remove_table = undefined
|
||||
} else {
|
||||
this.remove(i)
|
||||
}
|
||||
} else {
|
||||
const style = node.attrs.style || ''
|
||||
let newStyle = ''
|
||||
const item = items[tapIndex]
|
||||
let name
|
||||
let value
|
||||
if (item === '斜体') {
|
||||
name = 'font-style'
|
||||
value = 'italic'
|
||||
} else if (item === '粗体') {
|
||||
name = 'font-weight'
|
||||
value = 'bold'
|
||||
} else if (item === '下划线') {
|
||||
name = 'text-decoration'
|
||||
value = 'underline'
|
||||
} else if (item === '居中') {
|
||||
name = 'text-align'
|
||||
value = 'center'
|
||||
} else if (item === '缩进') {
|
||||
name = 'text-indent'
|
||||
value = '2em'
|
||||
}
|
||||
if (style.includes(name + ':')) {
|
||||
// 已有则取消
|
||||
newStyle = style.replace(new RegExp(name + ':[^;]+'), '')
|
||||
} else {
|
||||
// 没有则添加
|
||||
newStyle = style + ';' + name + ':' + value
|
||||
}
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.style', style, newStyle, true)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description 音视频被点击
|
||||
* @param {Event} e
|
||||
*/
|
||||
mediaTap (e) {
|
||||
if (this.properties.opts[5]) {
|
||||
const i = e.target.dataset.i
|
||||
const node = this.getNode(i)
|
||||
const items = this.root._getItem(node)
|
||||
this.root._maskTap()
|
||||
this.root._edit = this
|
||||
this.i = i
|
||||
this.root._tooltip({
|
||||
top: e.target.offsetTop - 30,
|
||||
items,
|
||||
success: tapIndex => {
|
||||
switch (items[tapIndex]) {
|
||||
case '封面':
|
||||
// 设置封面
|
||||
this.root.getSrc('img', node.attrs.poster || '').then(url => {
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.poster', node.attrs.poster, url instanceof Array ? url[0] : url, true)
|
||||
}).catch(() => { })
|
||||
break
|
||||
case '删除':
|
||||
this.remove(i)
|
||||
break
|
||||
case '循环':
|
||||
case '不循环':
|
||||
// 切换循环播放
|
||||
this.root.setData({
|
||||
['nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.loop']: !node.attrs.loop
|
||||
})
|
||||
wx.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
break
|
||||
case '自动播放':
|
||||
case '不自动播放':
|
||||
// 切换自动播放播放
|
||||
this.root.setData({
|
||||
['nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.autoplay']: !node.attrs.autoplay
|
||||
})
|
||||
wx.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
// 避免上层出现点击态
|
||||
this.root._lock = true
|
||||
setTimeout(() => {
|
||||
this.root._lock = false
|
||||
}, 50)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 改变样式
|
||||
* @param {String} name 属性名
|
||||
* @param {Number} i 第几个标签
|
||||
* @param {String} value 新值
|
||||
* @param {String} oldVal 旧值
|
||||
*/
|
||||
changeStyle (name, i, value, oldVal) {
|
||||
let style = this.getNode(i).attrs.style || ''
|
||||
if (style.includes(';' + name + ':' + oldVal)) {
|
||||
// style 中已经有
|
||||
style = style.replace(';' + name + ':' + oldVal, ';' + name + ':' + value)
|
||||
} else {
|
||||
// 没有则新增
|
||||
style += ';' + name + ':' + value
|
||||
}
|
||||
this.root.setData({
|
||||
['nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.style']: style
|
||||
})
|
||||
}
|
||||
},
|
||||
handler (file) {
|
||||
if (file.isBuffer()) {
|
||||
let content = file.contents.toString()
|
||||
if (file.path.includes('miniprogram' + path.sep + 'index.wxml')) {
|
||||
// 传递 editable 属性和路径
|
||||
content = content.replace(/opts\s*=\s*"{{\[([^\]]+)\]}}"/, 'opts="{{[$1,editable,placeholder,\'\']}}"')
|
||||
.replace(/<view(.*?)style\s*=\s*"{{containerStyle}}"/, '<view$1style="{{editable?\'min-height:200px;\':\'\'}}{{containerStyle}}" bindtap="_containTap"')
|
||||
// 工具弹窗
|
||||
.replace('</view>', ` <view wx:if="{{tooltip}}" class="_tooltip_contain" style="top:{{tooltip.top}}px">
|
||||
<view class="_tooltip">
|
||||
<view wx:for="{{tooltip.items}}" wx:key="index" class="_tooltip_item" data-i="{{index}}" bindtap="_tooltipTap">{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{slider}}" class="_slider" style="top:{{slider.top}}px">
|
||||
<slider value="{{slider.value}}" min="{{slider.min}}" max="{{slider.max}}" block-size="14" show-value activeColor="white" mp-alipay:style="padding:10px" bindchanging="_sliderChanging" bindchange="_sliderChange" />
|
||||
</view>
|
||||
<view wx:if="{{color}}" class="_tooltip_contain" style="top:{{color.top}}px">
|
||||
<view class="_tooltip" style="overflow-y: hidden;">
|
||||
<view wx:for="{{color.items}}" wx:key="index" class="_color_item" style="background-color:{{item}}" data-i="{{index}}" bindtap="_colorTap"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>`)
|
||||
} else if (file.path.includes('miniprogram' + path.sep + 'index.js')) {
|
||||
// 添加 editable 属性,发生变化时重新解析
|
||||
content = content.replace(/properties\s*:\s*{/, `properties: {
|
||||
editable: {
|
||||
type: null,
|
||||
observer (val) {
|
||||
if (this.properties.content) {
|
||||
this.setContent(val ? this.properties.content : this.getContent())
|
||||
} else if (val) {
|
||||
this.setData({
|
||||
nodes: [{
|
||||
name: 'p',
|
||||
attrs: {},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
}]
|
||||
})
|
||||
// #ifdef MP-TOUTIAO
|
||||
this.selectComponent('#_root', child => {
|
||||
child.root = this
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
if (!val) {
|
||||
this._maskTap()
|
||||
}
|
||||
}
|
||||
},
|
||||
placeholder: String,`)
|
||||
.replace(/didUpdate\s*\(e\)\s*{/, `didUpdate (e) {
|
||||
if (e.editable !== this.properties.editable) {
|
||||
const val = this.properties.editable
|
||||
if (this.properties.content) {
|
||||
this.setContent(val ? this.properties.content : this.getContent())
|
||||
} else if (val) {
|
||||
this.setData({
|
||||
nodes: [{
|
||||
name: 'p',
|
||||
attrs: {},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
}]
|
||||
})
|
||||
}
|
||||
if (!val) {
|
||||
this._maskTap()
|
||||
}
|
||||
}`)
|
||||
// 处理各类弹窗的事件
|
||||
.replace(/methods\s*:\s*{/, `methods: {
|
||||
_containTap() {
|
||||
if (!this._lock && !this.data.slider && !this.data.color) {
|
||||
this._edit = undefined
|
||||
this._maskTap()
|
||||
}
|
||||
},
|
||||
_tooltipTap(e) {
|
||||
this._tooltipcb(e.currentTarget.dataset.i)
|
||||
this.setData({
|
||||
tooltip: null
|
||||
})
|
||||
},
|
||||
_sliderChanging(e) {
|
||||
this._slideringcb(e.detail.value)
|
||||
},
|
||||
_sliderChange(e) {
|
||||
this._slidercb(e.detail.value)
|
||||
},
|
||||
_colorTap(e) {
|
||||
this._colorcb(e.currentTarget.dataset.i)
|
||||
this.setData({
|
||||
color: null
|
||||
})
|
||||
},`)
|
||||
} else if (file.path.includes('miniprogram' + path.sep + 'index.wxss')) {
|
||||
// 工具弹窗的样式
|
||||
content += `/* 提示条 */
|
||||
._tooltip_contain {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
left: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
._tooltip {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
height: 30px;
|
||||
padding: 0 3px;
|
||||
overflow: scroll;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
._tooltip_item {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
padding: 0 2vw;
|
||||
line-height: 30px;
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
._color_item {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 5px 2vw;
|
||||
border:1px solid #dfe2e5;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* 图片宽度滚动条 */
|
||||
._slider {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
._tooltip,
|
||||
._slider {
|
||||
background-color: black;
|
||||
border-radius: 3px;
|
||||
opacity: 0.75;
|
||||
}`
|
||||
} else if (file.path.includes('parser.js')) {
|
||||
content = content.replace(/popNode\s*=\s*function\s*\(\)\s*{/, 'popNode = function () {\n const editable = this.options.editable')
|
||||
// 不转换标签名
|
||||
.replace(/if\s*\(config.blockTags\[node.name\]\)\s*{[\s\S]+?}/, `if (config.blockTags[node.name]) {
|
||||
if (!editable) {
|
||||
node.name = 'div'
|
||||
}
|
||||
}`)
|
||||
// 转换表格和列表
|
||||
.replace(/node.c(\)|\s*&&|\s*\n)/g, '(node.c || editable)$1')
|
||||
.replace(/while\s*\(map\[row\s*\+\s*'.'\s*\+\s*col\]\)\s*{[\s\S]+?}/, `while (map[row + '.' + col]) {
|
||||
col++
|
||||
}
|
||||
if (editable) {
|
||||
td.r = row
|
||||
}`)
|
||||
// 不做 expose 处理
|
||||
.replace(/parser.prototype.expose\s*=\s*function\s*\(\)\s*{/, `parser.prototype.expose = function () {
|
||||
if (this.options.editable) return`)
|
||||
} else if (file.path.includes('node.wxml')) {
|
||||
content = content.replace(/opts\s*=\s*"{{opts}}"/, 'opts="{{[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6],opts[7]+i+\'_\']}}"')
|
||||
.replace(/opts\s*=\s*"{{opts}}"/, 'opts="{{[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6],opts[7]+i1+\'_\'+i2+\'_\'+i3+\'_\'+i4+\'_\'+i5+\'_\']}}"')
|
||||
.replace('!n.c', "opts[5]?(!n.children||n.name=='a'):!n.c")
|
||||
.replace(/!(n.?)\.c(?![a-z])/g, '(opts[5]?true:!$1.c)')
|
||||
.replace(/isInline\((.*?)\)/g, '(opts[5]?true:isInline($1))')
|
||||
// 修改普通标签
|
||||
.replace(/<view\s*wx:else\s*id(.+?)style="/, '<view wx:else data-i="{{path+i}}" bindtap="nodeTap" id$1style="{{ctrl[\'e\'+path+i]&&opts[5]!==\'simple\'?\'border:1px solid black;padding:5px;display:block;\':\'\'}}')
|
||||
.replace(/<view\s*wx:else\s*id(.+?)style="/, '<view wx:else data-i="{{\'\'+i1}}" bindtap="nodeTap" id$1style="{{ctrl[\'e\'+i1]&&opts[5]!==\'simple\'?\'border:1px solid black;padding:5px;display:block;\':\'\'}}')
|
||||
.replace(/<view\s*wx:else\s*id(.+?)style="/, '<view wx:else data-i="{{i1+\'_\'+i2}}" bindtap="nodeTap" id$1style="{{ctrl[\'e\'+i1+\'_\'+i2]&&opts[5]!==\'simple\'?\'border:1px solid black;padding:5px;display:block;\':\'\'}}')
|
||||
.replace(/<view\s*wx:else\s*id(.+?)style="/, '<view wx:else data-i="{{i1+\'_\'+i2+\'_\'+i3}}" bindtap="nodeTap" id$1style="{{ctrl[\'e\'+i1+\'_\'+i2+\'_\'+i3]&&opts[5]!==\'simple\'?\'border:1px solid black;padding:5px;display:block;\':\'\'}}')
|
||||
.replace(/<view\s*wx:else\s*id(.+?)style="/, '<view wx:else data-i="{{i1+\'_\'+i2+\'_\'+i3+\'_\'+i4}}" bindtap="nodeTap" id$1style="{{ctrl[\'e\'+i1+\'_\'+i2+\'_\'+i3+\'_\'+i4]&&opts[5]!==\'simple\'?\'border:1px solid black;padding:5px;display:block;\':\'\'}}')
|
||||
// 修改文本块
|
||||
.replace(/<!--\s*文本\s*-->[\s\S]+?<!--\s*链接\s*-->/,
|
||||
`<block wx:elif="{{n.type==='text'}}">
|
||||
<text wx:if="{{!ctrl['e'+i]}}" data-i="{{i}}" mp-weixin:user-select="{{opts[4]}}" decode="{{!opts[5]}}" bindtap="editStart">{{n.text}}
|
||||
<text wx:if="{{!n.text}}" style="color:gray">{{opts[6]||'请输入'}}</text>
|
||||
</text>
|
||||
<text wx:elif="{{ctrl['e'+i]===1}}" data-i="{{i}}" style="border:1px dashed black;min-width:50px;width:auto;padding:5px;display:block" catchtap="editStart">{{n.text}}
|
||||
<text wx:if="{{!n.text}}" style="color:gray">{{opts[6]||'请输入'}}</text>
|
||||
</text>
|
||||
<textarea wx:else style="{{opts[5]==='simple'?'':'border:1px dashed black;'}}min-width:50px;width:auto;padding:5px" auto-height maxlength="-1" focus="{{ctrl['e'+i]===3}}" value="{{n.text}}" data-i="{{i}}" bindinput="editInput" bindblur="editEnd" />
|
||||
</block>
|
||||
<text wx:elif="{{n.name==='br'}}">\\n</text>`)
|
||||
// 修改图片
|
||||
.replace(/<image(.+?)id="\{\{n.attrs.id/, '<image$1id="{{n.attrs.id||(\'n\'+i)')
|
||||
.replace('height:1px', "height:{{ctrl['h'+i]||1}}px")
|
||||
.replace('style="{{ctrl[i]', 'style="{{ctrl[\'e\'+i]&&opts[5]!==\'simple\'?\'border:1px dashed black;padding:3px;\':\'\'}}{{ctrl[i]')
|
||||
.replace(/weixin:show-menu-by-longpress\s*=\s*"{{(\S+?)}}"\s*baidu:image-menu-prevent\s*=\s*"{{(\S+?)}}"/, 'weixin:show-menu-by-longpress="{{!opts[5]&&$1}}" baidu:image-menu-prevent="{{opts[5]||$2}}"')
|
||||
// 修改音视频
|
||||
.replace('<video', '<video bindtap="mediaTap"')
|
||||
.replace('audio ', 'audio bindtap="mediaTap" ')
|
||||
.replace('card', 'card bindtap="mediaTap"')
|
||||
} else if (file.path.includes('node.js') && file.extname === '.js') {
|
||||
content = `
|
||||
const Parser = require('../parser')
|
||||
function getTop(e) {
|
||||
let top
|
||||
// #ifndef MP-ALIPAY
|
||||
top = e.detail.y
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
top = top = e.detail.pageY
|
||||
// #endif
|
||||
if (top - e.currentTarget.offsetTop < 150 || top < 600) {
|
||||
top = e.currentTarget.offsetTop
|
||||
}
|
||||
if (top < 30) {
|
||||
top += 70
|
||||
}
|
||||
return top - 30
|
||||
}` + content.replace('methods:', `detached () {
|
||||
if (this.root && this.root._edit === this) {
|
||||
this.root._edit = undefined
|
||||
}
|
||||
},
|
||||
methods:`)
|
||||
// 记录图片宽度
|
||||
.replace(/imgLoad\s*\(e\)\s*{/, `imgLoad (e) {
|
||||
// #ifdef MP-WEIXIN || MP-QQ
|
||||
if (this.properties.opts[5]) {
|
||||
setTimeout(() => {
|
||||
const id = this.getNode(i).attrs.id || ('n' + i)
|
||||
wx.createSelectorQuery().in(this).select('#' + id).boundingClientRect().exec(res => {
|
||||
this.setData({
|
||||
['ctrl.h'+i]: res[0].height
|
||||
})
|
||||
})
|
||||
}, 50)
|
||||
}
|
||||
// #endif`)
|
||||
.replace(/if\s*\(!node.w\)\s*{[\s\S]+?}/,
|
||||
`if (!node.w) {
|
||||
val = e.detail.width
|
||||
if (this.properties.opts[5]) {
|
||||
const data = {}
|
||||
const path = 'nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.'
|
||||
if (val < 150) {
|
||||
data[path + 'ignore'] = 'T'
|
||||
}
|
||||
data[path + 'width'] = val.toString()
|
||||
this.root.setData(data)
|
||||
}
|
||||
}`)
|
||||
// 处理图片点击
|
||||
.replace(/imgTap\s*\(e\)\s*{([\s\S]+?)},\s*\/\*/,
|
||||
`imgTap (e) {
|
||||
if (!this.properties.opts[5]) {$1} else {
|
||||
const i = e.target.dataset.i
|
||||
const node = this.getNode(i)
|
||||
const items = this.root._getItem(node)
|
||||
this.root._edit = this
|
||||
const parser = new Parser(this.root)
|
||||
this.i = i
|
||||
this.root._maskTap()
|
||||
this.setData({
|
||||
['ctrl.e' + i]: 1
|
||||
})
|
||||
this.root._mask.push(() => {
|
||||
this.setData({
|
||||
['ctrl.e' + i]: 0
|
||||
})
|
||||
})
|
||||
this.root._tooltip({
|
||||
top: getTop(e),
|
||||
items,
|
||||
success: tapIndex => {
|
||||
if (items[tapIndex] === '换图') {
|
||||
// 换图
|
||||
this.root.getSrc('img', node.attrs.src || '').then(url => {
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.src', node.attrs.src, parser.getUrl(url instanceof Array ? url[0] : url), true)
|
||||
}).catch(() => { })
|
||||
} else if (items[tapIndex] === '宽度') {
|
||||
// 更改宽度
|
||||
const style = node.attrs.style || ''
|
||||
let value = style.match(/max-width:([0-9]+)%/)
|
||||
if (value) {
|
||||
value = parseInt(value[1])
|
||||
} else {
|
||||
value = 100
|
||||
}
|
||||
this.root._slider({
|
||||
min: 0,
|
||||
max: 100,
|
||||
value,
|
||||
top: getTop(e),
|
||||
changing: val => {
|
||||
// 变化超过 5% 更新时视图
|
||||
if (Math.abs(val - value) > 5) {
|
||||
this.changeStyle('max-width', i, val + '%', value + '%')
|
||||
value = val
|
||||
}
|
||||
},
|
||||
change: val => {
|
||||
if (val !== value) {
|
||||
this.changeStyle('max-width', i, val + '%', value + '%')
|
||||
value = val
|
||||
}
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.style', style, this.getNode(i).attrs.style)
|
||||
}
|
||||
})
|
||||
} else if (items[tapIndex] === '超链接') {
|
||||
// 将图片设置为链接
|
||||
this.root.getSrc('link', node.a ? node.a.href : '').then(url => {
|
||||
// 如果有 a 标签则替换 href
|
||||
if (node.a) {
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].a.href', node.a.href, parser.getUrl(url), true)
|
||||
} else {
|
||||
const link = {
|
||||
name: 'a',
|
||||
attrs: {
|
||||
href: parser.getUrl(url)
|
||||
},
|
||||
children: [node]
|
||||
}
|
||||
node.a = link.attrs
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + ']', node, link, true)
|
||||
}
|
||||
wx.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
}).catch(() => { })
|
||||
} else if (items[tapIndex] === '预览图') {
|
||||
// 设置预览图链接
|
||||
this.root.getSrc('img', node.attrs['original-src'] || '').then(url => {
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.original-src', node.attrs['original-src'], parser.getUrl(url instanceof Array ? url[0] : url), true)
|
||||
wx.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
}).catch(() => { })
|
||||
} else if (items[tapIndex] === '删除') {
|
||||
this.remove(i)
|
||||
} else {
|
||||
// 禁用 / 启用预览
|
||||
this.root.setData({
|
||||
['nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.ignore']: !node.attrs.ignore
|
||||
})
|
||||
wx.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
this.root._lock = true
|
||||
setTimeout(() => {
|
||||
this.root._lock = false
|
||||
}, 50)
|
||||
}
|
||||
},
|
||||
/*`)
|
||||
// 处理链接点击
|
||||
.replace(/linkTap\s*\(e\)\s*{([\s\S]+?)},\s*\/\*/,
|
||||
`linkTap (e) {
|
||||
if (!this.properties.opts[5]) {$1} else {
|
||||
const i = e.currentTarget.dataset.i
|
||||
const node = this.getNode(i)
|
||||
const items = this.root._getItem(node)
|
||||
this.root._tooltip({
|
||||
top: getTop(e),
|
||||
items,
|
||||
success: tapIndex => {
|
||||
if (items[tapIndex] === '更换链接') {
|
||||
this.root.getSrc('link', node.attrs.href).then(url => {
|
||||
this.root._editVal('nodes[' + (this.properties.opts[7] + i).replace(/_/g, '].children[') + '].attrs.href', node.attrs.href, url, true)
|
||||
wx.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
}).catch(() => { })
|
||||
} else {
|
||||
this.remove(i)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
/*`)
|
||||
}
|
||||
file.contents = Buffer.from(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
551
wechat-mini-program/node_modules/mp-html/plugins/editable/miniprogram/index.js
generated
vendored
Normal file
551
wechat-mini-program/node_modules/mp-html/plugins/editable/miniprogram/index.js
generated
vendored
Normal file
@@ -0,0 +1,551 @@
|
||||
/**
|
||||
* @fileoverview editable 插件
|
||||
*/
|
||||
const config = require('./config')
|
||||
const Parser = require('../parser')
|
||||
|
||||
function Editable (vm) {
|
||||
this.vm = vm
|
||||
this.editHistory = [] // 历史记录
|
||||
this.editI = -1 // 历史记录指针
|
||||
vm._mask = [] // 蒙版被点击时进行的操作
|
||||
|
||||
/**
|
||||
* @description 移动历史记录指针
|
||||
* @param {Number} num 移动距离
|
||||
*/
|
||||
const move = num => {
|
||||
const item = this.editHistory[this.editI + num]
|
||||
if (item) {
|
||||
this.editI += num
|
||||
vm.setData({
|
||||
[item.key]: item.value
|
||||
})
|
||||
}
|
||||
}
|
||||
vm.undo = () => move(-1) // 撤销
|
||||
vm.redo = () => move(1) // 重做
|
||||
|
||||
/**
|
||||
* @description 更新记录
|
||||
* @param {String} path 路径
|
||||
* @param {*} oldVal 旧值
|
||||
* @param {*} newVal 新值
|
||||
* @param {Boolean} set 是否更新到视图
|
||||
* @private
|
||||
*/
|
||||
vm._editVal = (path, oldVal, newVal, set) => {
|
||||
// 当前指针后的内容去除
|
||||
while (this.editI < this.editHistory.length - 1) {
|
||||
this.editHistory.pop()
|
||||
}
|
||||
|
||||
// 最多存储 30 条操作记录
|
||||
while (this.editHistory.length > 30) {
|
||||
this.editHistory.pop()
|
||||
this.editI--
|
||||
}
|
||||
|
||||
const last = this.editHistory[this.editHistory.length - 1]
|
||||
if (!last || last.key !== path) {
|
||||
if (last) {
|
||||
// 去掉上一次的新值
|
||||
this.editHistory.pop()
|
||||
this.editI--
|
||||
}
|
||||
// 存入这一次的旧值
|
||||
this.editHistory.push({
|
||||
key: path,
|
||||
value: oldVal
|
||||
})
|
||||
this.editI++
|
||||
}
|
||||
|
||||
// 存入本次的新值
|
||||
this.editHistory.push({
|
||||
key: path,
|
||||
value: newVal
|
||||
})
|
||||
this.editI++
|
||||
|
||||
// 更新到视图
|
||||
if (set) {
|
||||
vm.setData({
|
||||
[path]: newVal
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取菜单项
|
||||
* @private
|
||||
*/
|
||||
vm._getItem = function (node, up, down) {
|
||||
let items
|
||||
let i
|
||||
if (node === 'color') {
|
||||
return config.color
|
||||
}
|
||||
if (node.name === 'img') {
|
||||
items = config.img.slice(0)
|
||||
if (!vm.getSrc) {
|
||||
i = items.indexOf('换图')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
i = items.indexOf('超链接')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
i = items.indexOf('预览图')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
i = items.indexOf('禁用预览')
|
||||
if (i !== -1 && node.attrs.ignore) {
|
||||
items[i] = '启用预览'
|
||||
}
|
||||
} else if (node.name === 'a') {
|
||||
items = config.link.slice(0)
|
||||
if (!vm.getSrc) {
|
||||
i = items.indexOf('更换链接')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
} else if (node.name === 'video' || node.name === 'audio') {
|
||||
items = config.media.slice(0)
|
||||
i = items.indexOf('封面')
|
||||
if (!vm.getSrc && i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
i = items.indexOf('循环')
|
||||
if (node.attrs.loop && i !== -1) {
|
||||
items[i] = '不循环'
|
||||
}
|
||||
i = items.indexOf('自动播放')
|
||||
if (node.attrs.autoplay && i !== -1) {
|
||||
items[i] = '不自动播放'
|
||||
}
|
||||
} else if (node.name === 'card') {
|
||||
items = config.card.slice(0)
|
||||
} else {
|
||||
items = config.node.slice(0)
|
||||
}
|
||||
if (!up) {
|
||||
i = items.indexOf('上移')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
if (!down) {
|
||||
i = items.indexOf('下移')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 显示 tooltip
|
||||
* @param {object} obj
|
||||
* @private
|
||||
*/
|
||||
vm._tooltip = function (obj) {
|
||||
vm.setData({
|
||||
tooltip: {
|
||||
top: obj.top,
|
||||
items: obj.items
|
||||
}
|
||||
})
|
||||
vm._tooltipcb = obj.success
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 显示滚动条
|
||||
* @param {object} obj
|
||||
* @private
|
||||
*/
|
||||
vm._slider = function (obj) {
|
||||
vm.setData({
|
||||
slider: {
|
||||
min: obj.min,
|
||||
max: obj.max,
|
||||
value: obj.value,
|
||||
top: obj.top
|
||||
}
|
||||
})
|
||||
vm._slideringcb = obj.changing
|
||||
vm._slidercb = obj.change
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 显示颜色选择
|
||||
* @param {object} obj
|
||||
* @private
|
||||
*/
|
||||
vm._color = function (obj) {
|
||||
vm.setData({
|
||||
color: {
|
||||
items: obj.items,
|
||||
top: obj.top
|
||||
}
|
||||
})
|
||||
vm._colorcb = obj.success
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 点击蒙版
|
||||
* @private
|
||||
*/
|
||||
vm._maskTap = function () {
|
||||
// 隐藏所有悬浮窗
|
||||
while (this._mask.length) {
|
||||
(this._mask.pop())()
|
||||
}
|
||||
const data = {}
|
||||
if (this.data.tooltip) {
|
||||
data.tooltip = null
|
||||
}
|
||||
if (this.data.slider) {
|
||||
data.slider = null
|
||||
}
|
||||
if (this.data.color) {
|
||||
data.color = null
|
||||
}
|
||||
if (this.data.tooltip || this.data.slider || this.data.color) {
|
||||
this.setData(data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 插入节点
|
||||
* @param {Object} node
|
||||
*/
|
||||
function insert (node) {
|
||||
if (vm._edit) {
|
||||
vm._edit.insert(node)
|
||||
} else {
|
||||
const nodes = vm.data.nodes.slice(0)
|
||||
nodes.push(node)
|
||||
vm._editVal('nodes', vm.data.nodes, nodes, true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入指定 html 内容
|
||||
* @param {String} html 内容
|
||||
*/
|
||||
vm.insertHtml = html => {
|
||||
this.inserting = true
|
||||
const arr = new Parser(vm).parse(html)
|
||||
this.inserting = undefined
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
insert(arr[i])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入图片
|
||||
*/
|
||||
vm.insertImg = function () {
|
||||
vm.getSrc && vm.getSrc('img').then(src => {
|
||||
if (typeof src === 'string') {
|
||||
src = [src]
|
||||
}
|
||||
const parser = new Parser(vm)
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
insert({
|
||||
name: 'img',
|
||||
attrs: {
|
||||
src: parser.getUrl(src[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一个链接
|
||||
*/
|
||||
vm.insertLink = function () {
|
||||
vm.getSrc && vm.getSrc('link').then(url => {
|
||||
insert({
|
||||
name: 'a',
|
||||
attrs: {
|
||||
href: url
|
||||
},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: url
|
||||
}]
|
||||
})
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一个表格
|
||||
* @param {Number} rows 行数
|
||||
* @param {Number} cols 列数
|
||||
*/
|
||||
vm.insertTable = function (rows, cols) {
|
||||
const table = {
|
||||
name: 'table',
|
||||
attrs: {
|
||||
style: 'display:table;width:100%;margin:10px 0;text-align:center;border-spacing:0;border-collapse:collapse;border:1px solid gray'
|
||||
},
|
||||
children: []
|
||||
}
|
||||
for (let i = 0; i < rows; i++) {
|
||||
const tr = {
|
||||
name: 'tr',
|
||||
attrs: {},
|
||||
children: []
|
||||
}
|
||||
for (let j = 0; j < cols; j++) {
|
||||
tr.children.push({
|
||||
name: 'td',
|
||||
attrs: {
|
||||
style: 'padding:2px;border:1px solid gray'
|
||||
},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
})
|
||||
}
|
||||
table.children.push(tr)
|
||||
}
|
||||
insert(table)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 插入视频/音频
|
||||
* @param {Object} node
|
||||
*/
|
||||
function insertMedia (node) {
|
||||
if (typeof node.src === 'string') {
|
||||
node.src = [node.src]
|
||||
}
|
||||
const parser = new Parser(vm)
|
||||
// 拼接主域名
|
||||
for (let i = 0; i < node.src.length; i++) {
|
||||
node.src[i] = parser.getUrl(node.src[i])
|
||||
}
|
||||
insert({
|
||||
name: 'div',
|
||||
attrs: {
|
||||
style: 'text-align:center'
|
||||
},
|
||||
children: [node]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一个视频
|
||||
*/
|
||||
vm.insertVideo = function () {
|
||||
vm.getSrc && vm.getSrc('video').then(src => {
|
||||
insertMedia({
|
||||
name: 'video',
|
||||
attrs: {
|
||||
controls: 'T'
|
||||
},
|
||||
src
|
||||
})
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一个音频
|
||||
*/
|
||||
vm.insertAudio = function () {
|
||||
vm.getSrc && vm.getSrc('audio').then(attrs => {
|
||||
let src
|
||||
if (attrs.src) {
|
||||
src = attrs.src
|
||||
attrs.src = undefined
|
||||
} else {
|
||||
src = attrs
|
||||
attrs = {}
|
||||
}
|
||||
attrs.controls = 'T'
|
||||
insertMedia({
|
||||
name: 'audio',
|
||||
attrs,
|
||||
src
|
||||
})
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一段文本
|
||||
*/
|
||||
vm.insertText = function () {
|
||||
insert({
|
||||
name: 'p',
|
||||
attrs: {},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 清空内容
|
||||
*/
|
||||
vm.clear = function () {
|
||||
vm._maskTap()
|
||||
vm._edit = undefined
|
||||
vm.setData({
|
||||
nodes: [{
|
||||
name: 'p',
|
||||
attrs: {},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取编辑后的 html
|
||||
*/
|
||||
vm.getContent = function () {
|
||||
let html = '';
|
||||
// 递归遍历获取
|
||||
(function traversal (nodes, table) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let item = nodes[i]
|
||||
if (item.type === 'text') {
|
||||
// 编码实体
|
||||
html += item.text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>').replace(/\xa0/g, ' ')
|
||||
} else {
|
||||
// 还原被转换的 svg
|
||||
if (item.name === 'img' && (item.attrs.src || '').includes('data:image/svg+xml;utf8,')) {
|
||||
html += item.attrs.src.substr(24).replace(/%23/g, '#').replace('<svg', '<svg style="' + (item.attrs.style || '') + '"')
|
||||
continue
|
||||
} else if (item.name === 'video' || item.name === 'audio') {
|
||||
// 还原 video 和 audio 的 source
|
||||
if (item.src.length > 1) {
|
||||
item.children = []
|
||||
for (let j = 0; j < item.src.length; j++) {
|
||||
item.children.push({
|
||||
name: 'source',
|
||||
attrs: {
|
||||
src: item.src[j]
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
item.attrs.src = item.src[0]
|
||||
}
|
||||
} else if (item.name === 'div' && (item.attrs.style || '').includes('overflow:auto') && (item.children[0] || {}).name === 'table') {
|
||||
// 还原滚动层
|
||||
item = item.children[0]
|
||||
}
|
||||
// 还原 table
|
||||
if (item.name === 'table') {
|
||||
table = item.attrs
|
||||
if ((item.attrs.style || '').includes('display:grid')) {
|
||||
item.attrs.style = item.attrs.style.split('display:grid')[0]
|
||||
const children = [{
|
||||
name: 'tr',
|
||||
attrs: {},
|
||||
children: []
|
||||
}]
|
||||
for (let j = 0; j < item.children.length; j++) {
|
||||
item.children[j].attrs.style = item.children[j].attrs.style.replace(/grid-[^;]+;*/g, '')
|
||||
if (item.children[j].r !== children.length) {
|
||||
children.push({
|
||||
name: 'tr',
|
||||
attrs: {},
|
||||
children: [item.children[j]]
|
||||
})
|
||||
} else {
|
||||
children[children.length - 1].children.push(item.children[j])
|
||||
}
|
||||
}
|
||||
item.children = children
|
||||
}
|
||||
}
|
||||
html += '<' + item.name
|
||||
for (const attr in item.attrs) {
|
||||
let val = item.attrs[attr]
|
||||
if (!val) continue
|
||||
// bool 型省略值
|
||||
if (val === 'T' || val === true) {
|
||||
html += ' ' + attr
|
||||
continue
|
||||
} else if (item.name[0] === 't' && attr === 'style' && table) {
|
||||
// 取消为了显示 table 添加的 style
|
||||
val = val.replace(/;*display:table[^;]*/, '')
|
||||
if (table.border) {
|
||||
val = val.replace(/border[^;]+;*/g, $ => $.includes('collapse') ? $ : '')
|
||||
}
|
||||
if (table.cellpadding) {
|
||||
val = val.replace(/padding[^;]+;*/g, '')
|
||||
}
|
||||
if (!val) continue
|
||||
}
|
||||
html += ' ' + attr + '="' + val.replace(/"/g, '"') + '"'
|
||||
}
|
||||
html += '>'
|
||||
if (item.children) {
|
||||
traversal(item.children, table)
|
||||
html += '</' + item.name + '>'
|
||||
}
|
||||
}
|
||||
}
|
||||
})(vm.data.nodes)
|
||||
|
||||
// 其他插件处理
|
||||
for (let i = vm.plugins.length; i--;) {
|
||||
if (vm.plugins[i].onGetContent) {
|
||||
html = vm.plugins[i].onGetContent(html) || html
|
||||
}
|
||||
}
|
||||
|
||||
return html
|
||||
}
|
||||
}
|
||||
|
||||
Editable.prototype.onUpdate = function (content, config) {
|
||||
if (this.vm.properties.editable) {
|
||||
this.vm._maskTap()
|
||||
config.entities.amp = '&'
|
||||
if (!this.inserting) {
|
||||
this.vm._edit = undefined
|
||||
if (!content) {
|
||||
setTimeout(() => {
|
||||
this.vm.setData({
|
||||
nodes: [{
|
||||
name: 'p',
|
||||
attrs: {},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
}]
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Editable.prototype.onParse = function (node) {
|
||||
// 空白单元格可编辑
|
||||
if (this.vm.properties.editable && (node.name === 'td' || node.name === 'th') && !this.vm.getText(node.children)) {
|
||||
node.children.push({
|
||||
type: 'text',
|
||||
text: ''
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Editable
|
||||
744
wechat-mini-program/node_modules/mp-html/plugins/editable/uni-app/build.js
generated
vendored
Normal file
744
wechat-mini-program/node_modules/mp-html/plugins/editable/uni-app/build.js
generated
vendored
Normal file
@@ -0,0 +1,744 @@
|
||||
/* global getTop */
|
||||
module.exports = {
|
||||
style: `/* #ifndef H5 || MP-ALIPAY || APP-PLUS */
|
||||
._address,
|
||||
._article,
|
||||
._aside,
|
||||
._body,
|
||||
._caption,
|
||||
._center,
|
||||
._cite,
|
||||
._footer,
|
||||
._header,
|
||||
._html,
|
||||
._nav,
|
||||
._pre,
|
||||
._section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
._video {
|
||||
width: 300px;
|
||||
height: 225px;
|
||||
display: inline-block;
|
||||
background-color: black;
|
||||
}`,
|
||||
methods: {
|
||||
/**
|
||||
* @description 开始编辑文本
|
||||
* @param {Event} e
|
||||
*/
|
||||
editStart (e) {
|
||||
if (this.opts[5]) {
|
||||
const i = e.currentTarget.dataset.i
|
||||
if (!this.ctrl['e' + i] && this.opts[5] !== 'simple') {
|
||||
// 显示虚线框
|
||||
this.$set(this.ctrl, 'e' + i, 1)
|
||||
setTimeout(() => {
|
||||
this.root._mask.push(() => this.$set(this.ctrl, 'e' + i, 0))
|
||||
}, 50)
|
||||
this.root._edit = this
|
||||
this.i = i
|
||||
this.cursor = this.childs[i].text.length
|
||||
} else {
|
||||
if (this.opts[5] === 'simple') {
|
||||
this.root._edit = this
|
||||
this.i = i
|
||||
this.cursor = this.childs[i].text.length
|
||||
}
|
||||
this.root._mask.pop()
|
||||
this.root._maskTap()
|
||||
// 将 text 转为 textarea
|
||||
this.$set(this.ctrl, 'e' + i, 2)
|
||||
// 延时对焦,避免高度错误
|
||||
setTimeout(() => {
|
||||
this.$set(this.ctrl, 'e' + i, 3)
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description 输入文本
|
||||
* @param {Event} e
|
||||
*/
|
||||
editInput (e) {
|
||||
const i = e.target.dataset.i
|
||||
// 替换连续空格
|
||||
const value = e.detail.value.replace(/ {2,}/, $ => {
|
||||
let res = '\xa0'
|
||||
for (let i = 1; i < $.length; i++) {
|
||||
res += '\xa0'
|
||||
}
|
||||
return res
|
||||
})
|
||||
this.root._editVal(`${this.opts[7]}.${i}.text`, this.childs[i].text, value) // 记录编辑历史
|
||||
this.cursor = e.detail.cursor
|
||||
},
|
||||
/**
|
||||
* @description 完成编辑文本
|
||||
* @param {Event} e
|
||||
*/
|
||||
editEnd (e) {
|
||||
const i = e.target.dataset.i
|
||||
this.$set(this.ctrl, 'e' + i, 0)
|
||||
// 更新到视图
|
||||
this.root._setData(`${this.opts[7]}.${i}.text`, e.detail.value.replace(/ {2}/g, '\xa0 '))
|
||||
if (e.detail.cursor !== undefined) {
|
||||
this.cursor = e.detail.cursor
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description 插入一个标签
|
||||
* @param {Object} node 要插入的标签
|
||||
*/
|
||||
insert (node) {
|
||||
setTimeout(() => {
|
||||
const childs = this.childs.slice(0)
|
||||
if (!childs[this.i]) {
|
||||
childs.push(node)
|
||||
} else if (childs[this.i].text) {
|
||||
// 在文本中插入
|
||||
const text = childs[this.i].text
|
||||
if (node.type === 'text') {
|
||||
if (this.cursor) {
|
||||
childs[this.i].text = text.substring(0, this.cursor) + node.text + text.substring(this.cursor)
|
||||
} else {
|
||||
childs[this.i].text += node.text
|
||||
}
|
||||
} else {
|
||||
const list = []
|
||||
if (this.cursor) {
|
||||
list.push({
|
||||
type: 'text',
|
||||
text: text.substring(0, this.cursor)
|
||||
})
|
||||
}
|
||||
list.push(node)
|
||||
if (this.cursor < text.length) {
|
||||
list.push({
|
||||
type: 'text',
|
||||
text: text.substring(this.cursor)
|
||||
})
|
||||
}
|
||||
childs.splice(this.i, 1, ...list)
|
||||
}
|
||||
} else {
|
||||
childs.splice(parseInt(this.i) + 1, 0, node)
|
||||
}
|
||||
this.root._editVal(this.opts[7], this.childs, childs, true)
|
||||
this.i = parseInt(this.i) + 1
|
||||
}, 200)
|
||||
},
|
||||
/**
|
||||
* @description 移除第 i 个标签
|
||||
* @param {Number} i
|
||||
*/
|
||||
remove (i) {
|
||||
const arr = this.childs.slice(0)
|
||||
const delEle = arr.splice(i, 1)[0]
|
||||
if (delEle.name === 'img' || delEle.name === 'video' || delEle.name === 'audio') {
|
||||
let src = delEle.attrs.src
|
||||
if (delEle.src) {
|
||||
src = delEle.src.length === 1 ? delEle.src[0] : delEle.src
|
||||
}
|
||||
this.root.$emit('remove', {
|
||||
type: delEle.name,
|
||||
src
|
||||
})
|
||||
}
|
||||
this.root._edit = undefined
|
||||
this.root._maskTap()
|
||||
this.root._editVal(this.opts[7], this.childs, arr, true)
|
||||
},
|
||||
/**
|
||||
* @description 标签被点击
|
||||
* @param {Event} e
|
||||
*/
|
||||
nodeTap (e) {
|
||||
if (this.opts[5]) {
|
||||
if (this.root._lock) return
|
||||
this.root._lock = true
|
||||
setTimeout(() => {
|
||||
this.root._lock = false
|
||||
}, 50)
|
||||
if (this.ctrl['e' + this.i] === 3) return
|
||||
this.root._maskTap()
|
||||
this.root._edit = this
|
||||
if (this.opts[5] === 'simple') return
|
||||
let start = this.opts[7].lastIndexOf('children.')
|
||||
if (start !== -1) {
|
||||
start += 9
|
||||
} else {
|
||||
start = 6
|
||||
}
|
||||
const i = parseInt(this.opts[7].substring(start, this.opts[7].lastIndexOf('.children')))
|
||||
let parent = this.$parent
|
||||
while (parent && parent.$options.name !== 'node') {
|
||||
parent = parent.$parent
|
||||
}
|
||||
let remove = () => {
|
||||
parent.remove(i)
|
||||
}
|
||||
if (this.opts[7].length - parent.opts[7].length > 15) {
|
||||
const parts = this.opts[7].split('.')
|
||||
let childs = parent.childs
|
||||
const i = parseInt(parts[parent.opts[7].split('.').length])
|
||||
const oldParent = parent
|
||||
// 删除整个表格
|
||||
remove = () => {
|
||||
oldParent.remove(i)
|
||||
}
|
||||
for (let i = parent.opts[7].split('.').length; i < parts.length - 2; i++) {
|
||||
childs = childs[parts[i]]
|
||||
}
|
||||
const that = this
|
||||
parent = {
|
||||
childs,
|
||||
opts: [undefined, undefined, undefined, undefined, undefined, undefined, undefined, parts.slice(0, parts.length - 2).join('.')],
|
||||
changeStyle (name, i, value, oldVal) {
|
||||
let style = this.childs[i].attrs.style || ''
|
||||
if (style.includes(';' + name + ':' + oldVal)) {
|
||||
style = style.replace(';' + name + ':' + oldVal, ';' + name + ':' + value)
|
||||
} else {
|
||||
style += ';' + name + ':' + value
|
||||
}
|
||||
that.root._setData(`${this.opts[7]}.${i}.attrs.style`, style)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!parent) return
|
||||
// 显示实线框
|
||||
this.$set(this.ctrl, 'root', 1)
|
||||
this.root._mask.push(() => this.$set(this.ctrl, 'root', 0))
|
||||
if (this.childs.length === 1 && this.childs[0].type === 'text' && !this.ctrl.e0) {
|
||||
this.$set(this.ctrl, 'e0', 1)
|
||||
this.root._mask.push(() => this.$set(this.ctrl, 'e0', 0))
|
||||
this.i = 0
|
||||
this.cursor = this.childs[0].text.length
|
||||
}
|
||||
const items = this.root._getItem(parent.childs[i], i !== 0, i !== parent.childs.length - 1)
|
||||
this.root._tooltip({
|
||||
top: getTop(e),
|
||||
items,
|
||||
success: tapIndex => {
|
||||
if (items[tapIndex] === '大小') {
|
||||
// 改变字体大小
|
||||
const style = parent.childs[i].attrs.style || ''
|
||||
let value = style.match(/;font-size:([0-9]+)px/)
|
||||
if (value) {
|
||||
value = parseInt(value[1])
|
||||
} else {
|
||||
value = 16
|
||||
}
|
||||
this.root._slider({
|
||||
min: 10,
|
||||
max: 30,
|
||||
value,
|
||||
top: getTop(e),
|
||||
changing: val => {
|
||||
if (Math.abs(val - value) > 2) {
|
||||
// 字号变换超过 2 时更新到视图
|
||||
parent.changeStyle('font-size', i, val + 'px', value + 'px')
|
||||
value = e.detail.value
|
||||
}
|
||||
},
|
||||
change: val => {
|
||||
if (val !== value) {
|
||||
parent.changeStyle('font-size', i, val + 'px', value + 'px')
|
||||
}
|
||||
this.root._editVal(`${parent.opts[7]}.${i}.attrs.style`, style, parent.childs[i].attrs.style)
|
||||
}
|
||||
})
|
||||
} else if (items[tapIndex] === '颜色') {
|
||||
// 改变文字颜色
|
||||
const items = this.root._getItem('color')
|
||||
this.root._color({
|
||||
top: getTop(e),
|
||||
items,
|
||||
success: tapIndex => {
|
||||
const style = parent.childs[i].attrs.style || ''
|
||||
const value = style.match(/;color:([^;]+)/)
|
||||
parent.changeStyle('color', i, items[tapIndex], value ? value[1] : undefined)
|
||||
this.root._editVal(`${parent.opts[7]}.${i}.attrs.style`, style, parent.childs[i].attrs.style)
|
||||
}
|
||||
})
|
||||
} else if (items[tapIndex] === '上移' || items[tapIndex] === '下移') {
|
||||
const arr = parent.childs.slice(0)
|
||||
const item = arr[i]
|
||||
if (items[tapIndex] === '上移') {
|
||||
arr[i] = arr[i - 1]
|
||||
arr[i - 1] = item
|
||||
} else {
|
||||
arr[i] = arr[i + 1]
|
||||
arr[i + 1] = item
|
||||
}
|
||||
this.root._editVal(parent.opts[7], parent.childs, arr, true)
|
||||
} else if (items[tapIndex] === '删除') {
|
||||
remove()
|
||||
} else {
|
||||
const style = parent.childs[i].attrs.style || ''
|
||||
let newStyle = ''
|
||||
const item = items[tapIndex]
|
||||
let name
|
||||
let value
|
||||
if (item === '斜体') {
|
||||
name = 'font-style'
|
||||
value = 'italic'
|
||||
} else if (item === '粗体') {
|
||||
name = 'font-weight'
|
||||
value = 'bold'
|
||||
} else if (item === '下划线') {
|
||||
name = 'text-decoration'
|
||||
value = 'underline'
|
||||
} else if (item === '居中') {
|
||||
name = 'text-align'
|
||||
value = 'center'
|
||||
} else if (item === '缩进') {
|
||||
name = 'text-indent'
|
||||
value = '2em'
|
||||
}
|
||||
if (style.includes(name + ':')) {
|
||||
// 已有则取消
|
||||
newStyle = style.replace(new RegExp(name + ':[^;]+'), '')
|
||||
} else {
|
||||
// 没有则添加
|
||||
newStyle = style + ';' + name + ':' + value
|
||||
}
|
||||
this.root._editVal(`${parent.opts[7]}.${i}.attrs.style`, style, newStyle, true)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description 音视频被点击
|
||||
* @param {Event} e
|
||||
*/
|
||||
mediaTap (e, index) {
|
||||
if (this.opts[5]) {
|
||||
const i = e.target.dataset.i || index
|
||||
const node = this.childs[i]
|
||||
const items = this.root._getItem(node)
|
||||
this.root._maskTap()
|
||||
this.root._edit = this
|
||||
this.i = i
|
||||
this.root._tooltip({
|
||||
top: e.currentTarget.offsetTop - 30,
|
||||
items,
|
||||
success: tapIndex => {
|
||||
switch (items[tapIndex]) {
|
||||
case '封面':
|
||||
// 设置封面
|
||||
this.root.getSrc('img', node.attrs.poster || '').then(url => {
|
||||
this.root._editVal(`${this.opts[7]}.${i}.attrs.poster`, node.attrs.poster, url instanceof Array ? url[0] : url, true)
|
||||
}).catch(() => { })
|
||||
break
|
||||
case '删除':
|
||||
this.remove(i)
|
||||
break
|
||||
case '循环':
|
||||
case '不循环':
|
||||
// 切换循环播放
|
||||
this.root._setData(`${this.opts[7]}.${i}.attrs.loop`, !node.attrs.loop)
|
||||
uni.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
break
|
||||
case '自动播放':
|
||||
case '不自动播放':
|
||||
// 切换自动播放播放
|
||||
this.root._setData(`${this.opts[7]}.${i}.attrs.autoplay`, !node.attrs.autoplay)
|
||||
uni.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
// 避免上层出现点击态
|
||||
this.root._lock = true
|
||||
setTimeout(() => {
|
||||
this.root._lock = false
|
||||
}, 50)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 改变样式
|
||||
* @param {String} name 属性名
|
||||
* @param {Number} i 第几个标签
|
||||
* @param {String} value 新值
|
||||
* @param {String} oldVal 旧值
|
||||
*/
|
||||
changeStyle (name, i, value, oldVal) {
|
||||
let style = this.childs[i].attrs.style || ''
|
||||
if (style.includes(';' + name + ':' + oldVal)) {
|
||||
// style 中已经有
|
||||
style = style.replace(';' + name + ':' + oldVal, ';' + name + ':' + value)
|
||||
} else {
|
||||
// 没有则新增
|
||||
style += ';' + name + ':' + value
|
||||
}
|
||||
this.root._setData(`${this.opts[7]}.${i}.attrs.style`, style)
|
||||
}
|
||||
},
|
||||
handler (file) {
|
||||
if (file.isBuffer()) {
|
||||
let content = file.contents.toString()
|
||||
if (file.path.includes('mp-html.vue')) {
|
||||
// 传递 editable 属性和路径
|
||||
content = content.replace(/opts\s*=\s*"\[([^\]]+)\]"/, 'opts="[$1,editable,placeholder,\'nodes\']"')
|
||||
.replace(/<view(.*?):style\s*=\s*"containerStyle"/, '<view$1:style="(editable?\'min-height:200px;\':\'\')+containerStyle" @tap="_containTap"')
|
||||
// 工具弹窗
|
||||
.replace(/<\/view>\s*<\/template>/, ` <view v-if="tooltip" class="_tooltip_contain" :style="'top:'+tooltip.top+'px'">
|
||||
<view class="_tooltip">
|
||||
<view v-for="(item, index) in tooltip.items" v-bind:key="index" class="_tooltip_item" :data-i="index" @tap="_tooltipTap">{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="slider" class="_slider" :style="'top:'+slider.top+'px'">
|
||||
<slider :value="slider.value" :min="slider.min" :max="slider.max" handle-size="14" block-size="14" show-value activeColor="white" style="padding:3px" @changing="_sliderChanging" @change="_sliderChange" />
|
||||
</view>
|
||||
<view v-if="color" class="_tooltip_contain" :style="'top:'+color.top+'px'">
|
||||
<view class="_tooltip" style="overflow-y: hidden;">
|
||||
<view v-for="(item, index) in color.items" v-bind:key="index" class="_color_item" :style="'background-color:'+item" :data-i="index" @tap="_colorTap"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>`)
|
||||
// 添加 data
|
||||
.replace(/data\s*\(\)\s*{\s*return\s*{/, `data() {
|
||||
return {
|
||||
tooltip: null,
|
||||
slider: null,
|
||||
color: null,`)
|
||||
// 添加 editable 属性
|
||||
.replace(/props\s*:\s*{/, `props: {
|
||||
editable: [Boolean, String],
|
||||
placeholder: String,`)
|
||||
// 添加 watch
|
||||
.replace(/watch\s*:\s*{/, `watch: {
|
||||
editable(val) {
|
||||
this.setContent(val ? this.content : this.getContent())
|
||||
if (!val)
|
||||
this._maskTap()
|
||||
},`)
|
||||
.replace(/if\s*\(this.content/, 'if ((this.content || this.editable)')
|
||||
// 处理各类弹窗的事件
|
||||
.replace(/methods\s*:\s*{/, `methods: {
|
||||
_containTap() {
|
||||
if (!this._lock && !this.slider && !this.color) {
|
||||
this._edit = undefined
|
||||
this._maskTap()
|
||||
}
|
||||
},
|
||||
_tooltipTap(e) {
|
||||
this._tooltipcb(e.currentTarget.dataset.i)
|
||||
this.$set(this, 'tooltip', null)
|
||||
},
|
||||
_sliderChanging(e) {
|
||||
this._slideringcb(e.detail.value)
|
||||
},
|
||||
_sliderChange(e) {
|
||||
this._slidercb(e.detail.value)
|
||||
},
|
||||
_colorTap(e) {
|
||||
this._colorcb(e.currentTarget.dataset.i)
|
||||
this.$set(this, 'color', null)
|
||||
},`)
|
||||
// 工具弹窗的样式
|
||||
.replace('</style>', `
|
||||
/* 提示条 */
|
||||
._tooltip_contain {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
left: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
._tooltip {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
height: 30px;
|
||||
padding: 0 3px;
|
||||
overflow: scroll;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
._tooltip_item {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
padding: 0 2vw;
|
||||
line-height: 30px;
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
._color_item {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 5px 2vw;
|
||||
border:1px solid #dfe2e5;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* 图片宽度滚动条 */
|
||||
._slider {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
._tooltip,
|
||||
._slider {
|
||||
background-color: black;
|
||||
border-radius: 3px;
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>`)
|
||||
} else if (file.path.includes('parser.js')) {
|
||||
// 不做 expose 处理
|
||||
content = content.replace(/parser.prototype.expose\s*=\s*function\s*\(\)\s*{/, `parser.prototype.expose = function () {
|
||||
if (this.options.editable) return`)
|
||||
.replace(/popNode\s*=\s*function\s*\(\)\s*{/, 'popNode = function () {\n const editable = this.options.editable')
|
||||
// 不转换标签名
|
||||
.replace(/if\s*\(config.blockTags\[node.name\]\)\s*{[\s\S]+?}/, `if (config.blockTags[node.name]) {
|
||||
if (!editable) {
|
||||
node.name = 'div'
|
||||
}
|
||||
}`)
|
||||
// 转换表格和列表
|
||||
.replace(/else\s*if\s*\(node.c\)/, 'else if (!editable && node.c )')
|
||||
.replace(/node.c(\)|\s*&&|\s*\n)/g, '(node.c || editable)$1')
|
||||
.replace(/while\s*\(map\[row\s*\+\s*'.'\s*\+\s*col\]\)\s*{[\s\S]+?}/, `while (map[row + '.' + col]) {
|
||||
col++
|
||||
}
|
||||
if (editable) {
|
||||
td.r = row
|
||||
}`)
|
||||
.replace(/let\s+str\s*=\s*'<video style="width:100%;height:100%"'/, `let str = '<video style="width:100%;height:100%"'
|
||||
if (editable) {
|
||||
attrs.controls = ''
|
||||
}`)
|
||||
} else if (file.path.includes('node.vue')) {
|
||||
content =
|
||||
// 传递 opts
|
||||
content.replace(/:childs\s*=\s*"tbody.children"\s*:opts="opts"/, ':childs="tbody.children" :opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6],opts[7]+\'.\'+i+\'.children.\'+x+\'.children\']"')
|
||||
.replace(/:childs\s*=\s*"n2.children"\s*:opts="opts"/, ':childs="n2.children" :opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6],opts[7]+\'.\'+i+\'.children.\'+j+\'.children\']"')
|
||||
.replace(/:childs\s*=\s*"tr.children"\s*:opts="opts"/, ':childs="tr.children" :opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6],opts[7]+\'.\'+i+\'.children.\'+x+\'.children.\'+y+\'.children\']"')
|
||||
.replace(/:childs\s*=\s*"td.children"\s*:opts="opts"/, ':childs="td.children" :opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6],opts[7]+\'.\'+i+\'.children.\'+x+\'.children.\'+y+\'.children.\'+z+\'.children\']"')
|
||||
.replace(/opts\s*=\s*"opts"/g, 'opts="[opts[0],opts[1],opts[2],opts[3],opts[4],opts[5],opts[6],opts[7]+\'.\'+i+\'.children\']"')
|
||||
// 不使用 rich-text
|
||||
.replace(/!n.c/g, '!opts[5]&&!n.c').replace('&&n.c', '&&(n.c||opts[5])')
|
||||
// 修改普通标签
|
||||
.replace(/<view\s+:id(.+?)style="/, '<view @tap="nodeTap" :id$1style="(ctrl.root&&opts[5]!==\'simple\'?\'border:1px solid black;padding:5px;display:block;\':\'\')+')
|
||||
// 修改文本块
|
||||
.replace(/<!--\s*文本\s*-->[\s\S]+?<!--\s*链接\s*-->/,
|
||||
`<!-- 文本 -->
|
||||
<text v-else-if="n.type==='text'&&!ctrl['e'+i]" :data-i="i" :user-select="opts[4]" :decode="!opts[5]" @tap="editStart">{{n.text}}
|
||||
<text v-if="!n.text" style="color:gray">{{opts[6]||'请输入'}}</text>
|
||||
</text>
|
||||
<text v-else-if="n.type==='text'&&ctrl['e'+i]===1" :data-i="i" style="border:1px dashed black;min-width:50px;width:auto;padding:5px;display:block" @tap.stop="editStart">{{n.text}}
|
||||
<text v-if="!n.text" style="color:gray">{{opts[6]||'请输入'}}</text>
|
||||
</text>
|
||||
<textarea v-else-if="n.type==='text'" :style="opts[5]==='simple'?'':'border:1px dashed black;'+'min-width:50px;width:auto;padding:5px'" auto-height maxlength="-1" :focus="ctrl['e'+i]===3" :value="n.text" :data-i="i" @input="editInput" @blur="editEnd" />
|
||||
<text v-else-if="n.name==='br'">\\n</text>
|
||||
<!-- 链接 -->`)
|
||||
// 修改图片
|
||||
.replace(/<image(.+?)id="n.attrs.id/, '<image$1id="n.attrs.id||(\'n\'+i)')
|
||||
.replace('height:1px', "height:'+(ctrl['h'+i]||1)+'px")
|
||||
.replace(/:style\s*=\s*"\(ctrl\[i\]/g, ':style="(ctrl[\'e\'+i]&&opts[5]!==\'simple\'?\'border:1px dashed black;padding:3px;\':\'\')+(ctrl[i]')
|
||||
.replace(/show-menu-by-longpress\s*=\s*"(\S+?)"\s*:image-menu-prevent\s*=\s*"(\S+?)"/, 'show-menu-by-longpress="!opts[5]&&$1" :image-menu-prevent="opts[5]||$2"')
|
||||
// 修改音视频
|
||||
.replace('v-else-if="n.html"', 'v-else-if="n.html" :data-i="i" @tap="mediaTap"')
|
||||
.replace('<video', '<video :show-center-play-btn="!opts[5]" @tap="mediaTap"')
|
||||
.replace('<audio ', '<audio @tap="mediaTap" ')
|
||||
.replace('<my-audio ', '<my-audio @onClick="mediaTap($event, i)" ')
|
||||
.replace('card ', 'card @click="mediaTap($event, i)" ')
|
||||
.replace('<script>',
|
||||
`<script>
|
||||
import Parser from '../parser'
|
||||
function getTop(e) {
|
||||
let top
|
||||
// #ifdef H5 && VUE3
|
||||
top = e.pageY
|
||||
// #endif
|
||||
// #ifdef (H5 && VUE2) || APP-PLUS
|
||||
top = e.touches[0].pageY
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
top = e.detail.pageY
|
||||
// #endif
|
||||
// #ifndef H5 || MP-ALIPAY || APP-PLUS
|
||||
top = e.detail.y
|
||||
// #endif
|
||||
if (top - e.currentTarget.offsetTop < 150 || top < 600) {
|
||||
top = e.currentTarget.offsetTop
|
||||
}
|
||||
if (top < 30) {
|
||||
top += 70
|
||||
}
|
||||
return top - 30
|
||||
}`)
|
||||
// 周期处理
|
||||
.replace(/beforeDestroy\s*\(\)\s*{/, `beforeDestroy () {
|
||||
if (this.root && this.root._edit === this) {
|
||||
this.root._edit = undefined
|
||||
}`)
|
||||
// 记录图片宽度
|
||||
.replace(/imgLoad\s*\(e\)\s*{/, `imgLoad(e) {
|
||||
// #ifdef MP-WEIXIN || MP-QQ
|
||||
if (this.opts[5])
|
||||
this.$nextTick(() => {
|
||||
const id = this.childs[i].attrs.id || ('n' + i)
|
||||
uni.createSelectorQuery().in(this).select('#' + id).boundingClientRect().exec(res => {
|
||||
this.$set(this.ctrl, 'h'+i, res[0].height)
|
||||
})
|
||||
})
|
||||
// #endif`)
|
||||
.replace(/if\s*\(!this.childs\[i\].w\)\s*{[\s\S]+?}/,
|
||||
`if (!this.childs[i].w) {
|
||||
this.$set(this.ctrl, i, e.detail.width)
|
||||
if (this.opts[5]) {
|
||||
const path = this.opts[7] + '.' + i + '.attrs.'
|
||||
if (e.detail.width < 150)
|
||||
this.root._setData(path + 'ignore', 'T')
|
||||
this.root._setData(path + 'width', e.detail.width.toString())
|
||||
}
|
||||
}`)
|
||||
// 处理图片长按
|
||||
.replace(/imgLongTap\s*\(\)\s*{/, `imgLongTap() {
|
||||
if (this.opts[5]) return`)
|
||||
// 处理图片点击
|
||||
.replace(/imgTap\s*\(e\)\s*{([\s\S]+?)},\s*\/\*/,
|
||||
`imgTap (e) {
|
||||
if (!this.opts[5]) {$1} else {
|
||||
const i = e.currentTarget.dataset.i
|
||||
const node = this.childs[i]
|
||||
const items = this.root._getItem(node)
|
||||
const parser = new Parser(this.root)
|
||||
this.root._edit = this
|
||||
this.i = i
|
||||
this.root._maskTap()
|
||||
this.$set(this.ctrl, 'e' + i, 1)
|
||||
this.root._mask.push(() => this.$set(this.ctrl, 'e' + i, 0))
|
||||
this.root._tooltip({
|
||||
top: getTop(e),
|
||||
items,
|
||||
success: tapIndex => {
|
||||
if (items[tapIndex] === '换图') {
|
||||
// 换图
|
||||
this.root.getSrc('img', node.attrs.src || '').then(url => {
|
||||
this.root._editVal(this.opts[7] + '.' + i + '.attrs.src', node.attrs.src, parser.getUrl(url instanceof Array ? url[0] : url), true)
|
||||
}).catch(() => { })
|
||||
} else if (items[tapIndex] === '宽度') {
|
||||
// 更改宽度
|
||||
const style = node.attrs.style || ''
|
||||
let value = style.match(/max-width:([0-9]+)%/)
|
||||
if (value) {
|
||||
value = parseInt(value[1])
|
||||
} else {
|
||||
value = 100
|
||||
}
|
||||
this.root._slider({
|
||||
min: 0,
|
||||
max: 100,
|
||||
value,
|
||||
top: getTop(e),
|
||||
changing: val => {
|
||||
// 变化超过 5% 更新时视图
|
||||
if (Math.abs(val - value) > 5) {
|
||||
this.changeStyle('max-width', i, val + '%', value + '%')
|
||||
value = val
|
||||
}
|
||||
},
|
||||
change: val => {
|
||||
if (val !== value) {
|
||||
this.changeStyle('max-width', i, val + '%', value + '%')
|
||||
value = val
|
||||
}
|
||||
this.root._editVal(this.opts[7] + '.' + i + '.attrs.style', style, this.childs[i].attrs.style)
|
||||
}
|
||||
})
|
||||
} else if (items[tapIndex] === '超链接') {
|
||||
// 将图片设置为链接
|
||||
this.root.getSrc('link', node.a ? node.a.href : '').then(url => {
|
||||
// 如果有 a 标签则替换 href
|
||||
if (node.a) {
|
||||
this.root._editVal(this.opts[7] + '.' + i + '.a.href', node.a.href, parser.getUrl(url), true)
|
||||
} else {
|
||||
const link = {
|
||||
name: 'a',
|
||||
attrs: {
|
||||
href: parser.getUrl(url)
|
||||
},
|
||||
children: [node]
|
||||
}
|
||||
node.a = link.attrs
|
||||
this.root._editVal(this.opts[7] + '.' + i, node, link, true)
|
||||
}
|
||||
wx.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
}).catch(() => { })
|
||||
} else if (items[tapIndex] === '预览图') {
|
||||
// 设置预览图链接
|
||||
this.root.getSrc('img', node.attrs['original-src'] || '').then(url => {
|
||||
this.root._editVal(this.opts[7] + '.' + i + '.attrs.original-src', node.attrs['original-src'], parser.getUrl(url instanceof Array ? url[0] : url), true)
|
||||
uni.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
}).catch(() => { })
|
||||
} else if (items[tapIndex] === '删除') {
|
||||
this.remove(i)
|
||||
} else {
|
||||
// 禁用 / 启用预览
|
||||
this.root._setData(this.opts[7] + '.' + i + '.attrs.ignore', !node.attrs.ignore)
|
||||
uni.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
this.root._lock = true
|
||||
setTimeout(() => {
|
||||
this.root._lock = false
|
||||
}, 50)
|
||||
}
|
||||
},
|
||||
/*`)
|
||||
// 处理链接点击
|
||||
.replace(/linkTap\s*\(e\)\s*{([\s\S]+?)},\s*\/\*/,
|
||||
`linkTap (e) {
|
||||
if (!this.opts[5]) {$1} else {
|
||||
const i = e.currentTarget.dataset.i
|
||||
const node = this.childs[i]
|
||||
const items = this.root._getItem(node)
|
||||
this.root._tooltip({
|
||||
top: getTop(e),
|
||||
items,
|
||||
success: tapIndex => {
|
||||
if (items[tapIndex] === '更换链接') {
|
||||
this.root.getSrc('link', node.attrs.href).then(url => {
|
||||
this.root._editVal(this.opts[7] + '.' + i + '.attrs.href', node.attrs.href, url, true)
|
||||
uni.showToast({
|
||||
title: '成功'
|
||||
})
|
||||
}).catch(() => { })
|
||||
} else {
|
||||
this.remove(i)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
/*`)
|
||||
}
|
||||
file.contents = Buffer.from(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
553
wechat-mini-program/node_modules/mp-html/plugins/editable/uni-app/index.js
generated
vendored
Normal file
553
wechat-mini-program/node_modules/mp-html/plugins/editable/uni-app/index.js
generated
vendored
Normal file
@@ -0,0 +1,553 @@
|
||||
/**
|
||||
* @fileoverview editable 插件
|
||||
*/
|
||||
const config = require('./config')
|
||||
const Parser = require('../parser')
|
||||
|
||||
function Editable (vm) {
|
||||
this.vm = vm
|
||||
this.editHistory = [] // 历史记录
|
||||
this.editI = -1 // 历史记录指针
|
||||
vm._mask = [] // 蒙版被点击时进行的操作
|
||||
|
||||
vm._setData = function (path, val) {
|
||||
const paths = path.split('.')
|
||||
let target = vm
|
||||
for (let i = 0; i < paths.length - 1; i++) {
|
||||
target = target[paths[i]]
|
||||
}
|
||||
vm.$set(target, paths.pop(), val)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 移动历史记录指针
|
||||
* @param {Number} num 移动距离
|
||||
*/
|
||||
const move = num => {
|
||||
setTimeout(() => {
|
||||
const item = this.editHistory[this.editI + num]
|
||||
if (item) {
|
||||
this.editI += num
|
||||
vm._setData(item.key, item.value)
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
vm.undo = () => move(-1) // 撤销
|
||||
vm.redo = () => move(1) // 重做
|
||||
|
||||
/**
|
||||
* @description 更新记录
|
||||
* @param {String} path 更新内容路径
|
||||
* @param {*} oldVal 旧值
|
||||
* @param {*} newVal 新值
|
||||
* @param {Boolean} set 是否更新到视图
|
||||
* @private
|
||||
*/
|
||||
vm._editVal = (path, oldVal, newVal, set) => {
|
||||
// 当前指针后的内容去除
|
||||
while (this.editI < this.editHistory.length - 1) {
|
||||
this.editHistory.pop()
|
||||
}
|
||||
|
||||
// 最多存储 30 条操作记录
|
||||
while (this.editHistory.length > 30) {
|
||||
this.editHistory.pop()
|
||||
this.editI--
|
||||
}
|
||||
|
||||
const last = this.editHistory[this.editHistory.length - 1]
|
||||
if (!last || last.key !== path) {
|
||||
if (last) {
|
||||
// 去掉上一次的新值
|
||||
this.editHistory.pop()
|
||||
this.editI--
|
||||
}
|
||||
// 存入这一次的旧值
|
||||
this.editHistory.push({
|
||||
key: path,
|
||||
value: oldVal
|
||||
})
|
||||
this.editI++
|
||||
}
|
||||
|
||||
// 存入本次的新值
|
||||
this.editHistory.push({
|
||||
key: path,
|
||||
value: newVal
|
||||
})
|
||||
this.editI++
|
||||
|
||||
// 更新到视图
|
||||
if (set) {
|
||||
vm._setData(path, newVal)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取菜单项
|
||||
* @private
|
||||
*/
|
||||
vm._getItem = function (node, up, down) {
|
||||
let items
|
||||
let i
|
||||
if (node === 'color') {
|
||||
return config.color
|
||||
}
|
||||
if (node.name === 'img') {
|
||||
items = config.img.slice(0)
|
||||
if (!vm.getSrc) {
|
||||
i = items.indexOf('换图')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
i = items.indexOf('超链接')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
i = items.indexOf('预览图')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
i = items.indexOf('禁用预览')
|
||||
if (i !== -1 && node.attrs.ignore) {
|
||||
items[i] = '启用预览'
|
||||
}
|
||||
} else if (node.name === 'a') {
|
||||
items = config.link.slice(0)
|
||||
if (!vm.getSrc) {
|
||||
i = items.indexOf('更换链接')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
} else if (node.name === 'video' || node.name === 'audio') {
|
||||
items = config.media.slice(0)
|
||||
i = items.indexOf('封面')
|
||||
if (!vm.getSrc && i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
i = items.indexOf('循环')
|
||||
if (node.attrs.loop && i !== -1) {
|
||||
items[i] = '不循环'
|
||||
}
|
||||
i = items.indexOf('自动播放')
|
||||
if (node.attrs.autoplay && i !== -1) {
|
||||
items[i] = '不自动播放'
|
||||
}
|
||||
} else if (node.name === 'card') {
|
||||
items = config.card.slice(0)
|
||||
} else {
|
||||
items = config.node.slice(0)
|
||||
}
|
||||
if (!up) {
|
||||
i = items.indexOf('上移')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
if (!down) {
|
||||
i = items.indexOf('下移')
|
||||
if (i !== -1) {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 显示 tooltip
|
||||
* @param {object} obj
|
||||
* @private
|
||||
*/
|
||||
vm._tooltip = function (obj) {
|
||||
vm.$set(vm, 'tooltip', {
|
||||
top: obj.top,
|
||||
items: obj.items
|
||||
})
|
||||
vm._tooltipcb = obj.success
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 显示滚动条
|
||||
* @param {object} obj
|
||||
* @private
|
||||
*/
|
||||
vm._slider = function (obj) {
|
||||
vm.$set(vm, 'slider', {
|
||||
min: obj.min,
|
||||
max: obj.max,
|
||||
value: obj.value,
|
||||
top: obj.top
|
||||
})
|
||||
vm._slideringcb = obj.changing
|
||||
vm._slidercb = obj.change
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 显示颜色选择
|
||||
* @param {object} obj
|
||||
* @private
|
||||
*/
|
||||
vm._color = function (obj) {
|
||||
vm.$set(vm, 'color', {
|
||||
items: obj.items,
|
||||
top: obj.top
|
||||
})
|
||||
vm._colorcb = obj.success
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 点击蒙版
|
||||
* @private
|
||||
*/
|
||||
vm._maskTap = function () {
|
||||
// 隐藏所有悬浮窗
|
||||
while (vm._mask.length) {
|
||||
(vm._mask.pop())()
|
||||
}
|
||||
if (vm.tooltip) {
|
||||
vm.$set(vm, 'tooltip', null)
|
||||
}
|
||||
if (vm.slider) {
|
||||
vm.$set(vm, 'slider', null)
|
||||
}
|
||||
if (vm.color) {
|
||||
vm.$set(vm, 'color', null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 插入节点
|
||||
* @param {Object} node
|
||||
*/
|
||||
function insert (node) {
|
||||
if (vm._edit) {
|
||||
vm._edit.insert(node)
|
||||
} else {
|
||||
const nodes = vm.nodes.slice(0)
|
||||
nodes.push(node)
|
||||
vm._editVal('nodes', vm.nodes, nodes, true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入指定 html 内容
|
||||
* @param {String} html 内容
|
||||
*/
|
||||
vm.insertHtml = html => {
|
||||
this.inserting = true
|
||||
const arr = new Parser(vm).parse(html)
|
||||
this.inserting = undefined
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
insert(arr[i])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入图片
|
||||
*/
|
||||
vm.insertImg = function () {
|
||||
vm.getSrc && vm.getSrc('img').then(src => {
|
||||
if (typeof src === 'string') {
|
||||
src = [src]
|
||||
}
|
||||
const parser = new Parser(vm)
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
insert({
|
||||
name: 'img',
|
||||
attrs: {
|
||||
src: parser.getUrl(src[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一个链接
|
||||
*/
|
||||
vm.insertLink = function () {
|
||||
vm.getSrc && vm.getSrc('link').then(url => {
|
||||
insert({
|
||||
name: 'a',
|
||||
attrs: {
|
||||
href: url
|
||||
},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: url
|
||||
}]
|
||||
})
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一个表格
|
||||
* @param {Number} rows 行数
|
||||
* @param {Number} cols 列数
|
||||
*/
|
||||
vm.insertTable = function (rows, cols) {
|
||||
const table = {
|
||||
name: 'table',
|
||||
attrs: {
|
||||
style: 'display:table;width:100%;margin:10px 0;text-align:center;border-spacing:0;border-collapse:collapse;border:1px solid gray'
|
||||
},
|
||||
children: []
|
||||
}
|
||||
for (let i = 0; i < rows; i++) {
|
||||
const tr = {
|
||||
name: 'tr',
|
||||
attrs: {},
|
||||
children: []
|
||||
}
|
||||
for (let j = 0; j < cols; j++) {
|
||||
tr.children.push({
|
||||
name: 'td',
|
||||
attrs: {
|
||||
style: 'padding:2px;border:1px solid gray'
|
||||
},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
})
|
||||
}
|
||||
table.children.push(tr)
|
||||
}
|
||||
insert(table)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 插入视频/音频
|
||||
* @param {Object} node
|
||||
*/
|
||||
function insertMedia (node) {
|
||||
if (typeof node.src === 'string') {
|
||||
node.src = [node.src]
|
||||
}
|
||||
const parser = new Parser(vm)
|
||||
// 拼接主域名
|
||||
for (let i = 0; i < node.src.length; i++) {
|
||||
node.src[i] = parser.getUrl(node.src[i])
|
||||
}
|
||||
insert({
|
||||
name: 'div',
|
||||
attrs: {
|
||||
style: 'text-align:center'
|
||||
},
|
||||
children: [node]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一个视频
|
||||
*/
|
||||
vm.insertVideo = function () {
|
||||
vm.getSrc && vm.getSrc('video').then(src => {
|
||||
insertMedia({
|
||||
name: 'video',
|
||||
attrs: {
|
||||
controls: 'T'
|
||||
},
|
||||
children: [],
|
||||
src,
|
||||
// #ifdef APP-PLUS
|
||||
html: `<video src="${src}" style="width:100%;height:100%"></video>`
|
||||
// #endif
|
||||
})
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一个音频
|
||||
*/
|
||||
vm.insertAudio = function () {
|
||||
vm.getSrc && vm.getSrc('audio').then(attrs => {
|
||||
let src
|
||||
if (attrs.src) {
|
||||
src = attrs.src
|
||||
attrs.src = undefined
|
||||
} else {
|
||||
src = attrs
|
||||
attrs = {}
|
||||
}
|
||||
attrs.controls = 'T'
|
||||
insertMedia({
|
||||
name: 'audio',
|
||||
attrs,
|
||||
children: [],
|
||||
src
|
||||
})
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 在光标处插入一段文本
|
||||
*/
|
||||
vm.insertText = function () {
|
||||
insert({
|
||||
name: 'p',
|
||||
attrs: {},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 清空内容
|
||||
*/
|
||||
vm.clear = function () {
|
||||
vm._maskTap()
|
||||
vm._edit = undefined
|
||||
vm.$set(vm, 'nodes', [{
|
||||
name: 'p',
|
||||
attrs: {},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
}])
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取编辑后的 html
|
||||
*/
|
||||
vm.getContent = function () {
|
||||
let html = '';
|
||||
// 递归遍历获取
|
||||
(function traversal (nodes, table) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let item = nodes[i]
|
||||
if (item.type === 'text') {
|
||||
html += item.text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>').replace(/\xa0/g, ' ') // 编码实体
|
||||
} else {
|
||||
if (item.name === 'img') {
|
||||
item.attrs.i = ''
|
||||
// 还原被转换的 svg
|
||||
if ((item.attrs.src || '').includes('data:image/svg+xml;utf8,')) {
|
||||
html += item.attrs.src.substr(24).replace(/%23/g, '#').replace('<svg', '<svg style="' + (item.attrs.style || '') + '"')
|
||||
continue
|
||||
}
|
||||
} else if (item.name === 'video' || item.name === 'audio') {
|
||||
// 还原 video 和 audio 的 source
|
||||
item = JSON.parse(JSON.stringify(item))
|
||||
if (item.src.length > 1) {
|
||||
item.children = []
|
||||
for (let j = 0; j < item.src.length; j++) {
|
||||
item.children.push({
|
||||
name: 'source',
|
||||
attrs: {
|
||||
src: item.src[j]
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
item.attrs.src = item.src[0]
|
||||
}
|
||||
} else if (item.name === 'div' && (item.attrs.style || '').includes('overflow:auto') && (item.children[0] || {}).name === 'table') {
|
||||
// 还原滚动层
|
||||
item = item.children[0]
|
||||
}
|
||||
// 还原 table
|
||||
if (item.name === 'table') {
|
||||
item = JSON.parse(JSON.stringify(item))
|
||||
table = item.attrs
|
||||
if ((item.attrs.style || '').includes('display:grid')) {
|
||||
item.attrs.style = item.attrs.style.split('display:grid')[0]
|
||||
const children = [{
|
||||
name: 'tr',
|
||||
attrs: {},
|
||||
children: []
|
||||
}]
|
||||
for (let j = 0; j < item.children.length; j++) {
|
||||
item.children[j].attrs.style = item.children[j].attrs.style.replace(/grid-[^;]+;*/g, '')
|
||||
if (item.children[j].r !== children.length) {
|
||||
children.push({
|
||||
name: 'tr',
|
||||
attrs: {},
|
||||
children: [item.children[j]]
|
||||
})
|
||||
} else {
|
||||
children[children.length - 1].children.push(item.children[j])
|
||||
}
|
||||
}
|
||||
item.children = children
|
||||
}
|
||||
}
|
||||
html += '<' + item.name
|
||||
for (const attr in item.attrs) {
|
||||
let val = item.attrs[attr]
|
||||
if (!val) continue
|
||||
if (val === 'T' || val === true) {
|
||||
// bool 型省略值
|
||||
html += ' ' + attr
|
||||
continue
|
||||
} else if (item.name[0] === 't' && attr === 'style' && table) {
|
||||
// 取消为了显示 table 添加的 style
|
||||
val = val.replace(/;*display:table[^;]*/, '')
|
||||
if (table.border) {
|
||||
val = val.replace(/border[^;]+;*/g, $ => $.includes('collapse') ? $ : '')
|
||||
}
|
||||
if (table.cellpadding) {
|
||||
val = val.replace(/padding[^;]+;*/g, '')
|
||||
}
|
||||
if (!val) continue
|
||||
}
|
||||
html += ' ' + attr + '="' + val.replace(/"/g, '"') + '"'
|
||||
}
|
||||
html += '>'
|
||||
if (item.children) {
|
||||
traversal(item.children, table)
|
||||
html += '</' + item.name + '>'
|
||||
}
|
||||
}
|
||||
}
|
||||
})(vm.nodes)
|
||||
|
||||
// 其他插件处理
|
||||
for (let i = vm.plugins.length; i--;) {
|
||||
if (vm.plugins[i].onGetContent) {
|
||||
html = vm.plugins[i].onGetContent(html) || html
|
||||
}
|
||||
}
|
||||
|
||||
return html
|
||||
}
|
||||
}
|
||||
|
||||
Editable.prototype.onUpdate = function (content, config) {
|
||||
if (this.vm.editable) {
|
||||
this.vm._maskTap()
|
||||
config.entities.amp = '&'
|
||||
if (!this.inserting) {
|
||||
this.vm._edit = undefined
|
||||
if (!content) {
|
||||
setTimeout(() => {
|
||||
this.vm.$set(this.vm, 'nodes', [{
|
||||
name: 'p',
|
||||
attrs: {},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: ''
|
||||
}]
|
||||
}])
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Editable.prototype.onParse = function (node) {
|
||||
// 空白单元格可编辑
|
||||
if (this.vm.editable && (node.name === 'td' || node.name === 'th') && !this.vm.getText(node.children)) {
|
||||
node.children.push({
|
||||
type: 'text',
|
||||
text: ''
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Editable
|
||||
15
wechat-mini-program/node_modules/mp-html/plugins/emoji/README.md
generated
vendored
Normal file
15
wechat-mini-program/node_modules/mp-html/plugins/emoji/README.md
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# emoji
|
||||
功能:解析 *emoji*
|
||||
大小:*≈3KB*
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | √ | √ | √ | √ |
|
||||
|
||||
说明:
|
||||
将形如 *[笑脸]* 的文本替换为 *emoji* 字符 😄
|
||||
匹配模式可以通过修改 *reg* 变量实现
|
||||
默认配置了 *177* 个常用的 *emoji* 小表情,可以自行按照需要修改 *data* 变量
|
||||
|
||||
?> 与 *editable* 插件共用时,导出编辑好的 *html* 内容,会将 *emoji* 字符编码为文本形式,便于存储
|
||||
203
wechat-mini-program/node_modules/mp-html/plugins/emoji/index.js
generated
vendored
Normal file
203
wechat-mini-program/node_modules/mp-html/plugins/emoji/index.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* @fileoverview emoji 插件
|
||||
*/
|
||||
const reg = /\[(\S+?)\]/g
|
||||
const data = {
|
||||
笑脸: '😄',
|
||||
生病: '😷',
|
||||
破涕为笑: '😂',
|
||||
吐舌: '😝',
|
||||
脸红: '😳',
|
||||
恐惧: '😱',
|
||||
失望: '😔',
|
||||
无语: '😒',
|
||||
眨眼: '😉',
|
||||
酷: '😎',
|
||||
哭: '😭',
|
||||
痴迷: '😍',
|
||||
吻: '😘',
|
||||
思考: '🤔',
|
||||
困惑: '😕',
|
||||
颠倒: '🙃',
|
||||
钱: '🤑',
|
||||
惊讶: '😲',
|
||||
白眼: '🙄',
|
||||
叹气: '😤',
|
||||
睡觉: '😴',
|
||||
书呆子: '🤓',
|
||||
愤怒: '😡',
|
||||
面无表情: '😑',
|
||||
张嘴: '😮',
|
||||
量体温: '🤒',
|
||||
呕吐: '🤮',
|
||||
光环: '😇',
|
||||
幽灵: '👻',
|
||||
外星人: '👽',
|
||||
机器人: '🤖',
|
||||
捂眼镜: '🙈',
|
||||
捂耳朵: '🙉',
|
||||
捂嘴: '🙊',
|
||||
婴儿: '👶',
|
||||
男孩: '👦',
|
||||
女孩: '👧',
|
||||
男人: '👨',
|
||||
女人: '👩',
|
||||
老人: '👴',
|
||||
老妇人: '👵',
|
||||
警察: '👮',
|
||||
王子: '🤴',
|
||||
公主: '🤴',
|
||||
举手: '🙋',
|
||||
跑步: '🏃',
|
||||
家庭: '👪',
|
||||
眼睛: '👀',
|
||||
鼻子: '👃',
|
||||
耳朵: '👂',
|
||||
舌头: '👅',
|
||||
嘴: '👄',
|
||||
心: '❤️',
|
||||
心碎: '💔',
|
||||
雪人: '☃️',
|
||||
情书: '💌',
|
||||
大便: '💩',
|
||||
闹钟: '⏰',
|
||||
眼镜: '👓',
|
||||
雨伞: '☂️',
|
||||
音乐: '🎵',
|
||||
话筒: '🎤',
|
||||
游戏机: '🎮',
|
||||
喇叭: '📢',
|
||||
耳机: '🎧',
|
||||
礼物: '🎁',
|
||||
电话: '📞',
|
||||
电脑: '💻',
|
||||
打印机: '🖨️',
|
||||
手电筒: '🔦',
|
||||
灯泡: '💡',
|
||||
书本: '📖',
|
||||
信封: '✉️',
|
||||
药丸: '💊',
|
||||
口红: '💄',
|
||||
手机: '📱',
|
||||
相机: '📷',
|
||||
电视: '📺',
|
||||
中: '🀄',
|
||||
垃圾桶: '🚮',
|
||||
厕所: '🚾',
|
||||
感叹号: '❗',
|
||||
禁: '🈲',
|
||||
可: '🉑',
|
||||
彩虹: '🌈',
|
||||
旋风: '🌀',
|
||||
雷电: '⚡',
|
||||
雪花: '❄️',
|
||||
星星: '⭐',
|
||||
水滴: '💧',
|
||||
玫瑰: '🌹',
|
||||
加油: '💪',
|
||||
左: '👈',
|
||||
右: '👉',
|
||||
上: '👆',
|
||||
下: '👇',
|
||||
手掌: '🖐️',
|
||||
好的: '👌',
|
||||
好: '👍',
|
||||
差: '👎',
|
||||
胜利: '✌',
|
||||
拳头: '👊',
|
||||
挥手: '👋',
|
||||
鼓掌: '👏',
|
||||
猴子: '🐒',
|
||||
狗: '🐶',
|
||||
狼: '🐺',
|
||||
猫: '🐱',
|
||||
老虎: '🐯',
|
||||
马: '🐎',
|
||||
独角兽: '🦄',
|
||||
斑马: '🦓',
|
||||
鹿: '🦌',
|
||||
牛: '🐮',
|
||||
猪: '🐷',
|
||||
羊: '🐏',
|
||||
长颈鹿: '🦒',
|
||||
大象: '🐘',
|
||||
老鼠: '🐭',
|
||||
蝙蝠: '🦇',
|
||||
刺猬: '🦔',
|
||||
熊猫: '🐼',
|
||||
鸽子: '🕊️',
|
||||
鸭子: '🦆',
|
||||
兔子: '🐇',
|
||||
老鹰: '🦅',
|
||||
青蛙: '🐸',
|
||||
蛇: '🐍',
|
||||
龙: '🐉',
|
||||
鲸鱼: '🐳',
|
||||
海豚: '🐬',
|
||||
足球: '⚽',
|
||||
棒球: '⚾',
|
||||
篮球: '🏀',
|
||||
排球: '🏐',
|
||||
橄榄球: '🏉',
|
||||
网球: '🎾',
|
||||
骰子: '🎲',
|
||||
鸡腿: '🍗',
|
||||
蛋糕: '🎂',
|
||||
啤酒: '🍺',
|
||||
饺子: '🥟',
|
||||
汉堡: '🍔',
|
||||
薯条: '🍟',
|
||||
意大利面: '🍝',
|
||||
干杯: '🥂',
|
||||
筷子: '🥢',
|
||||
糖果: '🍬',
|
||||
奶瓶: '🍼',
|
||||
爆米花: '🍿',
|
||||
邮局: '🏤',
|
||||
医院: '🏥',
|
||||
银行: '🏦',
|
||||
酒店: '🏨',
|
||||
学校: '🏫',
|
||||
城堡: '🏰',
|
||||
火车: '🚂',
|
||||
高铁: '🚄',
|
||||
地铁: '🚇',
|
||||
公交: '🚌',
|
||||
救护车: '🚑',
|
||||
消防车: '🚒',
|
||||
警车: '🚓',
|
||||
出租车: '🚕',
|
||||
汽车: '🚗',
|
||||
货车: '🚛',
|
||||
自行车: '🚲',
|
||||
摩托: '🛵',
|
||||
红绿灯: '🚥',
|
||||
帆船: '⛵',
|
||||
游轮: '🛳️',
|
||||
轮船: '⛴️',
|
||||
飞机: '✈️',
|
||||
直升机: '🚁',
|
||||
缆车: '🚠',
|
||||
警告: '⚠️',
|
||||
禁止: '⛔'
|
||||
}
|
||||
|
||||
function Emoji () {
|
||||
|
||||
}
|
||||
|
||||
Emoji.prototype.onUpdate = function (content) {
|
||||
return content.replace(reg, ($, $1) => {
|
||||
if (data[$1]) return data[$1]
|
||||
return $
|
||||
})
|
||||
}
|
||||
|
||||
Emoji.prototype.onGetContent = function (content) {
|
||||
for (const item in data) {
|
||||
content = content.replace(new RegExp(data[item], 'g'), '[' + item + ']')
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
module.exports = Emoji
|
||||
26
wechat-mini-program/node_modules/mp-html/plugins/highlight/README.md
generated
vendored
Normal file
26
wechat-mini-program/node_modules/mp-html/plugins/highlight/README.md
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# highlight
|
||||
功能:代码块高亮显示
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | √ | √ | √ | √ |
|
||||
|
||||
说明:
|
||||
大小:*≈16KB*
|
||||
编辑 *plugins/highlight/config.js* 顶部的选项,可以选择是否需要以下功能:
|
||||
- *copyByLongPress* 是否需要长按代码块时显示复制代码内容菜单(*uni-app nvue* 暂不支持)
|
||||
- *showLanguageName* 是否在代码块右上角显示语言的名称
|
||||
- *showLineNumber* 是否在左侧显示行号
|
||||
|
||||
> 修改该配置后需要重新生成组件包,在构建后的组件包中修改配置无法生效
|
||||
|
||||
引入本插件后,*html* 中符合以下格式的 *pre* 将被高亮处理:
|
||||
```html
|
||||
<!-- pre 中内含一个 code,并在 pre 或 code 的 class 中设置 language- -->
|
||||
<pre><code class="language-css">p { color: red }</code></pre>
|
||||
```
|
||||
|
||||
> 与 *editable* 插件共用时,编辑状态下,不会进行高亮,可以直接修改代码文本
|
||||
|
||||
> 本插件的高亮功能依赖于 [prismjs](https://prismjs.com/),默认配置中仅支持 *html*、*css*、*c-like*、*javascript* 语言和 *Tomorrow Night* 主题,如果需要更多语言或更换主题请前往 [官网](https://prismjs.com/download.html) 下载对应的 *prism.min.js* 和 *prism.css* 并替换 *plugins/highlight/* 目录下的文件
|
||||
5
wechat-mini-program/node_modules/mp-html/plugins/highlight/config.js
generated
vendored
Normal file
5
wechat-mini-program/node_modules/mp-html/plugins/highlight/config.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
copyByLongPress: false, // 是否需要长按代码块时显示复制代码内容菜单
|
||||
showLanguageName: false, // 是否在代码块右上角显示语言的名称
|
||||
showLineNumber: false // 是否显示行号
|
||||
}
|
||||
96
wechat-mini-program/node_modules/mp-html/plugins/highlight/index.js
generated
vendored
Normal file
96
wechat-mini-program/node_modules/mp-html/plugins/highlight/index.js
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @fileoverview highlight 插件
|
||||
* Include prismjs (https://prismjs.com)
|
||||
*/
|
||||
const prism = require('./prism.min')
|
||||
const config = require('./config')
|
||||
const Parser = require('../parser')
|
||||
|
||||
function Highlight (vm) {
|
||||
this.vm = vm
|
||||
}
|
||||
|
||||
Highlight.prototype.onParse = function (node, vm) {
|
||||
if (node.name === 'pre') {
|
||||
if (vm.options.editable) {
|
||||
node.attrs.class = (node.attrs.class || '') + ' hl-pre'
|
||||
return
|
||||
}
|
||||
let i
|
||||
for (i = node.children.length; i--;) {
|
||||
if (node.children[i].name === 'code') break
|
||||
}
|
||||
if (i === -1) return
|
||||
const code = node.children[i]
|
||||
let className = code.attrs.class + ' ' + node.attrs.class
|
||||
i = className.indexOf('language-')
|
||||
if (i === -1) {
|
||||
i = className.indexOf('lang-')
|
||||
if (i === -1) {
|
||||
className = 'language-text'
|
||||
i = 9
|
||||
} else {
|
||||
i += 5
|
||||
}
|
||||
} else {
|
||||
i += 9
|
||||
}
|
||||
let j
|
||||
for (j = i; j < className.length; j++) {
|
||||
if (className[j] === ' ') break
|
||||
}
|
||||
const lang = className.substring(i, j)
|
||||
if (code.children.length) {
|
||||
const text = this.vm.getText(code.children).replace(/&/g, '&')
|
||||
if (!text) return
|
||||
if (node.c) {
|
||||
node.c = undefined
|
||||
}
|
||||
if (prism.languages[lang]) {
|
||||
code.children = (new Parser(this.vm).parse(
|
||||
// 加一层 pre 保留空白符
|
||||
'<pre>' + prism.highlight(text, prism.languages[lang], lang).replace(/token /g, 'hl-') + '</pre>'))[0].children
|
||||
}
|
||||
node.attrs.class = 'hl-pre'
|
||||
code.attrs.class = 'hl-code'
|
||||
if (config.showLanguageName) {
|
||||
node.children.push({
|
||||
name: 'div',
|
||||
attrs: {
|
||||
class: 'hl-language',
|
||||
style: 'user-select:none'
|
||||
},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: lang
|
||||
}]
|
||||
})
|
||||
}
|
||||
if (config.copyByLongPress) {
|
||||
node.attrs.style += (node.attrs.style || '') + ';user-select:none'
|
||||
node.attrs['data-content'] = text
|
||||
vm.expose()
|
||||
}
|
||||
if (config.showLineNumber) {
|
||||
const line = text.split('\n').length; const children = []
|
||||
for (let k = line; k--;) {
|
||||
children.push({
|
||||
name: 'span',
|
||||
attrs: {
|
||||
class: 'span'
|
||||
}
|
||||
})
|
||||
}
|
||||
node.children.push({
|
||||
name: 'span',
|
||||
attrs: {
|
||||
class: 'line-numbers-rows'
|
||||
},
|
||||
children
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Highlight
|
||||
88
wechat-mini-program/node_modules/mp-html/plugins/highlight/miniprogram/build.js
generated
vendored
Normal file
88
wechat-mini-program/node_modules/mp-html/plugins/highlight/miniprogram/build.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
const config = require('../config')
|
||||
|
||||
const build = {
|
||||
import: 'prism.css',
|
||||
handler (file) {
|
||||
if (file.path.includes('prism.css')) {
|
||||
// 将标签名选择器和属性选择器转为 class 选择器(组件内仅支持 class 选择器)
|
||||
file.contents = Buffer.from(file.contents.toString().replace(/pre([[)])/g, '.hl-pre$1').replace(/code/g, '.hl-code').replace(/\[class\*="?language-"?\]/g, '').replace(/:not[^,}]+[,}]*/g, '').replace(/\.token\./g, '.hl-'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.showLanguageName || config.showLineNumber) {
|
||||
// pre 内部的 code 进行滚动,避免行号和语言名称跟随滚动
|
||||
build.style = `.hl-pre {
|
||||
position: relative;
|
||||
}
|
||||
.hl-code {
|
||||
overflow: auto;
|
||||
display: block;
|
||||
}`
|
||||
}
|
||||
|
||||
if (config.copyByLongPress) {
|
||||
build.template = '<rich-text wx:if="{{n.attrs[\'data-content\']}}" nodes="{{[n]}}" data-content="{{n.attrs[\'data-content\']}}" data-lang="{{n.attrs[\'data-lang\']}}" bindlongpress="copyCode" />'
|
||||
build.methods = {
|
||||
copyCode (e) {
|
||||
wx.showActionSheet({
|
||||
itemList: ['复制代码'],
|
||||
success: () =>
|
||||
wx.setClipboardData({
|
||||
data: e.currentTarget.dataset.content
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.showLanguageName) {
|
||||
build.style = (build.style || '') +
|
||||
`.hl-language {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
text-align: right;
|
||||
top: 3px;
|
||||
}
|
||||
.hl-pre {
|
||||
padding-top: 1.5em;
|
||||
}`
|
||||
}
|
||||
|
||||
if (config.showLineNumber) {
|
||||
build.style = (build.style || '') +
|
||||
`.hl-pre {
|
||||
font-size: 14px;
|
||||
padding-left: 3.8em;
|
||||
counter-reset: linenumber;
|
||||
}
|
||||
.line-numbers-rows {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: ${config.showLanguageName ? 1.5 : 1}em;
|
||||
font-size: 100%;
|
||||
left: 0;
|
||||
width: 3em; /* works for line-numbers below 1000 lines */
|
||||
letter-spacing: -1px;
|
||||
border-right: 1px solid #999;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.line-numbers-rows .span {
|
||||
display: block;
|
||||
counter-increment: linenumber;
|
||||
}
|
||||
.line-numbers-rows .span:before {
|
||||
content: counter(linenumber);
|
||||
color: #999;
|
||||
display: block;
|
||||
padding-right: 0.8em;
|
||||
text-align: right;
|
||||
}`
|
||||
}
|
||||
|
||||
module.exports = build
|
||||
125
wechat-mini-program/node_modules/mp-html/plugins/highlight/prism.css
generated
vendored
Normal file
125
wechat-mini-program/node_modules/mp-html/plugins/highlight/prism.css
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
/* PrismJS 1.22.0
|
||||
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
|
||||
* Based on https://github.com/chriskempson/tomorrow-theme
|
||||
* @author Rose Pritchard
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: #ccc;
|
||||
background: none;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #2d2d2d;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.block-comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.token.tag,
|
||||
.token.attr-name,
|
||||
.token.namespace,
|
||||
.token.deleted {
|
||||
color: #e2777a;
|
||||
}
|
||||
|
||||
.token.function-name {
|
||||
color: #6196cc;
|
||||
}
|
||||
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.function {
|
||||
color: #f08d49;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.class-name,
|
||||
.token.constant,
|
||||
.token.symbol {
|
||||
color: #f8c555;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.important,
|
||||
.token.atrule,
|
||||
.token.keyword,
|
||||
.token.builtin {
|
||||
color: #cc99cd;
|
||||
}
|
||||
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.attr-value,
|
||||
.token.regex,
|
||||
.token.variable {
|
||||
color: #7ec699;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url {
|
||||
color: #67cdcc;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.token.inserted {
|
||||
color: green;
|
||||
}
|
||||
|
||||
7
wechat-mini-program/node_modules/mp-html/plugins/highlight/prism.min.js
generated
vendored
Normal file
7
wechat-mini-program/node_modules/mp-html/plugins/highlight/prism.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
88
wechat-mini-program/node_modules/mp-html/plugins/highlight/uni-app/build.js
generated
vendored
Normal file
88
wechat-mini-program/node_modules/mp-html/plugins/highlight/uni-app/build.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
const config = require('../config')
|
||||
|
||||
const build = {
|
||||
import: 'prism.css',
|
||||
handler (file) {
|
||||
if (file.path.includes('prism.css')) {
|
||||
// 将标签名选择器和属性选择器转为 class 选择器(组件内仅支持 class 选择器)
|
||||
file.contents = Buffer.from(file.contents.toString().replace(/pre([[)])/g, '.hl-pre$1').replace(/code/g, '.hl-code').replace(/\[class\*="?language-"?\]/g, '').replace(/:not[^,}]+[,}]*/g, '').replace(/\.token\./g, '.hl-'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.showLanguageName || config.showLineNumber) {
|
||||
// pre 内部的 code 进行滚动,避免行号和语言名称跟随滚动
|
||||
build.style = `.hl-pre {
|
||||
position: relative;
|
||||
}
|
||||
.hl-code {
|
||||
overflow: auto;
|
||||
display: block;
|
||||
}`
|
||||
}
|
||||
|
||||
if (config.copyByLongPress) {
|
||||
build.template = '<rich-text v-if="n.attrs&&n.attrs[\'data-content\']" :nodes="[n]" :data-content="n.attrs[\'data-content\']" :data-lang="n.attrs[\'data-lang\']" @longpress="copyCode" />'
|
||||
build.methods = {
|
||||
copyCode (e) {
|
||||
uni.showActionSheet({
|
||||
itemList: ['复制代码'],
|
||||
success: () =>
|
||||
uni.setClipboardData({
|
||||
data: e.currentTarget.dataset.content
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.showLanguageName) {
|
||||
build.style = (build.style || '') +
|
||||
`.hl-language {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
text-align: right;
|
||||
top: 3px;
|
||||
}
|
||||
.hl-pre {
|
||||
padding-top: 1.5em;
|
||||
}`
|
||||
}
|
||||
|
||||
if (config.showLineNumber) {
|
||||
build.style = (build.style || '') +
|
||||
`.hl-pre {
|
||||
font-size: 14px;
|
||||
padding-left: 3.8em;
|
||||
counter-reset: linenumber;
|
||||
}
|
||||
.line-numbers-rows {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: ${config.showLanguageName ? 1.5 : 1}em;
|
||||
font-size: 100%;
|
||||
left: 0;
|
||||
width: 3em; /* works for line-numbers below 1000 lines */
|
||||
letter-spacing: -1px;
|
||||
border-right: 1px solid #999;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.line-numbers-rows .span {
|
||||
display: block;
|
||||
counter-increment: linenumber;
|
||||
}
|
||||
.line-numbers-rows .span:before {
|
||||
content: counter(linenumber);
|
||||
color: #999;
|
||||
display: block;
|
||||
padding-right: 0.8em;
|
||||
text-align: right;
|
||||
}`
|
||||
}
|
||||
|
||||
module.exports = build
|
||||
24
wechat-mini-program/node_modules/mp-html/plugins/img-cache/README.md
generated
vendored
Normal file
24
wechat-mini-program/node_modules/mp-html/plugins/img-cache/README.md
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
功能:图片本地缓存
|
||||
大小:*≈4KB*
|
||||
作者:[@PentaTea](https://github.com/PentaTea)
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| | | | | | √(仅支持 app 的 vue 页面) |
|
||||
|
||||
说明:
|
||||
引入本插件后,会给组件添加一个 *img-cache* 属性,将该属性设置为 *true* 后,将自动下载引用的图片并将 *src* 属性更换为本地地址
|
||||
同时在组件实例上挂载了 *imgCache* 对象,扩充缓存控制能力
|
||||
|
||||
*imgCache* 对象属性和方法:
|
||||
|
||||
| 属性 | 功能 |
|
||||
|:---:|:---:|
|
||||
| list | 当前缓存的 url 列表 |
|
||||
| get(url) | 传入 url 获得本地地址 |
|
||||
| delete(url) | 传入 url 删除缓存记录 |
|
||||
| add(url) | 传入 url 并下载目标为缓存 |
|
||||
| clear() | 清空所有缓存 |
|
||||
|
||||
!> 请尽量确保 *src* 中含有文件后缀名,不以后缀结尾也没关系,插件会从路径中推测合理的图片后缀,如果完全不包含后缀信息可能会无法保存到相册
|
||||
16
wechat-mini-program/node_modules/mp-html/plugins/img-cache/build.js
generated
vendored
Normal file
16
wechat-mini-program/node_modules/mp-html/plugins/img-cache/build.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
module.exports = {
|
||||
main: 'index.js',
|
||||
platform: ['uni-app'],
|
||||
handler (file, platform) {
|
||||
if (platform === 'uni-app') {
|
||||
// 添加 img-cache 属性
|
||||
if (file.path.includes('mp-html.vue')) {
|
||||
file.contents = Buffer.from(
|
||||
file.contents
|
||||
.toString()
|
||||
.replace(/props\s*:\s*{/, 'props: {\n ImgCache: Boolean,')
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
138
wechat-mini-program/node_modules/mp-html/plugins/img-cache/index.js
generated
vendored
Normal file
138
wechat-mini-program/node_modules/mp-html/plugins/img-cache/index.js
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
const data = {
|
||||
name: 'imgcache',
|
||||
prefix: 'imgcache_'
|
||||
}
|
||||
function ImgCache (vm) {
|
||||
this.vm = vm // 保存实例在其他周期使用
|
||||
this.i = 0 // 用于标记第几张图
|
||||
vm.imgCache = {
|
||||
get list () {
|
||||
return uni
|
||||
.getStorageInfoSync()
|
||||
.keys.filter((key) => key.startsWith(data.prefix))
|
||||
.map((key) => key.split(data.prefix)[1])
|
||||
},
|
||||
get (url) {
|
||||
return uni.getStorageSync(data.prefix + url)
|
||||
},
|
||||
delete (url) {
|
||||
const path = uni.getStorageSync(data.prefix + url)
|
||||
if (!path) return false
|
||||
plus.io.resolveLocalFileSystemURL(path, (entry) => {
|
||||
entry.remove()
|
||||
})
|
||||
uni.removeStorageSync(data.prefix + url)
|
||||
return true
|
||||
},
|
||||
async add (url) {
|
||||
const filename = await download(url)
|
||||
if (filename) {
|
||||
uni.setStorageSync(data.prefix + url, filename)
|
||||
return 'file://' + plus.io.convertLocalFileSystemURL(filename)
|
||||
}
|
||||
return null
|
||||
},
|
||||
clear () {
|
||||
uni
|
||||
.getStorageInfoSync()
|
||||
.keys.filter((key) => key.startsWith(data.prefix))
|
||||
.forEach((key) => {
|
||||
uni.removeStorageSync(key)
|
||||
})
|
||||
|
||||
plus.io.resolveLocalFileSystemURL(`_doc/${data.name}/`, (entry) => {
|
||||
entry.removeRecursively(
|
||||
(entry) => {
|
||||
console.log(`${data.name}缓存删除成功`, entry)
|
||||
},
|
||||
(e) => {
|
||||
console.log(`${data.name}缓存删除失败`, e)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
ImgCache.prototype.onParse = function (node, parser) {
|
||||
// 启用本插件 && 解析图片标签 && 拥有src属性 && 是网络图片
|
||||
if (
|
||||
this.vm.ImgCache &&
|
||||
node.name === 'img' &&
|
||||
node.attrs.src &&
|
||||
/^https?:\/\//.test(node.attrs.src)
|
||||
) {
|
||||
const src = node.attrs.src
|
||||
node.attrs.src = ''
|
||||
node.attrs.i = this.vm.imgList.length + this.i++
|
||||
parser.expose()
|
||||
|
||||
async function getUrl (path) {
|
||||
if (await resolveFile(path)) return path
|
||||
const filename = await download(src)
|
||||
filename && uni.setStorageSync(data.prefix + src, filename)
|
||||
return filename
|
||||
}
|
||||
|
||||
uni.getStorage({
|
||||
key: data.prefix + src,
|
||||
success: async (res) => {
|
||||
const path = await getUrl(res.data)
|
||||
const url = path
|
||||
? 'file://' + plus.io.convertLocalFileSystemURL(path)
|
||||
: src
|
||||
node.attrs.src = url
|
||||
this.vm.imgList[node.attrs.i] = path || src
|
||||
},
|
||||
fail: async () => {
|
||||
const path = await getUrl()
|
||||
const url = path
|
||||
? 'file://' + plus.io.convertLocalFileSystemURL(path)
|
||||
: src
|
||||
node.attrs.src = url
|
||||
this.vm.imgList[node.attrs.i] = path || src
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const taskQueue = new Set()
|
||||
|
||||
function download (url) {
|
||||
return new Promise((resolve) => {
|
||||
if (taskQueue.has(url)) return
|
||||
taskQueue.add(url)
|
||||
const suffix = /.+\.(jpg|jpeg|png|bmp|gif|webp)/.exec(url)
|
||||
const name = `${makeid(8)}_${Date.now()}${suffix ? '.' + suffix[1] : ''}`
|
||||
const task = plus.downloader.createDownload(
|
||||
url,
|
||||
{ filename: `_doc/${data.name}/${name}` },
|
||||
(download, status) => {
|
||||
taskQueue.delete(url)
|
||||
resolve(status === 200 ? download.filename : null)
|
||||
}
|
||||
)
|
||||
task.start()
|
||||
})
|
||||
}
|
||||
|
||||
// 判断文件存在
|
||||
function resolveFile (url) {
|
||||
return new Promise((resolve) => {
|
||||
plus.io.resolveLocalFileSystemURL(url, resolve, () => resolve(null))
|
||||
})
|
||||
}
|
||||
|
||||
// 生成uuid
|
||||
function makeid (length) {
|
||||
let result = ''
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * characters.length))
|
||||
}
|
||||
return result
|
||||
}
|
||||
// #endif
|
||||
|
||||
module.exports = ImgCache
|
||||
16
wechat-mini-program/node_modules/mp-html/plugins/latex/README.md
generated
vendored
Normal file
16
wechat-mini-program/node_modules/mp-html/plugins/latex/README.md
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# latex
|
||||
功能:渲染 *latex* 公式
|
||||
大小:**≈300KB**
|
||||
作者:[@Zeng-J](https://github.com/Zeng-J)
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | √ | √ | √ | √ |
|
||||
|
||||
说明:
|
||||
引入本插件后,会将 *$xxx$* 的文本内容按照 *latex* 规则进行解析和渲染
|
||||
|
||||
> 与 *editable* 插件共用时,编辑状态下,公式不会渲染,可以直接修改公式文本
|
||||
|
||||
> 本插件通过 [katex-mini](https://github.com/rojer95/katex-mini) 解析 *latex* 文本,[字体文件](https://github.com/KaTeX/KaTeX/tree/main/fonts) 建议自行转存
|
||||
14
wechat-mini-program/node_modules/mp-html/plugins/latex/build.js
generated
vendored
Normal file
14
wechat-mini-program/node_modules/mp-html/plugins/latex/build.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
import: 'katex.css',
|
||||
handler (file) {
|
||||
if (file.isBuffer()) {
|
||||
let content = file.contents.toString()
|
||||
if (file.basename === 'node.wxml') {
|
||||
content = content.replace(/(n.?)\.name==='a'\|\|/g, "$1.name==='a'||$1.l||")
|
||||
} else if (file.basename === 'node.vue') {
|
||||
content = content.replace(/!handler.isInline\((.*?)\)/, '(n.l||!handler.isInline($1))')
|
||||
}
|
||||
file.contents = Buffer.from(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
80
wechat-mini-program/node_modules/mp-html/plugins/latex/index.js
generated
vendored
Normal file
80
wechat-mini-program/node_modules/mp-html/plugins/latex/index.js
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @fileoverview latex 插件
|
||||
* katex.min.js来源 https://github.com/rojer95/katex-mini
|
||||
*/
|
||||
const parse = require('./katex.min')
|
||||
|
||||
function Latex () {
|
||||
|
||||
}
|
||||
|
||||
Latex.prototype.onParse = function (node, vm) {
|
||||
// $...$包裹的内容为latex公式
|
||||
if (!vm.options.editable && node.type === 'text' && node.text.includes('$')) {
|
||||
const part = node.text.split(/(\${1,2})/)
|
||||
const children = []
|
||||
let status = 0
|
||||
for (let i = 0; i < part.length; i++) {
|
||||
if (i % 2 === 0) {
|
||||
// 文本内容
|
||||
if (part[i]) {
|
||||
if (status === 0) {
|
||||
children.push({
|
||||
type: 'text',
|
||||
text: part[i]
|
||||
})
|
||||
} else {
|
||||
if (status === 1) {
|
||||
// 行内公式
|
||||
const nodes = parse.default(part[i])
|
||||
children.push({
|
||||
name: 'span',
|
||||
attrs: {},
|
||||
l: 'T',
|
||||
f: 'display:inline-block',
|
||||
children: nodes
|
||||
})
|
||||
} else {
|
||||
// 块公式
|
||||
const nodes = parse.default(part[i], {
|
||||
displayMode: true
|
||||
})
|
||||
children.push({
|
||||
name: 'div',
|
||||
attrs: {
|
||||
style: 'text-align:center'
|
||||
},
|
||||
children: nodes
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 分隔符
|
||||
if (part[i] === '$' && part[i + 2] === '$') {
|
||||
// 行内公式
|
||||
status = 1
|
||||
part[i + 2] = ''
|
||||
} else if (part[i] === '$$' && part[i + 2] === '$$') {
|
||||
// 块公式
|
||||
status = 2
|
||||
part[i + 2] = ''
|
||||
} else {
|
||||
if (part[i] && part[i] !== '$$') {
|
||||
// 普通$符号
|
||||
part[i + 1] = part[i] + part[i + 1]
|
||||
}
|
||||
// 重置状态
|
||||
status = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
delete node.type
|
||||
delete node.text
|
||||
node.name = 'span'
|
||||
node.attrs = {}
|
||||
node.children = children
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Latex
|
||||
1070
wechat-mini-program/node_modules/mp-html/plugins/latex/katex.css
generated
vendored
Normal file
1070
wechat-mini-program/node_modules/mp-html/plugins/latex/katex.css
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
wechat-mini-program/node_modules/mp-html/plugins/latex/katex.min.js
generated
vendored
Normal file
1
wechat-mini-program/node_modules/mp-html/plugins/latex/katex.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
17
wechat-mini-program/node_modules/mp-html/plugins/markdown/README.md
generated
vendored
Normal file
17
wechat-mini-program/node_modules/mp-html/plugins/markdown/README.md
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# markdown
|
||||
功能:渲染 *markdown*
|
||||
大小:*≈37KB*
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | √ | √ | √ | √ |
|
||||
|
||||
说明:
|
||||
引入本插件后,会给组件添加一个 *markdown* 属性,将该属性设置为 *true* 后,即可通过 *content* 属性或 *setContent* 方法设置 *markdown* 内容即可
|
||||
|
||||
若开启 *use-anchor* 属性,所有标题 `*# xxx*` 都会被设置为锚点,通过链接 `[xxx](#xxx)` 可以直接跳转
|
||||
|
||||
> 本插件通过 [marked](https://github.com/markedjs/marked) 解析 *markdown* 文本,部分 *css* 摘选自 [github-markdown-css](https://github.com/sindresorhus/github-markdown-css)
|
||||
|
||||
> 本插件可以和 *highlight* 插件共用,实现 *markdown* 中代码块的高亮效果
|
||||
34
wechat-mini-program/node_modules/mp-html/plugins/markdown/index.js
generated
vendored
Normal file
34
wechat-mini-program/node_modules/mp-html/plugins/markdown/index.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @fileoverview markdown 插件
|
||||
* Include marked (https://github.com/markedjs/marked)
|
||||
* Include github-markdown-css (https://github.com/sindresorhus/github-markdown-css)
|
||||
*/
|
||||
const marked = require('./marked.min')
|
||||
let index = 0
|
||||
|
||||
function Markdown (vm) {
|
||||
this.vm = vm
|
||||
vm._ids = {}
|
||||
}
|
||||
|
||||
Markdown.prototype.onUpdate = function (content) {
|
||||
if (this.vm.properties.markdown) {
|
||||
return marked(content)
|
||||
}
|
||||
}
|
||||
|
||||
Markdown.prototype.onParse = function (node, vm) {
|
||||
if (vm.options.markdown) {
|
||||
// 中文 id 需要转换,否则无法跳转
|
||||
if (vm.options.useAnchor && node.attrs && /[\u4e00-\u9fa5]/.test(node.attrs.id)) {
|
||||
const id = 't' + index++
|
||||
this.vm._ids[node.attrs.id] = id
|
||||
node.attrs.id = id
|
||||
}
|
||||
if (node.name === 'p' || node.name === 'table' || node.name === 'tr' || node.name === 'th' || node.name === 'td' || node.name === 'blockquote' || node.name === 'pre' || node.name === 'code') {
|
||||
node.attrs.class = `md-${node.name} ${node.attrs.class || ''}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Markdown
|
||||
6
wechat-mini-program/node_modules/mp-html/plugins/markdown/marked.min.js
generated
vendored
Normal file
6
wechat-mini-program/node_modules/mp-html/plugins/markdown/marked.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
70
wechat-mini-program/node_modules/mp-html/plugins/markdown/miniprogram/build.js
generated
vendored
Normal file
70
wechat-mini-program/node_modules/mp-html/plugins/markdown/miniprogram/build.js
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
style:
|
||||
`.md-p {
|
||||
margin-block-start: 1em;
|
||||
margin-block-end: 1em;
|
||||
}
|
||||
|
||||
.md-table,
|
||||
.md-blockquote {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.md-table {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.md-tr {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #c6cbd1;
|
||||
}
|
||||
|
||||
.md-table .md-tr:nth-child(2n) {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
|
||||
.md-th,
|
||||
.md-td {
|
||||
padding: 6px 13px !important;
|
||||
border: 1px solid #dfe2e5;
|
||||
}
|
||||
|
||||
.md-th {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.md-blockquote {
|
||||
padding: 0 1em;
|
||||
color: #6a737d;
|
||||
border-left: 0.25em solid #dfe2e5;
|
||||
}
|
||||
|
||||
.md-code {
|
||||
padding: 0.2em 0.4em;
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27, 31, 35, 0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.md-pre .md-code {
|
||||
padding: 0;
|
||||
font-size: 100%;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}`,
|
||||
handler (file) {
|
||||
// 添加 markdown 属性
|
||||
if (file.path.includes('miniprogram' + path.sep + 'index.js')) {
|
||||
file.contents = Buffer.from(file.contents.toString().replace(/properties\s*:\s*{/, 'properties: {\n markdown: Boolean,')
|
||||
// 处理中文 id
|
||||
.replace(/navigateTo\s*\(id,\s*offset\)\s*{/, 'navigateTo (id, offset) {\n id = this._ids[decodeURI(id)] || id'))
|
||||
}
|
||||
}
|
||||
}
|
||||
68
wechat-mini-program/node_modules/mp-html/plugins/markdown/uni-app/build.js
generated
vendored
Normal file
68
wechat-mini-program/node_modules/mp-html/plugins/markdown/uni-app/build.js
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
module.exports = {
|
||||
style:
|
||||
`.md-p {
|
||||
margin-block-start: 1em;
|
||||
margin-block-end: 1em;
|
||||
}
|
||||
|
||||
.md-table,
|
||||
.md-blockquote {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.md-table {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.md-tr {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #c6cbd1;
|
||||
}
|
||||
|
||||
.md-table .md-tr:nth-child(2n) {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
|
||||
.md-th,
|
||||
.md-td {
|
||||
padding: 6px 13px !important;
|
||||
border: 1px solid #dfe2e5;
|
||||
}
|
||||
|
||||
.md-th {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.md-blockquote {
|
||||
padding: 0 1em;
|
||||
color: #6a737d;
|
||||
border-left: 0.25em solid #dfe2e5;
|
||||
}
|
||||
|
||||
.md-code {
|
||||
padding: 0.2em 0.4em;
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27, 31, 35, 0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.md-pre .md-code {
|
||||
padding: 0;
|
||||
font-size: 100%;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}`,
|
||||
handler (file) {
|
||||
// 添加 markdown 属性
|
||||
if (file.path.includes('mp-html.vue')) {
|
||||
file.contents = Buffer.from(file.contents.toString().replace(/props\s*:\s*{/, 'props: {\n markdown: Boolean,')
|
||||
// 处理中文 id
|
||||
.replace(/navigateTo\s*\(id,\s*offset\)\s*{/, 'navigateTo (id, offset) {\n id = this._ids[decodeURI(id)] || id'))
|
||||
}
|
||||
}
|
||||
}
|
||||
46
wechat-mini-program/node_modules/mp-html/plugins/search/README.md
generated
vendored
Normal file
46
wechat-mini-program/node_modules/mp-html/plugins/search/README.md
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# search
|
||||
功能:关键词搜索
|
||||
大小:*≈1.5KB*
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | √ | √ | √ | √(nvue 不支持) |
|
||||
|
||||
说明:
|
||||
引入后会在组件实例上挂载一个 *search* 方法,用于关键词搜索
|
||||
|
||||
输入值
|
||||
|
||||
| 参数名 | 类型 | 默认值 | 说明 |
|
||||
|:---:|:---:|:---:|---|
|
||||
| key | String 或 RegExp | - | 要搜索的关键词,支持字符串和正则 |
|
||||
| anchor | Boolean | false | 是否将搜索结果设置为锚点 |
|
||||
| style | String | background-color:yellow | 标记搜索结果的样式 |
|
||||
|
||||
返回值:*Promise*
|
||||
|
||||
| 属性 | 类型 | 说明 |
|
||||
|:---:|:---:|---|
|
||||
| num | Number | 搜索结果数量 |
|
||||
| highlight | Function(i, style='background-color:#FF9632') | 高亮第 i(1 ~ num)个结果,将其样式设置为 style |
|
||||
| jump | Function(i, offset) | 跳转到第 i(1 ~ num)个结果,偏移量为 offset,anchor 为 true 才可用 |
|
||||
|
||||
示例:
|
||||
```javascript
|
||||
function search (key) {
|
||||
// ctx 为组件实例
|
||||
ctx.search(key, true).then(res => {
|
||||
res.highlight(1)
|
||||
res.jump(1, -50) // 高亮第 1 个结果并跳转到该位置,偏移量 -50
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
附加说明:
|
||||
1. 不传入 *key*(或为空)时即可取消搜索,取消所有的高亮,还原到原来的效果
|
||||
2. 进行新的搜索时旧的搜索结果将被还原,旧的结果中的 *highlight* 等方法不再可用
|
||||
3. 调用 *highlight* 方法高亮一个结果时,之前被高亮的结果会被还原,即始终只有一个结果被高亮
|
||||
4. *key* 传入字符串时大小写敏感,如果要忽略大小写可以用正则的 *i*(字符串搜索效率高于正则)
|
||||
5. 设置 *anchor* 为 *true* 会一定程度上降低效率,非必要不要开启
|
||||
6. 暂不支持跨标签搜索,即只有一个文本节点内包含整个关键词才能被搜索到
|
||||
137
wechat-mini-program/node_modules/mp-html/plugins/search/miniprogram/index.js
generated
vendored
Normal file
137
wechat-mini-program/node_modules/mp-html/plugins/search/miniprogram/index.js
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* @fileoverview search 插件
|
||||
*/
|
||||
|
||||
function Search (vm) {
|
||||
/**
|
||||
* @description 关键词搜索
|
||||
* @param {regexp|string} key 要搜索的关键词
|
||||
* @param {boolean} anchor 是否将搜索结果设置为锚点
|
||||
* @param {string} style 搜索结果的样式
|
||||
*/
|
||||
vm.search = function (key, anchor, style = 'background-color:yellow') {
|
||||
const obj = {}
|
||||
const stack = []
|
||||
const res = [];
|
||||
|
||||
// 遍历搜索
|
||||
(function traversal (nodes, path) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i]
|
||||
if (node.type === 'text' && key) {
|
||||
const arr = node.text.split(key)
|
||||
const children = []
|
||||
if (arr.length > 1) {
|
||||
// 找到关键词
|
||||
for (let j = 0; j < arr.length; j++) {
|
||||
if (arr[j]) {
|
||||
children.push({
|
||||
type: 'text',
|
||||
text: arr[j]
|
||||
})
|
||||
}
|
||||
if (j !== arr.length - 1) {
|
||||
// 关键词转为一个 span
|
||||
res.push(`${path}[${i}].children[${children.length}].attrs.style`)
|
||||
children.push({
|
||||
name: 'span',
|
||||
attrs: {
|
||||
id: anchor ? 'search' + res.length : undefined, // 用于锚点的 id
|
||||
style
|
||||
},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: key instanceof RegExp ? key.exec(node.text)[0] : key
|
||||
}]
|
||||
})
|
||||
}
|
||||
}
|
||||
if (key instanceof RegExp) {
|
||||
key.exec(node.text)
|
||||
}
|
||||
if (anchor) {
|
||||
for (let l = stack.length; l--;) {
|
||||
// 给父组件做标记,将该标签暴露出来
|
||||
if (stack[l].c) {
|
||||
break
|
||||
} else {
|
||||
obj[stack[l].path] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
obj[`${path}[${i}]`] = {
|
||||
name: 'span',
|
||||
c: anchor ? 1 : undefined,
|
||||
s: 1,
|
||||
children
|
||||
}
|
||||
}
|
||||
} else if (node.s) {
|
||||
let text = ''
|
||||
// 复原上一次的结果
|
||||
for (let k = 0; k < node.children.length; k++) {
|
||||
const child = node.children[k]
|
||||
if (child.text) {
|
||||
text += child.text
|
||||
} else {
|
||||
text += child.children[0].text
|
||||
}
|
||||
}
|
||||
nodes[i] = {
|
||||
type: 'text',
|
||||
text
|
||||
}
|
||||
if (key && (key instanceof RegExp ? key.test(text) : text.includes(key))) {
|
||||
i--
|
||||
} else {
|
||||
obj[`${path}[${i}]`] = nodes[i]
|
||||
}
|
||||
} else if (node.children) {
|
||||
stack.push({
|
||||
path: `${path}[${i}].c`,
|
||||
c: node.c || node.name === 'table'
|
||||
})
|
||||
traversal(node.children, `${path}[${i}].children`)
|
||||
stack.pop()
|
||||
}
|
||||
}
|
||||
})(vm.data.nodes, 'nodes')
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
vm.setData(obj, () => {
|
||||
resolve({
|
||||
num: res.length, // 结果数量
|
||||
/**
|
||||
* @description 高亮某一个结果
|
||||
* @param {number} i 第几个
|
||||
* @param {string} hlstyle 高亮的样式
|
||||
*/
|
||||
highlight (i, hlstyle = 'background-color:#FF9632') {
|
||||
if (i < 1 || i > res.length) return
|
||||
const obj = {}
|
||||
if (this.last) {
|
||||
obj[res[this.last - 1]] = style
|
||||
}
|
||||
this.last = i
|
||||
obj[res[i - 1]] = hlstyle
|
||||
vm.setData(obj)
|
||||
},
|
||||
/**
|
||||
* @description 跳转到搜索结果
|
||||
* @param {number} i 第几个
|
||||
* @param {number} offset 偏移量
|
||||
*/
|
||||
jump: anchor
|
||||
? (i, offset) => {
|
||||
if (i > 0 && i <= res.length) {
|
||||
vm.navigateTo('search' + i, offset)
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Search
|
||||
132
wechat-mini-program/node_modules/mp-html/plugins/search/uni-app/index.js
generated
vendored
Normal file
132
wechat-mini-program/node_modules/mp-html/plugins/search/uni-app/index.js
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @fileoverview search 插件
|
||||
*/
|
||||
function Search (vm) {
|
||||
/**
|
||||
* @description 关键词搜索
|
||||
* @param {regexp|string} key 要搜索的关键词
|
||||
* @param {boolean} anchor 是否将搜索结果设置为锚点
|
||||
* @param {string} style 搜索结果的样式
|
||||
*/
|
||||
vm.search = function (key, anchor, style = 'background-color:yellow') {
|
||||
const res = []
|
||||
const stack = [];
|
||||
|
||||
// 遍历搜索
|
||||
(function traversal (nodes) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let node = nodes[i]
|
||||
if (node.type === 'text' && key) {
|
||||
const text = node.text
|
||||
const arr = text.split(key)
|
||||
if (arr.length > 1) {
|
||||
node = {
|
||||
name: 'span',
|
||||
attrs: {},
|
||||
type: 'node',
|
||||
c: 1,
|
||||
s: 1,
|
||||
children: []
|
||||
}
|
||||
vm.$set(nodes, i, node)
|
||||
for (let j = 0; j < arr.length; j++) {
|
||||
if (arr[j]) {
|
||||
node.children.push({
|
||||
type: 'text',
|
||||
text: arr[j]
|
||||
})
|
||||
}
|
||||
if (j !== arr.length - 1) {
|
||||
// 关键词转为一个 span
|
||||
node.children.push({
|
||||
name: 'span',
|
||||
attrs: {
|
||||
id: anchor ? 'search' + (res.length + 1) : undefined, // 用于锚点的 id
|
||||
style: style
|
||||
},
|
||||
// #ifdef VUE3
|
||||
c: 1,
|
||||
// #endif
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: key instanceof RegExp ? key.exec(text)[0] : key
|
||||
}]
|
||||
})
|
||||
res.push(node.children[node.children.length - 1].attrs)
|
||||
}
|
||||
}
|
||||
if (key instanceof RegExp) {
|
||||
key.exec(text)
|
||||
}
|
||||
if (anchor) {
|
||||
for (let l = stack.length; l--;) {
|
||||
if (stack[l].c) {
|
||||
break
|
||||
} else {
|
||||
vm.$set(stack[l], 'c', 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (node.s) {
|
||||
let text = ''
|
||||
// 复原上一次的结果
|
||||
for (let k = 0; k < node.children.length; k++) {
|
||||
const child = node.children[k]
|
||||
if (child.text) {
|
||||
text += child.text
|
||||
} else {
|
||||
text += child.children[0].text
|
||||
}
|
||||
}
|
||||
vm.$set(nodes, i, {
|
||||
type: 'text',
|
||||
text
|
||||
})
|
||||
if (key && (key instanceof RegExp ? key.test(text) : text.includes(key))) {
|
||||
i--
|
||||
}
|
||||
} else if (node.children) {
|
||||
stack.push(node)
|
||||
traversal(node.children)
|
||||
stack.pop()
|
||||
}
|
||||
}
|
||||
})(vm.nodes)
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
num: res.length, // 结果数量
|
||||
/**
|
||||
* @description 高亮某一个结果
|
||||
* @param {number} i 第几个
|
||||
* @param {string} hlstyle 高亮的样式
|
||||
*/
|
||||
highlight (i, hlstyle = 'background-color:#FF9632') {
|
||||
if (i < 1 || i > res.length) return
|
||||
if (this.last) {
|
||||
res[this.last - 1].style = style
|
||||
}
|
||||
this.last = i
|
||||
res[i - 1].style = hlstyle
|
||||
},
|
||||
/**
|
||||
* @description 跳转到搜索结果
|
||||
* @param {number} i 第几个
|
||||
* @param {number} offset 偏移量
|
||||
*/
|
||||
jump: anchor
|
||||
? (i, offset) => {
|
||||
if (i > 0 && i <= res.length) {
|
||||
vm.navigateTo('search' + i, offset)
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
})
|
||||
}, 200)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Search
|
||||
30
wechat-mini-program/node_modules/mp-html/plugins/style/README.md
generated
vendored
Normal file
30
wechat-mini-program/node_modules/mp-html/plugins/style/README.md
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# style
|
||||
功能:解析和匹配 *style* 标签中的样式
|
||||
|
||||
> 这里的 *style* 标签指的是传入 *content* 属性中的 *html* 里包含的 *style* 标签,且 *style* 标签要放在其他标签前面才能生效
|
||||
|
||||
大小:*≈3.5KB*
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | √ | √ | √ | √ (nvue 直接支持) |
|
||||
|
||||
说明:
|
||||
支持以下选择器:
|
||||
|
||||
| 名称 | 示例 |
|
||||
|:---:|---|
|
||||
| 标签名选择器 | p {} |
|
||||
| class 选择器 | .class {} |
|
||||
| id 选择器 | #id {} |
|
||||
| 多选择器交集 | p.class {} |
|
||||
| 多选择器并集 | p, .class {} |
|
||||
| 后代选择器 | .class1 .class2 {} |
|
||||
| 子选择器 | .class1 > .class2 {} |
|
||||
| 伪类 | .class::before {} |
|
||||
|
||||
伪类仅支持 *before* 和 *after*,支持 *attr* 方法
|
||||
不支持的选择器(属性选择器等)将被忽略
|
||||
|
||||
> 由于小程序中无法动态写入 *css*,本插件的实现原理是通过解析,将匹配的样式添加到各标签的行内 *style* 中去,请慎用宽泛的选择器,以免大大增加解析结果大小,减慢渲染速度
|
||||
129
wechat-mini-program/node_modules/mp-html/plugins/style/index.js
generated
vendored
Normal file
129
wechat-mini-program/node_modules/mp-html/plugins/style/index.js
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* @fileoverview style 插件
|
||||
*/
|
||||
// #ifndef APP-PLUS-NVUE
|
||||
const Parser = require('./parser')
|
||||
// #endif
|
||||
|
||||
function Style () {
|
||||
this.styles = []
|
||||
}
|
||||
|
||||
// #ifndef APP-PLUS-NVUE
|
||||
Style.prototype.onParse = function (node, vm) {
|
||||
// 获取样式
|
||||
if (node.name === 'style' && node.children.length && node.children[0].type === 'text') {
|
||||
this.styles = this.styles.concat(new Parser().parse(node.children[0].text))
|
||||
} else if (node.name) {
|
||||
// 匹配样式(对非文本标签)
|
||||
// 存储不同优先级的样式 name < class < id < 后代
|
||||
let matched = ['', '', '', '']
|
||||
for (let i = 0, len = this.styles.length; i < len; i++) {
|
||||
const item = this.styles[i]
|
||||
let res = match(node, item.key || item.list[item.list.length - 1])
|
||||
let j
|
||||
if (res) {
|
||||
// 后代选择器
|
||||
if (!item.key) {
|
||||
j = item.list.length - 2
|
||||
for (let k = vm.stack.length; j >= 0 && k--;) {
|
||||
// 子选择器
|
||||
if (item.list[j] === '>') {
|
||||
// 错误情况
|
||||
if (j < 1 || j > item.list.length - 2) break
|
||||
if (match(vm.stack[k], item.list[j - 1])) {
|
||||
j -= 2
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
} else if (match(vm.stack[k], item.list[j])) {
|
||||
j--
|
||||
}
|
||||
}
|
||||
res = 4
|
||||
}
|
||||
if (item.key || j < 0) {
|
||||
// 添加伪类
|
||||
if (item.pseudo && node.children) {
|
||||
let text
|
||||
item.style = item.style.replace(/content:([^;]+)/, (_, $1) => {
|
||||
text = $1.replace(/['"]/g, '')
|
||||
// 处理 attr 函数
|
||||
.replace(/attr\((.+?)\)/, (_, $1) => node.attrs[$1.trim()] || '')
|
||||
// 编码 \xxx
|
||||
.replace(/\\(\w{4})/, (_, $1) => String.fromCharCode(parseInt($1, 16)))
|
||||
return ''
|
||||
})
|
||||
const pseudo = {
|
||||
name: 'span',
|
||||
attrs: {
|
||||
style: item.style
|
||||
},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text
|
||||
}]
|
||||
}
|
||||
if (item.pseudo === 'before') {
|
||||
node.children.unshift(pseudo)
|
||||
} else {
|
||||
node.children.push(pseudo)
|
||||
}
|
||||
} else {
|
||||
matched[res - 1] += item.style + (item.style[item.style.length - 1] === ';' ? '' : ';')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
matched = matched.join('')
|
||||
if (matched.length > 2) {
|
||||
node.attrs.style = matched + (node.attrs.style || '')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 匹配样式
|
||||
* @param {object} node 要匹配的标签
|
||||
* @param {string|string[]} keys 选择器
|
||||
* @returns {number} 0:不匹配;1:name 匹配;2:class 匹配;3:id 匹配
|
||||
*/
|
||||
function match (node, keys) {
|
||||
function matchItem (key) {
|
||||
if (key[0] === '#') {
|
||||
// 匹配 id
|
||||
if (node.attrs.id && node.attrs.id.trim() === key.substr(1)) return 3
|
||||
} else if (key[0] === '.') {
|
||||
// 匹配 class
|
||||
key = key.substr(1)
|
||||
const selectors = (node.attrs.class || '').split(' ')
|
||||
for (let i = 0; i < selectors.length; i++) {
|
||||
if (selectors[i].trim() === key) return 2
|
||||
}
|
||||
} else if (node.name === key) {
|
||||
// 匹配 name
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// 多选择器交集
|
||||
if (keys instanceof Array) {
|
||||
let res = 0
|
||||
for (let j = 0; j < keys.length; j++) {
|
||||
const tmp = matchItem(keys[j])
|
||||
// 任意一个不匹配就失败
|
||||
if (!tmp) return 0
|
||||
// 优先级最大的一个作为最终优先级
|
||||
if (tmp > res) {
|
||||
res = tmp
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
return matchItem(keys)
|
||||
}
|
||||
// #endif
|
||||
|
||||
module.exports = Style
|
||||
175
wechat-mini-program/node_modules/mp-html/plugins/style/parser.js
generated
vendored
Normal file
175
wechat-mini-program/node_modules/mp-html/plugins/style/parser.js
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
const blank = {
|
||||
' ': true,
|
||||
'\n': true,
|
||||
'\t': true,
|
||||
'\r': true,
|
||||
'\f': true
|
||||
}
|
||||
|
||||
function Parser () {
|
||||
this.styles = []
|
||||
this.selectors = []
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 解析 css 字符串
|
||||
* @param {string} content css 内容
|
||||
*/
|
||||
Parser.prototype.parse = function (content) {
|
||||
new Lexer(this).parse(content)
|
||||
return this.styles
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 解析到一个选择器
|
||||
* @param {string} name 名称
|
||||
*/
|
||||
Parser.prototype.onSelector = function (name) {
|
||||
// 不支持的选择器
|
||||
if (name.includes('[') || name.includes('*') || name.includes('@')) return
|
||||
const selector = {}
|
||||
// 伪类
|
||||
if (name.includes(':')) {
|
||||
const info = name.split(':')
|
||||
const pseudo = info.pop()
|
||||
if (pseudo === 'before' || pseudo === 'after') {
|
||||
selector.pseudo = pseudo
|
||||
name = info[0]
|
||||
} else return
|
||||
}
|
||||
|
||||
// 分割交集选择器
|
||||
function splitItem (str) {
|
||||
const arr = []
|
||||
let i, start
|
||||
for (i = 1, start = 0; i < str.length; i++) {
|
||||
if (str[i] === '.' || str[i] === '#') {
|
||||
arr.push(str.substring(start, i))
|
||||
start = i
|
||||
}
|
||||
}
|
||||
if (!arr.length) {
|
||||
return str
|
||||
} else {
|
||||
arr.push(str.substring(start, i))
|
||||
return arr
|
||||
}
|
||||
}
|
||||
|
||||
// 后代选择器
|
||||
if (name.includes(' ')) {
|
||||
selector.list = []
|
||||
const list = name.split(' ')
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].length) {
|
||||
// 拆分子选择器
|
||||
const arr = list[i].split('>')
|
||||
for (let j = 0; j < arr.length; j++) {
|
||||
selector.list.push(splitItem(arr[j]))
|
||||
if (j < arr.length - 1) {
|
||||
selector.list.push('>')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selector.key = splitItem(name)
|
||||
}
|
||||
|
||||
this.selectors.push(selector)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 解析到选择器内容
|
||||
* @param {string} content 内容
|
||||
*/
|
||||
Parser.prototype.onContent = function (content) {
|
||||
// 并集选择器
|
||||
for (let i = 0; i < this.selectors.length; i++) {
|
||||
this.selectors[i].style = content
|
||||
}
|
||||
this.styles = this.styles.concat(this.selectors)
|
||||
this.selectors = []
|
||||
}
|
||||
|
||||
/**
|
||||
* @description css 词法分析器
|
||||
* @param {object} handler 高层处理器
|
||||
*/
|
||||
function Lexer (handler) {
|
||||
this.selector = ''
|
||||
this.style = ''
|
||||
this.handler = handler
|
||||
}
|
||||
|
||||
Lexer.prototype.parse = function (content) {
|
||||
this.i = 0
|
||||
this.content = content
|
||||
this.state = this.blank
|
||||
for (let len = content.length; this.i < len; this.i++) {
|
||||
this.state(content[this.i])
|
||||
}
|
||||
}
|
||||
|
||||
Lexer.prototype.comment = function () {
|
||||
this.i = this.content.indexOf('*/', this.i) + 1
|
||||
if (!this.i) {
|
||||
this.i = this.content.length
|
||||
}
|
||||
}
|
||||
|
||||
Lexer.prototype.blank = function (c) {
|
||||
if (!blank[c]) {
|
||||
if (c === '/' && this.content[this.i + 1] === '*') {
|
||||
this.comment()
|
||||
return
|
||||
}
|
||||
this.selector += c
|
||||
this.state = this.name
|
||||
}
|
||||
}
|
||||
|
||||
Lexer.prototype.name = function (c) {
|
||||
if (c === '/' && this.content[this.i + 1] === '*') {
|
||||
this.comment()
|
||||
return
|
||||
}
|
||||
if (c === '{' || c === ',' || c === ';') {
|
||||
this.handler.onSelector(this.selector.trimEnd())
|
||||
this.selector = ''
|
||||
if (c !== '{') {
|
||||
while (blank[this.content[++this.i]]);
|
||||
}
|
||||
if (this.content[this.i] === '{') {
|
||||
this.floor = 1
|
||||
this.state = this.val
|
||||
} else {
|
||||
this.selector += this.content[this.i]
|
||||
}
|
||||
} else if (blank[c]) {
|
||||
this.selector += ' '
|
||||
} else {
|
||||
this.selector += c
|
||||
}
|
||||
}
|
||||
|
||||
Lexer.prototype.val = function (c) {
|
||||
if (c === '/' && this.content[this.i + 1] === '*') {
|
||||
this.comment()
|
||||
return
|
||||
}
|
||||
if (c === '{') {
|
||||
this.floor++
|
||||
} else if (c === '}') {
|
||||
this.floor--
|
||||
if (!this.floor) {
|
||||
this.handler.onContent(this.style)
|
||||
this.style = ''
|
||||
this.state = this.blank
|
||||
return
|
||||
}
|
||||
}
|
||||
this.style += c
|
||||
}
|
||||
|
||||
module.exports = Parser
|
||||
2
wechat-mini-program/node_modules/mp-html/plugins/template/README.md
generated
vendored
Normal file
2
wechat-mini-program/node_modules/mp-html/plugins/template/README.md
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# 模板
|
||||
这是一个插件模板,需要开发插件时,可以从这个模板开始
|
||||
65
wechat-mini-program/node_modules/mp-html/plugins/template/build.js
generated
vendored
Normal file
65
wechat-mini-program/node_modules/mp-html/plugins/template/build.js
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @description 插件构建文件模板
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* @description 入口文件
|
||||
* @type {String}
|
||||
* @default 'index.js'
|
||||
*/
|
||||
main: 'index.js',
|
||||
/**
|
||||
* @description 支持的平台
|
||||
* @type {String[]}
|
||||
* @default ['mp-weixin','mp-qq','mp-baidu','mp-alipay','mp-toutiao','uni-app']
|
||||
*/
|
||||
platform: ['mp-weixin', 'mp-qq', 'mp-baidu', 'mp-alipay', 'mp-toutiao', 'uni-app'],
|
||||
/**
|
||||
* @description 要被添加到模板文件中的标签(将被添加到 src/node/node.wxml)
|
||||
* 必须要有 wx:if 表明什么情况下使用该标签
|
||||
* n 表示标签结构体,<node> 标签用于递归显示子节点(可参考源文件中的写法)
|
||||
* @type {String}
|
||||
*/
|
||||
template: '',
|
||||
/**
|
||||
* @description 用于处理模板中事件的方法(将被添加到 src/node/node.js)
|
||||
* 需要触发顶层组件的事件请使用 this.root.triggerEvent
|
||||
* @type {Object}
|
||||
*/
|
||||
methods: {
|
||||
|
||||
},
|
||||
/**
|
||||
* @description 用于模板文件的 css 样式(将被添加到 src/node/node.wxss)
|
||||
* @type {String}
|
||||
*/
|
||||
style: '',
|
||||
/**
|
||||
* @description 要被引入到模板文件的 css 文件路径(将被添加到 src/node/node.wxss)
|
||||
* @type {String|String[]}
|
||||
*/
|
||||
import: [],
|
||||
/**
|
||||
* @description 在模板中需要使用的组件或插件列表(将被添加到 src/node/node.json)
|
||||
* @type {Object}
|
||||
*/
|
||||
usingComponents: {
|
||||
|
||||
},
|
||||
/**
|
||||
* @description 自定义文件处理器
|
||||
* 如果上述处理还无法满足要求,可以在此方法中进行处理
|
||||
* 所有 src 目录下的文件和本插件目录下的文件都会经过此方法的处理
|
||||
* @param {Vinyl} file 关于该文件对象的格式可参考 https://github.com/gulpjs/vinyl#instance-methods
|
||||
* @param {String} platform 平台
|
||||
*/
|
||||
handler (file, platform) {
|
||||
let content = file.contents.toString()
|
||||
// 进行处理
|
||||
if (platform === 'xxx') {
|
||||
content = content.replace('aaa', 'bbb')
|
||||
}
|
||||
file.contents = Buffer.from(content)
|
||||
}
|
||||
}
|
||||
67
wechat-mini-program/node_modules/mp-html/plugins/template/index.js
generated
vendored
Normal file
67
wechat-mini-program/node_modules/mp-html/plugins/template/index.js
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @fileoverview 插件入口文件模板
|
||||
*/
|
||||
|
||||
const data = {} // 全局数据
|
||||
|
||||
/**
|
||||
* @description 组件被创建时将实例化插件
|
||||
* @param {Component} vm 组件实例
|
||||
*/
|
||||
function Plugin (vm) {
|
||||
this.vm = vm // 保存实例在其他周期使用
|
||||
this.compData = {} // 仅在单个组件中使用的数据
|
||||
data.xxx = 'xxx' // 记录全局数据
|
||||
}
|
||||
|
||||
/**
|
||||
* @description html 数据更新时触发
|
||||
* @param {string} content 要更新的 html 字符串
|
||||
* @param {object} config 解析配置
|
||||
* @returns {string|void} 如果要对 html 字符串进行一些预处理,则返回处理后的字符串
|
||||
*/
|
||||
Plugin.prototype.onUpdate = function (content, config) {
|
||||
config.ignoreTags.xxx = true // 移除 xxx 标签
|
||||
// 对 html 内容进行预处理并返回修改,没有修改则不需要返回
|
||||
return content
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 解析到一个标签时触发
|
||||
* @param {object} node 标签
|
||||
* @param {object} parser 解析器实例
|
||||
* @returns {boolean|void} 如果返回 false 将移除该标签
|
||||
*/
|
||||
Plugin.prototype.onParse = function (node, parser) {
|
||||
// 处理文本标签
|
||||
if (node.type === 'text') {
|
||||
// node.text 文本内容
|
||||
} else {
|
||||
// 处理元素标签
|
||||
// node.name 标签名
|
||||
// node.attrs 属性列表
|
||||
// node.children 子节点(非自闭合标签有)
|
||||
|
||||
if (node.name === 'xxx') {
|
||||
parser.expose() // 如果该标签不能被 rich-text 包含,需要调用此方法暴露出来
|
||||
// parser.options 组件传入的一些解析属性
|
||||
// parser.stack 可以从栈中获取祖先节点
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description dom 树加载完毕时触发(load 事件)
|
||||
*/
|
||||
Plugin.prototype.onLoad = function () {
|
||||
// 可以获取媒体 context 对象等
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 组件被移除时触发
|
||||
*/
|
||||
Plugin.prototype.onDetached = function () {
|
||||
// 可以释放一些必要的资源(计时器等)
|
||||
}
|
||||
|
||||
module.exports = Plugin
|
||||
18
wechat-mini-program/node_modules/mp-html/plugins/txv-video/README.md
generated
vendored
Normal file
18
wechat-mini-program/node_modules/mp-html/plugins/txv-video/README.md
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# txv-video
|
||||
功能:使用腾讯视频
|
||||
大小:*≈1KB*
|
||||
支持平台:
|
||||
|
||||
| 微信小程序 | QQ 小程序 | 百度小程序 | 支付宝小程序 | 头条小程序 | uni-app |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| √ | √ | | | | √ (h5 和 app 直接支持) |
|
||||
|
||||
说明:
|
||||
引入本插件后,*html* 中符合下方格式的 *iframe* 标签(*src* 中含有 *vid*)将被转为通过腾讯视频播放:
|
||||
```html
|
||||
<iframe src="https://v.qq.com/txp/iframe/player.html?vid=xxxxxx" allowFullScreen="true"></iframe>
|
||||
```
|
||||
|
||||
同时,其可以被 *pause-video* 属性控制
|
||||
|
||||
!> 本插件仅用于将官方 [腾讯视频插件](https://github.com/tvfe/txv-miniprogram-plugin) 应用于本组件,仅在微信和 *qq* 平台有效,使用前请确认已经成功申请使用该插件并按要求在小程序 *app.json* 中配置完成,否则可能报错 **This application has not registered any plugins yet** 且无法生效
|
||||
3
wechat-mini-program/node_modules/mp-html/plugins/txv-video/build.js
generated
vendored
Normal file
3
wechat-mini-program/node_modules/mp-html/plugins/txv-video/build.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
platform: ['mp-weixin', 'mp-qq', 'uni-app']
|
||||
}
|
||||
46
wechat-mini-program/node_modules/mp-html/plugins/txv-video/index.js
generated
vendored
Normal file
46
wechat-mini-program/node_modules/mp-html/plugins/txv-video/index.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @fileoverview txv-video 插件
|
||||
* Include txv-video (https://github.com/tvfe/txv-miniprogram-plugin)
|
||||
*/
|
||||
const TxvVideo = function (vm) {
|
||||
this.vm = vm
|
||||
}
|
||||
|
||||
// #ifdef MP-WEIXIN || MP-QQ
|
||||
try {
|
||||
const TxvContext = requirePlugin('tencentvideo')
|
||||
|
||||
TxvVideo.prototype.onLoad = function () {
|
||||
setTimeout(() => {
|
||||
for (let i = 0; i < this.videos.length; i++) {
|
||||
const ctx = TxvContext.getTxvContext(this.videos[i])
|
||||
ctx.id = this.videos[i]
|
||||
this.vm._videos.push(ctx)
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('使用txv-video扩展需注册腾讯视频插件')
|
||||
}
|
||||
|
||||
TxvVideo.prototype.onUpdate = function (_, config) {
|
||||
config.trustTags['txv-video'] = true
|
||||
this.videos = []
|
||||
}
|
||||
|
||||
TxvVideo.prototype.onParse = function (node, parser) {
|
||||
if (node.name === 'iframe' && (node.attrs.src || '').includes('vid')) {
|
||||
const vid = node.attrs.src.match(/vid=([^&\s]+)/)
|
||||
if (vid) {
|
||||
node.name = 'txv-video'
|
||||
node.attrs.vid = vid[1]
|
||||
this.videos.push(vid[1])
|
||||
node.attrs.src = undefined
|
||||
parser.expose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
module.exports = TxvVideo
|
||||
6
wechat-mini-program/node_modules/mp-html/plugins/txv-video/miniprogram/build.js
generated
vendored
Normal file
6
wechat-mini-program/node_modules/mp-html/plugins/txv-video/miniprogram/build.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
template: '<txv-video wx:if="{{n.name==\'txv-video\'}}" vid="{{n.attrs.vid}}" playerid="{{n.attrs.vid}}" id="{{n.attrs.vid}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" controls data-i="{{i}}" bindplay="play" binderror="mediaError" />',
|
||||
usingComponents: {
|
||||
'txv-video': 'plugin://tencentvideo/video'
|
||||
}
|
||||
}
|
||||
3
wechat-mini-program/node_modules/mp-html/plugins/txv-video/uni-app/build.js
generated
vendored
Normal file
3
wechat-mini-program/node_modules/mp-html/plugins/txv-video/uni-app/build.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
template: '<txv-video v-if="n.name==\'txv-video\'" :vid="n.attrs.vid" :playerid="n.attrs.vid" :id="n.attrs.vid" :class="n.attrs.class" :style="n.attrs.style" controls :data-i="i" @play="play" @error="mediaError" />'
|
||||
}
|
||||
Reference in New Issue
Block a user