123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- <template>
- <div v-if="state.mounted" :style="style" class="ba-editor wangeditor" v-loading="state.loading">
- <Toolbar class="wangeditor-toolbar" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
- <Editor ref="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, nextTick, ref } from "vue";
- import { IEditorConfig, IToolbarConfig, i18nChangeLanguage, IDomEditor } 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";
- import { stat } from "fs";
- 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;
- disable?: boolean;
- }
- type VideoInsertFnType = (url: string, poster: string) => void
- type ImgInsertFnType = (url: string, alt: string, href: string) => void
- const props = withDefaults(defineProps<Props>(), {
- height: "320px",
- mode: "default",
- placeholder: "请输入内容...",
- modelValue: "",
- disable: false,
- 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
- loading: boolean
- } = reactive({
- mounted: false,
- value: props.modelValue,
- editorConfig: props.editorConfig,
- editorStyle: props.editorStyle,
- loading: false
- });
- 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);
- state.loading = true;
- fileUpload(fd)
- .then((res) => {
- if (res.code == 1) {
- insertFn(res.data.previewUrl, res.data.originalFilename, res.data.previewUrl);
- }
- NProgress.done();
- state.loading = false;
- })
- .catch((err) => {
- NProgress.done();
- state.loading = false;
- });
- }
- };
- // 视频上传配置
- 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);
- state.loading = true;
- fileUpload(fd)
- .then((res) => {
- console.log("上传视频", res.data);
- if (res.code == 1) {
- let poster = "http://admin.baoxiaojia.dingsenhulian.com/uploadFile/1596051044679421952.jpg";
- insertFn(res.data.previewUrl, poster);
- }
- NProgress.done();
- state.loading = false;
- })
- .catch((err) => {
- NProgress.done();
- state.loading = false;
- });
- }
- };
- state.editorStyle.height = props.height;
- state.editorStyle["overflow-y"] = "hidden";
- state.mounted = true;
- //设置只读
- nextTick(() => {
- if (props.disable) {
- editorRef.value.disable();
- }
- });
- });
- const editor = ref();
- // 自定义粘贴事件
- const handlePaste = (
- editor: IDomEditor,
- event: ClipboardEvent,
- callback: (val: boolean) => void
- ) => {
- // editor.insertText('test')
- callback(false);
- };
- // 组件销毁时,也及时销毁编辑器
- 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;
- }
- );
- </script>
- <style lang="scss">
- .ba-editor {
- border: 1px solid var(--color-sub-3);
- 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(--color-sub-3);
- }
- video {
- width: 100%;
- }
- </style>
|