效果

在这里插入图片描述

父组件ArticleText

<template>
  <div id="ArticleText">
    <el-affix :offset="0">
      <div id="ArticleTextHead">
        <div id="ArticleTextHeadBack">
          <i class="el-icon-arrow-left"></i>
          文章管理
        </div>
        <el-divider direction="vertical"></el-divider>
        <div id="ArticleTextHeadTitle">
          <el-input
              placeholder="请输入内容"
              v-model="articleTitle"
              maxlength="100"
              show-word-limit></el-input>
          <div id="ArticleTextHeadTitleOperate">
            <el-button type="info" plain>保存草稿</el-button>
            <el-button type="danger" plain>发布文章</el-button>
            <el-avatar size="medium" :src="circleUrl"></el-avatar>
          </div>
        </div>
      </div>
    </el-affix>
    <div id="ArticleTextContent">
      <TextEditor v-model="content"/>
    </div>
  </div>
</template>

<script>
import TextEditor from "@/components/TextEditor";

export default {
  name: "ArticleText",
  components: {TextEditor},
  data() {
    return {
      articleTitle: "",
      content: "",
      circleUrl: "https://profile.csdnimg.cn/B/4/2/3_kaisarh"
    }
  },
}
</script>

<style lang="scss" scoped>
#ArticleText {
  height: 100%;
  width: 100%;
  position: relative;
  user-select: none;
  display: flex;
  flex-direction: column;

  #ArticleTextHead {
    height: 75px;
    width: 100%;
    //background-color: red;
    display: flex;
    align-items: center;
    box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
    z-index: 999;
    background-color: white;

    #ArticleTextHeadBack {
      width: 150px;
      font-weight: bold;
      font-size: 24px;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
    }

    .el-divider {
      height: 24px;
    }

    #ArticleTextHeadTitle {
      display: flex;
      align-items: center;
      width: 90%;
      //background-color: yellow;
      position: relative;

      .el-input {
        flex: 1;
      }

      #ArticleTextHeadTitleOperate {
        width: 250px;
        margin: 0 30px;
        display: flex;
        justify-content: space-around;
        align-items: center;

        .el-avatar {
          cursor: pointer;
        }
      }
    }
  }

  #ArticleTextContent {
    flex: 1;
    margin-top: 10px;
    width: 100%;
  }
}
</style>

富文本编辑器子组件TextEditor

<template>
  <div id="TextEditor">
    <div id="TextEditorNav">
      <div ref='toolbarContainer'></div>
    </div>
    <div id="TextEditorMain">
      <div id="TextEditorMainCode">
        <div ref='textContainer'></div>
      </div>
      <div id="TextEditorMainShow" v-html="articleHtml"></div>
    </div>
  </div>
</template>

<script>
import WangEditor from 'wangeditor';
import {textService, updateFile} from './../service/api/index'
import axios from "axios";
// 设置菜单
const menus = [
  'head',
  'bold',
  'fontSize',
  'fontName',
  'italic',
  'underline',
  'strikeThrough',
  'indent',
  'lineHeight',
  'foreColor',
  'backColor',
  'link',
  'todo',
  'justify',
  'quote',
  'emoticon',
  'image',
  'splitLine',
]
const LOCAL_BASE_URL = '/api';
export default {
  name: "TextEditor",
  data() {
    return {
      articleHtml: "",
      editor: null
    }
  },
  mounted() {
    // 设置工具栏和编辑区分开
    this.editor = new WangEditor(this.$refs.toolbarContainer, this.$refs.textContainer);
    // 设置z-index
    const editor = this.editor;
    editor.config.zIndex = 1;
    // 设置内容变化事件
    editor.config.onchange = (newHtml) => {
      this.onChange(newHtml)
    };
    // 设置placeholder
    editor.config.placeholder = '请输入博文内容';
    // 图片菜单配置
    editor.config.menus = menus;
    // 图片上传配置
    editor.config.showLinkImg = false;
    editor.config.showLinkImgAlt = false;
    editor.config.showLinkImgHref = false;
    // 自己实现图片上传
    editor.config.customUploadImg = (resultFiles, insertImgFn) => {
      this.updateImg(resultFiles[0], insertImgFn);
    }
    editor.create();
  },
  methods: {
    onChange(newHtml) {
      console.log(newHtml);
      this.articleHtml = newHtml;
    },
    updateImg(image, insertImgFn) {
      if (image) {
        const file = new FormData()
        file.append('image', image);
        updateFile(file).then(res => {
          if (res.status === 200) {
            insertImgFn(res.result);
          }
        })
      }
    }
  }
}
</script>

<style lang="scss" scoped>
#TextEditor {
  height: 100%;
  width: 100%;
  position: relative;
  display: flex;
  flex-direction: column;

  #TextEditorNav {
    height: 40px;
    width: 98%;
    margin-left: 1%;
    margin-right: 1%;
    background-color: yellow;
  }

  #TextEditorMain {
    flex: 1;
    width: 100%;
    margin-top: 10px;
    margin-bottom: 10px;
    display: flex;
    justify-content: space-around;

    #TextEditorMainCode,
    #TextEditorMainShow {
      box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
      width: 48%;
      height: 100%;
    }
  }

  .toolbar {
    border: 1px solid #ccc;
  }

  .text {
    border: 1px solid #ccc;
    min-height: 400px;
  }
}
</style>

服务器端图片上传接口

router.post('/uploadImage', (req, res, next) => {
    let form = new formidable.IncomingForm();
    form.uploadDir = path.join(__dirname, '../public/uploads/images');
    form.keepExtensions = true;
    form.parse(req, function (err, fields, files) {
        if (err) {
            throw err;
        }
        if (files.image.path) {
            let image_url = 'http://localhost:3000/uploads/images/' + path.basename(files.image.path);
            res.json({
                status: 200,
                result: image_url
            });
        } else {
            res.json({
                status: 1,
                result: '图片路径出现问题!'
            });
        }
    });
})
Logo

前往低代码交流专区

更多推荐