+
+function deleteFile(id) {
+ if (!confirm("确定删除?")) return;
+ const i = files.findIndex(f => f.id === id);
+ if (i > -1) files.splice(i, 1);
+ selected.delete(id);
+ render();
+}
+
+render();
+
\ No newline at end of file
diff --git a/code/file-preview/custom-file/index.css b/code/file-preview/custom-file/index.css
index 3c9d611f..7c1294b1 100644
--- a/code/file-preview/custom-file/index.css
+++ b/code/file-preview/custom-file/index.css
@@ -322,3 +322,15 @@
border-radius: 10px;
z-index: 1;
}
+
+/* 预览状态样式 */
+.g3-file-container .file-item.file-item-preview {
+ border-color: #4caf50;
+ box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2);
+ background: rgba(76, 175, 80, 0.05);
+}
+
+.g3-file-container .file-item.file-item-preview:hover {
+ border-color: #4caf50;
+ box-shadow: 0 6px 18px rgba(76, 175, 80, 0.2);
+}
diff --git a/code/file-preview/custom-file/index.js b/code/file-preview/custom-file/index.js
index c8dc4cb0..c89c302d 100644
--- a/code/file-preview/custom-file/index.js
+++ b/code/file-preview/custom-file/index.js
@@ -1,9 +1,10 @@
(function ($) {
- 'use strict';
+ "use strict";
var G3File = {
files: [],
selectedFiles: new Set(),
+ previewFiles: new Set(),
loadScripts: function (items, callback) {
var self = this;
@@ -25,7 +26,7 @@
// 防重复加载
var key = item.key;
- if (window[key + '_loading']) {
+ if (window[key + "_loading"]) {
var timer = setInterval(function () {
if (item.check && item.check()) {
clearInterval(timer);
@@ -35,19 +36,19 @@
return;
}
- window[key + '_loading'] = true;
+ window[key + "_loading"] = true;
- var script = document.createElement('script');
+ var script = document.createElement("script");
script.src = item.src;
script.onload = function () {
- window[key + '_loading'] = false;
+ window[key + "_loading"] = false;
done();
};
script.onerror = function () {
- window[key + '_loading'] = false;
- console.error(key + ' load failed');
+ window[key + "_loading"] = false;
+ console.error(key + " load failed");
done();
};
@@ -61,36 +62,42 @@
init: function (containerId, options) {
var self = this;
- this.$container = $('#' + containerId);
+ this.$container = $("#" + containerId);
this.options = $.extend({}, this.defaults, options);
- this.language = this.languages[this.options.language] || this.languages['zh-CN'];
+ this.language =
+ this.languages[this.options.language] || this.languages["zh-CN"];
this.files = this.options.data || [];
+ this.selectedFiles = new Set(); // 选中文件集合
+ this.previewFiles = new Set(); // 预览文件集合
- this.loadScripts([
- {
- key: 'fileSaver',
- src: '/custom-file/libs/FileSaver.min.js',
- check: function () {
- return typeof window.saveAs === 'function';
+ this.loadScripts(
+ [
+ {
+ key: "fileSaver",
+ src: "/custom-file/libs/FileSaver.min.js",
+ check: function () {
+ return typeof window.saveAs === "function";
+ },
+ },
+ {
+ key: "jsZip",
+ src: "/custom-file/libs/jszip.min.js",
+ check: function () {
+ return typeof window.JSZip === "function";
+ },
+ },
+ ],
+ function () {
+ self.render();
+ self.bindEvents();
+ self.updatePermissions(self.options.permissions);
+
+ if (self.options.data?.length) {
+ self.loadFiles(self.options.data);
}
},
- {
- key: 'jsZip',
- src: '/custom-file/libs/jszip.min.js',
- check: function () {
- return typeof window.JSZip === 'function';
- }
- }
- ], function () {
- self.render();
- self.bindEvents();
- self.updatePermissions(self.options.permissions);
-
- if (self.options.data?.length) {
- self.loadFiles(self.options.data);
- }
- });
+ );
return this;
},
@@ -104,56 +111,57 @@
onDownload: null,
onSelect: null,
onUpload: null,
- language: 'zh-CN',
+ onPreview: null,
+ language: "zh-CN",
maxFileSize: 10 * 1024 * 1024, // 默认最大10MB
permissions: {
select: true,
upload: true,
download: true,
delete: true,
- view: true
- }
+ view: true,
+ },
},
// =====================
// 多语言
// =====================
languages: {
- 'zh-CN': {
- selectAll: '全选',
- download: '批量下载',
- delete: '删除',
- upload: '上传',
- select: '选择',
- confirmDelete: '确定要删除选中的 {count} 个文件吗?',
- downloading: '准备下载 {count} 个文件',
- deleting: '删除中...',
- name: '名称',
- size: '大小',
- date: '日期',
- pending: '未上传',
- selectFiles: '选择文件',
- emptyTip1: '拖拽文件到这里 …',
- emptyTip2: '支持多文件同时上传/下载',
- emptyTip3: '支持粘贴图片上传',
- fileSizeError: '文件大小超过限制'
+ "zh-CN": {
+ selectAll: "全选",
+ download: "批量下载",
+ delete: "删除",
+ upload: "上传",
+ select: "选择",
+ confirmDelete: "确定要删除选中的 {count} 个文件吗?",
+ downloading: "准备下载 {count} 个文件",
+ deleting: "删除中...",
+ name: "名称",
+ size: "大小",
+ date: "日期",
+ pending: "未上传",
+ selectFiles: "选择文件",
+ emptyTip1: "拖拽文件到这里 …",
+ emptyTip2: "支持多文件同时上传/下载",
+ emptyTip3: "支持粘贴图片上传",
+ fileSizeError: "文件大小超过限制",
+ },
+ "en-US": {
+ selectAll: "Select All",
+ download: "Batch Download",
+ delete: "Delete",
+ upload: "Upload",
+ select: "Select",
+ confirmDelete: "Are you sure to delete {count} selected files?",
+ downloading: "Preparing to download {count} files",
+ deleting: "Deleting...",
+ name: "Name",
+ size: "Size",
+ date: "Date",
+ pending: "Pending",
+ selectFiles: "Select Files",
+ fileSizeError: "File size exceeds limit",
},
- 'en-US': {
- selectAll: 'Select All',
- download: 'Batch Download',
- delete: 'Delete',
- upload: 'Upload',
- select: 'Select',
- confirmDelete: 'Are you sure to delete {count} selected files?',
- downloading: 'Preparing to download {count} files',
- deleting: 'Deleting...',
- name: 'Name',
- size: 'Size',
- date: 'Date',
- pending: 'Pending',
- selectFiles: 'Select Files',
- fileSizeError: 'File size exceeds limit'
- }
},
// =====================
@@ -183,29 +191,29 @@
`,
default_file: `
- `
+ `,
},
// =====================
// 生成唯一ID
// =====================
generateUniqueId: function () {
- return 'file_' + Math.random().toString(36).slice(2) + Date.now();
+ return "file_" + Math.random().toString(36).slice(2) + Date.now();
},
// =====================
// 获取文件图标
// =====================
getFileIcon: function (filename) {
- var ext = filename.split('.').pop().toLowerCase();
- return this.staticIconMap[ext] || this.staticIconMap['default_file'];
+ var ext = filename.split(".").pop().toLowerCase();
+ return this.staticIconMap[ext] || this.staticIconMap["default_file"];
},
// =====================
// 更新语言
// =====================
updateLanguage: function (language) {
- this.language = this.languages[language] || this.languages['zh-CN'];
+ this.language = this.languages[language] || this.languages["zh-CN"];
this.render();
return this;
},
@@ -259,15 +267,15 @@
this.$container.html(html);
- this.$fileList = this.$container.find('.main');
- this.$emptyTip = this.$container.find('.empty-tip');
- this.$checkAll = this.$container.find('.check-all-checkbox');
- this.$btnDownload = this.$container.find('.download-all');
- this.$btnDelete = this.$container.find('.delete-all');
- this.$btnUpload = this.$container.find('.upload');
- this.$btnSelect = this.$container.find('.select');
- this.$fileInput = this.$container.find('.file-input');
- this.$pendingCount = this.$container.find('.pending-number');
+ this.$fileList = this.$container.find(".main");
+ this.$emptyTip = this.$container.find(".empty-tip");
+ this.$checkAll = this.$container.find(".check-all-checkbox");
+ this.$btnDownload = this.$container.find(".download-all");
+ this.$btnDelete = this.$container.find(".delete-all");
+ this.$btnUpload = this.$container.find(".upload");
+ this.$btnSelect = this.$container.find(".select");
+ this.$fileInput = this.$container.find(".file-input");
+ this.$pendingCount = this.$container.find(".pending-number");
return this;
},
@@ -276,8 +284,8 @@
var self = this;
// 选择
- this.$checkAll.off('change').on('change', function () {
- var checked = $(this).is(':checked');
+ this.$checkAll.off("change").on("change", function () {
+ var checked = $(this).is(":checked");
self.selectedFiles.clear();
@@ -287,20 +295,20 @@
});
}
- self.$fileList.find('.file-checkbox').prop('checked', checked);
+ self.$fileList.find(".file-checkbox").prop("checked", checked);
self.updateToolbarState();
});
// 批量下载
- this.$btnDownload.off('click').on('click', function () {
+ this.$btnDownload.off("click").on("click", function () {
var idSet = self.selectedFiles || new Set();
var files = self.files.filter(function (item) {
return idSet.has(item._id);
});
- if (typeof self.options?.onDownloadBatch === 'function') {
+ if (typeof self.options?.onDownloadBatch === "function") {
self.options.onDownloadBatch(files);
return;
}
@@ -309,7 +317,7 @@
});
// 批量删除
- this.$btnDelete.off('click').on('click', function () {
+ this.$btnDelete.off("click").on("click", function () {
var idSet = self.selectedFiles || new Set();
if (idSet.size === 0) return;
@@ -321,7 +329,7 @@
// =====================
// 1. 优先走外部回调(如果你要接后端)
// =====================
- if (typeof self.options?.onDeleteBatch === 'function') {
+ if (typeof self.options?.onDeleteBatch === "function") {
self.options.onDeleteBatch(deleteFiles, function (success) {
if (!success) return;
@@ -340,17 +348,17 @@
// =====================
// 选择按钮点击事件(选择本地文件)
// =====================
- this.$btnSelect.off('click').on('click', function () {
+ this.$btnSelect.off("click").on("click", function () {
self.$fileInput.click();
});
// =====================
// 上传按钮点击事件(交给外部处理)
// =====================
- this.$btnUpload.off('click').on('click', function () {
+ this.$btnUpload.off("click").on("click", function () {
// 获取所有未上传的文件
- const pendingFiles = self.files.filter(file => !file.uploaded);
- if (typeof self.options.onUpload === 'function') {
+ const pendingFiles = self.files.filter((file) => !file.uploaded);
+ if (typeof self.options.onUpload === "function") {
self.options.onUpload(pendingFiles);
}
});
@@ -358,7 +366,7 @@
// =====================
// 文件选择事件
// =====================
- this.$fileInput.off('change').on('change', function (e) {
+ this.$fileInput.off("change").on("change", function (e) {
var files = e.target.files;
if (!files || files.length === 0) return;
@@ -376,15 +384,15 @@
}
if (invalidFiles.length > 0) {
- var errorMsg = self.language.fileSizeError || '文件大小超过限制';
- alert(errorMsg + ': ' + invalidFiles.map(f => f.name).join(', '));
+ var errorMsg = self.language.fileSizeError || "文件大小超过限制";
+ alert(errorMsg + ": " + invalidFiles.map((f) => f.name).join(", "));
}
if (validFiles.length > 0) {
// 在本地临时插入文件
var tempFiles = validFiles.map(function (file) {
// 为图片文件创建本地临时连接
- let url = '';
+ let url = "";
if (self.isImageFile(file.name)) {
url = URL.createObjectURL(file);
}
@@ -396,7 +404,7 @@
uploadDate: new Date().toISOString(),
uploaded: false, // 标记为未上传
url: url, // 图片文件使用本地临时连接,其他文件为空
- file: file // 保存原始文件对象
+ file: file, // 保存原始文件对象
};
});
@@ -405,13 +413,13 @@
self.renderFileList();
self.updateToolbarState();
- if (typeof self.options.onSelect === 'function') {
+ if (typeof self.options.onSelect === "function") {
self.options.onSelect(validFiles);
}
}
// 重置文件输入框,以便可以重复选择相同的文件
- self.$fileInput.val('');
+ self.$fileInput.val("");
});
},
@@ -419,10 +427,10 @@
// 文件判断
// =====================
isImageFile: function (filename) {
- if (!filename || typeof filename !== 'string') return false;
+ if (!filename || typeof filename !== "string") return false;
- const ext = filename.slice(filename.lastIndexOf('.') + 1).toLowerCase();
- const imageExts = new Set(['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']);
+ const ext = filename.slice(filename.lastIndexOf(".") + 1).toLowerCase();
+ const imageExts = new Set(["jpg", "jpeg", "png", "gif", "bmp", "webp"]);
return imageExts.has(ext);
},
@@ -433,33 +441,31 @@
updateToolbarState: function () {
var hasSelection = this.selectedFiles.size > 0;
- this.$btnDownload.prop('disabled', !hasSelection);
- this.$btnDelete.prop('disabled', !hasSelection);
+ this.$btnDownload.prop("disabled", !hasSelection);
+ this.$btnDelete.prop("disabled", !hasSelection);
var allSelected =
- this.files.length > 0 &&
- this.selectedFiles.size === this.files.length;
+ this.files.length > 0 && this.selectedFiles.size === this.files.length;
- this.$checkAll.prop('checked', allSelected);
+ this.$checkAll.prop("checked", allSelected);
// 更新未上传文件数量
- var pendingCount = this.files.filter(file => !file.uploaded).length;
+ var pendingCount = this.files.filter((file) => !file.uploaded).length;
this.$pendingCount.text(pendingCount);
var self = this;
- this.$fileList.find('.file-item').each(function () {
- var id = $(this).data('id');
+ this.$fileList.find(".file-item").each(function () {
+ var id = $(this).data("id");
var isSelected = self.selectedFiles.has(id);
- $(this).find('.file-checkbox')
- .prop('checked', isSelected);
+ $(this).find(".file-checkbox").prop("checked", isSelected);
// 添加或移除选中样式
if (isSelected) {
- $(this).addClass('file-item-selected');
+ $(this).addClass("file-item-selected");
} else {
- $(this).removeClass('file-item-selected');
+ $(this).removeClass("file-item-selected");
}
});
},
@@ -473,7 +479,7 @@
this.files = files.map(function (file) {
return Object.assign({}, file, {
_id: self.generateUniqueId(),
- uploaded: true
+ uploaded: true,
});
});
@@ -498,7 +504,7 @@
this.$fileList.show();
this.$emptyTip.hide();
- var html = '';
+ var html = "";
$.each(this.files, function (_, file) {
html += self.createFileItem(file);
});
@@ -512,38 +518,50 @@
// =====================
formatFileSize: function (bytes) {
if (bytes === null || bytes === undefined || isNaN(bytes) || bytes <= 0) {
- return '-';
+ return "-";
}
var k = 1024;
- var sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
+ var sizes = ["B", "KB", "MB", "GB", "TB"];
var i = Math.floor(Math.log(bytes) / Math.log(k));
i = Math.max(0, Math.min(i, sizes.length - 1));
- return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i];
+ return (bytes / Math.pow(k, i)).toFixed(2) + " " + sizes[i];
},
// =====================
// 格式化日期
// =====================
formatDate: function (dateStr) {
- if (!dateStr) return '-';
+ if (!dateStr) return "-";
var date = new Date(dateStr);
if (isNaN(date.getTime())) {
- return '-';
+ return "-";
}
var year = date.getFullYear();
- var month = ('0' + (date.getMonth() + 1)).slice(-2);
- var day = ('0' + date.getDate()).slice(-2);
- var hours = ('0' + date.getHours()).slice(-2);
- var minutes = ('0' + date.getMinutes()).slice(-2);
- var seconds = ('0' + date.getSeconds()).slice(-2);
+ var month = ("0" + (date.getMonth() + 1)).slice(-2);
+ var day = ("0" + date.getDate()).slice(-2);
+ var hours = ("0" + date.getHours()).slice(-2);
+ var minutes = ("0" + date.getMinutes()).slice(-2);
+ var seconds = ("0" + date.getSeconds()).slice(-2);
- return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;
+ return (
+ year +
+ "-" +
+ month +
+ "-" +
+ day +
+ " " +
+ hours +
+ ":" +
+ minutes +
+ ":" +
+ seconds
+ );
},
// =====================
@@ -555,7 +573,7 @@
const downloadDisabled = !perm.download;
const deleteDisabled = !perm.delete;
- let content = '';
+ let content = "";
if (this.isImageFile(file.name)) {
content = `

`;
@@ -564,15 +582,23 @@
}
// 未上传标记
- const pendingBadge = file.uploaded ? '' : `
+ const pendingBadge = file.uploaded
+ ? ""
+ : `
${this.language.pending}
`;
+ // 预览状态(使用class标识)
+ const isPreview = this.previewFiles.has(file._id);
+
+ // 选中状态
+ const isSelected = this.selectedFiles.has(file._id);
+
return `
-
-
+
+
${content}
${pendingBadge}
@@ -583,11 +609,11 @@
-
+
${this.staticIconMap.download}
-
+
${this.staticIconMap.delete}
@@ -606,26 +632,57 @@
var self = this;
// =====================
- // 文件item点击事件
+ // 文件item点击事件(用于预览)
// =====================
- this.$fileList.off('click', '.file-item')
- .on('click', '.file-item', function (e) {
- var $checkbox = $(this).find('.file-checkbox');
- $checkbox.prop('checked', !$checkbox.is(':checked'));
- $checkbox.trigger('change');
- });
+ this.$fileList
+ .off("click", ".file-item")
+ .on("click", ".file-item", function (e) {
+ var target = $(e.target);
+ // 如果点击的是 checkbox、download、delete、more 按钮,则不执行预览逻辑
+ if (
+ target.is(".file-checkbox") ||
+ target.closest(".file-action").length > 0
+ ) {
+ return;
+ }
- this.$fileList.off('click', '.download')
- .on('click', '.download', function (e) {
- e.preventDefault();
- e.stopPropagation();
-
- var id = $(this).closest('.file-item').data('id');
- var file = self.files.find(f => f._id === id);
+ var id = $(this).data("id");
+ var file = self.files.find((f) => f._id === id);
if (!file) return;
- if (typeof self.options.onDownload === 'function') {
+ // 切换预览状态(只允许同时预览一个文件)
+ var isPreview = self.previewFiles.has(id);
+ if (isPreview) {
+ // 取消预览
+ self.previewFiles.delete(id);
+ } else {
+ // 开启预览前,先清除其他文件的预览状态
+ self.previewFiles.clear();
+ self.previewFiles.add(id);
+ }
+
+ // 重新渲染文件列表以更新预览状态
+ self.renderFileList();
+
+ // 调用外部回调,传递文件和预览状态
+ if (typeof self.options.onPreview === "function") {
+ self.options.onPreview(file, !isPreview);
+ }
+ });
+
+ this.$fileList
+ .off("click", ".download")
+ .on("click", ".download", function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ var id = $(this).closest(".file-item").data("id");
+ var file = self.files.find((f) => f._id === id);
+
+ if (!file) return;
+
+ if (typeof self.options.onDownload === "function") {
self.options.onDownload([file]);
return;
}
@@ -633,11 +690,12 @@
self.internalDownload(file);
});
- this.$fileList.off('change', '.file-checkbox')
- .on('change', '.file-checkbox', function () {
- var id = $(this).closest('.file-item').data('id');
+ this.$fileList
+ .off("change", ".file-checkbox")
+ .on("change", ".file-checkbox", function () {
+ var id = $(this).closest(".file-item").data("id");
- if ($(this).is(':checked')) {
+ if ($(this).is(":checked")) {
self.selectedFiles.add(id);
} else {
self.selectedFiles.delete(id);
@@ -646,17 +704,18 @@
self.updateToolbarState();
});
- this.$fileList.off('click', '.delete')
- .on('click', '.delete', function (e) {
+ this.$fileList
+ .off("click", ".delete")
+ .on("click", ".delete", function (e) {
e.preventDefault();
e.stopPropagation();
- var id = $(this).closest('.file-item').data('id');
- var file = self.files.find(f => f._id === id);
+ var id = $(this).closest(".file-item").data("id");
+ var file = self.files.find((f) => f._id === id);
if (!file) return;
- if (typeof self.options.onDelete === 'function') {
+ if (typeof self.options.onDelete === "function") {
self.options.onDelete([file]);
return;
}
@@ -667,11 +726,10 @@
// =====================
// 更多按钮点击事件(阻止冒泡)
// =====================
- this.$fileList.off('click', '.more')
- .on('click', '.more', function (e) {
- e.preventDefault();
- e.stopPropagation();
- });
+ this.$fileList.off("click", ".more").on("click", ".more", function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ });
},
getUniqueFileName: function (fileName, files) {
@@ -683,8 +741,13 @@
// 如果文件名已经存在,则在文件名后加上 (1), (2), ...
while (files.has(uniqueFileName)) {
// 基于原始文件名为基础来添加计数器
- const fileNameWithoutExtension = originalFileName.replace(/\.[^/.]+$/, "");
- const extension = originalFileName.slice(originalFileName.lastIndexOf("."));
+ const fileNameWithoutExtension = originalFileName.replace(
+ /\.[^/.]+$/,
+ "",
+ );
+ const extension = originalFileName.slice(
+ originalFileName.lastIndexOf("."),
+ );
uniqueFileName = `${fileNameWithoutExtension} (${counter})${extension}`;
counter++;
}
@@ -694,13 +757,13 @@
getFileBlob: async function (url) {
if (!url) {
- throw new Error('无效的文件URL');
+ throw new Error("无效的文件URL");
}
const res = await fetch(url);
if (!res.ok) {
- throw new Error('网络请求失败,状态码:' + res.status);
+ throw new Error("网络请求失败,状态码:" + res.status);
}
// ⭐ 比 blob 更轻一点(后续 zip 更快)
@@ -714,11 +777,11 @@
// =====================
internalDownload: async function (file) {
if (!file) {
- console.warn('下载失败:文件对象无效', file);
+ console.warn("下载失败:文件对象无效", file);
return;
}
- console.log('开始下载文件:', file.name);
+ console.log("开始下载文件:", file.name);
try {
let blob;
@@ -730,14 +793,13 @@
// 未上传的文件,使用本地文件对象
blob = file.file;
} else {
- console.warn('下载失败:文件没有URL或本地文件对象', file);
+ console.warn("下载失败:文件没有URL或本地文件对象", file);
return;
}
- saveAs(blob, file.name || '未命名文件');
-
+ saveAs(blob, file.name || "未命名文件");
} catch (err) {
- console.error('文件下载失败:', err);
+ console.error("文件下载失败:", err);
}
},
@@ -766,7 +828,7 @@
// 未上传的文件,使用本地文件对象
content = await item.file.arrayBuffer();
} else {
- console.warn('跳过失败文件:', item.name, '没有URL或本地文件对象');
+ console.warn("跳过失败文件:", item.name, "没有URL或本地文件对象");
failedCount++;
return;
}
@@ -776,9 +838,8 @@
zip.file(uniqueFileName, content);
addedFiles.add(uniqueFileName);
-
} catch (err) {
- console.warn('跳过失败文件:', item.name, err);
+ console.warn("跳过失败文件:", item.name, err);
failedCount++;
} finally {
current++;
@@ -793,14 +854,12 @@
};
try {
- await Promise.all(
- Array.from({ length: concurrency }, runner)
- );
+ await Promise.all(Array.from({ length: concurrency }, runner));
const blob = await zip.generateAsync({ type: "blob" });
const now = new Date();
- const pad = n => n < 10 ? "0" + n : n;
+ const pad = (n) => (n < 10 ? "0" + n : n);
const fileName =
now.getFullYear() +
@@ -812,11 +871,10 @@
saveAs(blob, fileName + ".zip");
console.log(
- `下载完成:${total - failedCount}/${total},失败:${failedCount}`
+ `下载完成:${total - failedCount}/${total},失败:${failedCount}`,
);
callback && callback();
-
} catch (err) {
console.error("Batch download failed:", err);
callback && callback();
@@ -833,10 +891,10 @@
try {
URL.revokeObjectURL(file.url);
} catch (e) {
- console.warn('Failed to revoke object URL:', e);
+ console.warn("Failed to revoke object URL:", e);
}
}
- this.files = this.files.filter(f => f._id !== file._id);
+ this.files = this.files.filter((f) => f._id !== file._id);
this.selectedFiles.delete(file._id);
this.updateToolbarState();
this.renderFileList();
@@ -847,7 +905,7 @@
// =====================
internalDeleteBatch: function (files) {
if (!files || files.length === 0) return;
- files.forEach(file => this.internalDelete(file));
+ files.forEach((file) => this.internalDelete(file));
this.updateToolbarState();
this.renderFileList();
},
@@ -859,16 +917,92 @@
this.options.permissions = permissions;
- this.$btnSelect?.prop('disabled', !permissions.select);
- this.$btnUpload?.prop('disabled', !permissions.upload);
- this.$btnDownload?.prop('disabled', !permissions.download);
- this.$btnDelete?.prop('disabled', !permissions.delete);
+ this.$btnSelect?.prop("disabled", !permissions.select);
+ this.$btnUpload?.prop("disabled", !permissions.upload);
+ this.$btnDownload?.prop("disabled", !permissions.download);
+ this.$btnDelete?.prop("disabled", !permissions.delete);
- this.$fileList.find('.file-action.download')
- .toggleClass('is-disabled', !permissions.download);
+ this.$fileList
+ .find(".file-action.download")
+ .toggleClass("is-disabled", !permissions.download);
- this.$fileList.find('.file-action.delete')
- .toggleClass('is-disabled', !permissions.delete);
+ this.$fileList
+ .find(".file-action.delete")
+ .toggleClass("is-disabled", !permissions.delete);
+ },
+
+ // =====================
+ // 外部方法:切换文件选中状态
+ // 参数:fileId - 文件ID,isSelected - 是否选中(可选,不传则切换)
+ // =====================
+ toggleSelect: function (fileId, isSelected) {
+ var file = this.files.find((f) => f._id === fileId);
+ if (!file) return;
+
+ var currentSelected = this.selectedFiles.has(fileId);
+ var newSelected =
+ isSelected !== undefined ? isSelected : !currentSelected;
+
+ if (newSelected) {
+ this.selectedFiles.add(fileId);
+ } else {
+ this.selectedFiles.delete(fileId);
+ }
+
+ this.updateToolbarState();
+ this.renderFileList();
+
+ return newSelected;
+ },
+
+ // =====================
+ // 外部方法:获取当前选中的文件列表
+ // =====================
+ getSelectedFiles: function () {
+ return this.files.filter((f) => this.selectedFiles.has(f._id));
+ },
+
+ // =====================
+ // 外部方法:获取当前预览的文件列表
+ // =====================
+ getPreviewFiles: function () {
+ return this.files.filter((f) => this.previewFiles.has(f._id));
+ },
+
+ // =====================
+ // 外部方法:获取所有文件列表
+ // =====================
+ getFiles: function () {
+ return this.files;
+ },
+
+ // =====================
+ // 外部方法:切换文件预览状态
+ // 参数:fileId - 文件ID,isPreview - 是否预览(可选,不传则切换)
+ // =====================
+ togglePreview: function (fileId, isPreview) {
+ var file = this.files.find((f) => f._id === fileId);
+ if (!file) return;
+
+ var currentPreview = this.previewFiles.has(fileId);
+ var newPreview = isPreview !== undefined ? isPreview : !currentPreview;
+
+ if (newPreview) {
+ // 开启预览前,先清除其他文件的预览状态(只允许同时预览一个)
+ this.previewFiles.clear();
+ this.previewFiles.add(fileId);
+ } else {
+ this.previewFiles.delete(fileId);
+ }
+
+ this.renderFileList();
+
+ // 调用外部回调
+ if (typeof this.options.onPreview === "function") {
+ this.options.onPreview(file, newPreview);
+ }
+
+ return newPreview;
},
};
@@ -876,33 +1010,33 @@
// jQuery 插件封装
// ==========================
$.fn.G3File = function (options) {
-
var args = Array.prototype.slice.call(arguments, 1);
var instance;
+ var methodResult; // 存储方法调用结果
this.each(function () {
var $this = $(this);
- instance = $this.data('G3File');
+ instance = $this.data("G3File");
// ========= 初始化 =========
if (!instance) {
instance = Object.create(G3File);
instance.init(this.id, options);
- $this.data('G3File', instance);
+ $this.data("G3File", instance);
// ========= 方法调用 =========
- } else if (typeof options === 'string') {
+ } else if (typeof options === "string") {
var method = options;
if (instance[method]) {
- instance[method].apply(instance, args);
+ // 保存方法调用结果
+ methodResult = instance[method].apply(instance, args);
}
}
});
- // ⭐关键:返回 instance(支持 file.xxx)
- return instance || this;
+ // 如果是方法调用,返回方法结果;否则返回实例
+ return methodResult !== undefined ? methodResult : instance || this;
};
-
-})(jQuery);
\ No newline at end of file
+})(jQuery);
diff --git a/code/file-preview/index.html b/code/file-preview/index.html
index 87a457d3..46ee3b1d 100644
--- a/code/file-preview/index.html
+++ b/code/file-preview/index.html
@@ -31,6 +31,10 @@
+
+
+
+
@@ -54,6 +58,14 @@
onUpload: function () {
console.log('Upload clicked');
alert('上传功能');
+ },
+ onPreview: function (file, isPreview) {
+ console.log('预览事件:', file.name, '预览状态:', isPreview);
+ if (isPreview) {
+ alert('开始预览文件: ' + file.name);
+ } else {
+ alert('停止预览文件: ' + file.name);
+ }
}
});
@@ -70,6 +82,45 @@
{ name: '项目报告2.docx', size: 524288, uploadDate: '2024-02-20T14:45:00', url: '' },
]);
});
+
+ // 测试外部方法:切换选中状态
+ $('#toggleSelectBtn').click(function () {
+ const allFiles = file.getFiles();
+ if (allFiles.length === 0) {
+ alert('请先加载文件');
+ return;
+ }
+ // 获取第一个文件的ID并切换选中状态
+ const firstFileId = allFiles[0]._id;
+ const isSelected = file.toggleSelect(firstFileId);
+ console.log('文件', allFiles[0].name, '选中状态:', isSelected);
+ });
+
+ // 测试外部方法:获取选中文件
+ $('#getSelectedBtn').click(function () {
+ const selectedFiles = file.getSelectedFiles();
+ console.log('选中的文件:', selectedFiles);
+ alert('选中的文件数量: ' + selectedFiles.length + '\n文件列表: ' + selectedFiles.map(f => f.name).join(', '));
+ });
+
+ // 测试外部方法:切换预览状态
+ $('#togglePreviewBtn').click(function () {
+ const allFiles = file.getFiles();
+ if (allFiles.length === 0) {
+ alert('请先加载文件');
+ return;
+ }
+ const firstFileId = allFiles[0]._id;
+ const isPreview = file.togglePreview(firstFileId);
+ console.log('文件', allFiles[0].name, '预览状态:', isPreview);
+ });
+
+ // 测试外部方法:获取预览文件
+ $('#getPreviewBtn').click(function () {
+ const previewFiles = file.getPreviewFiles();
+ console.log('预览的文件:', previewFiles);
+ alert('预览的文件数量: ' + previewFiles.length + '\n文件列表: ' + previewFiles.map(f => f.name).join(', '));
+ });
});
diff --git a/code/file-preview/test.html b/code/file-preview/test.html
new file mode 100644
index 00000000..9fd5806d
--- /dev/null
+++ b/code/file-preview/test.html
@@ -0,0 +1,168 @@
+
+
+
+
+
+
附件管理示例
+
+
+
+
+
+
+
+ 已选 0 个
+
+
+
+
+
+
+
+
+
+