|
|
@@ -1,149 +1,315 @@
|
|
|
<template>
|
|
|
<tiny-editor v-model="content" :init="init" :key="componentKey" id="textarea"></tiny-editor>
|
|
|
- </template>
|
|
|
-
|
|
|
- <script>
|
|
|
- import editor from '@tinymce/tinymce-vue';
|
|
|
- import url from '@/utils/baseUrl';
|
|
|
-
|
|
|
- export default {
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import editor from '@tinymce/tinymce-vue';
|
|
|
+import url from '@/utils/baseUrl';
|
|
|
+
|
|
|
+export default {
|
|
|
name: 'myEditor',
|
|
|
props: {
|
|
|
- value: {
|
|
|
- type: String,
|
|
|
- default: ''
|
|
|
- }
|
|
|
+ value: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ }
|
|
|
},
|
|
|
data() {
|
|
|
- return {
|
|
|
- componentKey: 0,
|
|
|
- content: '',
|
|
|
- init: {
|
|
|
- selector: '#textarea',
|
|
|
- height: 550,
|
|
|
- language_url: 'js/tinymce/langs/zh_CN.js',
|
|
|
- language: 'zh_CN',
|
|
|
- plugins: 'paste image axupimgs code lists advlist table anchor autosave charmap fullscreen hr insertdatetime link preview print searchreplace wordcount indent2em',
|
|
|
- toolbar: [
|
|
|
- "undo redo | styleselect | fontselect | fontsizeselect | bold italic underline strikethrough subscript superscript charmap | indent2em | alignleft aligncenter alignright alignjustify | lineheight | outdent indent",
|
|
|
- "forecolor backcolor | link | removeformat | image axupimgs | table | searchreplace cut copy paste | hr blockquote anchor | bullist numlist | insertdatetime | restoredraft | code | fullscreen preview print wordcount"
|
|
|
- ],
|
|
|
- font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;宋体=simsun,serif',
|
|
|
- fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 42px 48px',
|
|
|
- branding: false,
|
|
|
- paste_data_images: true,
|
|
|
- paste_postprocess: (editor, args) => {
|
|
|
- const images = args.node.querySelectorAll('img[src^="data:image/"]');
|
|
|
- if (images.length > 0) {
|
|
|
- Array.from(images).forEach((img) => {
|
|
|
- const blob = this.dataURLtoBlob(img.src);
|
|
|
- this.uploadImageFromBlob(blob,
|
|
|
- (imageUrl) => {
|
|
|
- editor.dom.setAttrib(img, 'src', imageUrl);
|
|
|
- editor.dom.setAttrib(img, 'alt', '粘贴的图片');
|
|
|
- },
|
|
|
- (error) => {
|
|
|
- console.error('图片上传失败:', error);
|
|
|
- editor.dom.remove(img);
|
|
|
- }
|
|
|
- );
|
|
|
- });
|
|
|
+ return {
|
|
|
+ componentKey: 0,
|
|
|
+ content: '',
|
|
|
+ init: {
|
|
|
+ selector: '#textarea',
|
|
|
+ height: 550,
|
|
|
+ language_url: 'js/tinymce/langs/zh_CN.js',
|
|
|
+ language: 'zh_CN',
|
|
|
+ plugins: 'paste link image axupimgs code lists advlist table anchor autosave charmap fullscreen hr insertdatetime link preview print searchreplace wordcount indent2em',
|
|
|
+ toolbar: [
|
|
|
+ "undo redo | styleselect | fontselect | fontsizeselect | bold italic underline strikethrough subscript superscript charmap | indent2em | alignleft aligncenter alignright alignjustify | lineheight | outdent indent",
|
|
|
+ "forecolor backcolor | link | removeformat | image axupimgs | table | searchreplace cut copy paste | hr blockquote anchor | bullist numlist | insertdatetime | restoredraft | code | fullscreen preview print wordcount"
|
|
|
+ ],
|
|
|
+ font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;宋体=simsun,serif',
|
|
|
+ fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 42px 48px',
|
|
|
+ branding: false,
|
|
|
+ paste_data_images: true,
|
|
|
+ paste_postprocess: (editor, args) => {
|
|
|
+ const images = args.node.querySelectorAll('img[src^="data:image/"]');
|
|
|
+ if (images.length > 0) {
|
|
|
+ Array.from(images).forEach((img) => {
|
|
|
+ const blob = this.dataURLtoBlob(img.src);
|
|
|
+ this.uploadImageFromBlob(blob,
|
|
|
+ (imageUrl) => {
|
|
|
+ editor.dom.setAttrib(img, 'src', imageUrl);
|
|
|
+ editor.dom.setAttrib(img, 'alt', '粘贴的图片');
|
|
|
+ },
|
|
|
+ (error) => {
|
|
|
+ console.error('图片上传失败:', error);
|
|
|
+ editor.dom.remove(img);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ images_upload_handler: (blobInfo, success, failure) => {
|
|
|
+ const xhr = new XMLHttpRequest();
|
|
|
+ xhr.withCredentials = false;
|
|
|
+ xhr.open('POST', url.baseUrl + '/public/uploadFile');
|
|
|
+ xhr.onload = () => {
|
|
|
+ if (xhr.status < 200 || xhr.status >= 300) {
|
|
|
+ failure('HTTP Error: ' + xhr.status);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const json = JSON.parse(xhr.responseText);
|
|
|
+ if (json.code != 200) {
|
|
|
+ failure('图片上传失败! ' + json.message);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ success(json.data.imgUrl);
|
|
|
+
|
|
|
+ const editor = tinymce.activeEditor;
|
|
|
+ const nodeChangeHandler = (e) => {
|
|
|
+ const imgElm = e.element;
|
|
|
+ if (imgElm.nodeName === 'IMG' && imgElm.src === json.data.imgUrl) {
|
|
|
+ editor.dom.setAttrib(imgElm, 'alt', json.data.oldName || '全国政务信息一体化应用平台');
|
|
|
+ editor.off('NodeChange', nodeChangeHandler);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ editor.on('NodeChange', nodeChangeHandler);
|
|
|
+ };
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append('file', blobInfo.blob());
|
|
|
+ xhr.send(formData);
|
|
|
+ },
|
|
|
+ //上传文件
|
|
|
+ file_picker_callback: (cb, value, meta) => {
|
|
|
+ console.log(this);
|
|
|
+ const input = document.createElement('input');
|
|
|
+ input.setAttribute('type', 'file');
|
|
|
+ // 设置允许的文件格式
|
|
|
+ input.setAttribute('accept', '.pdf,.doc,.docx,.jpg,.jpeg,.png,.gif,.zip,.rar,.mp3,.mp4,.ppt,.xls');
|
|
|
+
|
|
|
+ input.addEventListener('change', (e) => {
|
|
|
+ const file = e.target.files[0];
|
|
|
+ if (!file) return;
|
|
|
+ // 检查文件格式
|
|
|
+ const allowedExtensions = ['.pdf', '.doc', '.docx','.png', '.jpg', '.jpeg', '.gif', '.mp3','.mp4', '.zip', '.rar', '.ppt','.xls'];
|
|
|
+ const fileName = file.name.toLowerCase();
|
|
|
+ const fileExtension = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
|
|
|
+ const isValidFile = allowedExtensions.includes(fileExtension);
|
|
|
+
|
|
|
+ if (!isValidFile) {
|
|
|
+ this.$message({
|
|
|
+ type: 'error',
|
|
|
+ message: '只允许上传PDF、Word、Excel、PPT、图片、音频、视频和压缩格式的文件!'
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示上传中提示
|
|
|
+ const loadingMessage = this.$message({
|
|
|
+ type: 'info',
|
|
|
+ message: '文件上传中...',
|
|
|
+ duration: 0
|
|
|
+ });
|
|
|
+
|
|
|
+ // 创建FormData并上传
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append('file', file);
|
|
|
+
|
|
|
+ // 调用您的上传方法
|
|
|
+ this.$store.dispatch('pool/uploadFile', formData).then(res => {
|
|
|
+ console.log('上传结果:', res);
|
|
|
+ if (res.code === 200 && res.data && res.data.imgUrl) {
|
|
|
+ const fileUrl = res.data.imgUrl;
|
|
|
+ const originalFileName = file.name;
|
|
|
+
|
|
|
+ // 关闭上传中提示
|
|
|
+ loadingMessage.close();
|
|
|
+
|
|
|
+ this.$message({
|
|
|
+ type: 'success',
|
|
|
+ message: '文件上传成功!请在链接对话框中设置标题',
|
|
|
+ duration: 3000
|
|
|
+ });
|
|
|
+
|
|
|
+ // 定义文件类型对应的图标
|
|
|
+ const iconMap = {
|
|
|
+ '.doc': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285836397142.png',
|
|
|
+ '.docx': 'http://192.168.1.234:19000/pre/image/png/20260113/1768290036895691.png',
|
|
|
+ '.jpg': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285943518814.png',
|
|
|
+ '.jpeg': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285943518814.png',
|
|
|
+ '.mp3': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285970917457.png',
|
|
|
+ '.mp4': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285980356293.png',
|
|
|
+ '.pdf': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285992141969.png',
|
|
|
+ '.png': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286002939847.png',
|
|
|
+ '.gif': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286002939847.png',
|
|
|
+ '.ppt': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286013209864.png',
|
|
|
+ '.xls': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286021736124.png',
|
|
|
+ '.zip': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286028878150.png',
|
|
|
+ '.rar': 'http://192.168.1.234:19000/pre/image/png/20260113/1768287422984578.png'
|
|
|
+ };
|
|
|
+
|
|
|
+ // 获取对应图标
|
|
|
+ const iconUrl = iconMap[fileExtension] || 'http://192.168.1.234:19000/pre/image/png/20260113/1768285943518814.png';
|
|
|
+
|
|
|
+ // 使用原文件名作为默认标题
|
|
|
+ const defaultTitle = originalFileName;
|
|
|
+
|
|
|
+ // 调用回调函数,将文件URL传递给链接对话框
|
|
|
+ // 用户可以在对话框中编辑链接文本后再插入
|
|
|
+ cb(fileUrl, {
|
|
|
+ title: defaultTitle,
|
|
|
+ text: defaultTitle, // 默认显示原文件名
|
|
|
+ href: fileUrl,
|
|
|
+ // 可以传递一些额外数据用于后续处理
|
|
|
+ metadata: {
|
|
|
+ fileUrl: fileUrl,
|
|
|
+ iconUrl: iconUrl,
|
|
|
+ fileName: originalFileName,
|
|
|
+ fileExtension: fileExtension
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ } else {
|
|
|
+ loadingMessage.close();
|
|
|
+ console.error('上传失败:', res);
|
|
|
+ this.$message({
|
|
|
+ type: 'error',
|
|
|
+ message: '文件上传失败:' + (res.message || '未知错误')
|
|
|
+ });
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ loadingMessage.close();
|
|
|
+ console.error('上传错误:', error);
|
|
|
+ this.$message({
|
|
|
+ type: 'error',
|
|
|
+ message: '网络错误,请重试!'
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ input.click();
|
|
|
+ },
|
|
|
+ setup: (editor) => {
|
|
|
+ this.editorInstance = editor;
|
|
|
+ editor.setContent(this.value);
|
|
|
+ editor.on('change', () => {
|
|
|
+ this.$emit('input', editor.getContent());
|
|
|
+ });
|
|
|
+ // 添加自定义文件上传按钮
|
|
|
+ editor.ui.registry.addButton('attachment', {
|
|
|
+ icon: 'attachment',
|
|
|
+ tooltip: '插入文件链接',
|
|
|
+ onAction: () => {
|
|
|
+ // 打开链接对话框,但填充文件上传功能
|
|
|
+ editor.execCommand('mceLink');
|
|
|
+
|
|
|
+ // 或者直接打开文件上传
|
|
|
+ editor.execCommand('mceLink', false, {
|
|
|
+ title: '上传文件',
|
|
|
+ filetype: 'file'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 监听链接插入事件
|
|
|
+ editor.on('SetContent', (e) => {
|
|
|
+ // 延迟执行,确保链接已插入
|
|
|
+ setTimeout(() => {
|
|
|
+ // 查找新插入的链接(可能需要更精确的检测逻辑)
|
|
|
+ const links = editor.getBody().querySelectorAll('a[href]');
|
|
|
+ links.forEach(link => {
|
|
|
+ // 检查是否是需要添加图标的文件链接
|
|
|
+ const href = link.getAttribute('href');
|
|
|
+ if (href && !link.querySelector('img')) {
|
|
|
+ // 这里可以根据href判断是否是文件链接
|
|
|
+ // 例如检查URL是否包含特定路径或文件扩展名
|
|
|
+ const fileExtensions = ['.pdf', '.doc', '.docx', '.jpg', '.jpeg', '.png', '.gif', '.mp3', '.mp4', '.zip', '.rar', '.ppt', '.xls'];
|
|
|
+ const isFileLink = fileExtensions.some(ext => href.toLowerCase().endsWith(ext));
|
|
|
+
|
|
|
+ if (isFileLink) {
|
|
|
+ const fileName = link.textContent || '文件';
|
|
|
+ const fileExtension = href.substring(href.lastIndexOf('.')).toLowerCase();
|
|
|
+
|
|
|
+ // 定义文件类型对应的图标
|
|
|
+ const iconMap = {
|
|
|
+ '.doc': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285836397142.png',
|
|
|
+ '.docx': 'http://192.168.1.234:19000/pre/image/png/20260113/1768290036895691.png',
|
|
|
+ '.jpg': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285943518814.png',
|
|
|
+ '.jpeg': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285943518814.png',
|
|
|
+ '.mp3': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285970917457.png',
|
|
|
+ '.mp4': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285980356293.png',
|
|
|
+ '.pdf': 'http://192.168.1.234:19000/pre/image/png/20260113/1768285992141969.png',
|
|
|
+ '.png': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286002939847.png',
|
|
|
+ '.gif': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286002939847.png',
|
|
|
+ '.ppt': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286013209864.png',
|
|
|
+ '.xls': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286021736124.png',
|
|
|
+ '.zip': 'http://192.168.1.234:19000/pre/image/png/20260113/1768286028878150.png',
|
|
|
+ '.rar': 'http://192.168.1.234:19000/pre/image/png/20260113/1768287422984578.png'
|
|
|
+ };
|
|
|
+
|
|
|
+ const iconUrl = iconMap[fileExtension] || iconMap['.jpg'];
|
|
|
+
|
|
|
+ // 创建带图标的链接
|
|
|
+ const iconHtml = `<img src="${iconUrl}" style="height: 30px; vertical-align: middle; margin-right: 5px;" alt="${fileExtension}图标"/>`;
|
|
|
+ link.innerHTML = iconHtml + fileName;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }, 100);
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
- },
|
|
|
- images_upload_handler: (blobInfo, success, failure) => {
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ dataURLtoBlob(dataURL) {
|
|
|
+ const arr = dataURL.split(',');
|
|
|
+ const mime = arr[0].match(/:(.*?);/)[1];
|
|
|
+ const bstr = atob(arr[1]);
|
|
|
+ let n = bstr.length;
|
|
|
+ const u8arr = new Uint8Array(n);
|
|
|
+ while (n--) {
|
|
|
+ u8arr[n] = bstr.charCodeAt(n);
|
|
|
+ }
|
|
|
+ return new Blob([u8arr], { type: mime });
|
|
|
+ },
|
|
|
+ //上传图片
|
|
|
+ uploadImageFromBlob(blob, success, failure) {
|
|
|
const xhr = new XMLHttpRequest();
|
|
|
xhr.withCredentials = false;
|
|
|
xhr.open('POST', url.baseUrl + '/public/uploadFile');
|
|
|
xhr.onload = () => {
|
|
|
- if (xhr.status < 200 || xhr.status >= 300) {
|
|
|
- failure('HTTP Error: ' + xhr.status);
|
|
|
- return;
|
|
|
- }
|
|
|
- const json = JSON.parse(xhr.responseText);
|
|
|
- if (json.code != 200) {
|
|
|
- failure('图片上传失败! ' + json.message);
|
|
|
- return;
|
|
|
- }
|
|
|
- success(json.data.imgUrl);
|
|
|
-
|
|
|
- const editor = tinymce.activeEditor;
|
|
|
- const nodeChangeHandler = (e) => {
|
|
|
- const imgElm = e.element;
|
|
|
- if (imgElm.nodeName === 'IMG' && imgElm.src === json.data.imgUrl) {
|
|
|
- editor.dom.setAttrib(imgElm, 'alt', json.data.oldName || '全国政务信息一体化应用平台');
|
|
|
- editor.off('NodeChange', nodeChangeHandler);
|
|
|
+ if (xhr.status < 200 || xhr.status >= 300) {
|
|
|
+ failure('HTTP Error: ' + xhr.status);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const json = JSON.parse(xhr.responseText);
|
|
|
+ if (json.code != 200) {
|
|
|
+ failure('图片上传失败! ' + json.message);
|
|
|
+ return;
|
|
|
}
|
|
|
- };
|
|
|
- editor.on('NodeChange', nodeChangeHandler);
|
|
|
+ success(json.data.imgUrl);
|
|
|
};
|
|
|
const formData = new FormData();
|
|
|
- formData.append('file', blobInfo.blob());
|
|
|
+ formData.append('file', blob);
|
|
|
xhr.send(formData);
|
|
|
- },
|
|
|
- setup: (editor) => {
|
|
|
- this.editorInstance = editor;
|
|
|
- editor.setContent(this.value);
|
|
|
- editor.on('change', () => {
|
|
|
- this.$emit('input', editor.getContent());
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
- },
|
|
|
- methods: {
|
|
|
- dataURLtoBlob(dataURL) {
|
|
|
- const arr = dataURL.split(',');
|
|
|
- const mime = arr[0].match(/:(.*?);/)[1];
|
|
|
- const bstr = atob(arr[1]);
|
|
|
- let n = bstr.length;
|
|
|
- const u8arr = new Uint8Array(n);
|
|
|
- while(n--) {
|
|
|
- u8arr[n] = bstr.charCodeAt(n);
|
|
|
- }
|
|
|
- return new Blob([u8arr], {type: mime});
|
|
|
- },
|
|
|
-
|
|
|
- uploadImageFromBlob(blob, success, failure) {
|
|
|
- const xhr = new XMLHttpRequest();
|
|
|
- xhr.withCredentials = false;
|
|
|
- xhr.open('POST', url.baseUrl + '/public/uploadFile');
|
|
|
-
|
|
|
- xhr.onload = () => {
|
|
|
- if (xhr.status < 200 || xhr.status >= 300) {
|
|
|
- failure('HTTP Error: ' + xhr.status);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const json = JSON.parse(xhr.responseText);
|
|
|
- if (json.code != 200) {
|
|
|
- failure('图片上传失败! ' + json.message);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- success(json.data.imgUrl);
|
|
|
- };
|
|
|
-
|
|
|
- const formData = new FormData();
|
|
|
- formData.append('file', blob);
|
|
|
- xhr.send(formData);
|
|
|
- },
|
|
|
-
|
|
|
- reinitEditor() {
|
|
|
- this.componentKey += 1;
|
|
|
- }
|
|
|
+ },
|
|
|
+ reinitEditor() {
|
|
|
+ this.componentKey += 1;
|
|
|
+ },
|
|
|
},
|
|
|
watch: {
|
|
|
- value(newVal) {
|
|
|
- if (this.editorInstance && newVal !== this.editorInstance.getContent()) {
|
|
|
- this.content = newVal;
|
|
|
+ value(newVal) {
|
|
|
+ if (this.editorInstance && newVal !== this.editorInstance.getContent()) {
|
|
|
+ this.content = newVal;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
},
|
|
|
components: {
|
|
|
- 'tiny-editor': editor,
|
|
|
+ 'tiny-editor': editor,
|
|
|
},
|
|
|
mounted() {
|
|
|
- this.reinitEditor();
|
|
|
- },
|
|
|
- }
|
|
|
- </script>
|
|
|
+ this.reinitEditor();
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|