This commit is contained in:
caishi 2020-05-09 09:43:44 +08:00
parent 5d7344fb1c
commit f2f9f27086
3 changed files with 165 additions and 28 deletions

123
src/common/marked.js Normal file
View File

@ -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

View File

@ -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>)
}

View File

@ -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>
)