Redbattle
踩坑
环境
项目
配置
知识点
踩坑
环境
项目
配置
知识点
  • 视频方案
  • DNS
  • 主题切换
  • 图片性能优化
  • 富文本预览
  • 区域滚动方法
  • 底部固定区域
  • CSS实现背景透明文字不透明
  • 文字展开收起
  • 二次开发
  • scheme
2024-03-25

富文本预览

  • 使用 shadow dom 进行样式隔绝
  • 使用 prismjs 进行 code 插件样式格式化
  • 图片预览大图使用 Element UI 自带的图片组件
<template>
  <div class="rt-view">
    <rt-view v-if="contentHtml" :content="contentHtml" />
    <el-image ref="rt_view_image" :src="previewImage" :preview-src-list="previewImageList" />
  </div>
</template>

<script>
  import Prism from 'prismjs'
  // import { txt2Html } from '@/utils/index'

  let vm = {}

  class ViewElement extends HTMLElement {
    constructor() {
      super()
      this.attachShadow({ mode: 'open' })
    }
    connectedCallback() {
      // html
      const htmlText = document.createElement('div')
      htmlText.setAttribute('id', 'rt_view')
      htmlText.innerHTML = this.getAttribute('content')

      // css
      const cssLink = document.createElement('link')
      cssLink.href = './prism.css' // prism.css 文件地址
      cssLink.rel = 'stylesheet'

      // style
      const styleText = document.createElement('style')
      styleText.textContent = 'img{max-width: 100%;} pre{overflow-x: auto;}'

      // append
      this.shadowRoot.appendChild(cssLink)
      this.shadowRoot.appendChild(styleText)
      this.shadowRoot.appendChild(htmlText)

      // set code
      const preDom = this.shadowRoot.querySelectorAll('pre')
      preDom.forEach(item => {
        const codeDom = item.querySelector('code')
        if (codeDom && codeDom.textContent) {
          const languages = item.getAttribute('class')?.split('language-')[1]
          if (languages) {
            if (Object.prototype.hasOwnProperty.call(Prism.languages, languages)) {
              codeDom.innerHTML = Prism.highlight(item.textContent, Prism.languages[languages], languages)
            } else {
              codeDom.innerHTML = Prism.highlight(item.textContent, Prism.languages['text'], 'text')
            }
          }
        }
      })

      // set table
      this.shadowRoot.querySelectorAll('table').forEach(item => {
        const tableParentDom = document.createElement('div')
        tableParentDom.setAttribute('style', 'width: 100%; overflow: auto;')
        item.parentNode.replaceChild(tableParentDom, item)
        tableParentDom.appendChild(item)
      })

      //  view image
      this.shadowRoot.querySelectorAll('img').forEach(item => {
        const currentSrc = item.getAttribute('src')
        // 图片点击放大,带链接的图片除外
        if (currentSrc && !item.closest('a')?.getAttribute('href')) {
          const style = item.getAttribute('style')
          item.setAttribute('style', 'cursor: zoom-in;' + style)
          item.addEventListener('click', e => {
            e.stopPropagation()
            vm.handleView(currentSrc)
          })
        }
      })
    }
    disconnectedCallback() {
      this.shadowRoot.querySelectorAll('img').forEach(item => {
        item.removeEventListener('click', null)
      })
    }
  }

  export default {
    name: 'RbRichTextView',
    props: {
      content: {
        type: String,
        default: ''
      }
    },
    watch: {
      content: {
        handler(e) {
          this.contentHtml = ''
          if (e) {
            // if (/\.txt$/.test(e)) {
            //   txt2Html(e).then(html => {
            //     this.contentHtml = html
            //   })
            // } else {
            //   this.contentHtml = e
            // }
            this.contentHtml = e
          }
        },
        immediate: true
      }
    },
    data() {
      return {
        contentHtml: '',
        previewImage: '',
        previewImageList: []
      }
    },
    created() {
      vm = this
      if (!customElements.get('rt-view')) {
        customElements.define('rt-view', ViewElement)
      }
    },
    methods: {
      handleView(e) {
        this.previewImage = e
        this.previewImageList = [e]
        setTimeout(() => {
          this.$refs['rt_view_image']?.clickHandler()
        })
      }
    }
  }
</script>

<style lang="less" scoped>
  .rt-view {
    word-break: break-all;
    ::v-deep {
      .el-image__inner,
      .el-image__error {
        display: none;
      }
    }
  }
</style>
最近更新
01
烧虾球
05-13
02
二次开发
12-20
03
文字展开收起
10-17
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式