feat(api): 新增通用上传文件方法
- 添加 uploadFileWidthSn 和 uploadFileWidthSnForAnswer 方法用于上传文件 - 实现 getOssInfo 方法获取 OSS 信息 - 添加 cosUpload3D 和 cosUpload 方法上传文件到 COS- 实现 docuHousecosUpload 方法上传文件到阿里云和文档库 - 优化文件命名和错误处理
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<div class="flex contenteditable align-center space-between" :class="className">
|
||||
<p
|
||||
:id="'editor' + id"
|
||||
ref="editor"
|
||||
:contenteditable="active"
|
||||
class="van-field contenteditable-content"
|
||||
@focus="onFocus"
|
||||
v-html="modelValue"
|
||||
></p>
|
||||
<div class="right-icon ml10">
|
||||
@@ -13,13 +15,15 @@
|
||||
|
||||
<div v-if="showAction && active" ref="editorAction" class="editor-action">
|
||||
<button v-for="item in actions" :key="item.name" @click="funEvent(item, $event)">
|
||||
{{ item.label }}
|
||||
<van-icon class-prefix="mobilefont" :name="item.icon"></van-icon>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineEmits, ref, onMounted, watch } from 'vue';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import CommonApi from '@/api/common.js';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -36,6 +40,8 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
const id = ref(uuidv4());
|
||||
|
||||
const editor = ref(null);
|
||||
const editorAction = ref(null);
|
||||
const emit = defineEmits(['update:modelValue', 'blur']);
|
||||
@@ -43,8 +49,9 @@ const save = (e) => {
|
||||
emit('update:modelValue', e.innerHTML);
|
||||
};
|
||||
|
||||
const savedRange = ref(null);
|
||||
|
||||
const functions = {
|
||||
// todo 点击按钮之后 如何判断 才能让按钮再次点击 不消失 获取重新聚焦 再次选中文本?
|
||||
boldModern: () => {
|
||||
document.execCommand('bold', true, null);
|
||||
},
|
||||
@@ -53,13 +60,45 @@ const functions = {
|
||||
},
|
||||
italic: () => {
|
||||
document.execCommand('italic', false, null);
|
||||
},
|
||||
|
||||
uploadImage: async() => {
|
||||
// 保存当前光标位置
|
||||
savedRange.value = saveSelection();
|
||||
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
|
||||
fileInput.click();
|
||||
|
||||
fileInput.onchange = async(e) => {
|
||||
const [file] = e.target.files;
|
||||
if (!file) return;
|
||||
if (file.size > 2 * 1024 * 1024) {
|
||||
console.error('文件大小不能超过2M');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await CommonApi.cosUpload(file);
|
||||
const img = document.createElement('img');
|
||||
img.src = data.url;
|
||||
img.onload = () => {
|
||||
const scale = img.naturalHeight / 50;
|
||||
const width = (img.naturalWidth / scale).toFixed(0);
|
||||
const height = 50;
|
||||
const maxWidth = img.naturalWidth;
|
||||
const maxHeight = img.naturalHeight;
|
||||
const style = `style="width:${width}px;height:${height}px;max-width:${maxWidth}px;max-height:${maxHeight}px"`;
|
||||
insertImageAtCaret(`<img src=${data.url} ${style}/>`);
|
||||
|
||||
// 恢复光标位置
|
||||
restoreSelection(savedRange.value);
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const funEvent = (item) => {
|
||||
// 保持焦点在编辑器
|
||||
const selection = window.getSelection();
|
||||
selection.getRangeAt(0);
|
||||
functions[item.fun]();
|
||||
};
|
||||
|
||||
@@ -82,21 +121,22 @@ const actions = [
|
||||
{
|
||||
label: '加粗',
|
||||
fun: 'boldModern',
|
||||
icon: ''
|
||||
icon: 'jiacu'
|
||||
},
|
||||
{
|
||||
label: '下划线',
|
||||
fun: 'underLine',
|
||||
icon: ''
|
||||
icon: 'xiahuaxian'
|
||||
},
|
||||
{
|
||||
label: '图片上传',
|
||||
fun: 'uploadImage',
|
||||
icon: ''
|
||||
icon: 'tupian'
|
||||
},
|
||||
{
|
||||
label: '倾斜',
|
||||
fun: 'italic'
|
||||
fun: 'italic',
|
||||
icon: 'qingxie'
|
||||
}
|
||||
];
|
||||
const checkContains = (element, target) => {
|
||||
@@ -112,8 +152,6 @@ onMounted(() => {
|
||||
showAction.value = true;
|
||||
});
|
||||
|
||||
// 如果点击了 editor 与 editorAction 其他地方就触发保存
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!editor.value || !editorAction.value) return;
|
||||
|
||||
@@ -127,19 +165,58 @@ onMounted(() => {
|
||||
emit('blur', editor.value);
|
||||
}
|
||||
});
|
||||
// editor.value.addEventListener('blur', () => {
|
||||
// setTimeout(() => {
|
||||
// showAction.value = false;
|
||||
// });
|
||||
// });
|
||||
|
||||
document.addEventListener('resize', () => {
|
||||
showAction.value = false;
|
||||
});
|
||||
});
|
||||
|
||||
// onBeforeUnmount(() => {
|
||||
// editor.value.removeEventListener('resize', handleResize);
|
||||
// });
|
||||
const onFocus = (e) => {
|
||||
// 阻止
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
console.log('Editor focused');
|
||||
};
|
||||
|
||||
// 保存当前光标位置
|
||||
const saveSelection = () => {
|
||||
const selection = window.getSelection();
|
||||
if (selection.rangeCount === 0) return null;
|
||||
|
||||
return selection.getRangeAt(0);
|
||||
};
|
||||
|
||||
// 恢复光标位置
|
||||
const restoreSelection = (range) => {
|
||||
if (!range) return;
|
||||
|
||||
const selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
};
|
||||
|
||||
// 在光标位置插入图片
|
||||
const insertImageAtCaret = (html) => {
|
||||
const selection = window.getSelection();
|
||||
if (selection.rangeCount === 0) return;
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
range.deleteContents();
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
const frag = document.createDocumentFragment();
|
||||
for (let child = div.firstChild; child; child = div.firstChild) {
|
||||
frag.appendChild(child);
|
||||
}
|
||||
|
||||
range.insertNode(frag);
|
||||
range.setStartAfter(frag);
|
||||
range.collapse(true);
|
||||
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -166,7 +243,12 @@ onMounted(() => {
|
||||
& button {
|
||||
border: none;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button + button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user