ua-markdown.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. <!-- uniapp vue2 markdown解析 -->
  2. <template>
  3. <view class="ua__markdown"><rich-text space="nbsp" :nodes="parseNodes(source)"
  4. @itemclick="handleItemClick"></rich-text></view>
  5. </template>
  6. <script>
  7. import MarkdownIt from './lib/markdown-it.min.js'
  8. import hljs from './lib/highlight/uni-highlight.min.js'
  9. import './lib/highlight/atom-one-dark.css'
  10. import parseHtml from './lib/html-parser.js'
  11. export default {
  12. props: {
  13. source: String,
  14. showLine: {
  15. type: [Boolean, String],
  16. default: true
  17. }
  18. },
  19. data() {
  20. return {
  21. markdown: null,
  22. copyCodeData: [],
  23. }
  24. },
  25. mounted() {
  26. let that = this
  27. this.markdown = MarkdownIt({
  28. html: true,
  29. highlight: function(str, lang) {
  30. let preCode = ""
  31. try {
  32. preCode = hljs.highlightAuto(str).value
  33. } catch (err) {
  34. preCode = that.markdown.utils.escapeHtml(str);
  35. }
  36. const lines = preCode.split(/\n/).slice(0, -1)
  37. // 添加自定义行号
  38. let html = lines.map((item, index) => {
  39. if (item == '') {
  40. return ''
  41. }
  42. return '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' +
  43. item + '</li>'
  44. }).join('')
  45. if (that.showLine) {
  46. html = '<ol style="padding: 0px 30px;">' + html + '</ol>'
  47. } else {
  48. html = '<ol style="padding: 0px 7px;list-style:none;">' + html + '</ol>'
  49. }
  50. let tempData = []
  51. tempData.push(str)
  52. that.copyCodeData = tempData
  53. let htmlCode = `<div class="markdown-wrap">`
  54. // #ifndef MP-WEIXIN
  55. htmlCode += `<div style="color: #aaa;text-align: right;font-size: 12px;padding:8px;">`
  56. htmlCode +=
  57. `${lang}<a class="copy-btn" code-data-index="${tempData.length - 1}" style="margin-left: 8px;">复制代码</a>`
  58. htmlCode += `</div>`
  59. // #endif
  60. htmlCode +=
  61. `<pre class="hljs" style="padding:10px 8px 0;margin-bottom:5px;overflow: auto;display: block;border-radius: 5px;"><code>${html}</code></pre>`;
  62. htmlCode += '</div>'
  63. return htmlCode
  64. }
  65. })
  66. },
  67. methods: {
  68. parseNodes(value) {
  69. if (!value) return
  70. if (!this.markdown) return
  71. // 解析<br />到\n
  72. value = value.replace(/<br>|<br\/>|<br \/>/g, "\n")
  73. value = value.replace(/&nbsp;/g, " ")
  74. let htmlString = ''
  75. if (value.split("```").length % 2) {
  76. let mdtext = value
  77. if (mdtext[mdtext.length - 1] != '\n') {
  78. mdtext += '\n'
  79. }
  80. htmlString = this.markdown.render(mdtext)
  81. } else {
  82. htmlString = this.markdown.render(value)
  83. }
  84. // 解决小程序表格边框型失效问题
  85. htmlString = htmlString.replace(/<table/g, `<table class="table"`)
  86. htmlString = htmlString.replace(/<tr/g, `<tr class="tr"`)
  87. htmlString = htmlString.replace(/<th>/g, `<th class="th">`)
  88. htmlString = htmlString.replace(/<td/g, `<td class="td"`)
  89. htmlString = htmlString.replace(/<hr>|<hr\/>|<hr \/>/g, `<hr class="hr">`)
  90. // #ifndef APP-NVUE
  91. return htmlString
  92. // #endif
  93. // 将htmlString转成htmlArray,反之使用rich-text解析
  94. // #ifdef APP-NVUE
  95. return parseHtml(htmlString)
  96. // #endif
  97. },
  98. handleItemClick(e) {
  99. let {
  100. attrs
  101. } = e.detail.node
  102. let {
  103. "code-data-index": codeDataIndex,
  104. "class": className
  105. } = attrs
  106. if (className == 'copy-btn') {
  107. uni.setClipboardData({
  108. data: this.copyCodeData[codeDataIndex],
  109. showToast: false,
  110. success() {
  111. uni.showToast({
  112. title: '复制成功',
  113. icon: 'none'
  114. });
  115. }
  116. })
  117. }
  118. }
  119. }
  120. }
  121. </script>
  122. <style lang="scss" scoped>
  123. .ua__markdown {
  124. font-size: 14px;
  125. line-height: 1.5;
  126. h1,
  127. h2,
  128. h3,
  129. h4,
  130. h5,
  131. h6 {
  132. font-family: inherit;
  133. font-weight: 500;
  134. line-height: 1.1;
  135. color: inherit;
  136. }
  137. h1,
  138. h2,
  139. h3 {
  140. margin-top: 20px;
  141. margin-bottom: 10px
  142. }
  143. h4,
  144. h5,
  145. h6 {
  146. margin-top: 10px;
  147. margin-bottom: 10px
  148. }
  149. .h1,
  150. h1 {
  151. font-size: 36px
  152. }
  153. .h2,
  154. h2 {
  155. font-size: 30px
  156. }
  157. .h3,
  158. h3 {
  159. font-size: 24px
  160. }
  161. .h4,
  162. h4 {
  163. font-size: 18px
  164. }
  165. .h5,
  166. h5 {
  167. font-size: 14px
  168. }
  169. .h6,
  170. h6 {
  171. font-size: 12px
  172. }
  173. a {
  174. background-color: transparent;
  175. color: #2196f3;
  176. text-decoration: none;
  177. }
  178. hr,
  179. ::v-deep .hr {
  180. margin-top: 20px;
  181. margin-bottom: 20px;
  182. border: 0;
  183. border-top: 1px solid #e5e5e5;
  184. }
  185. img {
  186. max-width: 35%;
  187. }
  188. p {
  189. margin: 0 0 10px
  190. }
  191. em {
  192. font-style: italic;
  193. font-weight: inherit;
  194. }
  195. ol,
  196. ul {
  197. margin-top: 0;
  198. margin-bottom: 10px;
  199. padding-left: 40px;
  200. }
  201. ol ol,
  202. ol ul,
  203. ul ol,
  204. ul ul {
  205. margin-bottom: 0;
  206. }
  207. ol ol,
  208. ul ol {
  209. list-style-type: lower-roman;
  210. }
  211. ol ol ol,
  212. ul ul ol {
  213. list-style-type: lower-alpha;
  214. }
  215. dl {
  216. margin-top: 0;
  217. margin-bottom: 20px;
  218. }
  219. dt {
  220. font-weight: 600;
  221. }
  222. dt,
  223. dd {
  224. line-height: 1.4;
  225. }
  226. .task-list-item {
  227. list-style-type: none;
  228. }
  229. .task-list-item input {
  230. margin: 0 .2em .25em -1.6em;
  231. vertical-align: middle;
  232. }
  233. pre {
  234. position: relative;
  235. z-index: 11;
  236. }
  237. code,
  238. kbd,
  239. pre,
  240. samp {
  241. font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
  242. }
  243. code:not(.hljs) {
  244. padding: 2px 4px;
  245. font-size: 90%;
  246. color: #c7254e;
  247. background-color: #ffe7ee;
  248. border-radius: 4px;
  249. }
  250. code:empty {
  251. display: none;
  252. }
  253. pre code.hljs {
  254. color: var(--vg__text-1);
  255. border-radius: 16px;
  256. background: var(--vg__bg-1);
  257. font-size: 12px;
  258. }
  259. .markdown-wrap {
  260. font-size: 12px;
  261. margin-bottom: 10px;
  262. }
  263. pre.code-block-wrapper {
  264. background: #2b2b2b;
  265. color: #f8f8f2;
  266. border-radius: 4px;
  267. overflow-x: auto;
  268. padding: 1em;
  269. position: relative;
  270. }
  271. pre.code-block-wrapper code {
  272. padding: auto;
  273. font-size: inherit;
  274. color: inherit;
  275. background-color: inherit;
  276. border-radius: 0;
  277. }
  278. .code-block-header__copy {
  279. font-size: 16px;
  280. margin-left: 5px;
  281. }
  282. abbr[data-original-title],
  283. abbr[title] {
  284. cursor: help;
  285. border-bottom: 1px dotted #777;
  286. }
  287. blockquote {
  288. padding: 10px 20px;
  289. margin: 0 0 20px;
  290. font-size: 17.5px;
  291. border-left: 5px solid #e5e5e5;
  292. }
  293. blockquote ol:last-child,
  294. blockquote p:last-child,
  295. blockquote ul:last-child {
  296. margin-bottom: 0
  297. }
  298. blockquote .small,
  299. blockquote footer,
  300. blockquote small {
  301. display: block;
  302. font-size: 80%;
  303. line-height: 1.42857143;
  304. color: #777
  305. }
  306. blockquote .small:before,
  307. blockquote footer:before,
  308. blockquote small:before {
  309. content: '\2014 \00A0'
  310. }
  311. .blockquote-reverse,
  312. blockquote.pull-right {
  313. padding-right: 15px;
  314. padding-left: 0;
  315. text-align: right;
  316. border-right: 5px solid #eee;
  317. border-left: 0
  318. }
  319. .blockquote-reverse .small:before,
  320. .blockquote-reverse footer:before,
  321. .blockquote-reverse small:before,
  322. blockquote.pull-right .small:before,
  323. blockquote.pull-right footer:before,
  324. blockquote.pull-right small:before {
  325. content: ''
  326. }
  327. .blockquote-reverse .small:after,
  328. .blockquote-reverse footer:after,
  329. .blockquote-reverse small:after,
  330. blockquote.pull-right .small:after,
  331. blockquote.pull-right footer:after,
  332. blockquote.pull-right small:after {
  333. content: '\00A0 \2014'
  334. }
  335. .footnotes {
  336. -moz-column-count: 2;
  337. -webkit-column-count: 2;
  338. column-count: 2
  339. }
  340. .footnotes-list {
  341. padding-left: 2em
  342. }
  343. table,
  344. ::v-deep .table {
  345. border-spacing: 0;
  346. border-collapse: collapse;
  347. width: 100%;
  348. max-width: 65em;
  349. overflow: auto;
  350. margin-top: 0;
  351. margin-bottom: 16px;
  352. }
  353. table tr,
  354. ::v-deep .table .tr {
  355. border-top: 1px solid #e5e5e5;
  356. }
  357. table th,
  358. table td,
  359. ::v-deep .table .th,
  360. ::v-deep .table .td {
  361. padding: 6px 13px;
  362. border: 1px solid #e5e5e5;
  363. }
  364. table th,
  365. ::v-deep .table .th {
  366. font-weight: 600;
  367. background-color: #eee;
  368. }
  369. .hljs[class*=language-]:before {
  370. position: absolute;
  371. z-index: 3;
  372. top: .8em;
  373. right: 1em;
  374. font-size: .8em;
  375. color: #999;
  376. }
  377. .hljs[class~=language-js]:before {
  378. content: "js"
  379. }
  380. .hljs[class~=language-ts]:before {
  381. content: "ts"
  382. }
  383. .hljs[class~=language-html]:before {
  384. content: "html"
  385. }
  386. .hljs[class~=language-md]:before {
  387. content: "md"
  388. }
  389. .hljs[class~=language-vue]:before {
  390. content: "vue"
  391. }
  392. .hljs[class~=language-css]:before {
  393. content: "css"
  394. }
  395. .hljs[class~=language-sass]:before {
  396. content: "sass"
  397. }
  398. .hljs[class~=language-scss]:before {
  399. content: "scss"
  400. }
  401. .hljs[class~=language-less]:before {
  402. content: "less"
  403. }
  404. .hljs[class~=language-stylus]:before {
  405. content: "stylus"
  406. }
  407. .hljs[class~=language-go]:before {
  408. content: "go"
  409. }
  410. .hljs[class~=language-java]:before {
  411. content: "java"
  412. }
  413. .hljs[class~=language-c]:before {
  414. content: "c"
  415. }
  416. .hljs[class~=language-sh]:before {
  417. content: "sh"
  418. }
  419. .hljs[class~=language-yaml]:before {
  420. content: "yaml"
  421. }
  422. .hljs[class~=language-py]:before {
  423. content: "py"
  424. }
  425. .hljs[class~=language-docker]:before {
  426. content: "docker"
  427. }
  428. .hljs[class~=language-dockerfile]:before {
  429. content: "dockerfile"
  430. }
  431. .hljs[class~=language-makefile]:before {
  432. content: "makefile"
  433. }
  434. .hljs[class~=language-javascript]:before {
  435. content: "js"
  436. }
  437. .hljs[class~=language-typescript]:before {
  438. content: "ts"
  439. }
  440. .hljs[class~=language-markup]:before {
  441. content: "html"
  442. }
  443. .hljs[class~=language-markdown]:before {
  444. content: "md"
  445. }
  446. .hljs[class~=language-json]:before {
  447. content: "json"
  448. }
  449. .hljs[class~=language-ruby]:before {
  450. content: "rb"
  451. }
  452. .hljs[class~=language-python]:before {
  453. content: "py"
  454. }
  455. .hljs[class~=language-bash]:before {
  456. content: "sh"
  457. }
  458. .hljs[class~=language-php]:before {
  459. content: "php"
  460. }
  461. }
  462. </style>