@toast-ui/vue-editor中Viewer和Editor封装与使用

裴意
2023-12-01

需求:

页面局部文本内容需要编辑

实现:

方案一:

texteara 内容换行‘\n’ 替换未<br> ,不支持富文本内容,加粗斜线等等

方案二:

使用@toast-ui/vue-editor富文本编辑 封装editor和viewer
“@toast-ui/vue-editor”: “^3.2.3”,
思想来源于vue-element-admin
vue-element-admin github
vue-editor使用方法
@toast-ui/editor使用

实现过程

封装editor

<template>
  <editor
    :ref="refEditor"
    :initial-value="editorText"
    :options="editorOptions"
    initial-edit-type="markdown"
    preview-style="vertical"
    @change="onEditorChange"
  />
</template>

<script>
import '@toast-ui/editor/dist/toastui-editor.css'
import { generateUUID } from '@/utils/generateUUID'
import { Editor } from '@toast-ui/vue-editor'

export default {
  components: {
    editor: Editor
  },
  props: {
    editorValue: {
      type: String,
      default: ''
    },
    refEditor: {
      type: String,
      default: generateUUID()
    }
  },
  data() {
    return {
      editorText: '',
      editorOptions: {
        minHeight: '100px',
        language: 'zh-CN',
        useCommandShortcut: true,
        usageStatistics: true,
        hideModeSwitch: true,
        toolbarItems: [
          ['heading', 'bold', 'italic', 'strike'],
          ['hr', 'quote'],
          ['ul', 'ol', 'task', 'indent', 'outdent'],
          ['table', 'image', 'link'],
          ['code', 'codeblock'],
          ['scrollSync']
        ]
      }
    }
  },
  watch: {
    editorValue: {
      handler: function(newVal) {
        this.$nextTick(() => {
          this.editorText = this.$refs[this.refEditor].invoke('setHTML', newVal)
        })
      },
      immediate: true
    }
  },
  methods: {
    onEditorChange() {
      this.$emit('onEditorChange', this.$refs[this.refEditor].invoke('getHTML'))
    },
    cancel() {
      this.editorText = this.$refs[this.refEditor].invoke('setHTML', this.editorValue)
    }
  }
}
</script>

封装viewer

关键代码 this.$refs[this.editorRef].editor.preview.setHTML(newVal)

<template>
  <Viewer
    :ref="editorRef"
    :initial-value="viewerText"
    :options="editorOptions"
  />
</template>
<script>
import '@toast-ui/editor/dist/toastui-editor-viewer.css'
import { Viewer } from '@toast-ui/vue-editor'

export default {
  components: {
    Viewer
  },
  props: {
    editorValue: {
      type: String,
      default: ''
    },
    editorRef: {
      type: String,
      required: false,
      default() {
        return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
      }
    }
  },
  data() {
    return {
      flag: false,
      viewerText: '',
      editorOptions: {
        minHeight: '100px',
        language: 'zh-CN',
        useCommandShortcut: true,
        usageStatistics: false,
        hideModeSwitch: true,
        toolbarItems: [
          ['heading', 'bold', 'italic', 'strike'],
          ['hr', 'quote'],
          ['ul', 'ol', 'task', 'indent', 'outdent'],
          ['table', 'image', 'link'],
          ['code', 'codeblock'],
          ['scrollSync']
        ]
      }
    }
  },
  computed: {
    // viewerText: {
    //   get() {
    //     return this.editorValue
    //   },
    //   set(val) {
    //     // this.$emit('input', [...val])
    //   }
    // },
    // viewerText() {
    //   return this.editorValue
    // }
  },
  watch: {
    editorValue: {
      handler: function(newVal) {
        this.viewerText = newVal
        this.$refs[this.editorRef].editor.preview.setHTML(newVal)
      }
    }
  },
  mounted() {
    this.$refs[this.editorRef].editor.preview.setHTML(this.editorValue)
  }
}
</script>

使用viewer和editor

<template>
  <div class="charts-show">
    <div v-if="chartTitle" class="chart-title"> {{ chartTitle }}</div>
    <div v-if="chartId">
      <Chart
        :id="chartId"
        :height="`${chartHeight}px`"
        width="100%"
        :options="chartOptions"
        @chartClick="handleChartClick"
        @chartDblClick="handleChartDblClick"
      />
    </div>
    <slot />
    <div v-if="chartEditor !== 'null'">
      <div v-if="showEditor" class="editor-wrap">
        <div class="editor-info">
          <Editor :ref-editor="`${chartId}-Editor`" :editor-value="chartEditorVal" @onEditorChange="handleEditorChange" />
        </div>
        <div class="editor-wrap-icon">
          <i class="el-icon-check" style="color: #409EFF" @click="ok" />
          <i class="el-icon-close" style="color: #409EFF;margin-top: 10px;" @click="cancel" />
        </div>
      </div>
      <div v-else class="viewer-wrap" @dblclick="handleViewer">
        <Viewer :editor-ref="`${chartId}-Viewer`" :editor-value="chartViewerVal" />
        <!-- <Viewer :initialValue="chartViewerVal" /> -->
        <!-- <div v-html="chartViewerVal" /> -->
      </div>
    </div>
  </div>
</template>
<script>
import Chart from '@/components/Charts/CommonChart.vue'
// import MarkdownEditor from '@/components/MarkdownEditor/index.vue'
import '@toast-ui/editor/dist/toastui-editor.css'
import Editor from '@/components/MarkdownEditor/Editor.vue'
import Viewer from '@/components/MarkdownEditor/Viewer.vue'
// import Viewer from "@/components/MarkdownEditor/index.vue";
import '@toast-ui/editor/dist/toastui-editor-viewer.css'
// import Viewer from '@toast-ui/editor/dist/toastui-editor-viewer'

export default {
  name: 'ChartsShow',
  components: {
    Chart,
    Editor,
    Viewer
  },
  props: {
    chartTitle: {
      require: true,
      type: String,
      default: ''
    },
    chartId: {
      require: true,
      type: String,
      default: ''
    },
    chartOptions: {
      require: true,
      type: Object,
      default: () => {}
    },
    chartHeight: {
      type: Number,
      default: 500
    },
    chartEditor: {
      type: String,
      default: ''
    },
    chartEditorId: {
      type: String,
      default: ''
    },
    analysisType: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      chartEditorVal: '',
      chartViewerVal: '',
      showEditor: false,
      editorOptions: {
        minHeight: '100px',
        language: 'zh-CN',
        useCommandShortcut: true,
        usageStatistics: false,
        hideModeSwitch: true,
        toolbarItems: [
          ['heading', 'bold', 'italic', 'strike'],
          ['hr', 'quote'],
          ['ul', 'ol', 'task', 'indent', 'outdent'],
          ['table', 'image', 'link'],
          ['code', 'codeblock'],
          ['scrollSync']
        ]
      }
    }
  },
  watch: {
    chartEditor(newVal) {
      this.chartEditorVal = newVal
      this.chartViewerVal = newVal
    },
    chartId(newVal) {
      this.showEditor = false
    }
  },
  // create() {
  //   this.showEditor = false
  // },
  methods: {
    ok() {
      this.$emit('editorChange', { id: this.chartEditorId, analysisType: this.analysisType, analysisContent: this.chartViewerVal })
      this.showEditor = false
    },
    cancel() {
      this.chartEditorVal = this.chartEditor
      this.chartViewerVal = this.chartEditor
      this.showEditor = false
    },
    handleEditorChange(value) {
      this.chartEditorVal = value
      this.chartViewerVal = value
    },
    // 切换分析内容
    handleChartClick(value) {
      console.log('handleChartClick', { id: this.chartEditorId, analysisType: this.analysisType, month: value.name })
      this.$emit('chartClick', { id: this.chartEditorId, analysisType: this.analysisType, month: value.name })
      this.showEditor = false
    },
    // 跳转页面
    handleChartDblClick(value) {
      console.log('handleChartDblClick', value)
      this.$emit('chartDblClick', { projectKey: value.seriesId })
    },
    // 双击查询
    handleViewer() {
      this.showEditor = !this.showEditor
    }
  }

}
</script>

<style lang="scss" scoped>
.charts-show {
  margin-bottom: 36px;
  .chart-title {
    border-left: 3px solid #1890ff;;
    padding-left: 8px;
    margin-left: 16px;
  }
  .editor-wrap{
    margin-left: 3%;
    margin-right: 4%;
    display: flex;
    .editor-info {
     flex: 1;
    }
    .editor-wrap-icon {
      width: 20px;
      margin-left: 10px;
      display: flex;
      flex-direction: column;
      justify-content: center;
    }
  }
  .viewer-wrap{
    margin-left: 3%;
    margin-right: 4%;
    padding: 8px 16px;
    border: 1px solid #2f2f2f;
  }
}
</style>

 类似资料: