parent
0880661727
commit
1a8a62f598
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
copyByClickCode: true, // 点击代码块复制
|
||||
showLanguageName: true, // 是否在代码块右上角显示语言的名称
|
||||
showLineNumber: false // 是否显示行号
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* @fileoverview highlight 插件
|
||||
* Include prismjs (https://prismjs.com)
|
||||
*/
|
||||
import prism from './prism.min'
|
||||
import config from './config'
|
||||
import Parser from '../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'
|
||||
code.attrs.style ='display:block;overflow: auto;'
|
||||
if (config.showLanguageName) {
|
||||
node.children.push({
|
||||
name: 'div',
|
||||
attrs: {
|
||||
class: 'hl-language',
|
||||
style: 'user-select:none;position:absolute;top:0;right:2px;font-size:10px;'
|
||||
},
|
||||
children: [{
|
||||
type: 'text',
|
||||
text: lang
|
||||
}]
|
||||
})
|
||||
}
|
||||
if (config.copyByClickCode) {
|
||||
node.attrs.style += (node.attrs.style || '') + ';user-select:none;'
|
||||
node.attrs['data-content'] = text
|
||||
node.children.push({
|
||||
name: 'div',
|
||||
attrs: {
|
||||
class: 'hl-copy',
|
||||
style: 'user-select:none;position:absolute;top:0;right:3px;font-size:10px;'
|
||||
},
|
||||
// children: [{
|
||||
// type: 'text',
|
||||
// text: '复制'
|
||||
// }]
|
||||
})
|
||||
vm.expose()
|
||||
// console.log('vm',node,vm)
|
||||
}
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Highlight
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @fileoverview markdown 插件
|
||||
* Include marked (https://github.com/markedjs/marked)
|
||||
* Include github-markdown-css (https://github.com/sindresorhus/github-markdown-css)
|
||||
*/
|
||||
import marked from './marked.min'
|
||||
let index = 0
|
||||
|
||||
function Markdown (vm) {
|
||||
this.vm = vm
|
||||
vm._ids = {}
|
||||
}
|
||||
|
||||
Markdown.prototype.onUpdate = function (content) {
|
||||
if (this.vm.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 || ''}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Markdown
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
||||
|
||||
export default Parser
|
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<view class="zero-markdown-view">
|
||||
<mp-html :key="mpkey" :selectable="selectable" :scroll-table='scrollTable' :tag-style="tagStyle"
|
||||
:markdown="true" :content="html">
|
||||
</mp-html>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import mpHtml from '../mp-html/mp-html';
|
||||
|
||||
|
||||
export default {
|
||||
name: 'zero-markdown-view',
|
||||
components: {
|
||||
mpHtml
|
||||
},
|
||||
props: {
|
||||
markdown: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
selectable: {
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
scrollTable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
themeColor: {
|
||||
type: String,
|
||||
default: '#007AFF'
|
||||
},
|
||||
codeBgColor: {
|
||||
type: String,
|
||||
default: '#2d2d2d'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
html: '',
|
||||
tagStyle: '',
|
||||
mpkey: 'zero'
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
markdown: function(val) {
|
||||
this.html = this.markdown
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initTagStyle();
|
||||
},
|
||||
mounted() {
|
||||
|
||||
this.html = this.markdown
|
||||
},
|
||||
methods: {
|
||||
|
||||
initTagStyle() {
|
||||
const themeColor = this.themeColor
|
||||
const codeBgColor = this.codeBgColor
|
||||
let zeroStyle = {
|
||||
p: `
|
||||
margin:5px 5px;
|
||||
font-size: 15px;
|
||||
line-height:1.75;
|
||||
letter-spacing:0.2em;
|
||||
word-spacing:0.1em;
|
||||
`,
|
||||
// 一级标题
|
||||
h1: `
|
||||
margin:25px 0;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: ${themeColor};
|
||||
padding:3px 10px 1px;
|
||||
border-bottom: 2px solid ${themeColor};
|
||||
border-top-right-radius:3px;
|
||||
border-top-left-radius:3px;
|
||||
`,
|
||||
// 二级标题
|
||||
h2: `
|
||||
margin:40px 0 20px 0;
|
||||
font-size: 20px;
|
||||
text-align:center;
|
||||
color:${themeColor};
|
||||
font-weight:bolder;
|
||||
padding-left:10px;
|
||||
// border:1px solid ${themeColor};
|
||||
`,
|
||||
// 三级标题
|
||||
h3: `
|
||||
margin:30px 0 10px 0;
|
||||
font-size: 18px;
|
||||
color: ${themeColor};
|
||||
padding-left:10px;
|
||||
border-left:3px solid ${themeColor};
|
||||
`,
|
||||
// 引用
|
||||
blockquote: `
|
||||
margin:15px 0;
|
||||
font-size:15px;
|
||||
color: #777777;
|
||||
border-left: 4px solid #dddddd;
|
||||
padding: 0 10px;
|
||||
`,
|
||||
// 列表
|
||||
ul: `
|
||||
margin: 10px 0;
|
||||
color: #555;
|
||||
`,
|
||||
li: `
|
||||
margin: 5px 0;
|
||||
color: #555;
|
||||
`,
|
||||
// 链接
|
||||
a: `
|
||||
// color: ${themeColor};
|
||||
`,
|
||||
// 加粗
|
||||
strong: `
|
||||
font-weight: border;
|
||||
color: ${themeColor};
|
||||
`,
|
||||
// 斜体
|
||||
em: `
|
||||
color: ${themeColor};
|
||||
letter-spacing:0.3em;
|
||||
`,
|
||||
// 分割线
|
||||
hr: `
|
||||
height:1px;
|
||||
padding:0;
|
||||
border:none;
|
||||
// border-top:medium solid #333;
|
||||
text-align:center;
|
||||
background-image:linear-gradient(to right,rgba(248,57,41,0),${themeColor},rgba(248,57,41,0));
|
||||
`,
|
||||
// 表格
|
||||
table: `
|
||||
border-spacing:0;
|
||||
overflow:auto;
|
||||
min-width:100%;
|
||||
margin:10px 0;
|
||||
border-collapse: collapse;
|
||||
`,
|
||||
th: `
|
||||
border: 1px solid #202121;
|
||||
color: #555;
|
||||
`,
|
||||
td: `
|
||||
color:#555;
|
||||
border: 1px solid #555555;
|
||||
`,
|
||||
pre: `
|
||||
border-radius: 5px;
|
||||
white-space: pre;
|
||||
background: ${codeBgColor};
|
||||
font-size:12px;
|
||||
position: relative;
|
||||
`,
|
||||
}
|
||||
this.tagStyle = zeroStyle
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.zero-markdown-view {
|
||||
padding: 15rpx;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,86 @@
|
||||
{
|
||||
"id": "zero-markdown-view",
|
||||
"displayName": "zero-markdown-view(markdown解析)",
|
||||
"version": "2.0.5",
|
||||
"description": "一行代码即可实现markdown解析,支持自定义主题色,支持vue2,vue3.",
|
||||
"keywords": [
|
||||
"markdown",
|
||||
"markdown解析",
|
||||
"代码块",
|
||||
"代码高亮",
|
||||
"mp-html"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "n"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "u",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "u",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue