Skip to content

第一次点击插入公式时,页面重新刷新的问题,刷新后再次点击插入就没有问题 #6

@zhenglingkun

Description

@zhenglingkun

我的项目是vue3的,用的是 element-plus 组件库。
在 element-plus 的 el-dialog 中引用的封装了一层的 Editor 组件,主要是引入了公式插入的代码。
我在一个dialog 中,引用了两个前面自己封装好的Editor。
el-dialog 弹框由添加按钮控制,点击显示弹框,然后点击 公式,选择一个预览的公式,当我第一次点击 插入公式按钮 时,整个页面都重新刷新了。
刷新完成后,再次在弹框中选择插入公式就没有问题。能分析一下这个是什么原因么

下面是我封装的 Editor.vue 组件代码

<script setup lang="ts">
import {
  onBeforeUnmount,
  computed,
  PropType,
  unref,
  nextTick,
  ref,
  watch,
  shallowRef,
  onMounted
} from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { IDomEditor, IEditorConfig, IToolbarConfig, i18nChangeLanguage } from '@wangeditor/editor'
import { propTypes } from '@/utils/propTypes'
import { isNumber } from '@/utils/is'
import { ElMessage } from 'element-plus'
import { useLocaleStore } from '@/store/modules/locale'
import { useUserStoreWithOut } from '@/store/modules/user'
import request from '@/config/axios'

const localeStore = useLocaleStore()
const action = window.TV_API + '/api/common/file/upload'
const userInfo = useUserStoreWithOut().getUserInfo

const currentLocale = computed(() => localeStore.getCurrentLocale)

i18nChangeLanguage(unref(currentLocale).lang)

const props = defineProps({
  editorId: propTypes.string.def('wangeEditor-1'),
  height: propTypes.oneOfType([Number, String]).def('500px'),
  editorConfig: {
    type: Object as PropType<IEditorConfig>,
    default: () => undefined
  },
  modelValue: propTypes.string.def('')
})

const emit = defineEmits(['change', 'update:modelValue'])

// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef<IDomEditor>()

const valueHtml = ref('<p>hello</p>')

onMounted(() => {
  // setTimeout(() => {
  //   valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>'
  // }, 1500)
})

watch(
  () => props.modelValue,
  (val: string) => {
    if (val === unref(valueHtml)) return
    valueHtml.value = val
  },
  {
    immediate: true
  }
)

// 监听
watch(
  () => valueHtml.value,
  (val: string) => {
    emit('update:modelValue', val)
  }
)

const handleCreated = (editor: IDomEditor) => {
  editorRef.value = editor
}

// 编辑器配置
const editorConfig = computed((): IEditorConfig => {
  return Object.assign(
    {
      readOnly: false,
      customAlert: (s: string, t: string) => {
        switch (t) {
          case 'success':
            ElMessage.success(s)
            break
          case 'info':
            ElMessage.info(s)
            break
          case 'warning':
            ElMessage.warning(s)
            break
          case 'error':
            ElMessage.error(s)
            break
          default:
            ElMessage.info(s)
            break
        }
      },
      autoFocus: false,
      scroll: true,
      uploadImgShowBase64: true,
      MENU_CONF: {
        header: {
          token: userInfo.token
        },
        uploadImage: {
          server: false,
          customUpload: async (file: File, insertFn: any) => {
            // 1. 创建FormData
            const formData = new FormData()
            formData.append('file', file) // 后端接收的参数名,需与后端一致

            try {
              // 2. 调用上传接口(替换为你的实际接口)
              const res: any = await request.post({
                url: action,
                data: formData,
                headersType: 'multipart/form-data'
              })

              // 3. 处理后端响应(根据实际接口返回格式调整)
              if (res.code === 0) {
                const imageUrl = res.data.url // 获取图片在线URL
                insertFn(imageUrl) // 插入图片到编辑器
              } else {
                alert(`上传失败:${res.msg || '未知错误'}`)
              }
            } catch (err) {
              console.error('图片上传失败:', err)
              alert('上传失败,请检查网络或联系管理员')
            }
          }
        }
      }
    },
    props.editorConfig || {}
  )
})

const editorStyle = computed(() => {
  return {
    height: isNumber(props.height) ? `${props.height}px` : props.height
  }
})

// 回调函数
const handleChange = (editor: IDomEditor) => {
  emit('change', editor)
}

// 组件销毁时,及时销毁编辑器
onBeforeUnmount(() => {
  const editor = unref(editorRef.value)

  // 销毁,并移除 editor
  editor?.destroy()
})

const getEditorRef = async (): Promise<IDomEditor> => {
  await nextTick()
  return unref(editorRef.value) as IDomEditor
}

const toolbarConfig: Partial<IToolbarConfig> = {
  insertKeys: {
    index: 0,
    keys: [
      'insertFormula', // “插入公式”菜单
      'kityFormula' // “编辑公式”菜单
    ]
  },
  excludeKeys: [
    'fullScreen',
    'emotion',
    'group-video',
    'insertImage',
    'insertLink',
    'todo',
    'color',
    'bgColor'
  ]
}

// console.log(editorRef)
// console.log(editorRef.value?.getAllMenuKeys())

defineExpose({
  getEditorRef
})
</script>

<template>
  <div class="border-1 border-solid border-[var(--el-border-color)] z-10" style="width: 100%">
    <!-- 工具栏 -->
    <Toolbar
      :editor="editorRef"
      :editorId="editorId"
      :default-config="toolbarConfig"
      class="border-0 b-b-1 border-solid border-[var(--el-border-color)]"
    />
    <!-- 编辑器 -->
    <Editor
      v-model="valueHtml"
      :editorId="editorId"
      :defaultConfig="editorConfig"
      :style="editorStyle"
      @on-change="handleChange"
      @on-created="handleCreated"
    />
  </div>
</template>

<style src="@wangeditor/editor/dist/css/style.css"></style>

插件的注册我放在项目的 main.ts 里了,下面是我在main.ts 里的代码,之前我是写在封装的Editor.vue 里的,也是一样的情况

import kityformula from '@/components/kityformula'
import { Boot } from '@wangeditor/editor'
import formulaModule from '@wangeditor/plugin-formula'
import { useEditorStore } from '@/store/modules/editor'

const editorStore = useEditorStore()
if (!editorStore.getEditorRegisterMenu) {
  Boot.registerModule(formulaModule)
  Boot.registerMenu(kityformula)
  editorStore.setEditorRegisterMenu(true)
}

el-dialog 中简化的代码如下:

<el-dialog
    v-model="dialogVisible"
    :before-close="handleClose"
    align-center
    title="XX管理"
    width="80%"
    @opened="handleOpen"
  >
    <el-form ref="formRef" :model="form" :rules="rules">
      <el-row :gutter="20">
          ... ...
        <el-col :span="12">
          <el-form-item label="问题描述" prop="content" label-position="top">
            <Editor ref="editorRef" v-model="form.content" height="300px" />
          </el-form-item>
        </el-col>

        <el-col :span="12">
          <el-form-item label="问题解析" prop="analysis" label-position="top">
            <Editor ref="analysisRef" v-model="form.analysis" height="300px" />
          </el-form-item>
        </el-col>
      </el-row>
      ... ...
    </el-form>

    <template #footer>
      <span class="dialog-footer">
        <el-button @click="handleClose">取消</el-button>
        <el-button :loading="loading" type="primary" @click="handleSave(formRef)">保存</el-button>
      </span>
    </template>
  </el-dialog>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions