123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- <template>
- <div class="editor-container">
- <Toolbar :editor="editorRef" :mode="mode" :defaultConfig="toolConfig" />
- <Editor
- :mode="mode"
- :defaultConfig="state.editorConfig"
- :style="{ height }"
- v-model="state.editorVal"
- @onCreated="handleCreated"
- @onChange="handleChange"
- @onBlur="handleBlur"
- :defaultContent="defaultContent"
- />
- </div>
- </template>
- <script lang="ts" setup name="wngEditor">
- import '@wangeditor-next/editor/dist/css/style.css' // 引入 css
- import { onBeforeUnmount, reactive, shallowRef, watch, nextTick, ref } from 'vue';
- import { IDomEditor } from '@wangeditor-next/editor'
- import { Editor, Toolbar } from '@wangeditor-next/editor-for-vue'
- import { Cookie } from '@/utils/storage';
- import { uploadFile } from '@/api/public/upload';
- import { ElMessage } from 'element-plus';
- // 全局配置
- // 定义父组件传过来的值
- const props = defineProps({
- // 是否禁用
- disable: {
- type: Boolean,
- default: () => false,
- },
- // 内容框默认 placeholder
- placeholder: {
- type: String,
- default: () => '请填写内容...',
- },
- // https://www.wangeditor.com/v5/getting-started.html#mode-%E6%A8%A1%E5%BC%8F
- // 模式,可选 <default|simple>,默认 default
- mode: {
- type: String,
- default: () => 'default',
- },
- // 高度
- height: {
- type: String,
- default: () => '310px',
- },
- // 双向绑定,用于获取 editor.getHtml()
- getHtml: String,
- // 双向绑定,用于获取 editor.getText()
- getText: String,
- defaultContent: {
- type: Array,
- default: () => [],
- },
- });
- // 定义子组件向父组件传值/事件
- const emit = defineEmits(['update:getHtml', 'update:getText', 'blur']);
- const editorRef = shallowRef<RefType>();
- const state = reactive<any>({
- editorConfig: {
- placeholder: props.placeholder,
- MENU_CONF: {},
- hoverbarKeys:{
- },
- },
- editorVal: props.getHtml,
- });
- const toolConfig = ref({
- excludeKeys: [
- // 排除掉视频上传
- 'group-video',
- ],
- });
- type InsertFnType = (url: string, alt: string, href: string) => void;
- // 图片上传
- state.editorConfig.MENU_CONF['uploadImage'] = {
- // 自定义上传
- async customUpload(file: File, insertFn: InsertFnType) {
- // 限制文件不能大于100M
- if (file.size > 10 * 1024 * 1024) {
- ElMessage.error('上传图片不能大于10M');
- return;
- }
- const formData = new FormData();
- formData.append('fileData', file);
- uploadFile(formData)
- .then((res) => {
- insertFn(import.meta.env.VITE_API_UPLOAD_URL + res.result.path, '加载失败', '');
- })
- .catch((err) => {
- ElMessage.error(err);
- });
- },
- };
- // //上传视频
- state.editorConfig.MENU_CONF['uploadVideo'] = {
- server: import.meta.env.VITE_API_UPLOAD_URL,
- // form-data fieldName ,默认值 'wangeditor-uploaded-video'
- fieldName: 'file',
- // 单个文件的最大体积限制,默认为 10M
- maxFileSize: 5 * 1024 * 1024, // 5M
- // 最多可上传几个文件,默认为 5
- maxNumberOfFiles: 3,
- // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
- allowedFileTypes: ['video/*'],
- // 将 meta 拼接到 url 参数中,默认 false
- metaWithUrl: false,
- // 自定义增加 http header
- headers: {
- Authorization: 'Bearer ' + Cookie.get('token'),
- userid: '',
- },
- // 跨域是否传递 cookie ,默认为 false
- withCredentials: true,
- // 超时时间,默认为 30 秒
- timeout: 15 * 1000, // 15 秒
- // 自定义插入视频
- customInsert(res: any, insertFn: any) {
- // 从 res 中找到 url alt href ,然后插图图片
- insertFn(res.data.url);
- },
- };
- // 行高
- /*state.editorConfig.MENU_CONF['lineHeight'] = {
- lineHeightList: ['1', '1.5', '2', '2.5'],
- };*/
- // 字体大小
- state.editorConfig.MENU_CONF['fontSize'] = {
- fontSizeList: [
- // 元素支持两种形式
- // 1. 字符串;
- // 2. { name: 'xxx', value: 'xxx' }
- '12px',
- '14px',
- '16px',
- '20px',
- '24px',
- '32px',
- '40px',
- '48px',
- ],
- };
- // 默认字体
- /*const defaultContent =ref([
- {
- type: "paragraph",
- children: [{ text: "" }],
- fontSize: "20px",
- fontFamily: "仿宋",
- },
- ],)*/
- // 编辑器回调函数
- const handleCreated = (editor: IDomEditor) => {
- editorRef.value = editor;
- };
- // 编辑器内容改变时
- const handleChange = (editor: IDomEditor) => {
- emit('update:getHtml', editor.getHtml());
- emit('update:getText', editor.getText());
- };
- // 页面销毁时
- onBeforeUnmount(() => {
- const editor = editorRef.value;
- if (editor == null) return;
- editor?.destroy();
- });
- nextTick(() => {
- // 监听双向绑定值改变,用于回显
- watch(
- () => props.getHtml,
- (val) => {
- state.editorVal = val;
- },
- {
- deep: true,
- }
- );
- // 监听是否禁用改变
- // https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I
- watch(
- () => props.disable,
- (bool) => {
- const editor = editorRef.value;
- if (editor == null) return;
- bool ? editor.disable() : editor.enable();
- },
- {
- deep: true,
- immediate: true,
- }
- );
- });
- const handleBlur = (editor: IDomEditor) => {
- emit('blur', editor);
- };
- // 暴露变量
- defineExpose({
- editorRef,
- });
- </script>
- <style scoped lang="scss">
- .editor-container {
- z-index: 99;
- border-radius: 5px;
- width: 100%;
- height: 100%;
- }
- </style>
|