forked from Gitlink/forgeplus-react
markded
This commit is contained in:
parent
5d7344fb1c
commit
f2f9f27086
|
@ -0,0 +1,123 @@
|
|||
import marked from 'marked'
|
||||
import { escape, unescape } from 'marked/src/helpers'
|
||||
import { renderToString } from 'katex';
|
||||
|
||||
const latexRegex = /\$+([^\$\n]+?)\$+/g
|
||||
//兼容之前的 ##标题式写法
|
||||
const headingRegex = /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/
|
||||
let toc = []
|
||||
let ctx = ["<ul>"]
|
||||
|
||||
export function cleanToc() {
|
||||
toc = []
|
||||
ctx = ["<ul>"]
|
||||
}
|
||||
|
||||
function buildToc(coll, k, level, ctx) {
|
||||
if (k >= coll.length || coll[k].level <= level) { return k }
|
||||
var node = coll[k]
|
||||
ctx.push("<li><a href='#" + node.anchor + "'>" + node.text + "</a>")
|
||||
k++
|
||||
var childCtx = []
|
||||
k = buildToc(coll, k, node.level, childCtx)
|
||||
if (childCtx.length > 0) {
|
||||
ctx.push("<ul>")
|
||||
childCtx.forEach(function (idm) {
|
||||
ctx.push(idm)
|
||||
});
|
||||
ctx.push("</ul>")
|
||||
}
|
||||
ctx.push("</li>")
|
||||
k = buildToc(coll, k, level, ctx)
|
||||
return k
|
||||
}
|
||||
|
||||
export function getTocContent() {
|
||||
buildToc(toc, 0, 0, ctx)
|
||||
ctx.push("</ul>")
|
||||
return ctx.join("")
|
||||
}
|
||||
|
||||
const tokenizer = {
|
||||
heading(src) {
|
||||
const cap = headingRegex.exec(src);
|
||||
if (cap) {
|
||||
return {
|
||||
type: 'heading',
|
||||
raw: cap[0],
|
||||
depth: cap[1].length,
|
||||
text: cap[2]
|
||||
};
|
||||
}
|
||||
},
|
||||
codespan(src) {
|
||||
const cap = this.rules.inline.code.exec(src);
|
||||
if (cap) {
|
||||
let text = cap[2].replace(/\n/g, ' ');
|
||||
const hasNonSpaceChars = /[^ ]/.test(text);
|
||||
const hasSpaceCharsOnBothEnds = text.startsWith(' ') && text.endsWith(' ');
|
||||
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
|
||||
text = text.substring(1, text.length - 1)
|
||||
}
|
||||
let match = text.match(latexRegex)
|
||||
if (match) {
|
||||
text = text.replace(latexRegex, (_, $1) => {
|
||||
return renderToString(unescape($1))
|
||||
})
|
||||
} else {
|
||||
text = escape(text, true);
|
||||
}
|
||||
return {
|
||||
type: 'codespan',
|
||||
raw: cap[0],
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const renderer = {
|
||||
code(code, infostring, escaped) {
|
||||
const lang = (infostring || '').match(/\S*/)[0];
|
||||
if (!lang) {
|
||||
return '<pre class="prettyprint linenums"><code>'
|
||||
+ (escaped ? code : escape(code, true))
|
||||
+ '</code></pre>';
|
||||
}
|
||||
|
||||
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
|
||||
return `<p class='editormd-tex'>${renderToString(unescape(code))}</p>`
|
||||
} else {
|
||||
return `<pre class="prettyprint linenums"><code class="language-${infostring}">${escaped ? code : escape(code, true)}</code></pre>\n`
|
||||
}
|
||||
}
|
||||
, paragraph(text) {
|
||||
let match = text.match(latexRegex)
|
||||
if (match) {
|
||||
text = text.replace(latexRegex, (_, $1) => {
|
||||
return renderToString(unescape($1))
|
||||
})
|
||||
}
|
||||
return '<p>' + text + '</p>\n';
|
||||
},
|
||||
heading(text, level, raw, slugger) {
|
||||
let anchor = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w\\u4e00-\\u9fa5]]+/g, '-');
|
||||
toc.push({
|
||||
anchor: anchor,
|
||||
level: level,
|
||||
text: text
|
||||
})
|
||||
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
|
||||
}
|
||||
}
|
||||
|
||||
marked.setOptions({
|
||||
silent: true,
|
||||
smartypants: true,
|
||||
gfm: true,
|
||||
pedantic: false
|
||||
})
|
||||
|
||||
marked.use({ tokenizer, renderer });
|
||||
|
||||
export default marked
|
|
@ -1,21 +1,48 @@
|
|||
import React, { useEffect, useRef } from 'react'
|
||||
import 'katex/dist/katex.min.css';
|
||||
import { exportMdtoHtml } from 'educoder'
|
||||
|
||||
import marked, { getTocContent, cleanToc } from '../common/marked'
|
||||
|
||||
let preRegex = /<pre[^>]*>/g
|
||||
|
||||
export default ({ value = '', is_md = true, className, style = {} }) => {
|
||||
let html = is_md ? exportMdtoHtml(value) : value
|
||||
console.log(html, '----========')
|
||||
let str = String(value)
|
||||
let html = is_md ? marked(str) : value
|
||||
if (str.match(/\[TOC\]/)) {
|
||||
html = html.replace("<p>[TOC]</p>", getTocContent())
|
||||
cleanToc()
|
||||
}
|
||||
|
||||
html = html.replace(/▁/g, "▁▁▁")
|
||||
const el = useRef()
|
||||
|
||||
function onAncherHandler(e) {
|
||||
let target = e.target
|
||||
if (target.tagName.toUpperCase() === 'A') {
|
||||
let ancher = target.getAttribute('href')
|
||||
if (ancher.startsWith('#')) {
|
||||
e.preventDefault()
|
||||
let viewEl = document.getElementById(ancher.replace('#', ''))
|
||||
if (viewEl) {
|
||||
viewEl.parentNode.scrollTop = viewEl.offsetTop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (el.current && html) {
|
||||
if (html.match(preRegex)) {
|
||||
window.PR.prettyPrint()
|
||||
}
|
||||
}
|
||||
}, [html, el.current])
|
||||
if (el.current) {
|
||||
el.current.addEventListener('click', onAncherHandler)
|
||||
return () => {
|
||||
el.current.removeEventListener('click', onAncherHandler)
|
||||
}
|
||||
}
|
||||
}, [html, el.current, onAncherHandler])
|
||||
|
||||
return (<div ref={el} style={style} className={`${className} markdown-body`} dangerouslySetInnerHTML={{ __html: html }}></div>)
|
||||
return (<div ref={el} style={style} className={`${className ? className : ''} markdown-body`} dangerouslySetInnerHTML={{ __html: html }}></div>)
|
||||
}
|
|
@ -190,31 +190,31 @@ class InfosPackage extends Component {
|
|||
<div className="white-panel edu-back-white pt20 pb20 clearfix ">
|
||||
<li className={category ? " font-16 whitepanelyslli" : "active font-16 whitepanelyslli"}><a
|
||||
onClick={() => this.changeCategory()} className="font-16 w32">全部</a></li>
|
||||
<li className={category == "manage" ? "active font-16 whitepanelysllis" : "font-16 whitepanelysllis"}><a
|
||||
<li className={category === "manage" ? "active font-16 whitepanelysllis" : "font-16 whitepanelysllis"}><a
|
||||
onClick={() => this.changeCategory("manage")}
|
||||
className="font-16 w66">{is_current ? "我" : "TA"}管理的</a></li>
|
||||
<li className={category == "study" ? "active font-16 whitepanelysllis" : "font-16 whitepanelysllis"}><a
|
||||
<li className={category === "study" ? "active font-16 whitepanelysllis" : "font-16 whitepanelysllis"}><a
|
||||
onClick={() => this.changeCategory("study")}
|
||||
className="font-16 w66">{is_current ? "我" : "TA"}学习的</a></li>
|
||||
</div>
|
||||
{
|
||||
category == "manage" ?
|
||||
category === "manage" ?
|
||||
<div className="edu-back-white padding10-30 clearfix secondNavs bor-top-greyE">
|
||||
<li className={status ? "whitepanelyslliss" : "active whitepanelyslliss"}><a
|
||||
onClick={() => this.changeStatus()} className="w32">全部</a></li>
|
||||
<li className={status == "unpublished " ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
<li className={status === "unpublished " ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
onClick={() => this.changeStatus("unpublished")} className="w60">未发布</a></li>
|
||||
<li className={status == "bidding" ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
<li className={status === "bidding" ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
onClick={() => this.changeStatus("bidding")} className="w60">竞标中</a></li>
|
||||
<li className={status == "finished" ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
<li className={status === "finished" ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
onClick={() => this.changeStatus("finished")} className="w60">已完成</a></li>
|
||||
</div> : category == "bidden" ?
|
||||
</div> : category === "bidden" ?
|
||||
<div className="edu-back-white padding10-30 clearfix secondNavs bor-top-greyE">
|
||||
<li className={status ? "whitepanelyslliss" : "active whitepanelyslliss"}><a
|
||||
onClick={() => this.changeStatus()} className="w32">全部</a></li>
|
||||
<li className={status == "bidding_lost" ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
<li className={status === "bidding_lost" ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
onClick={() => this.changeStatus("bidding_lost")} className="w60">未中标</a></li>
|
||||
<li className={status == "bidding_won" ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
<li className={status === "bidding_won" ? "active whitepanelysllisyt" : "whitepanelysllisyt"}><a
|
||||
onClick={() => this.changeStatus("bidding_won")} className="w60">已中标</a></li>
|
||||
</div> : ""
|
||||
}
|
||||
|
@ -223,14 +223,6 @@ class InfosPackage extends Component {
|
|||
<span className="fr color-grey-9">发布时间</span>
|
||||
</p>
|
||||
|
||||
{/*bidden_status: "pending"*/}
|
||||
|
||||
{/*category_id: 2*/}
|
||||
|
||||
{/*status: "published"*/}
|
||||
|
||||
{/*type: "bidden"*/}
|
||||
|
||||
{data === undefined ? <NoneData></NoneData> : data.project_packages.length === 0 ? <NoneData></NoneData> : data.project_packages.map((item, key) => {
|
||||
return (
|
||||
<div className="educontent project-packages-list" key={key}>
|
||||
|
@ -295,27 +287,22 @@ class InfosPackage extends Component {
|
|||
</a>
|
||||
|
||||
</div> : "" : ""}
|
||||
|
||||
{category == "manage" ? item.operation.can_edit === true && item.operation.can_delete === false ?
|
||||
{category === "manage" ? item.operation.can_edit === true && item.operation.can_delete === false ?
|
||||
<div className="item-operator none">
|
||||
<a href={`/crowdsourcing/${item.id}/edit`} title="编辑">
|
||||
<i className="fa fa-pencil"></i>
|
||||
</a>
|
||||
</div> : "" : ""}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{
|
||||
totalCount > 20 &&
|
||||
<div className="mt30 mb50 edu-txt-center">
|
||||
<Pagination showQuickJumper total={totalCount} onChange={this.changePage} pageSize={20} current={page} />
|
||||
</div>
|
||||
}
|
||||
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue