本來(lái)想用tinymce的vue版本直接用的桂敛,可是應(yīng)用過(guò)后出現(xiàn)一系列的問(wèn)題:
- 需要申請(qǐng) api-key
- 網(wǎng)絡(luò)加載過(guò)慢
- 依賴網(wǎng)絡(luò)
所以怠李,決定配置一套離線版的tinymce针姿!
步驟
- 項(xiàng)目基礎(chǔ)
vue3+ts+elementUI
- 第一步:下載離線版的tinymce
https://www.tiny.cloud/get-tiny/self-hosted/
注意:可以下載Dev
版本乒省,也可以下載Prod
蘑拯,tinymce.js
默認(rèn)加載原始文件钝满,不加載壓縮過(guò)后.min
的文件兜粘;
如果要下載Dev
的話,需要添加配置如下代碼:
Snipaste_2022-01-14_17-42-59.png
-
第二步:整理目錄
把下面紅圈里的文件復(fù)制出來(lái)弯蚜,放到項(xiàng)目的/public
目錄下
目錄 第三步:下載中文包
https://www.tiny.cloud/get-tiny/language-packages/
并把下載好的zh_CN.js
文件放在項(xiàng)目/public/tinymce/langs/
下面使用
@tinymce/tinymce-vue
組件
<template>
<Editor v-model="val" :init="config" :id="eid" />
</template>
<script lang="ts">
import { computed, watch, onMounted } from "vue";
import "/@public/tinymce/tinymce.min";
import Editor from "@tinymce/tinymce-vue";
import "/@public/tinymce/tinymce.d";
declare global {
interface Window {
tinymce: any;
}
}
import configDefault from "./default-config";
import getUploadHandler from "./getUploadHandler";
export default {
components: {
Editor,
},
props: {
value: {
type: String,
default: "",
},
disabled: {
type: Boolean,
default: false,
},
uploadUrl: {
type: String,
default: "",
},
init: {
type: Object,
default() {
return {};
},
},
},
emits: ["update:value"],
setup(props: any, context: any) {
const eid = Date.now();
// 雙向綁定
const val = computed({
get() {
return props.value;
},
set(newVal: string) {
context.emit("update:value", newVal);
},
});
// 配置對(duì)象
const config = computed(() => {
const obj = Object.assign(configDefault, props.init);
obj.images_upload_handler = getUploadHandler(props.uploadUrl);
return obj;
});
// 監(jiān)聽(tīng)disabled
watch(
() => props.disabled,
(val: boolean) => {
if (window.tinymce.editors && window.tinymce.editors[eid])
window.tinymce.editors[eid].setMode(val ? "readonly" : "design");
}
);
onMounted(() => {
window.tinymce.editors[eid].setMode(val ? "readonly" : "design");
});
return {
val,
config,
eid,
};
},
};
</script>
- 附件:
// default-config.ts
export default {
base_url: '/tinymce',
suffix: ".min",
language: "zh_CN",
height: 400,
menubar: false,
plugins: "quickbars lists paste table link image",
toolbar:
"link image table | cut copy paste | fontsizeselect | forecolor backcolor | bold italic underline | numlist bullist indent outdent alignjustify alignleft aligncenter alignright",
fontsize_formats:
"8px 10px 12px 14pt 16px 18px 20px 22px 24px 26px 28px 30px 36px",
}
// getUploadHandler.ts
const getUploadHandler = (uploadUrl: string): any => {
return (
blobInfo: any,
success: any,
failure: any,
progress: any
) => {
let xhr: any, formData: any;
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open("POST", uploadUrl);
xhr.upload.onprogress = function (e: any) {
progress((e.loaded / e.total) * 100);
};
xhr.onload = function () {
var json;
if (xhr.status == 403) {
failure("HTTP Error: " + xhr.status, { remove: true });
return;
}
if (xhr.status < 200 || xhr.status >= 300) {
failure("HTTP Error: " + xhr.status);
return;
}
json = JSON.parse(xhr.responseText);
const url = json.data.url;
if (!json || typeof url != "string") {
failure("Invalid JSON: " + xhr.responseText);
return;
}
success(url);
};
xhr.onerror = function () {
failure(
"Image upload failed due to a XHR Transport error. Code: " +
xhr.status
);
};
formData = new FormData();
formData.append("file", blobInfo.blob(), blobInfo.filename());
xhr.send(formData);
}
}
export default getUploadHandler