From 217838c6153edbdc89c88274fb7318ea6915d7b0 Mon Sep 17 00:00:00 2001 From: oneao Date: Wed, 29 Apr 2026 21:54:37 +0800 Subject: [PATCH] u --- .../.trae/documents/add-features-plan.md | 127 ++++++ .../.trae/documents/rename-callbacks-plan.md | 52 +++ code/file-preview/demo.html | 431 ++++++++++++++++++ code/file-preview/g3-file.md | 288 ++++++++++++ code/file-preview/g3-file/icon.js | 6 +- code/file-preview/g3-file/index.css | 228 +-------- code/file-preview/g3-file/index.js | 429 ++++------------- code/file-preview/g3-file/language.js | 15 - code/file-preview/iframe-demo.html | 160 +++++++ code/file-preview/index.html | 327 +++++-------- code/file-preview/test.html | 227 --------- 11 files changed, 1268 insertions(+), 1022 deletions(-) create mode 100644 code/file-preview/.trae/documents/add-features-plan.md create mode 100644 code/file-preview/.trae/documents/rename-callbacks-plan.md create mode 100644 code/file-preview/demo.html create mode 100644 code/file-preview/g3-file.md create mode 100644 code/file-preview/iframe-demo.html delete mode 100644 code/file-preview/test.html diff --git a/code/file-preview/.trae/documents/add-features-plan.md b/code/file-preview/.trae/documents/add-features-plan.md new file mode 100644 index 00000000..94f61063 --- /dev/null +++ b/code/file-preview/.trae/documents/add-features-plan.md @@ -0,0 +1,127 @@ +# 功能添加计划 + +## 需求分析 + +用户要求添加两个功能: + +### 功能一:双击事件扩展 + +- 添加 `onDblClick` 回调选项,供外部使用 +- 如果外部注册了 `onDblClick`,则优先执行外部回调 +- 如果外部未注册 `onDblClick`,则双击文件时跳转到文件预览页面(fullPreviewUrl) + +### 功能二:onUploadBefore 回调 + +- 添加 `onUploadBefore` 回调选项,参考 `onBeforeDelete` 的实现方式 +- 支持返回值类型: + - `false`: 阻止上传 + - Promise: 异步确认后再上传 + - Function: 回调确认后再上传 + - 其他/undefined: 继续执行上传 +- 同时将 `onBeforeDelete 名称修改为 onDeleteBefore` + +## 修改文件 + +1. **index.js**: + - 在 defaults 配置中添加 `onDblClick` 和 `onUploadBefore` + - 在 bindEvents 中添加文件项双击事件处理 + - 在 handleUpload 方法中添加 onUploadBefore 逻辑 + +## 实现步骤 + +### 步骤一:添加配置项 + +在 `defaults` 对象中添加: + +```javascript +onDblClick: null, +onUploadBefore: null, +``` + +### 步骤二:添加双击事件处理 + +在文件列表事件委托中添加双击事件: + +```javascript +// 文件项双击 +.on("dblclick", ".g3-file-item", function (e) { + // 阻止冒泡到子元素 + if ($(e.target).closest(".g3-file-action, .g3-file-checkbox, .g3-file-delete-btn").length) { + return; + } + var id = $(this).data("id"); + var file = self.getFileById(id); + if (!file) return; + if (!self.hasPermission(file, "view")) return; + + // 如果外部注册了 onDblClick,优先执行外部回调 + if (typeof self.options.onDblClick === "function") { + self.options.onDblClick(file); + return; + } + + // 否则跳转到预览页面 + var isUploaded = file.status === "uploaded"; + var isPreviewable = self.isPreviewableFile(file.name); + var fileUrl = file.url && file.url !== "" ? file.url : null; + + if (isUploaded && fileUrl && isPreviewable) { + var previewUrl = self.options.previewUrl || ""; + var fullPreviewUrl = previewUrl + ? previewUrl + G3FileUtils.base64Encode(fileUrl) + : fileUrl; + window.open(fullPreviewUrl, "_blank"); + } +}) +``` + +### 步骤三:添加 onUploadBefore 逻辑 + +在 `handleUpload` 方法开头添加: + +```javascript +// onUploadBefore 处理 +if (typeof self.options.onUploadBefore === "function") { + var result = self.options.onUploadBefore(pendingFiles); + if (result === false) return; + if (typeof result.then === "function") { + result.then(function (confirmed) { + if (confirmed) self.internalHandleUpload(pendingFiles, taskId); + }); + return; + } + if (typeof result === "function") { + result(function (confirmed) { + if (confirmed) self.internalHandleUpload(pendingFiles, taskId); + }); + return; + } +} +``` + +需要将原有的上传逻辑提取为 `internalHandleUpload` 方法。 + +## 风险处理 + +1. **兼容性风险**:确保修改不会影响现有功能 +2. **异步处理风险**:正确处理 Promise 和回调函数的情况 +3. **权限检查**:双击事件需要检查 view 权限 + +## 测试要点 + +1. 测试双击文件时: + - 注册 onDblClick 时是否优先执行外部回调 + - 未注册 onDblClick 时是否正确跳转到预览页面 + - 未上传的文件双击时不跳转 + - 无权限文件双击时不响应 +2. 测试上传时: + - onUploadBefore 返回 false 时是否阻止上传 + - onUploadBefore 返回 Promise 时是否等待异步结果 + - onUploadBefore 返回回调函数时是否正确处理 + +## 依赖分析 + +- 依赖 `isPreviewableFile` 方法判断文件是否可预览 +- 依赖 `G3FileUtils.base64Encode` 方法编码 URL +- 依赖 `getFileById` 方法获取文件信息 + diff --git a/code/file-preview/.trae/documents/rename-callbacks-plan.md b/code/file-preview/.trae/documents/rename-callbacks-plan.md new file mode 100644 index 00000000..80976788 --- /dev/null +++ b/code/file-preview/.trae/documents/rename-callbacks-plan.md @@ -0,0 +1,52 @@ +# Task 关闭按钮修复计划 + +## 问题分析 + +根据代码审查,`g3-task-close` 点击无反应的核心问题在于: + +1. **事件绑定方式错误**:使用直接绑定而非事件委托 +2. **taskId 未绑定到 DOM**:无法通过 DOM 获取当前任务 ID +3. **closeTask 方法不完善**:只删除数据,未删除对应的 DOM 元素 + +## 修复方案 + +### 1. 修改 renderTaskCenter 方法 + +* 在 DOM 元素上添加 `data-task-id` 属性 + +* 移除直接的事件绑定 + +### 2. 修改 closeTask 方法 + +* 同时删除数据和对应的 DOM 元素 + +### 3. 添加事件委托(在初始化时绑定) + +* 使用事件委托绑定 `.g3-task-close` 点击事件 + +### 4. 检查 pointer-events 样式 + +* 确保 `.g3-task-close` 按钮可点击 + +## 修改文件 + +* `d:\workspace\code\file-preview\g3-file\index.js` + +## 修改步骤 + +1. 在 `renderTaskCenter` 的 HTML 模板中添加 `data-task-id` +2. 移除 `renderTaskCenter` 中的直接事件绑定 +3. 修改 `closeTask` 方法,添加 DOM 删除逻辑 +4. 在初始化时添加事件委托绑定 + +## 风险评估 + +* 低风险:修改局部代码,不影响其他功能 + +* 需确保事件委托只绑定一次 + +## 验证方式 + +1. 点击关闭按钮,检查控制台是否输出正确的 taskId +2. 确认任务数据和 UI 都被正确移除 + diff --git a/code/file-preview/demo.html b/code/file-preview/demo.html new file mode 100644 index 00000000..7530090c --- /dev/null +++ b/code/file-preview/demo.html @@ -0,0 +1,431 @@ + + + + + + + G3File Demo + + + + + + + + + + + + + + + + + +
+

G3File 文件管理组件

+

演示文件上传、预览、下载、删除等功能

+
+ +
+ +
+
+

基础功能演示

+
+ + + +
+
+
+
+

提示:双击文件可预览(如果配置了预览地址且文件已上传),或触发自定义双击事件。

+
+
+
+
+ + +
+
+

回调事件演示

+
+
+
+

提示:操作文件时,下方控制台会显示相应的回调信息。

+
+
+
+

事件日志:

+
+
+
+
+ + +
+
+

API 操作演示

+
+
+
+ + + + +
+
+
+

API 调用结果:

+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/code/file-preview/g3-file.md b/code/file-preview/g3-file.md new file mode 100644 index 00000000..6d352936 --- /dev/null +++ b/code/file-preview/g3-file.md @@ -0,0 +1,288 @@ +# G3File 文件管理组件 + +## 概述 + +G3File 是一个基于 jQuery 的文件管理组件,提供文件上传、预览、下载、删除等功能,支持阿里云 OSS 上传和自定义上传。 + +## 文件结构 + +``` +g3-file/ +├── index.js # 主组件代码 +├── index.css # 样式文件 +├── common.js # 工具函数 +├── icon.js # 图标定义 +├── language.js # 语言配置 +└── libs/ # 第三方依赖 + ├── FileSaver.min.js + └── jszip.min.js +``` + +## 快速开始 + +### 引入依赖 + +```html + + + + + + + + + +``` + +### 初始化组件 + +```javascript +var file = $('#fileContainer').G3File({ + data: [ + { + name: 'example.pdf', + size: 1024000, + uploadDate: '2024-01-15T10:30:00', + url: 'http://example.com/example.pdf' + } + ], + previewUrl: 'https://preview.example.com?url=', + onUploadSuccess: function(file) { + console.log('上传成功:', file); + } +}); +``` + +## 配置选项 + +### 基础配置 + +| 配置项 | 类型 | 默认值 | 说明 | +|--------|------|--------|------| +| `data` | Array | `[]` | 初始化时的文件数据 | +| `mode` | String | `'aliyun'` | 上传模式 | +| `language` | String | `'zh-CN'` | 语言(zh-CN/en/ru) | +| `maxFileSize` | Number | `10485760` | 最大文件大小(字节) | +| `previewUrl` | String | `''` | 文件预览服务地址 | + +### 回调函数 + +| 配置项 | 参数 | 说明 | +|--------|------|------| +| `onDeleteBefore` | `files` | 删除前回调,返回 false/Promise/Function 可阻止删除 | +| `onDelete` | `files` | 删除后回调 | +| `onDownload` | `files` | 下载回调 | +| `onSelect` | `files` | 文件选中回调 | +| `onUpload` | `pendingFiles` | 上传开始回调 | +| `onUploadBefore` | `pendingFiles` | 上传前回调,返回 false/Promise/Function 可阻止上传 | +| `onUploadSuccess` | `file` | 单个文件上传成功回调 | +| `onUploadError` | `file`, `error` | 单个文件上传失败回调 | +| `onUploadComplete` | `successCount`, `failCount` | 全部上传完成回调 | +| `onDblClick` | `file` | 文件双击回调,优先于默认预览行为 | +| `customActionMenu` | `file` | 自定义操作菜单,返回菜单项数组 | + +### 上传配置 + +```javascript +uploadConfig: { + aliyun: { + getAliyunKey: function() { + // 返回 Promise,包含阿里云 OSS 凭证 + return Promise.resolve({ + accessid: 'your-access-key', + callback: '', + dir: 'upload/', + expire: 1704067200, + host: 'https://your-bucket.oss-cn-hangzhou.aliyuncs.com', + policy: 'your-policy', + signature: 'your-signature' + }); + } + }, + customUpload: null // 自定义上传函数 +} +``` + +### 权限配置 + +```javascript +globalPermission: { + upload: true, // 是否允许上传 + download: true, // 是否允许下载 + delete: true, // 是否允许删除 + view: true // 是否允许查看 +} +``` + +## 文件数据结构 + +```javascript +{ + _id: 'unique-id', // 文件唯一标识符(自动生成) + name: 'example.pdf', // 文件名 + size: 1024000, // 文件大小(字节) + uploadDate: '2024-01-15T10:30:00', // 上传日期(ISO格式) + url: 'http://example.com/example.pdf', // 文件访问URL + file: null, // 本地File对象(仅待上传文件有) + status: 'uploaded' // 文件状态:pending/uploading/uploaded/failed +} +``` + +## API 方法 + +### setFiles(files) + +设置文件列表,替换现有文件 + +```javascript +file.setFiles([ + { name: 'file1.pdf', size: 1024, uploadDate: '2024-01-01', url: '' }, + { name: 'file2.jpg', size: 2048, uploadDate: '2024-01-02', url: 'http://example.com/file2.jpg' } +]); +``` + +### getFileById(fileId) + +根据文件ID获取文件对象 + +```javascript +var file = file.getFileById('file_123'); +``` + +### getSelectedFiles() + +获取当前选中的文件列表 + +```javascript +var selected = file.getSelectedFiles(); +``` + +### updateState(state) + +更新组件状态 + +```javascript +// 清空选择 +file.updateState({ selectedFiles: [] }); +``` + +### destroy() + +销毁组件,清理资源 + +```javascript +file.destroy(); +``` + +## 使用示例 + +### 示例1:基础用法 + +```javascript +var file = $('#fileContainer').G3File({ + data: [], + previewUrl: 'https://preview.example.com?url=', + onUploadSuccess: function(file) { + console.log('上传成功:', file.name); + }, + onDelete: function(files) { + console.log('删除文件:', files.length); + } +}); +``` + +### 示例2:双击事件 + +```javascript +var file = $('#fileContainer').G3File({ + onDblClick: function(file) { + // 自定义双击处理,优先于默认预览行为 + alert('双击了文件: ' + file.name); + } +}); +``` + +### 示例3:上传前确认 + +```javascript +var file = $('#fileContainer').G3File({ + onUploadBefore: function(pendingFiles) { + // 返回 Promise 进行异步确认 + return new Promise((resolve) => { + if (confirm('确定上传 ' + pendingFiles.length + ' 个文件?')) { + resolve(true); + } else { + resolve(false); + } + }); + } +}); +``` + +### 示例4:自定义操作菜单 + +```javascript +var file = $('#fileContainer').G3File({ + customActionMenu: function(file) { + return [ + { + title: '审核', + onClick: function(file) { + alert('审核文件: ' + file.name); + } + }, + { + title: '编辑', + onClick: function(file) { + alert('编辑文件: ' + file.name); + }, + disabled: false // 可选,是否禁用 + } + ]; + } +}); +``` + +### 示例5:删除前确认 + +```javascript +var file = $('#fileContainer').G3File({ + onDeleteBefore: function(files) { + // 返回 Promise 进行异步确认(与 onUploadBefore 用法一致) + return new Promise((resolve) => { + if (confirm('确定删除 ' + files.length + ' 个文件?')) { + resolve(true); + } else { + resolve(false); + } + }); + } +}); +``` + +## 事件处理 + +### 双击行为优先级 + +1. 如果配置了 `onDblClick` 回调,优先执行回调 +2. 如果未配置且文件已上传、可预览、有URL,则在新窗口打开预览 + +### 支持的预览文件类型 + +组件会排除以下不可预览的文件类型: +- 压缩包:.rar, .zip, .7z, .tar, .gz, .bz2 +- 可执行文件:.exe, .dll, .bin, .msi, .cab, .rpm, .deb, .apk, .ipa +- 数据库文件:.dat, .db, .sqlite +- 临时文件:.bak, .tmp, .log, .cache +- 光盘镜像:.iso + +## 浏览器兼容性 + +支持 IE11+ 和现代浏览器(Chrome、Firefox、Safari、Edge) + +## 注意事项 + +1. 使用阿里云 OSS 上传时,必须配置 `uploadConfig.aliyun.getAliyunKey` 函数 +2. 文件大小限制通过 `maxFileSize` 配置,默认 10MB +3. 预览功能依赖 `previewUrl` 配置的外部预览服务 +4. 销毁组件时请调用 `destroy()` 方法,避免内存泄漏 \ No newline at end of file diff --git a/code/file-preview/g3-file/icon.js b/code/file-preview/g3-file/icon.js index 68369b88..e682a907 100644 --- a/code/file-preview/g3-file/icon.js +++ b/code/file-preview/g3-file/icon.js @@ -38,11 +38,7 @@ var G3FileIcons = { ` }, - modal: { - minimize: '', - maximize: '', - close: '' - }, + toast: { success: '', error: '', diff --git a/code/file-preview/g3-file/index.css b/code/file-preview/g3-file/index.css index c581cea0..d43f8cfb 100644 --- a/code/file-preview/g3-file/index.css +++ b/code/file-preview/g3-file/index.css @@ -6,7 +6,7 @@ border: 1px solid #eef0f2; border-radius: 8px; --g3-z-dropdown: 100; - --g3-z-modal: 200; + --g3-z-toast: 300; --g3-z-task: 400; --g3-z-lock: 500; @@ -907,230 +907,4 @@ display: block; } -/* ============================================================================ - Modal 样式 - ============================================================================ */ -.g3-modal-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - z-index: var(--g3-z-modal); - display: none; - align-items: center; - justify-content: center; -} - -.g3-modal-overlay.is-visible { - display: flex; -} - -.g3-modal { - width: 90%; - height: 90%; - background: #ffffff; - border-radius: 8px; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); - display: flex; - flex-direction: column; - overflow: hidden; - transition: - transform 0.3s ease, - opacity 0.3s ease; - transform: scale(0.95); - opacity: 0; -} - -.g3-modal-overlay.is-visible .g3-modal { - transform: scale(1); - opacity: 1; -} - -.g3-modal.is-maximized { - width: 100%; - height: 100%; - max-width: none; - border-radius: 0; -} - -.g3-modal-header { - height: 48px; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 16px; - background: #fafbfc; - border-bottom: 1px solid #eef0f2; - flex-shrink: 0; -} - -.g3-modal-title { - font-size: 14px; - font-weight: 500; - color: #333; - text-align: center; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.g3-modal-actions { - display: flex; - align-items: center; - gap: 4px; -} - -.g3-modal-btn { - width: 32px; - height: 32px; - border: none; - background: transparent; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - transition: background-color 0.2s ease; - color: #666; -} - -.g3-modal-btn:hover { - background: #f0f2f5; - color: #333; -} - -.g3-modal-btn-close:hover { - background: #ff4d4f; - color: #fff; -} - -.g3-modal-body { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.g3-modal-preview-toolbar { - height: 48px; - display: flex; - align-items: center; - padding: 0 16px; - background: #ffffff; - border-bottom: 1px solid #eef0f2; - flex-shrink: 0; -} - -.g3-modal-preview-nav { - display: flex; - align-items: center; - gap: 12px; -} - -.g3-modal-preview-nav .g3-modal-preview-counter { - font-size: 14px; - color: #666; - font-weight: 500; - min-width: 50px; - text-align: center; -} - -.g3-modal-preview-filename { - font-size: 14px; - color: #333; - font-weight: 500; - margin-left: 16px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.g3-modal-preview-btn { - width: 32px; - height: 32px; - border: 1px solid #d9d9d9; - border-radius: 4px; - background: #ffffff; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; - padding: 0; - color: #666; -} - -.g3-modal-preview-btn:hover:not(:disabled) { - border-color: #165dff; - background: rgba(22, 93, 255, 0.05); - color: #165dff; -} - -.g3-modal-preview-btn:disabled { - opacity: 0.4; - cursor: not-allowed; -} - -.g3-modal-preview-content { - flex: 1; - position: relative; - padding: 20px; - overflow: auto; - background: #fafbfc; -} - -.g3-modal-preview-content .g3-modal-preview-placeholder { - text-align: center; - color: #999; - font-size: 24px; -} - -.g3-modal-preview-content .g3-modal-preview-iframe { - width: 100%; - height: 100%; - border: none; - margin: 0; - padding: 0; - background: #ffffff; - position: absolute; - top: 0; - left: 0; - transition: opacity 0.3s ease; -} - -.g3-modal-preview-content .g3-modal-preview-iframe.current { - opacity: 1; - z-index: 1; -} - -.g3-modal-preview-content .g3-modal-preview-iframe.next { - opacity: 0; - z-index: 2; -} - -.g3-modal-footer { - display: none; - height: 48px; - display: flex; - align-items: center; - justify-content: flex-end; - padding: 0 16px; - background: #fafbfc; - border-top: 1px solid #eef0f2; - flex-shrink: 0; - visibility: hidden; - height: 0; - padding: 0; - border-top: none; -} - -.g3-modal-footer.has-content { - display: flex; - visibility: visible; - height: 48px; - padding: 0 16px; - border-top: 1px solid #eef0f2; -} diff --git a/code/file-preview/g3-file/index.js b/code/file-preview/g3-file/index.js index ba769b0b..4b6998b0 100644 --- a/code/file-preview/g3-file/index.js +++ b/code/file-preview/g3-file/index.js @@ -65,16 +65,17 @@ */ data: [], mode: "aliyun", - onBeforeDelete: null, + onDeleteBefore: null, onDelete: null, onDownload: null, onSelect: null, onUpload: null, + onUploadBefore: null, + onDblClick: null, onUploadSuccess: null, onUploadError: null, onUploadComplete: null, - onPreview: null, - getActionMenu: null, + customActionMenu: null, language: "zh-CN", maxFileSize: 10 * 1024 * 1024, // 上传配置 @@ -313,23 +314,6 @@
-
-
-
- -
- - -
-
-
- -
-
`; @@ -353,14 +337,6 @@ this.$previewName = this.$container.find(".g3-preview-name"); this.$previewCounter = this.$container.find(".g3-preview-counter"); - this.$modalOverlay = this.$container.find(".g3-modal-overlay"); - this.$modal = this.$container.find(".g3-modal"); - this.$modalTitle = this.$container.find(".g3-modal-title"); - this.$modalBody = this.$container.find(".g3-modal-body"); - this.$modalBtnToggle = this.$container.find(".g3-modal-btn-toggle"); - this.$modalBtnClose = this.$container.find(".g3-modal-btn-close"); - this.$modalFooter = this.$container.find(".g3-modal-footer"); - return this; }, @@ -431,7 +407,10 @@ var rect = $anchor[0].getBoundingClientRect(); var $mainContainer = this.$container.find(".main"); - var mainRect = $mainContainer.length > 0 ? $mainContainer[0].getBoundingClientRect() : null; + var mainRect = + $mainContainer.length > 0 + ? $mainContainer[0].getBoundingClientRect() + : null; var menuWidth = 160; var itemHeight = 36; @@ -549,12 +528,18 @@ // ${G3FileUtils.formatDate(file.uploadDate)} - var actionMenuItems = (this.options.getActionMenu && this.options.getActionMenu(file)) || []; - var moreActionHtml = actionMenuItems && actionMenuItems.length > 0 ? ` + var actionMenuItems = + (this.options.customActionMenu && + this.options.customActionMenu(file)) || + []; + var moreActionHtml = + actionMenuItems && actionMenuItems.length > 0 + ? `
${G3FileIcons.action.more}
- ` : ""; + ` + : ""; return `
@@ -845,7 +830,10 @@ var $currentIframe = $content.find(".g3-preview-iframe.current"); var $nextIframe = $content.find(".g3-preview-iframe.next"); - if ($nextIframe.length && this.previewCache.nextUrl === fullPreviewUrl) { + if ( + $nextIframe.length && + this.previewCache.nextUrl === fullPreviewUrl + ) { $currentIframe.removeClass("current").addClass("next").hide(); $nextIframe.removeClass("next").addClass("current").show(); this.previewCache.currentUrl = fullPreviewUrl; @@ -880,12 +868,14 @@ newIframe.on("error", function () { newIframe.remove(); - self.showPlaceholder($content, `

${self.language.urlError}

`); + self.showPlaceholder( + $content, + `

${self.language.urlError}

`, + ); }); $content.append(newIframe); this.previewCache.nextUrl = fullPreviewUrl; - } else { this.previewCache = null; var tipsHtml = ""; @@ -910,285 +900,6 @@ `); }, - showModalPlaceholder: function ($container, tipsHtml) { - $container.find(".g3-modal-preview-iframe").remove(); - $container.html(` -
- ${tipsHtml} -
- `); - }, - - openModal: function (options) { - var defaults = { - title: "", - content: "", - width: "80%", - height: "80%", - showToggle: true, - showClose: true - }; - - var opts = $.extend({}, defaults, options); - - this.$modalTitle.text(opts.title || ""); - this.$modalBody.html(opts.content || ""); - - this.$modal.css({ - width: opts.width, - height: opts.height - }); - - this.$modalBtnToggle.toggle(opts.showToggle); - this.$modalBtnClose.toggle(opts.showClose); - - this.$modalOverlay.addClass("is-visible"); - this.$modal.removeClass("is-maximized"); - this.$modalBtnToggle.html(G3FileIcons.modal.maximize); - this.$modalBtnToggle.attr("title", this.language.modal.maximize); - }, - - openPreviewModal: function (fileId) { - var file = this.getFileById(fileId); - if (!file) return; - - var isUploaded = file.status === "uploaded"; - var isPreviewable = this.isPreviewableFile(file.name); - var fileUrl = file.url && file.url !== "" ? file.url : null; - - var toolbarHtml = ` -
-
- - 0/0 - -
-
${file.name}
-
- `; - - var contentHtml = ""; - if (isUploaded && fileUrl && isPreviewable) { - var previewUrl = this.options.previewUrl || ""; - var fullPreviewUrl = previewUrl - ? previewUrl + G3FileUtils.base64Encode(fileUrl) - : fileUrl; - - contentHtml = ` -
- -
- `; - } else { - var tipsHtml = ""; - if (!isUploaded) { - tipsHtml = `

${this.language.uploadToPreview}

`; - } else if (!isPreviewable) { - tipsHtml = `

${this.language.downloadToPreview}

`; - } else if (!fileUrl) { - tipsHtml = `

${this.language.urlError}

`; - } - - contentHtml = ` -
-
- ${tipsHtml} -
-
- `; - } - - var previewContent = toolbarHtml + contentHtml; - - this.currentModalFileId = fileId; - - this.openModal({ - title: this.language.filePreview, - content: previewContent, - width: "90%", - height: "90%", - }); - - var self = this; - setTimeout(function () { - self.$modal.find(".g3-modal-preview-prev").off("click").on("click", function () { - self.modalPrev(); - }); - self.$modal.find(".g3-modal-preview-next").off("click").on("click", function () { - self.modalNext(); - }); - self.updateModalPreviewCounter(fileId); - }, 0); - }, - - updateModalPreviewCounter: function (fileId) { - var allFiles = this.getUploadedFiles(); - var currentIndex = allFiles.findIndex(function (f) { return f._id === fileId; }); - var total = allFiles.length; - - var $counter = this.$modal.find(".g3-modal-preview-counter"); - var $prevBtn = this.$modal.find(".g3-modal-preview-prev"); - var $nextBtn = this.$modal.find(".g3-modal-preview-next"); - - if ($counter.length) { - $counter.text((currentIndex + 1) + "/" + total); - } - if ($prevBtn.length) { - $prevBtn.prop("disabled", currentIndex === 0); - } - if ($nextBtn.length) { - $nextBtn.prop("disabled", currentIndex === total - 1); - } - }, - - modalPrev: function () { - var uploadedFiles = this.getUploadedFiles(); - if (uploadedFiles.length === 0) return; - - var currentIndex = this.getPreviewFileIndex(this.currentModalFileId); - var newIndex = currentIndex > 0 ? currentIndex - 1 : uploadedFiles.length - 1; - this.currentModalFileId = uploadedFiles[newIndex]._id; - this.updateModalPreviewWithFile(uploadedFiles[newIndex]); - }, - - modalNext: function () { - var uploadedFiles = this.getUploadedFiles(); - if (uploadedFiles.length === 0) return; - - var currentIndex = this.getPreviewFileIndex(this.currentModalFileId); - var newIndex = currentIndex < uploadedFiles.length - 1 ? currentIndex + 1 : 0; - this.currentModalFileId = uploadedFiles[newIndex]._id; - this.updateModalPreviewWithFile(uploadedFiles[newIndex]); - }, - - updateModalPreviewWithFile: function (file) { - var $content = this.$modal.find(".g3-modal-preview-content"); - var $title = this.$modal.find(".g3-modal-title"); - var $filename = this.$modal.find(".g3-modal-preview-filename"); - - if (!file || !$content.length) return; - - $title.text(this.language.filePreview); - if ($filename.length) { - $filename.text(file.name); - } - - var isUploaded = file.status === "uploaded"; - var isPreviewable = this.isPreviewableFile(file.name); - var fileUrl = file.url && file.url !== "" ? file.url : null; - - if (isUploaded && fileUrl && isPreviewable) { - var previewUrl = this.options.previewUrl || ""; - var fullPreviewUrl = previewUrl - ? previewUrl + G3FileUtils.base64Encode(fileUrl) - : fileUrl; - - var newIframe = $(` - - `); - - $content.html(newIframe); - } else { - var tipsHtml = ""; - if (!isUploaded) { - tipsHtml = `

${this.language.uploadToPreview}

`; - } else if (!isPreviewable) { - tipsHtml = `

${this.language.downloadToPreview}

`; - } else if (!fileUrl) { - tipsHtml = `

${this.language.urlError}

`; - } - - $content.html(` -
- ${tipsHtml} -
- `); - } - - this.updateModalPreviewCounter(file._id); - }, - - closeModal: function () { - this.$modalOverlay.removeClass("is-visible"); - this.$modalBody.html(""); - this.$modalTitle.text(""); - this.setModalFooter(""); - this.currentModalFileId = null; - }, - - toggleModalSize: function () { - var isMaximized = this.$modal.hasClass("is-maximized"); - - if (isMaximized) { - this.$modal.removeClass("is-maximized"); - this.$modal.css({ - width: "80%", - height: "80%", - maxWidth: "none", - borderRadius: "8px" - }); - this.$modalBtnToggle.html(G3FileIcons.modal.maximize); - this.$modalBtnToggle.attr("title", this.language.modal.maximize); - } else { - this.$modal.addClass("is-maximized"); - this.$modal.css({ - width: "100%", - height: "100%", - maxWidth: "none", - borderRadius: "0" - }); - this.$modalBtnToggle.html(G3FileIcons.modal.restore); - this.$modalBtnToggle.attr("title", this.language.modal.restore); - } - }, - - setModalTitle: function (title) { - if (this.$modalTitle) { - this.$modalTitle.text(title || ""); - } - }, - - setModalContent: function (content) { - if (this.$modalBody) { - this.$modalBody.html(content || ""); - } - }, - - setModalSize: function (width, height) { - if (!this.$modal) return; - - this.$modal.css({ - width: width || "80%", - height: height || "80%" - }); - }, - - setModalFooter: function (content) { - if (!this.$modalFooter) return; - - if (content && content.trim()) { - this.$modalFooter.html(content); - this.$modalFooter.addClass("has-content"); - } else { - this.$modalFooter.html(""); - this.$modalFooter.removeClass("has-content"); - } - }, - setPreviewFile: function (fileId) { var file = this.getFileById(fileId); if (file) { @@ -1352,9 +1063,6 @@ if (!file) return; if (!self.hasPermission(file, "view")) return; self.setPreviewFile(id); - if (typeof self.options.onPreview === "function") { - self.options.onPreview([file]); - } }) // 下载按钮 .on("click", ".g3-action-download", function (e) { @@ -1399,12 +1107,9 @@ var file = self.getFileById(id); if (!file) return; if (!self.hasPermission(file, "view")) return; - if (typeof self.options.onPreview === "function") { - self.options.onPreview([file]); - } self.setPreviewFile(id); }) - // 文件项双击打开 modal 预览 + // 文件项双击 .on("dblclick", ".g3-file-item", function (e) { if ( $(e.target).closest( @@ -1417,8 +1122,25 @@ var file = self.getFileById(id); if (!file) return; if (!self.hasPermission(file, "view")) return; - self.openPreviewModal(id); + + if (typeof self.options.onDblClick === "function") { + self.options.onDblClick(file); + return; + } + + var isUploaded = file.status === "uploaded"; + var isPreviewable = self.isPreviewableFile(file.name); + var fileUrl = file.url && file.url !== "" ? file.url : null; + + if (isUploaded && fileUrl && isPreviewable) { + var previewUrl = self.options.previewUrl || ""; + var fullPreviewUrl = previewUrl + ? previewUrl + G3FileUtils.base64Encode(fileUrl) + : fileUrl; + window.open(fullPreviewUrl, "_blank"); + } }) + // 更多按钮 .on("click", ".g3-action-more", function (e) { e.preventDefault(); @@ -1437,23 +1159,6 @@ self.openActionMenu($el, file); }); - // Modal 事件绑定 - this.$modalBtnClose.off("click").on("click", function () { - self.closeModal(); - }); - - this.$modalBtnToggle.off("click").on("click", function (e) { - e.preventDefault(); - e.stopPropagation(); - self.toggleModalSize(); - }); - - this.$modalOverlay.off("click").on("click", function (e) { - if (e.target === this) { - self.closeModal(); - } - }); - // 任务关闭按钮事件委托 this.$container.on("click", ".g3-task-close", function (e) { e.preventDefault(); @@ -1575,8 +1280,8 @@ return; } - if (typeof this.options.onBeforeDelete === "function") { - var result = this.options.onBeforeDelete(files); + if (typeof this.options.onDeleteBefore === "function") { + var result = this.options.onDeleteBefore(files); if (result === false) return; if (typeof result.then === "function") { result.then(function (confirmed) { @@ -1881,6 +1586,39 @@ return; } + if (typeof this.options.onUploadBefore === "function") { + var result = this.options.onUploadBefore(pendingFiles); + if (result === false) return; + if (typeof result.then === "function") { + result.then(function (confirmed) { + if (confirmed) + self.internalHandleUpload( + pendingFiles, + customUpload, + concurrency, + ); + }); + return; + } + if (typeof result === "function") { + result(function (confirmed) { + if (confirmed) + self.internalHandleUpload( + pendingFiles, + customUpload, + concurrency, + ); + }); + return; + } + } + + self.internalHandleUpload(pendingFiles, customUpload, concurrency); + }, + + internalHandleUpload: function (pendingFiles, customUpload, concurrency) { + var self = this; + var total = pendingFiles.length; var taskId = this.startTask({ id: G3FileUtils.generateUniqueId(), @@ -2273,7 +2011,9 @@ */ openActionMenu: function ($el, file) { var items = - (this.options.getActionMenu && this.options.getActionMenu(file)) || []; + (this.options.customActionMenu && + this.options.customActionMenu(file)) || + []; this.actionMenuState = { open: true, @@ -2404,10 +2144,7 @@ } if ( - Object.prototype.hasOwnProperty.call( - partialState, - "globalPermission", - ) + Object.prototype.hasOwnProperty.call(partialState, "globalPermission") ) { this.updatePermissions(partialState.globalPermission); } diff --git a/code/file-preview/g3-file/language.js b/code/file-preview/g3-file/language.js index 03ece5d2..01c35de9 100644 --- a/code/file-preview/g3-file/language.js +++ b/code/file-preview/g3-file/language.js @@ -57,12 +57,7 @@ var G3FileLanguages = { currentItem: "当前项", close: "关闭", }, - modal: { - maximize: "全屏", - restore: "恢复", - close: "关闭", }, - }, "en-US": { selectAll: "Select All", download: "Download", @@ -123,12 +118,7 @@ var G3FileLanguages = { currentItem: "Current", close: "Close", }, - modal: { - maximize: "Maximize", - restore: "Restore", - close: "Close", }, - }, "ru": { selectAll: "Выделить все", download: "Скачать", @@ -187,10 +177,5 @@ var G3FileLanguages = { currentItem: "Текущий", close: "Закрыть", }, - modal: { - maximize: "На весь экран", - restore: "Восстановить", - close: "Закрыть", }, - }, }; \ No newline at end of file diff --git a/code/file-preview/iframe-demo.html b/code/file-preview/iframe-demo.html new file mode 100644 index 00000000..a333690f --- /dev/null +++ b/code/file-preview/iframe-demo.html @@ -0,0 +1,160 @@ + + + + + + + G3File iframe 演示 + + + + + +
+
+

G3File 文件管理组件

+

通过 iframe 嵌入展示

+
+ +
+
+
+
+
+ index.html +
+ +
+ +
+

功能特性

+
    +
  • 文件列表展示与管理
  • +
  • 文件上传(支持阿里云 OSS)
  • +
  • 文件预览与下载
  • +
  • 文件删除
  • +
  • 双击事件支持
  • +
  • 自定义操作菜单
  • +
  • 上传/删除前确认回调
  • +
  • 多选与批量操作
  • +
+
+
+ + + \ No newline at end of file diff --git a/code/file-preview/index.html b/code/file-preview/index.html index 9f462c2c..0e6946ce 100644 --- a/code/file-preview/index.html +++ b/code/file-preview/index.html @@ -1,241 +1,164 @@ - - + + - 文件预览测试 + + + G3File 基础示例 + - - - - + - + - -
-
- - - - - -
- -
-
+
+
+

G3File 文件管理组件

+

基础示例 - 支持文件上传、预览、下载、删除等功能

+
- \ No newline at end of file diff --git a/code/file-preview/test.html b/code/file-preview/test.html deleted file mode 100644 index 2540f218..00000000 --- a/code/file-preview/test.html +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - G3 File UI Demo - - - - - - -
- - -
-
- -
- -
-
uploading-file.zip
-
- 3.2 MB -
-
-
-
-
- -
- - - -
-
- - -
-
- -
- -
-
finished.pdf
-
- 1.1 MB -
-
- -
- - - -
-
- - -
-
- -
- -
-
error-image.png
-
- 5.6 MB - 上传失败 -
-
- -
- - -
-
- -
- - - - - - \ No newline at end of file