feat(api): 新增通用上传文件方法

- 添加 uploadFileWidthSn 和 uploadFileWidthSnForAnswer 方法用于上传文件
- 实现 getOssInfo 方法获取 OSS 信息
- 添加 cosUpload3D 和 cosUpload 方法上传文件到 COS- 实现 docuHousecosUpload 方法上传文件到阿里云和文档库
- 优化文件命名和错误处理
This commit is contained in:
陈昱达
2025-03-16 14:43:55 +08:00
parent 6f3c89e15e
commit d015da898d
15 changed files with 462 additions and 119 deletions

View File

@@ -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>