|
@@ -1,8 +1,46 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div id="editor" />
|
|
|
|
|
- <!-- eslint-disable-next-line -->
|
|
|
|
|
- <!-- <div id="content-editor-view" class="view-box" v-html="modelValue"/> -->
|
|
|
|
|
- <Loading v-if="loading" />
|
|
|
|
|
|
|
+ <div class="editor-container">
|
|
|
|
|
+ <div id="editor" />
|
|
|
|
|
+ <!-- eslint-disable-next-line -->
|
|
|
|
|
+ <!-- <div id="content-editor-view" class="view-box" v-html="modelValue"/> -->
|
|
|
|
|
+ <Loading v-if="loading" />
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 图片调整模态框 -->
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-if="showImageModal" class="image-resize-modal" :style="{
|
|
|
|
|
+ top: modalPosition.top + 'px',
|
|
|
|
|
+ left: modalPosition.left + 'px'
|
|
|
|
|
+ }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="modal-content">
|
|
|
|
|
+ <!-- 一行显示:缩放控制、宽度、高度 -->
|
|
|
|
|
+ <div class="controls-row">
|
|
|
|
|
+ <div class="zoom-controls">
|
|
|
|
|
+ <button :disabled="currentZoom <= minZoom" class="zoom-btn" title="缩小 (10%)" @click="zoomOut">
|
|
|
|
|
+ <span>-</span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <span class="zoom-display">{{ currentZoom && Math.round(currentZoom * 100) }}%</span>
|
|
|
|
|
+ <button :disabled="currentZoom >= maxZoom" class="zoom-btn" title="放大 (10%)" @click="zoomIn">
|
|
|
|
|
+ <span>+</span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="size-input-group">
|
|
|
|
|
+ <label>宽度:</label>
|
|
|
|
|
+ <input v-model="imageSize.width" type="number" @input="updateImageSize" @keydown.enter.prevent />
|
|
|
|
|
+ <span>px</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="size-input-group">
|
|
|
|
|
+ <label>高度:</label>
|
|
|
|
|
+ <input v-model="imageSize.height" type="number" @input="updateImageSize" @keydown.enter.prevent />
|
|
|
|
|
+ <span>px</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <button class="close-btn" title="关闭" @click="closeImageModal">×</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
@@ -24,10 +62,6 @@ export default {
|
|
|
type: String,
|
|
type: String,
|
|
|
default: '',
|
|
default: '',
|
|
|
},
|
|
},
|
|
|
- traceId: {
|
|
|
|
|
- type: String,
|
|
|
|
|
- default: null,
|
|
|
|
|
- },
|
|
|
|
|
},
|
|
},
|
|
|
emits: ['update:modelValue'],
|
|
emits: ['update:modelValue'],
|
|
|
|
|
|
|
@@ -35,6 +69,16 @@ export default {
|
|
|
return {
|
|
return {
|
|
|
loading: false,
|
|
loading: false,
|
|
|
traceConfigDto: {},
|
|
traceConfigDto: {},
|
|
|
|
|
+ // 图片调整模态框相关数据
|
|
|
|
|
+ showImageModal: false,
|
|
|
|
|
+ currentImage: null,
|
|
|
|
|
+ originalImageSize: { width: 0, height: 0 },
|
|
|
|
|
+ imageSize: { width: 0, height: 0 },
|
|
|
|
|
+ modalPosition: { top: 0, left: 0 },
|
|
|
|
|
+ currentZoom: 1,
|
|
|
|
|
+ minZoom: 0.1, // 最小10%
|
|
|
|
|
+ maxZoom: 5.0, // 最大500%
|
|
|
|
|
+ contentChangeTimer: null, // 添加防抖定时器
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
|
|
|
|
@@ -185,6 +229,8 @@ export default {
|
|
|
const html = $('#editor').trumbowyg('html');
|
|
const html = $('#editor').trumbowyg('html');
|
|
|
const newHtml = html.replace(`[图片上传中${tempId}]`, `<img src="${imagUrl}" alt="图片" />`);
|
|
const newHtml = html.replace(`[图片上传中${tempId}]`, `<img src="${imagUrl}" alt="图片" />`);
|
|
|
$('#editor').trumbowyg('html', newHtml);
|
|
$('#editor').trumbowyg('html', newHtml);
|
|
|
|
|
+ // 将光标定位到图片后面
|
|
|
|
|
+ _self.setCursorPosition(imagUrl);
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -211,12 +257,42 @@ export default {
|
|
|
$('#editor').on('tbwchange', () => {
|
|
$('#editor').on('tbwchange', () => {
|
|
|
const html = $('#editor').trumbowyg('html');
|
|
const html = $('#editor').trumbowyg('html');
|
|
|
this.$emit('update:modelValue', html);
|
|
this.$emit('update:modelValue', html);
|
|
|
|
|
+
|
|
|
|
|
+ // 检查当前选中的图片是否还存在,如果不存在则关闭模态框
|
|
|
|
|
+ this.checkImageExistence();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 通过 Trumbowyg 事件系统监听编辑器变化
|
|
|
|
|
+ $('#editor').on('tbwchange tbwinit', () => {
|
|
|
|
|
+ // 每次内容变化后重新绑定图片点击事件
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.bindImageClickEvents();
|
|
|
|
|
+ }, 100);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 点击其他地方关闭模态框,同时作为图片点击的备用监听器
|
|
|
|
|
+ $(document).on('click', e => {
|
|
|
|
|
+ // 如果点击的是编辑器内的图片,显示调整模态框
|
|
|
|
|
+ if (e.target.tagName === 'IMG' && $(e.target).closest('#editor').length) {
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ e.stopPropagation();
|
|
|
|
|
+ this.showImageResizeModal(e.target);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果点击的不是模态框或图片,关闭模态框
|
|
|
|
|
+ if (!$(e.target).closest('.image-resize-modal, .trumbowyg-editor img, #editor img').length) {
|
|
|
|
|
+ this.closeImageModal();
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// Common.downloadByA();
|
|
// Common.downloadByA();
|
|
|
this.getTraceConfig();
|
|
this.getTraceConfig();
|
|
|
},
|
|
},
|
|
|
unmounted() {
|
|
unmounted() {
|
|
|
|
|
+ if (this.contentChangeTimer) {
|
|
|
|
|
+ clearTimeout(this.contentChangeTimer);
|
|
|
|
|
+ }
|
|
|
$('#editor').trumbowyg('destroy');
|
|
$('#editor').trumbowyg('destroy');
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
@@ -228,7 +304,6 @@ export default {
|
|
|
|
|
|
|
|
// 上传图片
|
|
// 上传图片
|
|
|
async uploadImg(file, insertFn) {
|
|
async uploadImg(file, insertFn) {
|
|
|
- console.log(file);
|
|
|
|
|
const _self = this;
|
|
const _self = this;
|
|
|
const formData = new FormData();
|
|
const formData = new FormData();
|
|
|
try {
|
|
try {
|
|
@@ -329,13 +404,340 @@ export default {
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
},
|
|
},
|
|
|
- },
|
|
|
|
|
|
|
+ // 获取图片缩放配置
|
|
|
|
|
+ showImageResizeModal(imgElement) {
|
|
|
|
|
+ this.currentImage = imgElement;
|
|
|
|
|
+
|
|
|
|
|
+ // 获取图片设置的样式尺寸(用户实际设置的值,不受容器限制影响)
|
|
|
|
|
+ const setWidth = parseInt(imgElement.style.width) || imgElement.naturalWidth;
|
|
|
|
|
+ const setHeight = parseInt(imgElement.style.height) || imgElement.naturalHeight;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有设置样式,使用计算样式作为备用
|
|
|
|
|
+ const computedStyle = window.getComputedStyle(imgElement);
|
|
|
|
|
+ const currentWidth = setWidth || parseInt(computedStyle.width) || imgElement.naturalWidth;
|
|
|
|
|
+ const currentHeight = setHeight || parseInt(computedStyle.height) || imgElement.naturalHeight;
|
|
|
|
|
+
|
|
|
|
|
+ // 保存原始尺寸(如果是第一次点击)
|
|
|
|
|
+ if (!imgElement.dataset.originalWidth) {
|
|
|
|
|
+ imgElement.dataset.originalWidth = imgElement.naturalWidth;
|
|
|
|
|
+ imgElement.dataset.originalHeight = imgElement.naturalHeight;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.originalImageSize = {
|
|
|
|
|
+ width: parseInt(imgElement.dataset.originalWidth),
|
|
|
|
|
+ height: parseInt(imgElement.dataset.originalHeight),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ this.imageSize = {
|
|
|
|
|
+ width: currentWidth,
|
|
|
|
|
+ height: currentHeight,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 计算当前缩放比例
|
|
|
|
|
+ if (this.originalImageSize.width == 0) {
|
|
|
|
|
+ this.currentZoom = 1;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.currentZoom = currentWidth / this.originalImageSize.width;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算模态框位置
|
|
|
|
|
+ this.calculateModalPosition(imgElement);
|
|
|
|
|
+
|
|
|
|
|
+ this.showImageModal = true;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 计算模态框位置的独立方法
|
|
|
|
|
+ calculateModalPosition(imgElement) {
|
|
|
|
|
+ const imgRect = imgElement.getBoundingClientRect();
|
|
|
|
|
+ const editorContainer = document.querySelector('.editor-container');
|
|
|
|
|
+ const containerRect = editorContainer.getBoundingClientRect();
|
|
|
|
|
+
|
|
|
|
|
+ // 获取Trumbowyg编辑器的box容器(包含工具栏和编辑区域)
|
|
|
|
|
+ const trumbowygBox = document.querySelector('.trumbowyg-box');
|
|
|
|
|
+ const trumbowygBoxRect = trumbowygBox ? trumbowygBox.getBoundingClientRect() : null;
|
|
|
|
|
+
|
|
|
|
|
+ // 模态框宽度
|
|
|
|
|
+ const modalWidth = 420;
|
|
|
|
|
+
|
|
|
|
|
+ // 编辑器固定高度
|
|
|
|
|
+ const editorHeight = 368;
|
|
|
|
|
+
|
|
|
|
|
+ let top, left;
|
|
|
|
|
+
|
|
|
|
|
+ // 判断是否需要将模态框显示在编辑器下方
|
|
|
|
|
+ const shouldShowBelowEditor = this.shouldShowModalBelowEditor(imgRect, trumbowygBoxRect, editorHeight);
|
|
|
|
|
+
|
|
|
|
|
+ if (shouldShowBelowEditor) {
|
|
|
|
|
+ // 模态框显示在编辑器容器的正下方
|
|
|
|
|
+ if (trumbowygBoxRect) {
|
|
|
|
|
+ top = trumbowygBoxRect.bottom - containerRect.top + 5;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 备用方案:使用固定位置
|
|
|
|
|
+ top = editorHeight + 40; // 工具栏高度约40px
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 水平位置:居中显示在编辑器下方
|
|
|
|
|
+ const centerX = trumbowygBoxRect ? (trumbowygBoxRect.left + trumbowygBoxRect.right) / 2 : containerRect.left + containerRect.width / 2;
|
|
|
|
|
+ left = centerX - containerRect.left - modalWidth * 2;
|
|
|
|
|
+
|
|
|
|
|
+ // 确保不超出容器边界
|
|
|
|
|
+ left = Math.max(0, Math.min(left, containerRect.width - modalWidth));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 小图片且在可视区域内:模态框显示在图片正下方
|
|
|
|
|
+ top = imgRect.bottom - containerRect.top + 5;
|
|
|
|
|
+ left = imgRect.left - containerRect.left;
|
|
|
|
|
+
|
|
|
|
|
+ // 确保不超出容器边界
|
|
|
|
|
+ left = Math.max(0, Math.min(left, containerRect.width - modalWidth));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.modalPosition = { top, left };
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 判断是否应该将模态框显示在编辑器下方
|
|
|
|
|
+ shouldShowModalBelowEditor(imgRect, trumbowygBoxRect, editorHeight) {
|
|
|
|
|
+ // 情况1:图片本身高度超过编辑器高度
|
|
|
|
|
+ if (this.imageSize.height > editorHeight) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 情况2:图片底部超出了编辑器的可视区域
|
|
|
|
|
+ if (trumbowygBoxRect) {
|
|
|
|
|
+ // 获取编辑器内容区域的底部位置(不包括工具栏)
|
|
|
|
|
+ const editorContentBottom = trumbowygBoxRect.bottom;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果图片底部超出了编辑器内容区域,说明图片有部分在滚动区域下方
|
|
|
|
|
+ if (imgRect.bottom > editorContentBottom) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 情况3:图片顶部在编辑器可视区域上方(图片被向上滚动了)
|
|
|
|
|
+ if (trumbowygBoxRect) {
|
|
|
|
|
+ // 获取编辑器内容区域的顶部位置(工具栏下方)
|
|
|
|
|
+ const trumbowyg = $('#editor').data('trumbowyg');
|
|
|
|
|
+ const toolbarHeight = trumbowyg && trumbowyg.$btnPane ? trumbowyg.$btnPane.height() : 40;
|
|
|
|
|
+ const editorContentTop = trumbowygBoxRect.top + toolbarHeight;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果图片顶部在编辑器内容区域上方,说明图片被部分滚动到上方了
|
|
|
|
|
+ if (imgRect.top < editorContentTop) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return false;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 关闭图片模态框
|
|
|
|
|
+ closeImageModal() {
|
|
|
|
|
+ this.showImageModal = false;
|
|
|
|
|
+ this.currentImage = null;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 更新图片尺寸
|
|
|
|
|
+ updateImageSize() {
|
|
|
|
|
+ if (!this.currentImage) return;
|
|
|
|
|
+
|
|
|
|
|
+ const width = parseInt(this.imageSize.width);
|
|
|
|
|
+ const height = parseInt(this.imageSize.height);
|
|
|
|
|
+
|
|
|
|
|
+ if (width > 0 && height > 0) {
|
|
|
|
|
+ this.currentImage.style.width = width + 'px';
|
|
|
|
|
+ this.currentImage.style.height = height + 'px';
|
|
|
|
|
+
|
|
|
|
|
+ // 更新缩放比例
|
|
|
|
|
+ this.currentZoom = width / this.originalImageSize.width;
|
|
|
|
|
+
|
|
|
|
|
+ // 强制同步编辑器内容
|
|
|
|
|
+ this.syncEditorContent();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 放大
|
|
|
|
|
+ zoomIn() {
|
|
|
|
|
+ if (this.currentZoom >= this.maxZoom) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.currentZoom = Math.min(this.currentZoom + 0.1, this.maxZoom);
|
|
|
|
|
+ this.applyZoom();
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 缩小
|
|
|
|
|
+ zoomOut() {
|
|
|
|
|
+ if (this.currentZoom <= this.minZoom) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.currentZoom = Math.max(this.currentZoom - 0.1, this.minZoom);
|
|
|
|
|
+ this.applyZoom();
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 触发缩放事件
|
|
|
|
|
+ applyZoom() {
|
|
|
|
|
+ if (!this.currentImage) return;
|
|
|
|
|
+
|
|
|
|
|
+ const newWidth = Math.round(this.originalImageSize.width * this.currentZoom);
|
|
|
|
|
+ const newHeight = Math.round(this.originalImageSize.height * this.currentZoom);
|
|
|
|
|
+
|
|
|
|
|
+ // 直接设置模态框显示的尺寸(用户设置的目标尺寸)
|
|
|
|
|
+ this.imageSize.width = newWidth;
|
|
|
|
|
+ this.imageSize.height = newHeight;
|
|
|
|
|
+
|
|
|
|
|
+ // 设置图片的样式尺寸
|
|
|
|
|
+ this.currentImage.style.width = newWidth + 'px';
|
|
|
|
|
+ this.currentImage.style.height = newHeight + 'px';
|
|
|
|
|
+
|
|
|
|
|
+ // 强制同步编辑器内容
|
|
|
|
|
+ this.syncEditorContent();
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 更新模态框中显示的尺寸值(获取图片设置的样式尺寸)
|
|
|
|
|
+ updateDisplayedSize() {
|
|
|
|
|
+ if (!this.currentImage) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 获取图片设置的样式尺寸(用户实际设置的值)
|
|
|
|
|
+ const setWidth = parseInt(this.currentImage.style.width) || this.currentImage.naturalWidth;
|
|
|
|
|
+ const setHeight = parseInt(this.currentImage.style.height) || this.currentImage.naturalHeight;
|
|
|
|
|
+
|
|
|
|
|
+ // 更新模态框中显示的尺寸值(显示用户设置的值,不是被限制后的显示值)
|
|
|
|
|
+ this.imageSize = {
|
|
|
|
|
+ width: setWidth,
|
|
|
|
|
+ height: setHeight,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 更新缩放比例(基于用户设置的尺寸)
|
|
|
|
|
+ if (this.originalImageSize.width > 0) {
|
|
|
|
|
+ this.currentZoom = setWidth / this.originalImageSize.width;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ // 同步编辑器内容
|
|
|
|
|
+ syncEditorContent() {
|
|
|
|
|
+ const trumbowyg = $('#editor').data('trumbowyg');
|
|
|
|
|
+ if (trumbowyg) {
|
|
|
|
|
+ // 强制同步DOM到HTML
|
|
|
|
|
+ trumbowyg.syncCode();
|
|
|
|
|
+
|
|
|
|
|
+ // 延迟触发内容变化事件
|
|
|
|
|
+ this.debouncedTriggerContentChange();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ // 防抖的内容变化触发器
|
|
|
|
|
+ debouncedTriggerContentChange() {
|
|
|
|
|
+ if (this.contentChangeTimer) {
|
|
|
|
|
+ clearTimeout(this.contentChangeTimer);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.contentChangeTimer = setTimeout(() => {
|
|
|
|
|
+ this.triggerContentChange();
|
|
|
|
|
+ }, 300); // 300ms 防抖
|
|
|
|
|
+ },
|
|
|
|
|
+ // 触发编辑器内容变化事件
|
|
|
|
|
+ triggerContentChange() {
|
|
|
|
|
+ const html = $('#editor').trumbowyg('html');
|
|
|
|
|
+ this.$emit('update:modelValue', html);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 给所有图片绑定点击事件
|
|
|
|
|
+ bindImageClickEvents() {
|
|
|
|
|
+ const _self = this;
|
|
|
|
|
+ const trumbowyg = $('#editor').data('trumbowyg');
|
|
|
|
|
+
|
|
|
|
|
+ if (!trumbowyg || !trumbowyg.$ed) {
|
|
|
|
|
+ console.log('bindImageClickEvents: Trumbowyg 编辑器未初始化');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查找所有图片
|
|
|
|
|
+ const images = trumbowyg.$ed.find('img');
|
|
|
|
|
+
|
|
|
|
|
+ // 先移除所有已有的图片点击事件,避免重复绑定
|
|
|
|
|
+ images.off('click.imageResize');
|
|
|
|
|
+
|
|
|
|
|
+ // 为每个图片添加点击事件
|
|
|
|
|
+ images.each(function () {
|
|
|
|
|
+ const img = $(this);
|
|
|
|
|
+
|
|
|
|
|
+ img.on('click.imageResize', function (e) {
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ e.stopPropagation();
|
|
|
|
|
+ _self.showImageResizeModal(this);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 粘贴后光标位于图片后
|
|
|
|
|
+ setCursorPosition(imagUrl) {
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const $editor = $('#editor');
|
|
|
|
|
+ const editorElement = $editor.data('trumbowyg').$ed[0];
|
|
|
|
|
+
|
|
|
|
|
+ // 找到刚插入的图片元素
|
|
|
|
|
+ const imgElements = editorElement.querySelectorAll(`img[src="${imagUrl}"]`);
|
|
|
|
|
+ const imgElement = imgElements[imgElements.length - 1]; // 获取最后一个(最新插入的)
|
|
|
|
|
+
|
|
|
|
|
+ if (imgElement) {
|
|
|
|
|
+ const range = document.createRange();
|
|
|
|
|
+ const selection = window.getSelection();
|
|
|
|
|
+
|
|
|
|
|
+ // 直接将光标定位在图片元素后面
|
|
|
|
|
+ range.setStartAfter(imgElement);
|
|
|
|
|
+ range.setEndAfter(imgElement);
|
|
|
|
|
+
|
|
|
|
|
+ selection.removeAllRanges();
|
|
|
|
|
+ selection.addRange(range);
|
|
|
|
|
+
|
|
|
|
|
+ // 确保编辑器获得焦点
|
|
|
|
|
+ editorElement.focus();
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('设置光标位置失败:', error);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 100); // 延迟确保DOM更新完成
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 检查当前选中的图片是否还存在于编辑器中
|
|
|
|
|
+ checkImageExistence() {
|
|
|
|
|
+ // 如果没有显示模态框或没有当前图片,直接返回
|
|
|
|
|
+ if (!this.showImageModal || !this.currentImage) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const trumbowyg = $('#editor').data('trumbowyg');
|
|
|
|
|
+ if (!trumbowyg || !trumbowyg.$ed) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ // 获取编辑器中所有的图片元素
|
|
|
|
|
+ const editorImages = trumbowyg.$ed.find('img');
|
|
|
|
|
|
|
|
|
|
+ // 检查当前选中的图片是否还在编辑器中
|
|
|
|
|
+ let imageExists = false;
|
|
|
|
|
+ editorImages.each((_, img) => {
|
|
|
|
|
+ if (img === this.currentImage) {
|
|
|
|
|
+ imageExists = true;
|
|
|
|
|
+ return false; // 跳出each循环
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 如果图片不存在了,关闭模态框
|
|
|
|
|
+ if (!imageExists) {
|
|
|
|
|
+ console.log('检测到图片已被删除,关闭模态框');
|
|
|
|
|
+ this.closeImageModal();
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('检查图片存在性时出错:', error);
|
|
|
|
|
+ // 出错时也关闭模态框,避免显示无效的模态框
|
|
|
|
|
+ this.closeImageModal();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
};
|
|
};
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style>
|
|
<style>
|
|
|
|
|
+.editor-container {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.trumbowyg-box .trumbowyg-editor {
|
|
.trumbowyg-box .trumbowyg-editor {
|
|
|
height: 368px !important;
|
|
height: 368px !important;
|
|
|
}
|
|
}
|
|
@@ -354,4 +756,123 @@ export default {
|
|
|
min-height: 200px;
|
|
min-height: 200px;
|
|
|
padding: 8px;
|
|
padding: 8px;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+/* 图片调整模态框样式 */
|
|
|
|
|
+.image-resize-modal {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ z-index: 1000;
|
|
|
|
|
+ background: white;
|
|
|
|
|
+ border: 1px solid #ddd;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ min-width: 420px;
|
|
|
|
|
+ max-width: 500px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.modal-content {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ padding: 4px 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.controls-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ margin: 12px;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.size-input-group {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.size-input-group label {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ min-width: 32px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.size-input-group input {
|
|
|
|
|
+ width: 60px;
|
|
|
|
|
+ padding: 4px 6px;
|
|
|
|
|
+ border: 1px solid #ddd;
|
|
|
|
|
+ border-radius: 3px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.size-input-group span {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.zoom-controls {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.zoom-btn {
|
|
|
|
|
+ width: 28px;
|
|
|
|
|
+ height: 28px;
|
|
|
|
|
+ border: 1px solid #ddd;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ background: white;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ transition: all 0.2s;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.zoom-btn:hover:not(:disabled) {
|
|
|
|
|
+ background: #f5f5f5;
|
|
|
|
|
+ border-color: #999;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.zoom-btn:disabled {
|
|
|
|
|
+ opacity: 0.5;
|
|
|
|
|
+ cursor: not-allowed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.zoom-display {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ min-width: 40px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.close-btn {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 4px;
|
|
|
|
|
+ right: 4px;
|
|
|
|
|
+ width: 20px;
|
|
|
|
|
+ height: 20px;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ background: none;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ font-size: 24px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ border-radius: 2px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.close-btn:hover {
|
|
|
|
|
+ background: #f5f5f5;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 图片选中状态 */
|
|
|
|
|
+.trumbowyg-editor img:hover {
|
|
|
|
|
+ outline: 2px solid #007bff;
|
|
|
|
|
+ outline-offset: 2px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|