vue3 + wangeditor富文本

  1. 下载插件

npm install @wangeditor/editor -S
npm install @wangeditor/editor-for-vue@next -S
  1. 创建 文件内容 index.vue

<template>
    <div v-if="state.mounted" :style="style" class="ba-editor wangeditor">
        <Toolbar class="wangeditor-toolbar" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
        <Editor
            :style="state.editorStyle"
            v-model="state.value"
            :defaultConfig="state.editorConfig"
            :mode="mode"
            @onCreated="handleCreated"
            @onChange="handleChange"
            v-bind="$attrs"
        />
    </div>
</template>

<script setup lang="ts">
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { onBeforeUnmount, reactive, shallowRef, onMounted, CSSProperties, watch } from 'vue'
import { IEditorConfig, IToolbarConfig, i18nChangeLanguage } from '@wangeditor/editor'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { useConfig } from '/@/stores/config'
import { fileUpload } from '/@/api/common'
import NProgress from 'nprogress'

interface Props {
    // 编辑区高度
    height?: string
    mode?: 'default' | 'simple'
    placeholder?: string
    modelValue: string
    // https://www.wangeditor.com/v5/toolbar-config.html#getconfig
    toolbarConfig?: Partial<IToolbarConfig>
    // https://www.wangeditor.com/v5/editor-config.html#placeholder
    editorConfig?: Partial<IEditorConfig>
    // 编辑区style
    editorStyle?: CSSProperties
    // 整体的style
    style?: CSSProperties
}

type VideoInsertFnType = (url: string) => void
type ImgInsertFnType = (url: string, alt: string, href: string) => void

const props = withDefaults(defineProps<Props>(), {
    height: '320px',
    mode: 'default',
    placeholder: '请输入内容...',
    modelValue: '',
    toolbarConfig: () => {
        return {}
    },
    editorConfig: () => {
        return {}
    },
    editorStyle: () => {
        {
            return {}
        }
    },
    style: () => {
        return {}
    },
})

const config = useConfig()
const editorRef = shallowRef()
const emits = defineEmits<{
    (e: 'update:modelValue', value: string): void
}>()

const state: {
    mounted: boolean
    value: string
    editorConfig: Partial<IEditorConfig>
    editorStyle: CSSProperties
} = reactive({
    mounted: false,
    value: props.modelValue,
    editorConfig: props.editorConfig,
    editorStyle: props.editorStyle,
})

onMounted(() => {
    i18nChangeLanguage(config.lang.defaultLang == 'zh-cn' ? 'zh-CN' : config.lang.defaultLang)
    state.editorConfig.placeholder = props.placeholder

    // 图片上传配置
    state.editorConfig.MENU_CONF = {}
    state.editorConfig.MENU_CONF['uploadImage'] = {
        fieldName: 'file',
        maxFileSize: 10 * 1024 * 1024, // 10M
        onProgress(progress: number) {
            NProgress.inc()
        },
        async customUpload(file: File, insertFn: ImgInsertFnType) {
            NProgress.configure({ showSpinner: true })
            NProgress.start()
            let fd = new FormData()
            fd.append('file', file)
            fileUpload(fd).then((res) => {
                if (res.code == 1) {
                    insertFn(res.data.file.full_url, res.data.file.name, res.data.file.full_url)
                }
                NProgress.done()
            })
        },
    }

    // 视频上传配置
    state.editorConfig.MENU_CONF['uploadVideo'] = {
        fieldName: 'file',
        onProgress(progress: number) {
            NProgress.inc()
        },
        async customUpload(file: File, insertFn: VideoInsertFnType) {
            NProgress.configure({ showSpinner: true })
            NProgress.start()
            let fd = new FormData()
            fd.append('file', file)
            fileUpload(fd).then((res) => {
                if (res.code == 1) {
                    insertFn(res.data.file.full_url)
                }
                NProgress.done()
            })
        },
    }

    state.editorStyle.height = props.height
    state.editorStyle['overflow-y'] = 'hidden'
    state.mounted = true
})

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
    if (editorRef.value == null) return
    editorRef.value.destroy()
})

const handleCreated = (editor: any) => {
    editorRef.value = editor // 记录 editor 实例
}

const handleChange = () => {
    emits('update:modelValue', editorRef.value.getHtml())
}

const getRef = () => {
    return editorRef.value
}

defineExpose({
    getRef,
})

watch(
    () => props.modelValue,
    (newVal) => {
        state.value = newVal == '' ? '<p></p>' : newVal
    }
)
</script>

<style scoped lang="scss">
.ba-editor {
    border: 1px solid var(--el-input-border-color,var(--el-border-color));
    z-index: 9999;
    :deep(.w-e-scroll) {
        scrollbar-width: none;
        &::-webkit-scrollbar {
            width: 5px;
        }
        &::-webkit-scrollbar-thumb {
            background: #eaeaea;
            border-radius: var(--el-border-radius-base);
            box-shadow: none;
            -webkit-box-shadow: none;
        }
        &:hover {
            &::-webkit-scrollbar-thumb:hover {
                background: #c8c9cc;
            }
        }
    }
}
.wangeditor-toolbar {
    border-bottom: 1px solid var(--el-input-border-color,var(--el-border-color));
}
</style>
  1. 页面引入

// 引入上面创建的 index.vue 文件
import Editor from "/@/components/editor/index.vue";
<el-form-item label="内容">
      <Editor v-model="" />
</el-form-item>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值