TinyMCE-----word表格图片进阶版

1、文件大小 1M 前置检查:在解析 Hex 阶段就进行大小判断,防止超大字符串导致浏览器卡死。

2、UI 体验:增加 400px 滚动高度,并确保任务"全部完事"后才提示手动关闭。

3、我是国际化 可以自己修改代码

javascript 复制代码
data:{
	uploadNotifications: {},
	totalUploadImages: 0,
	uploadedImageCount: 0,
	uploadNotificationInstance: null, // 全局通知实例
}

mounted 添加样式

javascript 复制代码
       const style = document.createElement('style');
        style.innerHTML = `
    @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
    @keyframes slideIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
    .status-spinning { display: inline-block; animation: spin 1s linear infinite; }
    .upload-item-animate { animation: slideIn 0.3s ease-out forwards; }
    .el-progress-bar__inner { transition: width 0.4s ease-in-out !important; }
`;
        document.head.appendChild(style);

tinymce paste 方法改造

javascript 复制代码
 ed.on('paste', async (event) => {
                        const rtf = event.clipboardData.getData('text/rtf');
                        if (rtf && rtf.includes('\\pict')) {
                            const extracted = extractHexImagesFromRTF(rtf);
                            _this.totalUploadImages = extracted.length; // 设置总数
                            _this.uploadedImageCount = 0; // 重置已上传数

                            if (_this.totalUploadImages === 0) return;

                            // 初始化主通知框
                            _this.updateUploadProgressNotification(0, 'init'); // 这里的参数只是为了触发通知框的创建

                            const uploadPromises = extracted.map(async (img, i) => {
                                const isTooLarge = img.hex.length > 2 * 1024 * 1024; // 1MB 限制

                                _this.updateUploadProgressNotification(i, 'processing'); // 更新单张图片状态

                                if (isTooLarge) {
                                    _this.updateUploadProgressNotification(i, 'tooLarge');
                                    return; // 跳过此图片
                                }

                                try {
                                    const base64 = _this.hexToBase64Sync(img.hex, img.mimeType);
                                    _this.updateUploadProgressNotification(i, 'uploading'); // 更新为上传中
                                    await _this.uploadSingleImage(base64, i);
                                    _this.updateUploadProgressNotification(i, 'success'); // 上传成功
                                } catch (err) {
                                    console.error('Upload failed:', err);
                                    _this.updateUploadProgressNotification(i, 'fail', err.message || 'Network error'); // 上传失败
                                }
                            });

                            await Promise.all(uploadPromises);
                            // Promise.all 完成后, updateUploadProgressNotification 会自动计算 totalCount === uploadedCount 并关闭
                        }
                    });
javascript 复制代码
updateUploadProgressNotification(imgIndex, status = 'processing', message = '') {
            // 快捷调用 $t
            const t = (key, params) => this.$t(`imageTask.${key}`, params);

            if (!this.uploadNotificationInstance) {
                this.uploadNotificationInstance = this.$notify({
                    title: t('title'),
                    dangerouslyUseHTMLString: true,
                    message: `
                <div id="image-upload-container" style="width: 300px;">
                    <p id="total-status-text" style="margin: 0 0 12px 0; font-size: 14px; color: #606266; font-weight: 500;">
                        ${t('preparing', { total: this.totalUploadImages })}
                    </p>
                    
                    <div class="el-progress el-progress--line" style="margin-bottom: 15px;">
                        <div class="el-progress-bar">
                            <div class="el-progress-bar__outer" style="height: 10px; background: #ebeef5; border-radius: 5px;">
                                <div class="el-progress-bar__inner" style="width: 0%; background: #409EFF; transition: width 0.4s ease-out;"></div>
                            </div>
                        </div>
                        <div class="el-progress__text" style="font-size: 12px; font-weight: bold; margin-top: 2px;">0%</div>
                    </div>

                    <ul id="image-individual-status" style="max-height: 400px; overflow-y: auto; padding: 0; margin: 0; list-style: none; border-top: 1px solid #f0f0f0; padding-top: 10px;"></ul>

                    <p id="manual-close-tip" style="display:none; margin-top:15px; font-size:12px; color:#909399; text-align:right; border-top: 1px dashed #eee; padding-top: 8px;">
                        ${t('manualClose')}
                    </p>
                </div>`,
                    duration: 0,
                    position: 'bottom-left',
                    onClose: () => {
                        this.uploadNotificationInstance = null;
                        this.uploadedImageCount = 0;
                        this.totalUploadImages = 0;
                    }
                });
            }

            const listContainer = document.getElementById('image-individual-status');
            if (!listContainer) return;

            let itemEl = document.getElementById(`img-status-${imgIndex}`);
            if (!itemEl) {
                itemEl = document.createElement('li');
                itemEl.id = `img-status-${imgIndex}`;
                itemEl.className = 'upload-item-animate';
                itemEl.style.cssText =
                    'font-size: 13px; padding: 8px 5px; display: flex; align-items: center; border-bottom: 1px solid #fafafa;';
                itemEl.innerHTML = `
            <span style="width: 55px; color: #909399; font-weight: bold;">${t('imgLabel')} ${imgIndex + 1}:</span>
            <span class="status-icon" style="margin: 0 10px;"></span>
            <span class="status-text" style="flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #666;">...</span>
        `;
                listContainer.appendChild(itemEl);
                listContainer.scrollTop = listContainer.scrollHeight;
            }

            const statusIcon = itemEl.querySelector('.status-icon');
            const statusText = itemEl.querySelector('.status-text');

            let isFinished = false;
            if (statusIcon && statusText) {
                switch (status) {
                    case 'processing':
                        statusIcon.innerHTML = '<span class="status-spinning">🔄</span>';
                        statusText.innerText = t('parsing');
                        break;
                    case 'uploading':
                        statusIcon.innerHTML = '<span class="status-spinning">⬆️</span>';
                        statusText.innerText = t('uploading');
                        break;
                    case 'tooLarge':
                        statusIcon.innerHTML = '⚠️';
                        statusText.innerText = t('tooLarge');
                        itemEl.style.color = '#E6A23C';
                        isFinished = true;
                        break;
                    case 'success':
                        statusIcon.innerHTML = '✅';
                        statusText.innerText = t('success');
                        itemEl.style.color = '#67C23A';
                        isFinished = true;
                        break;
                    case 'fail':
                        statusIcon.innerHTML = '❌';
                        statusText.innerText = t('error', { msg: message });
                        itemEl.style.color = '#F56C6C';
                        isFinished = true;
                        break;
                }
            }

            if (isFinished) {
                this.uploadedImageCount++;

                const progressPercent = Math.min(100, Math.round((this.uploadedImageCount / this.totalUploadImages) * 100));
                const barInner = this.uploadNotificationInstance.$el.querySelector('.el-progress-bar__inner');
                const barText = this.uploadNotificationInstance.$el.querySelector('.el-progress__text');
                const statusTotalText = document.getElementById('total-status-text');

                if (barInner) barInner.style.width = `${progressPercent}%`;
                if (barText) barText.innerText = `${progressPercent}%`;
                if (statusTotalText) {
                    statusTotalText.innerText = t('progress', { current: this.uploadedImageCount, total: this.totalUploadImages });
                }

                if (this.uploadedImageCount >= this.totalUploadImages) {
                    // 更新标题为完成状态
                    const titleEl = this.uploadNotificationInstance.$el.querySelector('.el-notification__title');
                    if (titleEl) titleEl.innerText = t('completed');

                    if (barInner) barInner.style.background = '#67C23A';

                    const tipEl = document.getElementById('manual-close-tip');
                    if (tipEl) tipEl.style.display = 'block';

                    if (statusTotalText) {
                        statusTotalText.innerText = t('allDone', { total: this.totalUploadImages });
                    }
                }
            }
        },
相关推荐
黎雁·泠崖2 小时前
Java面向对象:对象数组进阶实战
java·开发语言
%xiao Q2 小时前
GESP C++四级-216
java·开发语言·c++
西红市杰出青年2 小时前
Python异步----------信号量
开发语言·python
Ama_tor2 小时前
obsidian进阶の插件系列|Templater从小白到菜鸟
javascript·markdown·插件·obsidian
wuhen_n3 小时前
初识TypeScript
javascript·typescript
a程序小傲3 小时前
蚂蚁Java面试被问:向量数据库的相似度搜索和索引构建
开发语言·后端·python·架构·flask·fastapi
w***76553 小时前
JS vs jQuery:核心差异解析
开发语言·javascript·jquery
黎雁·泠崖3 小时前
Java面向对象:购物车系统完整版+全系列考点复盘
java·开发语言
初次见面我叫泰隆3 小时前
Qt——2、信号和槽
开发语言·c++·qt