From 059892a0ac18e8c5739b43e02411e4d7dc9cbfdd Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Tue, 13 Jan 2026 17:57:15 +0800
Subject: [PATCH] fix(file-upload): 修复文件上传组件的交互问题

---
 src/main/resources/templates/febs/views/modules/ai/fileUpload/index.html |  687 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 687 insertions(+), 0 deletions(-)

diff --git a/src/main/resources/templates/febs/views/modules/ai/fileUpload/index.html b/src/main/resources/templates/febs/views/modules/ai/fileUpload/index.html
new file mode 100644
index 0000000..613b626
--- /dev/null
+++ b/src/main/resources/templates/febs/views/modules/ai/fileUpload/index.html
@@ -0,0 +1,687 @@
+<div class="layui-fluid layui-anim febs-anim" id="febs-aiCompany-Info" lay-title="编辑">
+    <div class="layui-row febs-container">
+        <div class="layui-col-md12">
+            <div class="layui-fluid" id="aiCompany-info">
+                <form class="layui-form" action="" lay-filter="aiCompany-info-form">
+                    <div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief">
+                        <ul class="layui-tab-title">
+                            <li class="layui-this">基础信息</li>
+                        </ul>
+                        <div class="layui-tab-content">
+                            <div class="upload-container">
+                                <div class="upload-header">
+                                    <h2>大文件上传系统</h2>
+                                    <p>支持分片上传,最大支持10GB文件</p>
+                                </div>
+
+                                <!-- 上传区域 -->
+                                <div class="upload-area" id="uploadArea">
+                                    <i class="layui-icon layui-icon-upload-drag upload-icon"></i>
+                                    <div class="upload-text">
+                                        <p>点击或拖拽文件到此处上传</p>
+                                        <small>支持视频文件上传</small>
+                                    </div>
+                                </div>
+
+                                <!-- 文件信息 -->
+                                <div class="file-info" style="display: none;" id="fileInfo">
+                                    <div class="layui-row">
+                                        <div class="layui-col-xs6">
+                                            <p><strong>文件名:</strong><span id="fileName"></span></p>
+                                            <p><strong>文件大小:</strong><span id="fileSize"></span></p>
+                                            <p><strong>分片数量:</strong><span id="chunkCount"></span></p>
+                                        </div>
+                                        <div class="layui-col-xs6">
+                                            <p><strong>上传进度:</strong><span id="uploadProgress">0%</span></p>
+                                            <p><strong>状态:</strong><span id="uploadStatus">等待上传</span></p>
+                                            <p><strong>速度:</strong><span id="uploadSpeed">0KB/s</span></p>
+                                        </div>
+                                    </div>
+                                </div>
+
+                                <!-- 进度条 -->
+                                <div class="progress-container" style="display: none;" id="progressContainer">
+                                    <div class="progress-bar">
+                                        <div class="progress-fill" id="progressFill">0%</div>
+                                    </div>
+                                </div>
+
+                                <!-- 操作按钮 -->
+                                <div class="btn-group" id="btnGroup">
+                                    <button class="layui-btn layui-btn-normal" id="startUpload">开始上传</button>
+                                    <button class="layui-btn layui-btn-danger" id="cancelUpload">取消上传</button>
+                                </div>
+
+                                <!-- 播放区域 -->
+                                <div class="play-container" id="playContainer">
+                                    <h3 style="margin-bottom: 20px; color: #333;">视频播放</h3>
+                                    <video id="videoPlayer" controls>
+                                        <source id="videoSource" src="" type="video/mp4">
+                                        您的浏览器不支持视频播放
+                                    </video>
+                                    <div class="layui-row layui-col-space10" style="margin-top: 20px;">
+                                        <div class="layui-col-xs12">
+                                            <button class="layui-btn layui-btn-primary" id="refreshList">刷新文件列表</button>
+                                        </div>
+                                    </div>
+
+                                    <!-- 服务器文件列表 -->
+                                    <div style="margin-top: 20px;">
+                                        <h4 style="margin-bottom: 10px; color: #666;">服务器文件列表</h4>
+                                        <table class="layui-table" id="fileList">
+                                            <thead>
+                                            <tr>
+                                                <th>文件名</th>
+                                                <th>大小</th>
+                                                <th>上传时间</th>
+                                                <th>操作</th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            <!-- 文件列表将通过JS动态生成 -->
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="layui-form-item febs-hide">
+                        <button class="layui-btn" lay-submit="" lay-filter="aiCompany-info-form-submit" id="submit">保存</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>
+<style>
+    .upload-container {
+        max-width: 900px;
+        margin: 30px auto;
+        padding: 30px;
+        background: #f8f8f8;
+        border-radius: 8px;
+        box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+    }
+    
+    .upload-header {
+        text-align: center;
+        margin-bottom: 40px;
+    }
+    
+    .upload-header h2 {
+        color: #333;
+        margin-bottom: 10px;
+        font-size: 24px;
+    }
+    
+    .upload-header p {
+        color: #666;
+        font-size: 14px;
+    }
+    
+    .upload-area {
+        border: 2px dashed #1E9FFF;
+        border-radius: 8px;
+        padding: 50px;
+        text-align: center;
+        margin-bottom: 30px;
+        cursor: pointer;
+        transition: all 0.3s;
+        background: #fff;
+    }
+    
+    .upload-area:hover {
+        border-color: #009688;
+        background: #f0f9ff;
+    }
+    
+    .upload-icon {
+        font-size: 80px;
+        color: #1E9FFF;
+        margin-bottom: 20px;
+    }
+    
+    .upload-text {
+        font-size: 18px;
+        color: #666;
+    }
+    
+    .upload-text small {
+        font-size: 14px;
+        color: #999;
+        display: block;
+        margin-top: 10px;
+    }
+    
+    .file-info {
+        margin-top: 20px;
+        padding: 20px;
+        background: #fff;
+        border-radius: 6px;
+        border-left: 4px solid #1E9FFF;
+        box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+    }
+    
+    .file-info .layui-row {
+        margin: 0;
+    }
+    
+    .file-info p {
+        margin: 5px 0;
+        color: #333;
+    }
+    
+    .file-info strong {
+        color: #666;
+    }
+    
+    .progress-container {
+        margin: 20px 0;
+        background: #fff;
+        padding: 20px;
+        border-radius: 6px;
+        box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+    }
+    
+    .progress-bar {
+        height: 24px;
+        background: #f0f0f0;
+        border-radius: 12px;
+        overflow: hidden;
+        box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
+    }
+    
+    .progress-fill {
+        height: 100%;
+        background: linear-gradient(90deg, #1E9FFF, #009688);
+        border-radius: 12px;
+        width: 0%;
+        transition: width 0.3s;
+        text-align: center;
+        line-height: 24px;
+        color: #fff;
+        font-size: 12px;
+        font-weight: bold;
+    }
+    
+    .btn-group {
+        margin: 20px 0;
+        text-align: center;
+    }
+    
+    .btn-group .layui-btn {
+        margin: 0 10px;
+        padding: 0 30px;
+        height: 40px;
+        line-height: 40px;
+        font-size: 16px;
+    }
+    
+    .play-container {
+        margin-top: 40px;
+        padding: 30px;
+        background: #fff;
+        border-radius: 8px;
+        box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+    }
+    
+    .play-container h3 {
+        margin-bottom: 20px;
+        color: #333;
+        font-size: 20px;
+    }
+    
+    video {
+        width: 100%;
+        max-width: 800px;
+        height: auto;
+        border-radius: 4px;
+        box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+    }
+    
+    .list-controls {
+        margin: 20px 0;
+        text-align: center;
+    }
+    
+    .file-list-section {
+        margin-top: 30px;
+    }
+    
+    .file-list-section h4 {
+        margin-bottom: 15px;
+        color: #666;
+        font-size: 16px;
+        border-bottom: 1px solid #f0f0f0;
+        padding-bottom: 10px;
+    }
+    
+    .layui-table {
+        box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+    }
+    
+    .layui-table th {
+        background: #f8f8f8;
+        font-weight: bold;
+    }
+    
+    .layui-table tr:hover {
+        background: #f9f9f9;
+    }
+    
+    .layui-table .play-btn,
+    .layui-table .delete-btn {
+        margin: 0 5px;
+    }
+    
+    @media (max-width: 768px) {
+        .upload-container {
+            margin: 20px;
+            padding: 20px;
+        }
+        
+        .upload-area {
+            padding: 30px;
+        }
+        
+        .upload-icon {
+            font-size: 60px;
+        }
+        
+        .btn-group .layui-btn {
+            margin: 5px;
+            padding: 0 20px;
+        }
+    }
+</style>
+<!-- 表格操作栏 end -->
+<script data-th-inline="javascript">
+        layui.use(['layer', 'jquery'], function() {
+            var layer = layui.layer,
+            $ = layui.jquery;
+
+            // 全局变量
+            var file = null;
+            var fileMd5 = '';
+            var chunkSize = 5 * 1024 * 1024; // 5MB分片
+            var chunks = 0;
+            var currentChunk = 0;
+            var isUploading = false;
+            var startTime = 0;
+            var uploadedSize = 0;
+
+            // 初始化
+            init();
+
+            function init() {
+                // 拖拽上传
+                var uploadArea = document.getElementById('uploadArea');
+
+                uploadArea.addEventListener('dragover', function(e) {
+                    e.preventDefault();
+                    $(this).css('border-color', '#009688');
+                });
+
+                uploadArea.addEventListener('dragleave', function(e) {
+                    e.preventDefault();
+                    $(this).css('border-color', '#1E9FFF');
+                });
+
+                uploadArea.addEventListener('drop', function(e) {
+                    e.preventDefault();
+                    $(this).css('border-color', '#1E9FFF');
+                    handleFile(e.dataTransfer.files[0]);
+                });
+
+                // 点击上传
+                uploadArea.addEventListener('click', function(e) {
+                    // 阻止事件冒泡,避免干扰其他按钮
+                    e.stopPropagation();
+                    // 确保点击的是上传区域本身,而不是其他元素
+                    if (e.target === uploadArea || e.target.classList.contains('upload-icon') || e.target.classList.contains('upload-text')) {
+                        console.log('上传区域被点击');
+                        var input = document.createElement('input');
+                        input.type = 'file';
+                        input.accept = 'video/*';
+                        input.onchange = function() {
+                            if (this.files.length > 0) {
+                                console.log('选择了文件:', this.files[0].name);
+                                handleFile(this.files[0]);
+                            }
+                        };
+                        input.click();
+                    }
+                });
+
+                // 按钮事件 - 使用事件委托确保每次都能正确绑定
+                // 移除旧的事件绑定,避免重复绑定
+                $(document).off('click', '#startUpload');
+                $(document).off('click', '#cancelUpload');
+                $(document).off('click', '#refreshList');
+                
+                // 重新绑定事件
+                $(document).on('click', '#startUpload', function(e) {
+                    e.preventDefault();
+                    e.stopPropagation();
+                    console.log('开始上传按钮被点击 - 新绑定');
+                    startUpload();
+                });
+                
+                // 为取消上传按钮添加更强大的事件绑定
+                $(document).on('click', '#cancelUpload', function(e) {
+                    e.preventDefault();
+                    e.stopPropagation();
+                    console.log('取消上传按钮被点击 - 新事件委托');
+                    console.log('按钮状态:', $(this).prop('disabled'));
+                    console.log('按钮可见性:', $(this).is(':visible'));
+                    cancelUpload();
+                });
+                
+                $(document).on('click', '#refreshList', function(e) {
+                    e.preventDefault();
+                    e.stopPropagation();
+                    console.log('刷新列表按钮被点击 - 新绑定');
+                    refreshFileList();
+                });
+
+                // 初始化文件列表
+                refreshFileList();
+            }
+
+            // 处理文件
+            function handleFile(selectedFile) {
+                if (!selectedFile) return;
+
+                if (!selectedFile.type.startsWith('video/')) {
+                    layer.msg('请选择视频文件', {icon: 2});
+                    return;
+                }
+
+                // 重置之前的状态
+                currentChunk = 0;
+                uploadedSize = 0;
+                $('#uploadProgress').text('0%');
+                $('#uploadStatus').text('等待上传');
+                $('#uploadSpeed').text('0KB/s');
+                $('#progressFill').css('width', '0%').text('0%');
+
+                file = selectedFile;
+                chunks = Math.ceil(file.size / chunkSize);
+
+                // 显示文件信息
+                $('#fileName').text(file.name);
+                $('#fileSize').text(formatFileSize(file.size));
+                $('#chunkCount').text(chunks);
+                $('#fileInfo').show();
+                $('#progressContainer').show();
+                $('#btnGroup').show();
+
+                // 计算文件MD5(用于断点续传和文件标识)
+                calculateFileMd5();
+            }
+
+            // 计算文件MD5
+            function calculateFileMd5() {
+                layer.msg('正在计算文件MD5...', {icon: 16, time: 0});
+
+                var fileReader = new FileReader();
+                var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
+                var chunk = blobSlice.call(file, 0, Math.min(file.size, 10 * 1024 * 1024)); // 只取前10MB计算MD5
+
+                fileReader.onload = function(e) {
+                    var spark = new SparkMD5.ArrayBuffer();
+                    spark.append(e.target.result);
+                    fileMd5 = spark.end();
+                    layer.closeAll();
+                    layer.msg('文件MD5计算完成', {icon: 1});
+                };
+
+                fileReader.readAsArrayBuffer(chunk);
+            }
+
+            // 开始上传
+            function startUpload() {
+                if (!file) {
+                    layer.msg('请先选择文件', {icon: 2});
+                    return;
+                }
+
+                if (isUploading) {
+                    layer.msg('上传已在进行中', {icon: 2});
+                    return;
+                }
+
+                isUploading = true;
+                startTime = Date.now();
+                uploadedSize = 0;
+                currentChunk = 0;
+
+                $('#uploadStatus').text('上传中');
+                $('#startUpload').prop('disabled', true);
+                $('#cancelUpload').prop('disabled', true);
+
+                uploadNextChunk();
+            }
+
+            // 上传下一个分片
+            function uploadNextChunk() {
+                if (!isUploading) return;
+                if (currentChunk >= chunks) {
+                    // 所有分片上传完成,请求合并
+                    mergeChunks();
+                    return;
+                }
+
+                var start = currentChunk * chunkSize;
+                var end = Math.min(start + chunkSize, file.size);
+                var chunk = file.slice(start, end);
+
+                var formData = new FormData();
+                formData.append('file', chunk);
+                formData.append('fileName', file.name);
+                formData.append('chunk', currentChunk);
+                formData.append('chunks', chunks);
+                formData.append('fileMd5', fileMd5);
+
+                $.ajax({
+                    url: '/fileUpload/uploadChunk',
+                    type: 'POST',
+                    data: formData,
+                    processData: false,
+                    contentType: false,
+                    xhr: function() {
+                        var xhr = new XMLHttpRequest();
+                        xhr.upload.addEventListener('progress', function(e) {
+                            if (e.lengthComputable) {
+                                var chunkProgress = e.loaded / e.total;
+                                var totalProgress = (currentChunk + chunkProgress) / chunks;
+                                updateProgress(totalProgress);
+
+                                // 计算上传速度
+                                var elapsed = (Date.now() - startTime) / 1000;
+                                var speed = (uploadedSize + e.loaded) / elapsed;
+                                $('#uploadSpeed').text(formatFileSize(speed) + '/s');
+                            }
+                        });
+                        return xhr;
+                    },
+                    success: function(response) {
+                        if (response.code === 200 || response.success) {
+                            uploadedSize += (end - start);
+                            currentChunk++;
+                            uploadNextChunk();
+                        } else {
+                            layer.msg('分片上传失败: ' + response.message, {icon: 2});
+                            isUploading = false;
+                            $('#uploadStatus').text('上传失败');
+                            $('#startUpload').prop('disabled', false);
+                        }
+                    },
+                    error: function() {
+                        layer.msg('网络错误,请重试', {icon: 2});
+                        isUploading = false;
+                        $('#uploadStatus').text('上传失败');
+                        $('#startUpload').prop('disabled', false);
+                    }
+                });
+            }
+
+            // 合并分片
+            function mergeChunks() {
+                $.ajax({
+                    url: '/fileUpload/mergeChunks',
+                    type: 'POST',
+                    data: {
+                        fileName: file.name,
+                        fileMd5: fileMd5,
+                        chunks: chunks
+                    },
+                    success: function(response) {
+                        if (response.code === 200 || response.success) {
+                            layer.msg('文件上传成功,页面将刷新', {icon: 1, time: 1000});
+                            $('#uploadStatus').text('上传成功');
+                            $('#uploadProgress').text('100%');
+                            $('#progressFill').css('width', '100%').text('100%');
+                            
+                            // 刷新页面
+                            setTimeout(function() {
+                                location.reload();
+                            }, 1000);
+                        } else {
+                            layer.msg('文件合并失败: ' + response.message, {icon: 2});
+                            $('#uploadStatus').text('上传失败');
+                        }
+                    },
+                    error: function() {
+                        layer.msg('网络错误,请重试', {icon: 2});
+                        $('#uploadStatus').text('上传失败');
+                    },
+                    complete: function() {
+                        isUploading = false;
+                        $('#startUpload').prop('disabled', false);
+                        $('#cancelUpload').prop('disabled', true);
+                    }
+                });
+            }
+
+            // 取消上传
+            function cancelUpload() {
+                console.log('取消上传函数被调用');
+                
+                // 显示成功消息
+                layer.msg('上传已取消,页面将刷新', {icon: 1, time: 1000});
+                
+                // 刷新页面
+                setTimeout(function() {
+                    location.reload();
+                }, 1000);
+            }
+
+            // 更新进度
+            function updateProgress(progress) {
+                var percent = Math.round(progress * 100);
+                $('#uploadProgress').text(percent + '%');
+                $('#progressFill').css('width', percent + '%').text(percent + '%');
+            }
+
+            // 刷新文件列表
+            function refreshFileList() {
+                layer.msg('正在获取文件列表...', {icon: 16, time: 0});
+                $.ajax({
+                    url: '/fileUpload/list',
+                    type: 'GET',
+                    success: function(response) {
+                        layer.closeAll();
+                        if (response.code === 200 || response.success || response.data) {
+                            var fileList = response.data || [];
+                            var tbody = $('#fileList tbody');
+                            tbody.empty();
+
+                            if (fileList.length === 0) {
+                                var emptyTr = $('<tr></tr>');
+                                emptyTr.html(`
+                                    <td colspan="4" style="text-align: center; color: #999;">暂无文件</td>
+                                `);
+                                tbody.append(emptyTr);
+                            } else {
+                                fileList.forEach(function(item) {
+                                    var tr = $('<tr></tr>');
+                                    tr.html(`
+                                        <td>
+                                            <input type="radio" name="fileRadio" value="${item.fileName}">
+                                            ${item.fileName}
+                                        </td>
+                                        <td>${formatFileSize(item.fileSize)}</td>
+                                        <td>${item.uploadTime}</td>
+                                        <td>
+                                            <button class="layui-btn layui-btn-xs layui-btn-normal play-btn" data-file="${item.fileName}">
+                                                播放
+                                            </button>
+                                            <button class="layui-btn layui-btn-xs layui-btn-danger delete-btn" data-file="${item.fileName}">
+                                                删除
+                                            </button>
+                                        </td>
+                                    `);
+                                    tbody.append(tr);
+                                });
+
+                                // 绑定播放按钮事件
+                                $('.play-btn').click(function() {
+                                    var fileName = $(this).data('file');
+                                    var videoPlayer = document.getElementById('videoPlayer');
+                                    var videoSource = document.getElementById('videoSource');
+
+                                    videoSource.src = '/fileUpload/play/' + encodeURIComponent(fileName);
+                                    videoPlayer.load();
+                                    videoPlayer.play();
+                                });
+
+                                // 绑定删除按钮事件
+                                $('.delete-btn').click(function() {
+                                    var fileName = $(this).data('file');
+                                    layer.confirm('确定要删除此文件吗?', {
+                                        btn: ['确定', '取消']
+                                    }, function() {
+                                        $.ajax({
+                                            url: '/fileUpload/delete',
+                                            type: 'POST',
+                                            data: {fileName: fileName},
+                                            success: function(response) {
+                                                if (response.code === 200 || response.success) {
+                                                    layer.msg('删除成功', {icon: 1});
+                                                    refreshFileList();
+                                                } else {
+                                                    layer.msg('删除失败: ' + (response.message || response.msg), {icon: 2});
+                                                }
+                                            },
+                                            error: function() {
+                                                layer.msg('网络错误,请重试', {icon: 2});
+                                            }
+                                        });
+                                    });
+                                });
+                            }
+                        } else {
+                            layer.msg('获取文件列表失败: ' + (response.message || response.msg), {icon: 2});
+                        }
+                    },
+                    error: function() {
+                        layer.closeAll();
+                        layer.msg('网络错误,请重试', {icon: 2});
+                    }
+                });
+            }
+
+            // 格式化文件大小
+            function formatFileSize(bytes) {
+                if (bytes === 0) return '0 B';
+                var k = 1024;
+                var sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
+                var i = Math.floor(Math.log(bytes) / Math.log(k));
+                return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+            }
+        });
+</script>
+
+<script src="https://cdn.jsdelivr.net/npm/spark-md5@3.0.2/spark-md5.min.js"></script>
\ No newline at end of file

--
Gitblit v1.9.1