|
|
@ -109,8 +109,11 @@ |
|
|
|
v-model:value="content" |
|
|
|
:disabled="disabled" |
|
|
|
:init="config" |
|
|
|
placeholder="本地图片支持直接粘贴或拖拽至光标处" |
|
|
|
placeholder="支持直接粘贴或拖拽图片,也可点击工具栏图片按钮从文件库选择" |
|
|
|
/> |
|
|
|
<div class="file-selector-tip" v-if="editor == 1"> |
|
|
|
💡 提示:工具栏"图片"按钮从图片库选择,"上传"按钮快速上传图片;"视频"按钮从视频库选择,"上传视频"按钮快速上传视频 |
|
|
|
</div> |
|
|
|
<MdEditor |
|
|
|
v-if="editor == 2" |
|
|
|
v-model="content" |
|
|
@ -208,6 +211,24 @@ |
|
|
|
</a-spin> |
|
|
|
</a-form> |
|
|
|
</ele-modal> |
|
|
|
|
|
|
|
<!-- 文件库选择弹窗 --> |
|
|
|
<SelectData |
|
|
|
v-model:visible="showFileSelector" |
|
|
|
title="选择图片" |
|
|
|
type="image" |
|
|
|
class="file-selector-modal" |
|
|
|
@done="onFileSelected" |
|
|
|
/> |
|
|
|
|
|
|
|
<!-- 视频库选择弹窗 --> |
|
|
|
<SelectData |
|
|
|
v-model:visible="showVideoSelector" |
|
|
|
title="选择视频" |
|
|
|
type="video" |
|
|
|
class="file-selector-modal" |
|
|
|
@done="onVideoSelected" |
|
|
|
/> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script lang="ts" setup> |
|
|
@ -231,6 +252,7 @@ |
|
|
|
import { CmsNavigation } from '@/api/cms/cmsNavigation/model'; |
|
|
|
import SourceSelect from '@/views/cms/cmsArticle/dictionary/source-select.vue'; |
|
|
|
import { useWebsiteSettingStore } from '@/store/modules/setting'; |
|
|
|
import SelectData from '@/components/SelectFile/components/select-data.vue'; |
|
|
|
|
|
|
|
// 是否是修改 |
|
|
|
const isUpdate = ref(false); |
|
|
@ -351,42 +373,6 @@ |
|
|
|
trigger: 'blur' |
|
|
|
} |
|
|
|
], |
|
|
|
// image: [ |
|
|
|
// { |
|
|
|
// required: true, |
|
|
|
// message: '请上传图片', |
|
|
|
// type: 'string', |
|
|
|
// trigger: 'blur' |
|
|
|
// } |
|
|
|
// ], |
|
|
|
// files: [ |
|
|
|
// { |
|
|
|
// required: true, |
|
|
|
// message: '请上传轮播图', |
|
|
|
// type: 'string', |
|
|
|
// trigger: 'blur', |
|
|
|
// validator: async (_rule: RuleObject, value: string) => { |
|
|
|
// if (form.files == '') { |
|
|
|
// return Promise.reject('选择上传轮播图'); |
|
|
|
// } |
|
|
|
// return Promise.resolve(); |
|
|
|
// } |
|
|
|
// } |
|
|
|
// ], |
|
|
|
// categoryId: [ |
|
|
|
// { |
|
|
|
// required: true, |
|
|
|
// type: 'number', |
|
|
|
// message: '请选择栏目', |
|
|
|
// trigger: 'blur', |
|
|
|
// validator: async (_rule: RuleObject, value: string) => { |
|
|
|
// if (!form.categoryId) { |
|
|
|
// return Promise.reject('请选择栏目'); |
|
|
|
// } |
|
|
|
// return Promise.resolve(); |
|
|
|
// } |
|
|
|
// } |
|
|
|
// ], |
|
|
|
content: [ |
|
|
|
{ |
|
|
|
required: true, |
|
|
@ -451,22 +437,226 @@ |
|
|
|
}; |
|
|
|
|
|
|
|
const editorRef = ref<InstanceType<typeof TinymceEditor> | null>(null); |
|
|
|
|
|
|
|
// 文件库选择弹窗状态 |
|
|
|
const showFileSelector = ref(false); |
|
|
|
const fileSelectCallback = ref<((url: string) => void) | null>(null); |
|
|
|
|
|
|
|
// 视频库选择弹窗状态 |
|
|
|
const showVideoSelector = ref(false); |
|
|
|
const videoSelectCallback = ref<((url: string) => void) | null>(null); |
|
|
|
|
|
|
|
const config = ref({ |
|
|
|
height: 620, |
|
|
|
paste_data_images: true, |
|
|
|
automatic_uploads: true, |
|
|
|
|
|
|
|
// 自定义工具栏,移除默认的image和media按钮,添加自定义按钮 |
|
|
|
toolbar: [ |
|
|
|
'fullscreen preview code codesample emoticons custom_image_selector quick_upload custom_video_selector quick_video_upload', |
|
|
|
'undo redo | forecolor backcolor', |
|
|
|
'bold italic underline strikethrough', |
|
|
|
'alignleft aligncenter alignright alignjustify', |
|
|
|
'outdent indent | numlist bullist', |
|
|
|
'formatselect fontselect fontsizeselect', |
|
|
|
'link charmap anchor pagebreak | ltr rtl' |
|
|
|
].join(' | '), |
|
|
|
|
|
|
|
// 图片上传处理器 - 支持拖拽和粘贴上传 |
|
|
|
images_upload_handler: (blobInfo, success, error) => { |
|
|
|
const file = blobInfo.blob(); |
|
|
|
|
|
|
|
// 检查文件大小(限制为10MB) |
|
|
|
if (file.size > 10 * 1024 * 1024) { |
|
|
|
error('图片大小不能超过10MB'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 检查文件类型 |
|
|
|
if (!file.type.startsWith('image/')) { |
|
|
|
error('只能上传图片文件'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 显示上传进度提示 |
|
|
|
const loadingMsg = message.loading('图片上传中...', 0); |
|
|
|
|
|
|
|
uploadOss(file) |
|
|
|
.then((res) => { |
|
|
|
success(res.url); |
|
|
|
loadingMsg(); |
|
|
|
success(res.url || res.path); |
|
|
|
message.success('图片上传成功'); |
|
|
|
}) |
|
|
|
.catch((msg) => { |
|
|
|
error(msg); |
|
|
|
loadingMsg(); |
|
|
|
error(msg || '图片上传失败'); |
|
|
|
message.error('图片上传失败:' + msg); |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 图片工具栏 |
|
|
|
image_toolbar: 'alignleft aligncenter alignright | rotateleft rotateright | imageoptions', |
|
|
|
|
|
|
|
// 图片标题 |
|
|
|
image_title: true, |
|
|
|
|
|
|
|
// 图片描述 |
|
|
|
image_description: true, |
|
|
|
|
|
|
|
// 图片尺寸 |
|
|
|
image_dimensions: true, |
|
|
|
|
|
|
|
// 图片类别 |
|
|
|
image_class_list: [ |
|
|
|
{ title: '无样式', value: '' }, |
|
|
|
{ title: '响应式图片', value: 'img-responsive' }, |
|
|
|
{ title: '圆角图片', value: 'img-rounded' }, |
|
|
|
{ title: '圆形图片', value: 'img-circle' } |
|
|
|
], |
|
|
|
|
|
|
|
// 自定义按钮设置 |
|
|
|
setup: (editor: any) => { |
|
|
|
// 添加自定义图片选择按钮 |
|
|
|
editor.ui.registry.addButton('custom_image_selector', { |
|
|
|
text: '图片', |
|
|
|
icon: 'image', |
|
|
|
tooltip: '插入图片(从文件库选择或上传)', |
|
|
|
onAction: () => { |
|
|
|
// 打开文件库选择弹窗 |
|
|
|
fileSelectCallback.value = (url: string) => { |
|
|
|
editor.insertContent(`<img src="${url}" alt="图片" style="max-width: 100%;" />`); |
|
|
|
}; |
|
|
|
showFileSelector.value = true; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 添加快速上传按钮 |
|
|
|
editor.ui.registry.addButton('quick_upload', { |
|
|
|
text: '上传', |
|
|
|
icon: 'upload', |
|
|
|
tooltip: '快速上传图片', |
|
|
|
onAction: () => { |
|
|
|
const input = document.createElement('input'); |
|
|
|
input.type = 'file'; |
|
|
|
input.accept = 'image/*'; |
|
|
|
input.onchange = (e: any) => { |
|
|
|
const file = e.target.files[0]; |
|
|
|
if (file) { |
|
|
|
// 检查文件大小 |
|
|
|
if (file.size > 10 * 1024 * 1024) { |
|
|
|
message.error('图片大小不能超过10MB'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const loadingMsg = message.loading('图片上传中...', 0); |
|
|
|
uploadOss(file) |
|
|
|
.then((res) => { |
|
|
|
loadingMsg(); |
|
|
|
const imageUrl = res.url || res.path; |
|
|
|
editor.insertContent(`<img src="${imageUrl}" alt="${file.name}" style="max-width: 100%;" />`); |
|
|
|
message.success('图片上传成功'); |
|
|
|
}) |
|
|
|
.catch((msg) => { |
|
|
|
loadingMsg(); |
|
|
|
message.error('图片上传失败:' + msg); |
|
|
|
}); |
|
|
|
} |
|
|
|
}; |
|
|
|
input.click(); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 添加自定义视频选择按钮 |
|
|
|
editor.ui.registry.addButton('custom_video_selector', { |
|
|
|
text: '视频', |
|
|
|
icon: 'embed', |
|
|
|
tooltip: '插入视频(从视频库选择)', |
|
|
|
onAction: () => { |
|
|
|
// 打开视频库选择弹窗 |
|
|
|
videoSelectCallback.value = (url: string) => { |
|
|
|
// 插入视频标签,使用HTML5 video元素 |
|
|
|
editor.insertContent(` |
|
|
|
<video controls style="max-width: 100%; height: auto;"> |
|
|
|
<source src="${url}" type="video/mp4"> |
|
|
|
您的浏览器不支持视频播放。 |
|
|
|
</video> |
|
|
|
`); |
|
|
|
}; |
|
|
|
showVideoSelector.value = true; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 添加快速视频上传按钮 |
|
|
|
editor.ui.registry.addButton('quick_video_upload', { |
|
|
|
text: '上传视频', |
|
|
|
icon: 'upload', |
|
|
|
tooltip: '快速上传视频', |
|
|
|
onAction: () => { |
|
|
|
const input = document.createElement('input'); |
|
|
|
input.type = 'file'; |
|
|
|
input.accept = 'video/*'; |
|
|
|
input.onchange = (e: any) => { |
|
|
|
const file = e.target.files[0]; |
|
|
|
if (file) { |
|
|
|
// 检查文件大小(限制为100MB) |
|
|
|
if (file.size > 100 * 1024 * 1024) { |
|
|
|
message.error('视频大小不能超过100MB'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const loadingMsg = message.loading('视频上传中...', 0); |
|
|
|
uploadOss(file) |
|
|
|
.then((res) => { |
|
|
|
loadingMsg(); |
|
|
|
const videoUrl = res.path || res.downloadUrl; |
|
|
|
editor.insertContent(` |
|
|
|
<video controls style="max-width: 100%; height: auto;"> |
|
|
|
<source src="${videoUrl}" type="video/mp4"> |
|
|
|
您的浏览器不支持视频播放。 |
|
|
|
</video> |
|
|
|
`); |
|
|
|
message.success('视频上传成功'); |
|
|
|
}) |
|
|
|
.catch((msg) => { |
|
|
|
loadingMsg(); |
|
|
|
message.error('视频上传失败:' + msg); |
|
|
|
}); |
|
|
|
} |
|
|
|
}; |
|
|
|
input.click(); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 从文件库选择图片的回调 |
|
|
|
const onFileSelected = (data: FileRecord) => { |
|
|
|
if (fileSelectCallback.value) { |
|
|
|
// 使用文件的完整URL,确保有值 |
|
|
|
const imageUrl = data.url || data.path || ''; |
|
|
|
if (imageUrl) { |
|
|
|
fileSelectCallback.value(imageUrl); |
|
|
|
message.success('图片插入成功'); |
|
|
|
} |
|
|
|
fileSelectCallback.value = null; |
|
|
|
} |
|
|
|
showFileSelector.value = false; |
|
|
|
}; |
|
|
|
|
|
|
|
// 从视频库选择视频的回调 |
|
|
|
const onVideoSelected = (data: FileRecord) => { |
|
|
|
if (videoSelectCallback.value) { |
|
|
|
// 使用文件的完整URL,确保有值 |
|
|
|
const videoUrl = data.path || data.downloadUrl || ''; |
|
|
|
if (videoUrl) { |
|
|
|
videoSelectCallback.value(videoUrl); |
|
|
|
message.success('视频插入成功'); |
|
|
|
} |
|
|
|
videoSelectCallback.value = null; |
|
|
|
} |
|
|
|
showVideoSelector.value = false; |
|
|
|
}; |
|
|
|
|
|
|
|
const { resetFields } = useForm(form, rules); |
|
|
|
|
|
|
|
/* 保存编辑 */ |
|
|
@ -585,3 +775,50 @@ |
|
|
|
{ immediate: true } |
|
|
|
); |
|
|
|
</script> |
|
|
|
|
|
|
|
<style lang="less" scoped> |
|
|
|
.editor-content { |
|
|
|
:deep(.tox-tinymce) { |
|
|
|
border-radius: 6px; |
|
|
|
} |
|
|
|
|
|
|
|
// 编辑器内图片样式 |
|
|
|
:deep(.mce-content-body) { |
|
|
|
img { |
|
|
|
max-width: 100%; |
|
|
|
height: auto; |
|
|
|
|
|
|
|
&.img-responsive { |
|
|
|
width: 100%; |
|
|
|
height: auto; |
|
|
|
} |
|
|
|
|
|
|
|
&.img-rounded { |
|
|
|
border-radius: 8px; |
|
|
|
} |
|
|
|
|
|
|
|
&.img-circle { |
|
|
|
border-radius: 50%; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 文件选择提示 |
|
|
|
.file-selector-tip { |
|
|
|
color: #666; |
|
|
|
font-size: 12px; |
|
|
|
margin-top: 4px; |
|
|
|
} |
|
|
|
|
|
|
|
// 文件选择弹窗样式 |
|
|
|
:deep(.file-selector-modal) { |
|
|
|
.ant-modal { |
|
|
|
z-index: 10000 !important; |
|
|
|
} |
|
|
|
|
|
|
|
.ant-modal-mask { |
|
|
|
z-index: 9999 !important; |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |
|
|
|