ui框架-文件列表展示
ui框架-文件列表展示
介绍
UI框架的文件列表展示组件,可以展示文件夹,支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项,适用于文件管理、文件上传等场景。
功能特性
- 支持列表模式和网格模式的切换展示
- 支持文件和文件夹的层级展示
- 支持文件选择和文件夹选择
- 支持拖拽上传文件
- 支持多选/单选模式
- 支持文件类型过滤
- 支持文件大小限制
- 支持图片文件预览
- 支持返回上级目录
- 显示文件大小和最后修改时间
安装
npm install bbyh-ui-file-list-diaplay
使用示例
<template><FileListDisplaydefaultViewMode="grid":defaultFiles="files":acceptedTypes="['jpg', 'png', 'pdf']":maxFileSize="50":multiple="true":showFileSize="true":enableDrop="true"@select="handleSelect"@file-error="handleError"/>
</template><script setup>
import FileListDisplay from "bbyh-ui-file-list-diaplay";
import { ref } from 'vue';const files = ref([]);const handleSelect = (selectedFiles) => {console.log('Selected files:', selectedFiles);
};const handleError = (error) => {console.error('File error:', error);
};
</script>
Props 配置
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
defaultViewMode | String | ‘list’ | 默认视图模式,可选值:‘list’/‘grid’ |
defaultFiles | Array | [] | 默认显示的文件列表 |
multiple | Boolean | true | 是否允许多选 |
acceptedTypes | Array | [] | 允许的文件类型,空数组表示允许所有类型 |
maxFileSize | Number | 100 | 最大文件大小(MB) |
showFileSize | Boolean | true | 是否显示文件大小 |
enableDrop | Boolean | true | 是否允许拖拽上传 |
showToolbar | Boolean | true | 是否显示工具栏 |
showBackButton | Boolean | true | 是否显示返回上级目录按钮 |
事件
事件名 | 参数 | 说明 |
---|---|---|
select | selectedFiles | 选择文件时触发 |
enter-directory | directory | 进入文件夹时触发 |
file-error | {file, error} | 文件验证失败时触发 |
drop | files | 拖拽文件时触发 |
view-mode-change | mode | 视图模式改变时触发 |
支持的文件类型图标
- 常见文档:txt, doc, docx, xls, xlsx, ppt, pptx, pdf
- 图片:jpg, jpeg, png, gif, bmp, webp
- 开发相关:js, java, c, cpp, py, css, html, json
- 其他:exe, zip, rar 等
浏览器兼容性
- Chrome >= 80
- Firefox >= 75
- Safari >= 13
- Edge >= 80
开发说明
- 安装依赖
npm install
- 启动开发服务器
npm run serve
- 构建生产版本
npm run build
源码下载
ui框架-文件列表展示
npm仓库
ui框架-文件列表展示
核心代码
code/src/components/FileListDisplay.vue
<template><div class="file-list-display"><!-- 工具栏 --><div class="toolbar"><div class="view-mode"><span:class="['mode-item', { active: viewMode === 'list' }]"@click="viewMode = 'list'">列表模式</span><span:class="['mode-item', { active: viewMode === 'grid' }]"@click="viewMode = 'grid'">图标模式</span></div><div class="operations"><inputtype="file"ref="fileInput"@change="handleFileSelect"multiplestyle="display: none"><inputtype="file"ref="folderInput"@change="handleFolderSelect"webkitdirectorystyle="display: none"><button @click="selectFiles">选择文件</button><button @click="selectFolder">选择文件夹</button></div></div><!-- 文件列表区域 --><divclass="file-container":class="viewMode"@dragover.prevent@drop.prevent="handleDrop"><template v-if="currentDirectory.children.length"><!-- 显示返回上级目录按钮 --><divv-if="currentDirectory.parent"class="file-item"@click="navigateToParent"><div class="file-icon"><img :src="getFileIcon({type: 'back'})" alt="back"></div><div class="file-info"><div class="file-name">..</div></div></div><!-- 显示文件和文件夹 --><divv-for="item in currentDirectory.children":key="item.path"class="file-item":class="{ selected: selectedFiles.includes(item) }"@click="handleItemClick(item)"@dblclick="handleItemDoubleClick(item)"><div class="file-icon"><!-- 如果是图片文件则显示预览 --><imgv-if="isImageFile(item)":src="getImagePreview(item)":alt="item.name"class="image-preview"@error="handleImageError(item)"><!-- 非图片文件显示默认图标 --><img v-else :src="getFileIcon(item)" :alt="item.name" class="not-image-preview"></div><div class="file-info"><div class="file-name">{{ item.name }}</div><div class="file-size">{{formatFileSize(item.isDirectory ? calculateDirectorySize(item) : item.size)}}</div></div></div></template><div v-else class="empty-tip">当前文件夹为空</div></div></div>
</template><script setup>
import {onMounted, onUnmounted, ref, watch} from 'vue';
import {getFileIcon} from './fileIcons';
import {buildFileTree, calculateDirectorySize} from './fileUtils';// Props 定义
const props = defineProps({// 默认视图模式defaultViewMode: {type: String,default: 'list',validator: (value) => ['list', 'grid'].includes(value)},// 默认文件列表defaultFiles: {type: Array,default: () => []},// 是否允许多选multiple: {type: Boolean,default: true},// 允许的文件类型acceptedTypes: {type: Array,default: () => []},// 最大文件大小(MB)maxFileSize: {type: Number,default: 100},// 是否显示文件大小showFileSize: {type: Boolean,default: true},// 是否允许拖拽上传enableDrop: {type: Boolean,default: true},// 是否显示工具栏showToolbar: {type: Boolean,default: true},// 是否显示返回上级目录按钮showBackButton: {type: Boolean,default: true}
});// Emits 定义
const emit = defineEmits(['select', // 选择文件时触发'enter-directory', // 进入文件夹时触发'file-error', // 文件验证失败时触发'drop', // 拖拽文件时触发'view-mode-change' // 视图模式改变时触发
]);// 视图模式
const viewMode = ref(props.defaultViewMode);// 文件输入引用
const fileInput = ref();
const folderInput = ref();// 选择文件
const selectFiles = () => {fileInput.value.click();
};// 选择文件夹
const selectFolder = () => {folderInput.value.click();
};// 格式化文件大小
const formatFileSize = (size) => {if (size < 1024) return size + ' B';if (size < 1024 * 1024) return (size / 1024).toFixed(2) + ' KB';if (size < 1024 * 1024 * 1024) return (size / (1024 * 1024)).toFixed(2) + ' MB';return (size / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
};// 文件树根节点
const root = ref(buildFileTree([]));
// 当前显示的目录
const currentDirectory = ref(root.value);
// 选中的文件
const selectedFiles = ref([]);// 返回上级目录
const navigateToParent = () => {if (currentDirectory.value.parent) {currentDirectory.value = currentDirectory.value.parent;selectedFiles.value = [];}
};// 监听视图模式变化
watch(viewMode, (newMode) => {emit('view-mode-change', newMode);
});// 初始化默认文件
onMounted(() => {if (props.defaultFiles.length) {root.value = buildFileTree(props.defaultFiles);currentDirectory.value = root.value;}
});// 验证文件
const validateFile = (file) => {// 验证文件类型if (props.acceptedTypes.length) {const fileType = file.name.split('.').pop()?.toLowerCase();if (!props.acceptedTypes.includes(fileType)) {emit('file-error', {file,error: 'file-type-not-allowed'});return false;}}// 验证文件大小if (props.maxFileSize && file.size > props.maxFileSize * 1024 * 1024) {emit('file-error', {file,error: 'file-too-large'});return false;}return true;
};// 修改文件选择处理函数
const handleFileSelect = (event) => {const newFiles = Array.from(event.target.files).filter(validateFile);if (newFiles.length) {root.value = buildFileTree([...root.value.children.map(node => node.file), ...newFiles]);currentDirectory.value = root.value;emit('select', newFiles);}event.target.value = '';
};// 修改文件夹选择处理函数
const handleFolderSelect = (event) => {const newFiles = Array.from(event.target.files).filter(validateFile);if (newFiles.length) {root.value = buildFileTree([...root.value.children.map(node => node.file), ...newFiles]);currentDirectory.value = root.value;emit('select', newFiles);}event.target.value = '';
};// 修改拖拽处理函数
const handleDrop = (event) => {if (!props.enableDrop) return;const newFiles = Array.from(event.dataTransfer.files).filter(validateFile);if (newFiles.length) {root.value = buildFileTree([...root.value.children.map(node => node.file), ...newFiles]);currentDirectory.value = root.value;emit('drop', newFiles);}
};// 修改文件点击处理函数
const handleItemClick = (item) => {if (!props.multiple) {selectedFiles.value = [item];} else {const index = selectedFiles.value.indexOf(item);if (index === -1) {selectedFiles.value.push(item);} else {selectedFiles.value.splice(index, 1);}}emit('select', selectedFiles.value);
};// 修改双击处理函数
const handleItemDoubleClick = (item) => {if (item.isDirectory) {currentDirectory.value = item;selectedFiles.value = [];emit('enter-directory', item);}
};// 判断是否为图片文件
const isImageFile = (file) => {const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];const extension = file.name.split('.').pop()?.toLowerCase();return imageTypes.includes(extension);
};// 获取图片预览URL
const getImagePreview = (file) => {if (!file.file) return '';return URL.createObjectURL(file.file);
};// 处理图片加载错误
const handleImageError = (item) => {console.warn(`Failed to load preview for ${item.name}`);// 图片加载失败时使用默认图标return getFileIcon(item);
};// 在组件卸载时清理创建的URL
onUnmounted(() => {currentDirectory.value.children.forEach(item => {if (isImageFile(item)) {URL.revokeObjectURL(getImagePreview(item));}});
});
</script><style scoped>
.file-list-display {width: 100%;height: 100%;border: 1px solid #ddd;border-radius: 4px;
}.toolbar {padding: 10px;border-bottom: 1px solid #ddd;display: flex;justify-content: space-between;align-items: center;
}.view-mode .mode-item {padding: 5px 10px;cursor: pointer;margin-right: 10px;
}.view-mode .mode-item.active {background: #e6f7ff;color: #1890ff;border-radius: 4px;
}.operations button {margin-left: 10px;padding: 5px 15px;background-color: #67c23a;color: white;border: none;border-radius: 4px;cursor: pointer;
}.operations button:hover {background-color: #85ce61;
}.file-container {padding: 20px;min-height: 200px;
}.file-container.list .file-item {display: flex;align-items: center;padding: 10px;border-bottom: 1px solid #eee;
}.file-container.grid {display: grid;grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));gap: 20px;
}.file-container.grid .file-item {text-align: center;padding: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;
}.file-item {cursor: pointer;transition: background-color 0.2s;
}.file-item:hover {background-color: #f5f5f5;
}.file-item.selected {background-color: #e6f7ff;
}.file-icon {width: 60px;height: 60px;padding: 10px 0;display: flex;justify-content: center;align-items: center;
}/* 列表模式下的图片预览 */
.file-container.list .file-icon {width: 40px;height: 40px;padding: 2px;margin-right: 10px;
}.file-container.list .image-preview {width: 36px;height: 36px;object-fit: cover;border-radius: 4px;
}.file-container.list .not-image-preview {width: 36px;height: 36px;object-fit: cover;border-radius: 4px;
}/* 网格模式下的图片预览 */
.file-container.grid .file-icon {width: 100px;height: 100px;padding: 5px;
}.file-container.grid .image-preview {width: 90px;height: 90px;object-fit: cover;border-radius: 8px;
}.file-container.grid .not-image-preview {width: 90px;height: 90px;object-fit: cover;border-radius: 8px;
}.image-preview {box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);transition: transform 0.2s;
}.image-preview:hover {transform: scale(1.05);
}.file-info {flex: 1;
}.file-name {font-size: 14px;margin-bottom: 4px;word-break: break-all;
}.file-size {font-size: 12px;color: #999;
}.empty-tip {text-align: center;color: #999;padding: 40px 0;
}
</style>
code/src/components/fileIcons.js
const fileIcons = {// 文本文件txt: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#4A90E2" d="M14 2H6C4.89 2 4 2.89 4 4V20C4 21.11 4.89 22 6 22H18C19.11 22 20 21.11 20 20V8L14 2ZM16 18H8V16H16V18ZM16 14H8V12H16V14ZM13 9V3.5L18.5 9H13Z"/></svg>`,// 添加新的文件类型图标exe: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#F44336" d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M12,6A6,6 0 0,1 18,12A6,6 0 0,1 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6M12,8A4,4 0 0,0 8,12A4,4 0 0,0 12,16A4,4 0 0,0 16,12A4,4 0 0,0 12,8Z"/></svg>`,js: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#FFD600" d="M3,3H21V21H3V3M7.73,18.04C8.13,18.89 8.92,19.59 10.27,19.59C11.77,19.59 12.8,18.79 12.8,17.04V11.26H11.1V17C11.1,17.86 10.75,18.08 10.2,18.08C9.62,18.08 9.38,17.68 9.11,17.21L7.73,18.04M13.71,17.86C14.21,18.84 15.22,19.59 16.8,19.59C18.4,19.59 19.6,18.76 19.6,17.23C19.6,15.82 18.79,15.19 17.35,14.57L16.93,14.39C16.2,14.08 15.89,13.87 15.89,13.37C15.89,12.96 16.2,12.64 16.7,12.64C17.18,12.64 17.5,12.85 17.79,13.37L19.1,12.5C18.55,11.54 17.77,11.17 16.7,11.17C15.19,11.17 14.22,12.13 14.22,13.4C14.22,14.78 15.03,15.43 16.25,15.95L16.67,16.13C17.45,16.47 17.91,16.68 17.91,17.26C17.91,17.74 17.46,18.09 16.76,18.09C15.93,18.09 15.45,17.66 15.09,17.06L13.71,17.86Z"/></svg>`,java: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#E65100" d="M9.37,17.51C3.4,18.15 3.4,15.82 5,15.82C5,15.82 4.79,15.91 4.79,15.91C4.79,15.91 3,16.12 6.16,17.1C6.16,17.1 9.42,17.58 11.5,16.85C11.5,16.85 11.9,16.65 12.25,16.36C10.45,16.54 9.37,17.51 9.37,17.51M16.21,18.69C16.21,18.69 17.5,19.08 17.5,19.08C17.5,19.08 13.89,19.03 9.29,18.25C9.29,18.25 8.11,17.91 7.25,17.71C7.25,17.71 9.29,18.86 16.21,18.69M11.56,14.3C11.56,14.3 12,14.94 12,14.94C12,14.94 8.4,14.75 5.36,15.61C5.36,15.61 4,16.06 4,16.06C4,16.06 7.09,14.21 11.56,14.3M12.89,11.93C12.89,11.93 13.24,12.32 13.24,12.32C13.24,12.32 9.79,12.23 7.2,13.04C7.2,13.04 5.92,13.45 5.92,13.45C5.92,13.45 8.86,11.85 12.89,11.93M13.5,9.33C13.5,9.33 13.96,9.74 13.96,9.74C13.96,9.74 10.33,9.7 7.85,10.43C7.85,10.43 6.5,10.86 6.5,10.86C6.5,10.86 9.68,9.29 13.5,9.33M14,6.45C14,6.45 14.32,6.85 14.32,6.85C14.32,6.85 10.57,7.04 8.18,7.73C8.18,7.73 6.84,8.15 6.84,8.15C6.84,8.15 10.24,6.62 14,6.45M5.95,10.2L6.31,10.03C6.31,10.03 4.92,10.84 4.92,10.84C4.92,10.84 7.05,9.37 14.14,9.55C14.14,9.55 14.39,9.92 14.39,9.92C14.39,9.92 8.07,9.85 5.95,10.2M5.95,12.66L6.31,12.5C6.31,12.5 4.92,13.3 4.92,13.3C4.92,13.3 7.05,11.83 14.14,12.01C14.14,12.01 14.39,12.38 14.39,12.38C14.39,12.38 8.07,12.31 5.95,12.66M5.95,15.29L6.31,15.12C6.31,15.12 4.92,15.93 4.92,15.93C4.92,15.93 7.05,14.46 14.14,14.64C14.14,14.64 14.39,15.01 14.39,15.01C14.39,15.01 8.07,14.95 5.95,15.29Z"/></svg>`,c: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#0277BD" d="M16,2V4H14V2H10V4H8V2H4V22H8V20H10V22H14V20H16V22H20V2H16M18,20H16V18H14V20H10V18H8V20H6V4H8V6H10V4H14V6H16V4H18V20M16,10V8H8V10H16M16,16V14H8V16H16Z"/></svg>`,py: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#FFC107" d="M19.14,7.5A2.86,2.86 0 0,1 22,10.36V14.14A2.86,2.86 0 0,1 19.14,17H12C12,17.39 12.32,17.96 12.71,17.96H17V19.64A2.86,2.86 0 0,1 14.14,22.5H9.86A2.86,2.86 0 0,1 7,19.64V15.89C7,14.31 8.28,13.04 9.86,13.04H15.11C16.69,13.04 17.96,11.76 17.96,10.18V7.5H19.14M14.86,19.29C14.46,19.29 14.14,19.59 14.14,20.18C14.14,20.77 14.46,20.89 14.86,20.89A0.71,0.71 0 0,0 15.57,20.18C15.57,19.59 15.25,19.29 14.86,19.29M4.86,17.5C3.28,17.5 2,16.22 2,14.64V10.86C2,9.28 3.28,8 4.86,8H12C12,7.61 11.68,7.04 11.29,7.04H7V5.36C7,3.78 8.28,2.5 9.86,2.5H14.14C15.72,2.5 17,3.78 17,5.36V9.11C17,10.69 15.72,11.96 14.14,11.96H8.89C7.31,11.96 6.04,13.24 6.04,14.82V17.5H4.86M9.14,5.71C9.54,5.71 9.86,5.41 9.86,4.82C9.86,4.23 9.54,4.11 9.14,4.11C8.75,4.11 8.43,4.23 8.43,4.82C8.43,5.41 8.75,5.71 9.14,5.71Z"/></svg>`,cpp: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#00897B" d="M10.5,15.97L10.91,18.41C10.65,18.55 10.23,18.68 9.67,18.8C9.1,18.93 8.48,19 7.81,19C6.94,19 6.21,18.84 5.61,18.5C5,18.16 4.53,17.69 4.18,17.07C3.84,16.45 3.67,15.72 3.67,14.88L3.67,14.88V13.69C3.67,12.82 3.85,12.07 4.19,11.45C4.54,10.82 5,10.35 5.59,10.05C6.18,9.75 6.87,9.6 7.65,9.6C8.77,9.6 9.6,9.79 10.13,10.17C10.66,10.55 11,11.11 11.15,11.86L9.97,12.12C9.87,11.69 9.68,11.37 9.39,11.17C9.1,10.97 8.67,10.87 8.11,10.87C7.31,10.87 6.69,11.14 6.27,11.68C5.84,12.22 5.63,12.97 5.63,13.94L5.63,13.94V14.72C5.63,15.71 5.86,16.47 6.32,17.01C6.79,17.55 7.42,17.82 8.21,17.82C8.74,17.82 9.19,17.75 9.57,17.61L9.57,16.47H7.81V15.31H10.5V15.97M20,4C21.1,4 22,4.9 22,6V18C22,19.1 21.1,20 20,20H4C2.9,20 2,19.1 2,18V6C2,4.9 2.9,4 4,4H20M12.63,11.59H14.12V10.09H15.62V11.59H17.12V13.09H15.62V14.59H14.12V13.09H12.63V11.59M16.12,11.59H17.62V10.09H19.12V11.59H20.62V13.09H19.12V14.59H17.62V13.09H16.12V11.59Z"/></svg>`,css: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#42A5F5" d="M5,3L4.35,6.34H17.94L17.5,8.5H3.92L3.26,11.83H16.85L16.09,15.64L10.61,17.45L5.86,15.64L6.19,14H2.85L2.06,18L9.91,21L18.96,18L20.16,11.97L20.4,10.76L21.94,3H5Z"/></svg>`,html: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#E44D26" d="M12,17.56L16.07,16.43L16.62,10.33H9.38L9.2,8.3H16.8L17,6.31H7L7.56,12.32H14.45L14.22,14.9L12,15.5L9.78,14.9L9.64,13.24H7.64L7.93,16.43L12,17.56M4.07,3H19.93L18.5,19.2L12,21L5.5,19.2L4.07,3Z"/></svg>`,json: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#FBC02D" d="M5,3H7V5H5V10A2,2 0 0,1 3,12A2,2 0 0,1 5,14V19H7V21H5C3.93,20.73 3,20.1 3,19V15A2,2 0 0,0 1,13H0V11H1A2,2 0 0,0 3,9V5A2,2 0 0,1 5,3M19,3A2,2 0 0,1 21,5V9A2,2 0 0,0 23,11H24V13H23A2,2 0 0,0 21,15V19A2,2 0 0,1 19,21H17V19H19V14A2,2 0 0,1 21,12A2,2 0 0,1 19,10V5H17V3H19M12,15A1,1 0 0,1 13,16A1,1 0 0,1 12,17A1,1 0 0,1 11,16A1,1 0 0,1 12,15M8,15A1,1 0 0,1 9,16A1,1 0 0,1 8,17A1,1 0 0,1 7,16A1,1 0 0,1 8,15M16,15A1,1 0 0,1 17,16A1,1 0 0,1 16,17A1,1 0 0,1 15,16A1,1 0 0,1 16,15Z"/></svg>`,// Markdown文件md: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#7C4DFF" d="M20.56 18H3.44C2.65 18 2 17.37 2 16.59V7.41C2 6.63 2.65 6 3.44 6H20.56C21.35 6 22 6.63 22 7.41V16.59C22 17.37 21.35 18 20.56 18ZM6 12H8V16H10V12H12L9 9L6 12ZM14 16H16V12H18L15 9L12 12H14V16Z"/></svg>`,// PDF文件pdf: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#FF5252" d="M14,2L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2H14M18,20V9H13V4H6V20H18M10.92,12.31C10.68,11.54 10.15,9.08 11.55,9.04C12.95,9 12.03,12.16 12.03,12.16C12.42,13.65 14.05,14.72 14.05,14.72C14.55,14.57 17.4,14.24 17,15.72C16.57,17.2 13.5,15.81 13.5,15.81C11.55,15.95 10.09,16.47 10.09,16.47C8.96,18.58 7.64,19.5 7.1,18.61C6.43,17.5 9.23,16.07 9.23,16.07C10.68,13.72 10.92,12.31 10.92,12.31M11.57,13.15C11.17,14.45 10.37,15.84 10.37,15.84C11.22,15.5 13.08,15.11 13.08,15.11C11.94,14.11 11.57,13.15 11.57,13.15M13.81,15.1C14.05,15.23 15.03,15.63 15.35,15.4C15.67,15.17 14.3,14.96 13.81,15.1M11.33,9.93C10.67,9.89 10.65,10.86 10.76,11.45C10.87,12.04 11.16,11.76 11.16,11.76C11.16,11.76 11.64,9.97 11.33,9.93M9.94,16.61C9.94,16.61 8.7,17.45 8.7,17.71C8.7,17.97 9.45,17.15 9.94,16.61Z"/></svg>`,// 图片文件png: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#4CAF50" d="M19,19H5V5H19M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M13.96,12.29L11.21,15.83L9.25,13.47L6.5,17H17.5L13.96,12.29Z"/></svg>`,jpeg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#FF9800" d="M19,19H5V5H19M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M13.96,12.29L11.21,15.83L9.25,13.47L6.5,17H17.5L13.96,12.29Z"/></svg>`,// Excel文件xlsx: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#2E7D32" d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M15.8,20H14L12,16.6L10,20H8.2L11.1,15.5L8.2,11H10L12,14.4L14,11H15.8L12.9,15.5L15.8,20M13,9V3.5L18.5,9H13Z"/></svg>`,// Word文件docx: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#1976D2" d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M15.2,20H13.8L12,13.2L10.2,20H8.8L6.6,11H8.1L9.5,17.8L11.3,11H12.6L14.4,17.8L15.8,11H17.3L15.2,20M13,9V3.5L18.5,9H13Z"/></svg>`,// 文件夹folder: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#FFA000" d="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z"/></svg>`,// 在 fileIcons 对象中添加back: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#666666" d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"/></svg>`,// 默认文件图标default: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#757575" d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/></svg>`
};// 获取文件图标的函数
export function getFileIcon(file) {// 如果是文件夹if (file.type === 'directory') {return `data:image/svg+xml;base64,${btoa(fileIcons.folder)}`;}if (file.type === 'back') {return `data:image/svg+xml;base64,${btoa(fileIcons.back)}`;}// 获取文件扩展名const extension = file.name.split('.').pop()?.toLowerCase();// 根据文件扩展名返回对应的图标const iconSvg = fileIcons[extension] || fileIcons.default;return `data:image/svg+xml;base64,${btoa(iconSvg)}`;
}
code/src/components/fileUtils.js
// 文件节点类型
export class FileNode {constructor(file, parent = null) {this.name = file.name;this.size = file.size;this.type = file.type;this.lastModified = file.lastModified;this.isDirectory = file.type === 'directory';this.parent = parent;this.children = [];this.path = file.webkitRelativePath || file.name;this.file = file; // 保存原始文件对象}
}// 构建文件树
export function buildFileTree(files) {const root = {children: [],isDirectory: true,name: 'root'};for (const file of files) {const paths = file.webkitRelativePath ? file.webkitRelativePath.split('/') : [file.name];let currentNode = root;// 如果是来自文件夹选择的文件if (paths.length > 1) {// 遍历路径创建目录结构for (let i = 0; i < paths.length - 1; i++) {const dirName = paths[i];let dir = currentNode.children.find(child => child.name === dirName);if (!dir) {dir = new FileNode({name: dirName,type: 'directory',size: 0,lastModified: file.lastModified}, currentNode);currentNode.children.push(dir);}currentNode = dir;}}// 添加文件节点const fileNode = new FileNode(file, currentNode);currentNode.children.push(fileNode);}return root;
}// 计算文件夹大小
export function calculateDirectorySize(node) {if (!node.isDirectory) {return node.size;}return node.children.reduce((total, child) => {return total + calculateDirectorySize(child);}, 0);
}
效果展示
示例页面
图标模式
列表模式
相关文章:

ui框架-文件列表展示
ui框架-文件列表展示 介绍 UI框架的文件列表展示组件,可以展示文件夹,支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项,适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器
一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音…...

算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...

jdbc查询mysql数据库时,出现id顺序错误的情况
我在repository中的查询语句如下所示,即传入一个List<intager>的数据,返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致,会导致返回的id是从小到大排列的,但我不希望这样。 Query("SELECT NEW com…...

sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...

前端开发者常用网站
Can I use网站:一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use:Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站:MDN JavaScript权威网站:JavaScript | MDN...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...

如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
加密通信 + 行为分析:运营商行业安全防御体系重构
在数字经济蓬勃发展的时代,运营商作为信息通信网络的核心枢纽,承载着海量用户数据与关键业务传输,其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级,传统安全防护体系逐渐暴露出局限性&a…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...
Monorepo架构: Nx Cloud 扩展能力与缓存加速
借助 Nx Cloud 实现项目协同与加速构建 1 ) 缓存工作原理分析 在了解了本地缓存和远程缓存之后,我们来探究缓存是如何工作的。以计算文件的哈希串为例,若后续运行任务时文件哈希串未变,系统会直接使用对应的输出和制品文件。 2 …...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...

Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...
Vue3中的computer和watch
computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...

图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...

《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)
Name:3ddown Serial:FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名:Axure 序列号:8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...

02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...

uni-app学习笔记三十五--扩展组件的安装和使用
由于内置组件不能满足日常开发需要,uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件,需要安装才能使用。 一、安装扩展插件 安装方法: 1.访问uniapp官方文档组件部分:组件使用的入门教程 | uni-app官网 点击左侧…...