• tinymce公式提交问题


    创建公式后生成base64格式的图片,与普通上传图片冲突,需要单独上传

    1、判断需要上传的文件是否为普通文件,可以按照文件名称来判断,公式文件没有名称

    images_upload_handler中打印:console.log(blobInfo.blob())

    普通文件返回:

    公式文件返回:

    2、提取

    源文件:提取后:

     注:这里从富文本中提取的所有图片文件,每次上传后src都会变成文件链接

    3、取出数组中base64文件

    result.forEach(element => {

      if (element.url.indexOf('data:image/png;base64,') > -1) {

        bytes = element.url

      }

    })

    4、上传

    const config = {

      headers: {

        'Content-Type': 'multipart/form-data',

        accesscode: $cookies.get('accesscode')

      }

    }

    axios.post(url, data, config).then(res => {

      if (res.data.code === 200) {

        resolve(res.data.data.url) // 上传成功,在成功函数里填入图片路径

      } else {

        reject('上传失败' + res.data.message)

      }

    }).catch(() => {})

     

    完整代码:

    1. // src/components/teditor.vue
    2. <template>
    3. <editor v-model="myValue" :init="init" :disabled="disabled" :id="tinymceId"></editor>
    4. </template>
    5. <script setup lang="ts">
    6. // JS部分
    7. // 在js中引入所需的主题和组件
    8. import tinymce from 'tinymce/tinymce'
    9. import 'tinymce/skins/content/default/content.css'
    10. import Editor from '@tinymce/tinymce-vue'
    11. import 'tinymce/themes/silver'
    12. import 'tinymce/themes/silver/theme'
    13. import 'tinymce/icons/default' // 引入编辑器图标icon,不引入则不显示对应图标
    14. import 'tinymce/models/dom' // 这里是个坑 一定要引入
    15. // 在TinyMce.vue中接着引入相关插件
    16. import 'tinymce/icons/default/icons'
    17. import 'tinymce/plugins/image' // 插入上传图片插件
    18. import 'tinymce/plugins/media' // 插入视频插件
    19. import 'tinymce/plugins/table' // 插入表格插件
    20. import 'tinymce/plugins/lists' // 列表插件
    21. import 'tinymce/plugins/wordcount' // 字数统计插件
    22. import 'tinymce/plugins/code' // 源码
    23. import 'tinymce/plugins/fullscreen' // 全屏
    24. // 接下来定义编辑器所需要的插件数据
    25. import { reactive, ref } from 'vue'
    26. import { onMounted, defineEmits, watch } from '@vue/runtime-core'
    27. import axios from 'axios'
    28. // import { updateImg } from '@/api/order/order'
    29. const emits = defineEmits(['getContent'])
    30. // 这里我选择将数据定义在props里面,方便在不同的页面也可以配置出不同的编辑器,当然也可以直接在组件中直接定义
    31. const props = defineProps({
    32. value: {
    33. type: String,
    34. default: () => {
    35. return ''
    36. }
    37. },
    38. baseUrl: {
    39. type: String,
    40. default: ''
    41. },
    42. disabled: {
    43. type: Boolean,
    44. default: false
    45. },
    46. height: {
    47. type: Number,
    48. default: 145
    49. },
    50. plugins: {
    51. type: [String, Array],
    52. default: 'print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link kityformula-editor media template code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount textpattern autosave '
    53. }, // 必填
    54. toolbar: {
    55. type: [String, Array],
    56. default:
    57. 'code fullscreen | undo redo | codesample bold italic outdent indent | image kityformula-editor'
    58. } // 必填
    59. })
    60. // 用于接收外部传递进来的富文本
    61. const myValue = ref(props.value)
    62. const tinymceId = ref('vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + ''))
    63. // 定义一个对象 init初始化
    64. const init = reactive({
    65. selector: '#' + tinymceId.value, // 富文本编辑器的id,
    66. language_url: '/tinymce/langs/zh_CN.js', // 语言包的路径,具体路径看自己的项目,文档后面附上中文js文件
    67. language: 'zh_CN', // 语言
    68. skin_url: '/tinymce/skins/ui/oxide', // skin路径,具体路径看自己的项目
    69. width: '100%', // 编辑器宽度
    70. height: props.height, // 编辑器高度
    71. branding: false, // 是否禁用“Powered by TinyMCE”
    72. menubar: false, // 顶部菜单栏显示
    73. statusbar: false, // 底部状态栏显示
    74. automatic_uploads: true, // 图片自动上传
    75. image_dimensions: false, // 去除宽高属性
    76. plugins: props.plugins, // 这里的数据是在props里面就定义好了的
    77. toolbar: props.toolbar, // 这里的数据是在props里面就定义好了的
    78. menu: {
    79. edit: { title: '编辑', items: 'undo redo selectall' },
    80. insert: { title: '插入', items: 'image kityformula-editor | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents | insertdatetime' },
    81. format: { title: '格式', items: 'bold italic underline strikethrough | formats | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | blockquote subscript superscript removeformat' },
    82. tools: { title: '工具', items: 'spellchecker spellcheckerlanguage | a11ycheck code wordcount' },
    83. table: { title: '表格', items: 'inserttable | cell row column | advtablesort | tableprops deletetable' }
    84. },
    85. font_formats: 'Arial=arial,helvetica,sans-serif; 宋体=SimSun; 微软雅黑=Microsoft Yahei; Impact=impact,chicago;', // 字体
    86. fontsize_formats: '11px 12px 14px 16px 18px 24px 36px 48px 64px 72px', // 文字大小
    87. paste_convert_word_fake_lists: false, // 插入word文档需要该属性
    88. paste_webkit_styles: 'all',
    89. paste_merge_formats: true,
    90. nonbreaking_force_tab: false,
    91. paste_auto_cleanup_on_paste: false,
    92. file_picker_types: 'file',
    93. content_css: '/tinymce/skins/content/default/content.css', // 以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入
    94. // 图片上传
    95. images_upload_handler: (blobInfo) => new Promise((resolve, reject) => {
    96. if (blobInfo.blob().size / 1024 / 1024 > 2) {
    97. reject({ message: '上传失败,图片大小请控制在 2M 以内', remove: true })
    98. } else {
    99. const ph = process.env.VUE_APP_BASE_API
    100. const params = new FormData()
    101. params.append('file', blobInfo.blob())
    102. params.append('fwjjmc', 'public')
    103. params.append('wjjmc', 'fuwenben')
    104. const config = {
    105. headers: {
    106. 'Content-Type': 'multipart/form-data',
    107. accesscode: $cookies.get('accesscode')
    108. }
    109. }
    110. if (blobInfo.blob().name) {
    111. axios.post(`${process.env.VUE_APP_BASE_API}/api/v2/sys/wenjian/upload`, params, config).then(res => {
    112. if (res.data.code === 200) {
    113. resolve(ph + res.data.data.lj + res.data.data.mc) // 上传成功,在成功函数里填入图片路径
    114. } else {
    115. reject('上传失败' + res.data.message)
    116. }
    117. }).catch(() => {})
    118. } else {
    119. const patt = /<img[^>]+src=['"]([^'"]+)['"]+/g
    120. const result = []
    121. let bytes = ''
    122. let temp
    123. while ((temp = patt.exec(myValue.value)) !== null) {
    124. result.unshift({ url: temp[1] })
    125. }
    126. result.forEach(element => {
    127. if (element.url.indexOf('data:image/png;base64,') > -1) {
    128. bytes = element.url
    129. }
    130. })
    131. axios.post(`${process.env.VUE_APP_BASE_API}/api/v2/sys/wenjian/upload/gongshi`, { bytes: bytes, fwjjmc: 'public', wjjmc: 'fuwenben' }, config).then(res => {
    132. if (res.data.code === 200) {
    133. resolve(ph + res.data.data.lj + res.data.data.mc) // 上传成功,在成功函数里填入图片路径
    134. } else {
    135. reject('上传失败' + res.data.message)
    136. }
    137. }).catch(() => {})
    138. }
    139. }
    140. }),
    141. // 文件上传
    142. file_picker_callback: (callback, value, meta) => {
    143. // 文件分类
    144. const filetype = '.pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4'
    145. // 后端接收上传文件的地址
    146. const upurl = `${process.env.VUE_APP_BASE_API}/file/v2/upload?accesscode=${$cookies.get('accesscode')}`
    147. // 模拟出一个input用于添加本地文件
    148. const input = document.createElement('input')
    149. input.setAttribute('type', 'file')
    150. input.setAttribute('accept', filetype)
    151. input.click()
    152. input.onchange = function () {
    153. const file = this.files[0]
    154. const xhr = new XMLHttpRequest()
    155. xhr.withCredentials = false
    156. xhr.open('POST', upurl)
    157. xhr.onload = function () {
    158. if (xhr.status !== 200) {
    159. failure('HTTP Error: ' + xhr.status)
    160. return
    161. }
    162. const json = JSON.parse(xhr.responseText)
    163. if (!json || typeof json.data.showPath !== 'string') {
    164. failure('Invalid JSON: ' + xhr.responseText)
    165. return
    166. }
    167. const showPath = process.env.VUE_APP_BASE_API + json.data.showPath
    168. callback(showPath)
    169. }
    170. const formData = new FormData()
    171. formData.append('file', file, file.name)
    172. formData.append('fwjjmc', 'public')
    173. formData.append('wjjmc', 'fuwenben')
    174. xhr.send(formData)
    175. }
    176. }
    177. })
    178. function dataURLtoBlob (dataurl) {
    179. const arr = dataurl.split(','); const mime = arr[0].match(/:(.*?);/)[1]
    180. const bstr = atob(arr[1]); let n = bstr.length; const u8arr = new Uint8Array(n)
    181. while (n--) {
    182. u8arr[n] = bstr.charCodeAt(n)
    183. }
    184. return new Blob([u8arr], { type: mime })
    185. }
    186. function downloadFile (url, name) {
    187. const a = document.createElement('a') // 新建一个a链接
    188. a.setAttribute('href', url) // a链接的url为图片的url
    189. a.setAttribute('download', name)
    190. a.setAttribute('target', '_blank')
    191. const clickEvent = document.createEvent('MouseEvents')
    192. clickEvent.initEvent('click', true, true)
    193. a.dispatchEvent(clickEvent)
    194. }
    195. // 73so.com
    196. function downloadFileByBase64 (base64, name) {
    197. const myBlob = dataURLtoBlob(base64)
    198. const myUrl = URL.createObjectURL(myBlob) // 创建图片的临时url
    199. downloadFile(myUrl, name)
    200. }
    201. // 监听外部传递进来的的数据变化
    202. watch(
    203. () => props.value,
    204. () => {
    205. myValue.value = props.value
    206. emits('getContent', myValue.value)
    207. }
    208. )
    209. // 监听富文本中的数据变化
    210. watch(
    211. () => myValue.value,
    212. () => {
    213. emits('getContent', myValue.value)
    214. }
    215. )
    216. // 在onMounted中初始化编辑器
    217. onMounted(() => {
    218. tinymce.init({})
    219. })
    220. </script>

  • 相关阅读:
    爬虫代理请求转换selenium添加带有账密的socks5代理
    如何正确使用Vertx操作Redis(3.9.4带源码分析)
    三显智能氮气柜温度、湿度和氧含量控制介绍
    Oralce笔记-解决Oracle18c中ORA-28001: 口令已经失效
    HBase学习笔记(1)—— 知识点总结
    SAP-QM-采购过程模式与特性检验不匹配QD244
    Asp-Net-Core开发笔记:实现动态审计日志功能
    【Nginx】Nginx 鉴权 (htpasswd) + Nginx 文件下载配置
    深入探索RTPS/DDS协议:为嵌入式设备打造的可移植C++实现指南
    前后端分离项目,vue+uni-app+php+mysql订座预约小程序系统设计与实现
  • 原文地址:https://blog.csdn.net/u013486889/article/details/133140160