文章參考自 點選前往
// 安裝 tinymce-vue
npm install @tinymce/tinymce-vue -S
// 安裝 tinymce
npm install tinymce -S
tinymce預設英文,中文語言包
- 在
public
目錄下新建tinymce
,將上面下載的語言包解壓到該目錄- 在
node_modules
裡面找到tinymce
,將skins
目錄複製到public/tinymce
裡面- 最終形成以下目錄形式:
我們將其再封裝一下,方便自己在其他元件中使用
建立元件 Tinymce.vue 元件
<template>
<div class="tinymce-editor">
<Editor v-model="myValue"
:init="init"
:disabled="disabled"
@onClick="onClick">
</Editor>
</div>
</template>
<script>
import tinymce from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
import 'tinymce/themes/silver'
import 'tinymce/plugins/image';
import 'tinymce/plugins/media';
import 'tinymce/plugins/table';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/contextmenu';
import 'tinymce/plugins/wordcount';
import 'tinymce/plugins/colorpicker';
import 'tinymce/plugins/textcolor';
export default {
name: "Tinymce",
components: {
Editor
},
props: {
//傳入一個value,使元件支援v-model繫結
value: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
plugins: {
type: [String, Array],
default: 'lists image media table textcolor wordcount contextmenu'
},
toolbar: {
type: [String, Array],
default: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists image media table | removeformat'
}
},
data() {
return {
//初始化配置
init: {
language_url: '/tinymce/langs/zh_CN.js',// 語言包的路徑
language: 'zh_CN',//語言
skin_url: '/tinymce/skins/ui/oxide',// skin路徑
height: 300,//編輯器高度
plugins: this.plugins,
toolbar: this.toolbar,
branding: false,//是否禁用“Powered by TinyMCE”
menubar: false,//頂部選單欄顯示
//此處為圖片上傳處理函式,這個直接用了base64的圖片形式上傳圖片,
//如需ajax上傳可參考https://www.tiny.cloud/docs/configure/file-image-upload/#images_upload_handler
images_upload_handler: (blobInfo, success, failure) => {
const img = 'data:image/jpeg;base64,' + blobInfo.base64();
success(img)
}
},
myValue: this.value
}
},
mounted() {
tinymce.init({});
},
methods: {
//新增相關的事件,可用的事件參照文件=> https://github.com/tinymce/tinymce-vue => All available events
//需要什麼事件可以自己增加
onClick(e) {
this.$emit('onClick', e, tinymce)
},
//可以新增一些自己的自定義事件,如清空內容
clear() {
this.myValue = ''
}
},
watch: {
value(newValue) {
this.myValue = newValue
},
myValue(newValue) {
this.$emit('input', newValue)
}
}
}
</script>
<style scoped>
</style>
使用Tinymce.vue 元件
<template>
<a-form :form="form" @submit="handleSubmit">
<a-tag color="#108ee9">文章編輯</a-tag>
<a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章分類">
<a-select
style="width: 100%"
v-decorator="['category_id',{initialValue:article.article_id,rules: [{ required: true, message: '請選擇文章分類' }]}]"
>
<a-select-option value="0">請選擇分類</a-select-option>
<a-select-option v-for="cate in cate_list" :value="cate.category_id">{{ cate.category_name }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章名稱">
<a-input v-decorator="['article_title',{initialValue:article.article_title,rules: [{ required: true, message: '請填寫文章名稱' }]}]" />
</a-form-item>
<a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="關鍵詞" help="關鍵詞用英文逗號','分隔">
<a-input v-decorator="['keywords',{initialValue:article.keywords,rules: [{ required: true, message: '請填寫關鍵詞' }]}]" />
</a-form-item>
<a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章摘要">
<a-textarea v-decorator="['keywords',{initialValue:article.keywords,rules: [{ required: false, message: '請填寫關鍵詞' }]}]" autosize />
</a-form-item>
<a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章插圖">
<a-upload
name="article_pic"
listType="picture-card"
class="avatar-uploader"
:showUploadList="false"
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
:beforeUpload="beforeUpload"
@change="handleChange"
v-decorator="['article_pic']"
>
<img v-if="imageUrl" :src="imageUrl" alt="avatar" />
<div v-else>
<a-icon :type="loading ? 'loading' : 'plus'" />
<div class="ant-upload-text">選擇頭像</div>
</div>
</a-upload>
</a-form-item>
<a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章內容">
<!--使用 Tinymce.vue 元件-->
<tinymce_editor v-model="msg"
:disabled="disabled"
@onClick="onClick"
ref="editor">
</tinymce_editor>
</a-form-item>
<a-form-item :label-col="labelCol" :wrapper-col="wrapperCol">
<div>
<a-button type="primary" html-type="submit" class="login-form-button" style="margin-right: 5%;">
儲存
</a-button>
<a-button type="danger" class="login-form-button" @click="resetFields">
重置
</a-button>
</div>
</a-form-item>
</a-form>
</template>
<script>
import {EditArticle,SaveArticle} from "../../axios/api";
import TinyMce from '../../components/Tinymce';
function getBase64 (img, callback) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img)
}
export default {
name: "MenuEdit",
components:{
tinymce_editor:TinyMce,
},
data: function () {
return {
loading: false,
form: this.$form.createForm(this),
article_id: this.$route.query.article_id,
article:[],
cate_list:[],
imageUrl:"",
head_pic:"",
labelCol: {
xs: {span: 24},
sm: {span: 5},
},
wrapperCol: {
xs: {span: 24},
sm: {span: 12},
},
disabled: false,
msg:"歡迎來到全新編輯器"
};
},
created() {
EditArticle({
article_id:this.article_id
}).then((response)=>{
if (response.data.code == 1){
this.article = response.data.data.article;
this.cate_list = response.data.data.cate_list;
this.imageUrl = response.data.data.article.article_pic;
}else{
this.msg = response.data.msg;
this.openNotification();
}
}).catch((err)=>{
console.log(err);
})
},
methods:{
openNotification () {
this.$notification.open({
message: "提醒框",
description: this.msg,
});
},
handleSubmit (e) {
e.preventDefault();
this.form.validateFields((errors,values)=>{
if (errors){
console.log(errors);
}else{
values.article_id = this.article_id;
values.article_pic = this.article_pic;
this.postSubmit(values);
}
})
},
resetFields(){
this.form.resetFields()
},
postSubmit(param){
SaveArticle(param).then((res)=>{
if (res.data.code == 1){
this.$message.success(res.data.msg, 2);
}else{
this.$message.error(res.data.msg, 2);
}
}).catch((error)=>{
console.log(error);
})
},
handleChange (info) {
if (info.file.status === 'uploading') {
this.loading = true;
return
}
if (info.file.status === 'done') {
this.article_pic = info.file.response.thumbUrl;
// Get this url from response in real world.
getBase64(info.file.originFileObj, (imageUrl) => {
this.imageUrl = imageUrl;
this.loading = false;
})
}else if(info.file.status === 'error'){
this.$message.error("上傳失敗",2);
}
},
beforeUpload (file) {
const isJPG = file.type === 'image/jpeg'
if (!isJPG) {
this.$message.error('You can only upload JPG file!')
}
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
this.$message.error('Image must smaller than 2MB!')
}
return isJPG && isLt2M
},
//滑鼠單擊的事件
onClick(e, editor) {
console.log('Element clicked');
console.log(e);
console.log(editor);
},
}
};
</script>
<style scoped>
</style>