diff --git a/CHANGELOG.zh_CN.md b/CHANGELOG.zh_CN.md index 8f09c97..700c81b 100644 --- a/CHANGELOG.zh_CN.md +++ b/CHANGELOG.zh_CN.md @@ -16,6 +16,7 @@ - 新增`PageWrapper`组件。并应用于示例页面 - 新增标签页折叠功能 - 兼容旧版浏览器 +- tinymce 新增图片上传· ### 🐛 Bug Fixes @@ -24,6 +25,7 @@ - 修复表格内存溢出问题 - 修复`layout` 收缩展开功能在分割模式下失效 - 修复 modal 高度计算错误 +- 修复文件上传错误 ### 🎫 Chores diff --git a/src/components/Tinymce/src/Editor.vue b/src/components/Tinymce/src/Editor.vue index 7c01c4e..a28b8a3 100644 --- a/src/components/Tinymce/src/Editor.vue +++ b/src/components/Tinymce/src/Editor.vue @@ -1,5 +1,11 @@ <template> - <div class="tinymce-container" :style="{ width: containerWidth }"> + <div :class="prefixCls" :style="{ width: containerWidth }"> + <ImgUpload + @uploading="handleImageUploading" + @done="handleDone" + v-if="showImageUpload" + v-show="editorRef" + /> <textarea :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }"></textarea> </div> </template> @@ -24,6 +30,8 @@ import { bindHandlers } from './helper'; import lineHeight from './lineHeight'; import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; + import ImgUpload from './ImgUpload.vue'; + import { useDesign } from '/@/hooks/web/useDesign'; const CDN_URL = 'https://cdn.bootcdn.net/ajax/libs/tinymce/5.5.1'; @@ -33,12 +41,15 @@ name: 'Tinymce', inheritAttrs: false, props: basicProps, + components: { ImgUpload }, emits: ['change', 'update:modelValue'], setup(props, { emit, attrs }) { const editorRef = ref<any>(null); const tinymceId = ref<string>(snowUuid('tiny-vue')); const elRef = ref<Nullable<HTMLElement>>(null); + const { prefixCls } = useDesign('tinymce-container'); + const tinymceContent = computed(() => { return props.modelValue; }); @@ -140,7 +151,7 @@ bindHandlers(e, attrs, unref(editorRef)); } - function setValue(editor: any, val: string, prevVal: string) { + function setValue(editor: Recordable, val: string, prevVal?: string) { if ( editor && typeof val === 'string' && @@ -179,45 +190,54 @@ }); } + function handleImageUploading(name: string) { + const editor = unref(editorRef); + if (!editor) return; + const content = editor?.getContent() ?? ''; + setValue(editor, `${content}\n${getImgName(name)}`); + } + + function handleDone(name: string, url: string) { + const editor = unref(editorRef); + if (!editor) return; + + const content = editor?.getContent() ?? ''; + const val = content?.replace(getImgName(name), `<img src="${url}"/>`) ?? ''; + setValue(editor, val); + } + + function getImgName(name: string) { + return `[uploading:${name}]`; + } + return { + prefixCls, containerWidth, initOptions, tinymceContent, tinymceScriptSrc, elRef, tinymceId, + handleImageUploading, + handleDone, + editorRef, }; }, }); </script> -<style lang="less" scoped> - .tinymce-container { - position: relative; - line-height: normal; +<style lang="less" scoped></style> - .mce-fullscreen { - z-index: 10000; - } - } +<style lang="less"> + @prefix-cls: ~'@{namespace}-tinymce-container'; - .editor-custom-btn-container { - position: absolute; - top: 6px; - right: 6px; + .@{prefix-cls} { + position: relative; + line-height: normal; - &.fullscreen { - position: fixed; - z-index: 10000; + textarea { + z-index: -1; + visibility: hidden; } } - - .editor-upload-btn { - display: inline-block; - } - - textarea { - z-index: -1; - visibility: hidden; - } </style> diff --git a/src/components/Tinymce/src/ImgUpload.vue b/src/components/Tinymce/src/ImgUpload.vue new file mode 100644 index 0000000..0813c7e --- /dev/null +++ b/src/components/Tinymce/src/ImgUpload.vue @@ -0,0 +1,72 @@ +<template> + <div :class="prefixCls"> + <Upload + name="file" + multiple + @change="handleChange" + :action="uploadUrl" + :showUploadList="false" + accept=".jpg,.jpeg,.gif,.png,.webp" + > + <a-button type="primary">{{ t('component.upload.imgUpload') }}</a-button> + </Upload> + </div> +</template> +<script lang="ts"> + import { defineComponent } from 'vue'; + + import { Upload } from 'ant-design-vue'; + import { InboxOutlined } from '@ant-design/icons-vue'; + import { useDesign } from '/@/hooks/web/useDesign'; + import { useGlobSetting } from '/@/hooks/setting'; + import { useI18n } from '/@/hooks/web/useI18n'; + + export default defineComponent({ + name: 'TinymceImageUpload', + components: { Upload, InboxOutlined }, + emits: ['uploading', 'done', 'error'], + setup(_, { emit }) { + let uploading = false; + + const { uploadUrl } = useGlobSetting(); + const { t } = useI18n(); + const { prefixCls } = useDesign('tinymce-img-upload'); + function handleChange(info: Recordable) { + const file = info.file; + const status = file?.status; + + const url = file?.response?.url; + const name = file?.name; + if (status === 'uploading') { + if (!uploading) { + emit('uploading', name); + uploading = true; + } + } else if (status === 'done') { + emit('done', name, url); + uploading = false; + } else if (status === 'error') { + emit('error'); + uploading = false; + } + } + + return { + prefixCls, + handleChange, + uploadUrl, + t, + }; + }, + }); +</script> +<style lang="less" scoped> + @prefix-cls: ~'@{namespace}-tinymce-img-upload'; + + .@{prefix-cls} { + position: absolute; + top: 4px; + right: 10px; + z-index: 20; + } +</style> diff --git a/src/components/Tinymce/src/props.ts b/src/components/Tinymce/src/props.ts index ca07252..4da600c 100644 --- a/src/components/Tinymce/src/props.ts +++ b/src/components/Tinymce/src/props.ts @@ -1,18 +1,13 @@ import { PropType } from 'vue'; +import { propTypes } from '/@/utils/propTypes'; export const basicProps = { options: { type: Object as PropType<any>, default: {}, }, - value: { - type: String as PropType<string>, - // default: '' - }, - modelValue: { - type: String as PropType<string>, - // default: '' - }, + value: propTypes.string, + modelValue: propTypes.string, // 高度 height: { type: [Number, String] as PropType<string | number>, @@ -26,4 +21,5 @@ export const basicProps = { required: false, default: 'auto', }, + showImageUpload: propTypes.bool.def(true), }; diff --git a/src/layouts/default/header/components/user-dropdown/index.vue b/src/layouts/default/header/components/user-dropdown/index.vue index 2b59af6..90a647f 100644 --- a/src/layouts/default/header/components/user-dropdown/index.vue +++ b/src/layouts/default/header/components/user-dropdown/index.vue @@ -9,8 +9,13 @@ <template #overlay> <Menu @click="handleMenuClick"> - <MenuItem key="doc" :text="t('layout.header.dropdownItemDoc')" icon="gg:loadbar-doc" /> - <MenuDivider v-if="getShowDoc" /> + <MenuItem + key="doc" + :text="t('layout.header.dropdownItemDoc')" + icon="gg:loadbar-doc" + v-if="getShowDoc" + /> + <MenuDivider /> <MenuItem key="loginOut" :text="t('layout.header.dropdownItemLoginOut')" diff --git a/src/layouts/default/header/index.vue b/src/layouts/default/header/index.vue index 000ab0a..dfdbdac 100644 --- a/src/layouts/default/header/index.vue +++ b/src/layouts/default/header/index.vue @@ -84,7 +84,6 @@ } from './components'; import { useAppInject } from '/@/hooks/web/useAppInject'; import { useDesign } from '/@/hooks/web/useDesign'; - import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; export default defineComponent({ name: 'LayoutHeader', diff --git a/src/locales/lang/en/component/upload.ts b/src/locales/lang/en/component/upload.ts index c9b74f6..3e07d60 100644 --- a/src/locales/lang/en/component/upload.ts +++ b/src/locales/lang/en/component/upload.ts @@ -1,6 +1,7 @@ export default { save: 'Save', upload: 'Upload', + imgUpload: 'ImageUpload', uploaded: 'Uploaded', operating: 'Operating', diff --git a/src/locales/lang/zh_CN/component/upload.ts b/src/locales/lang/zh_CN/component/upload.ts index 5912f82..b1607ea 100644 --- a/src/locales/lang/zh_CN/component/upload.ts +++ b/src/locales/lang/zh_CN/component/upload.ts @@ -1,6 +1,7 @@ export default { save: '保存', upload: '上传', + imgUpload: '图片上传', uploaded: '已上传', operating: '操作',