文件浏览功能

This commit is contained in:
liuwei
2025-06-04 15:41:12 +08:00
parent 10269f1fa3
commit e970d6918a

View File

@@ -1,44 +1,82 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}文件浏览{% endblock %} {% block title %}文件浏览{% endblock %}
{% block content %} {% block content %}
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card"> <el-card class="box-card">
<div class="card-header"> <div slot="header" class="clearfix">
<h3 class="card-title">文件浏览</h3> <span>文件浏览</span>
<div class="card-tools"> <div style="float: right;">
<div class="input-group"> <el-input
<input type="text" id="current-path" class="form-control" readonly> v-model="currentPath"
<div class="input-group-append"> placeholder="当前路径"
<button class="btn btn-default" onclick="navigateUp()"> style="width: 300px; margin-right: 10px;"
<i class="fas fa-arrow-up"></i> 上级目录 readonly>
</button> </el-input>
</div> <el-button type="primary" size="small" @click="navigateUp">
</div> <i class="el-icon-arrow-up"></i> 上级目录
</el-button>
</div> </div>
</div> </div>
<div class="card-body"> <el-table
<div class="table-responsive"> :data="fileList"
<table class="table table-hover"> style="width: 100%"
<thead> v-loading="loading">
<tr> <el-table-column
<th>名称</th> prop="name"
<th>类型</th> label="名称"
<th>大小</th> min-width="200">
<th>修改时间</th> <template slot-scope="scope">
<th>操作</th> <el-link
</tr> :type="scope.row.is_dir ? 'primary' : 'info'"
</thead> @click="scope.row.is_dir ? navigateTo(scope.row.name) : null">
<tbody id="file-list"> <i :class="scope.row.is_dir ? 'el-icon-folder' : 'el-icon-document'"></i>
<!-- 文件列表将通过JavaScript动态加载 --> [[ scope.row.name ]]
</tbody> </el-link>
</table> </template>
</div> </el-table-column>
</div> <el-table-column
</div> prop="type"
label="类型"
width="100">
<template slot-scope="scope">
[[ scope.row.is_dir ? '目录' : '文件' ]]
</template>
</el-table-column>
<el-table-column
prop="size"
label="大小"
width="120">
<template slot-scope="scope">
[[ scope.row.is_dir ? '-' : formatFileSize(scope.row.size) ]]
</template>
</el-table-column>
<el-table-column
prop="modified"
label="修改时间"
width="180">
<template slot-scope="scope">
[[ formatDate(scope.row.modified) ]]
</template>
</el-table-column>
<el-table-column
label="操作"
width="120">
<template slot-scope="scope">
<el-button
v-if="!scope.row.is_dir"
type="primary"
size="mini"
@click="downloadFile(scope.row.name)">
下载
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div> </div>
</div> </div>
</div> </div>
@@ -46,98 +84,71 @@
{% block scripts %} {% block scripts %}
<script> <script>
let currentPath = ''; new Vue({
el: '#app',
function formatFileSize(bytes) { delimiters: ['[[', ']]'],
if (bytes === 0) return '0 B'; data() {
const k = 1024; return {
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; currentPath: '',
const i = Math.floor(Math.log(bytes) / Math.log(k)); fileList: [],
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; loading: false
} }
},
function formatDate(timestamp) { created() {
return new Date(timestamp * 1000).toLocaleString(); this.loadFiles('');
} },
methods: {
function loadFiles(path = '') { formatFileSize(bytes) {
currentPath = path; if (bytes === 0) return '0 B';
$('#current-path').val(path || '/'); const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
$.get('/api/list_files', { path: path }) const i = Math.floor(Math.log(bytes) / Math.log(k));
.done(function(response) { return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
if (response.success) { },
const fileList = $('#file-list'); formatDate(timestamp) {
fileList.empty(); return new Date(timestamp * 1000).toLocaleString();
},
response.data.items.forEach(function(item) { loadFiles(path) {
const row = $('<tr>'); this.loading = true;
this.currentPath = path || '/';
// 名称列
const nameCell = $('<td>'); axios.get('/api/list_files', {
const nameLink = $('<a>') params: { path: path }
.text(item.name) })
.attr('href', '#') .then(response => {
.css('color', item.is_dir ? '#007bff' : 'inherit'); if (response.data.success) {
this.fileList = response.data.data.items;
if (item.is_dir) { } else {
nameLink.click(function(e) { this.$message.error('加载文件列表失败:' + response.data.message);
e.preventDefault(); }
loadFiles(path ? path + '/' + item.name : item.name); })
}); .catch(error => {
} this.$message.error('加载文件列表失败');
console.error('Error:', error);
nameCell.append(nameLink); })
row.append(nameCell); .finally(() => {
this.loading = false;
// 类型列 });
row.append($('<td>').text(item.is_dir ? '目录' : '文件')); },
navigateTo(dirName) {
// 大小列 const newPath = this.currentPath === '/' ? dirName : this.currentPath + '/' + dirName;
row.append($('<td>').text(item.is_dir ? '-' : formatFileSize(item.size))); this.loadFiles(newPath);
},
// 修改时间列 navigateUp() {
row.append($('<td>').text(formatDate(item.modified))); if (!this.currentPath) return;
// 操作列 const lastSlashIndex = this.currentPath.lastIndexOf('/');
const actionsCell = $('<td>'); if (lastSlashIndex === -1) {
if (!item.is_dir) { this.loadFiles('');
const downloadBtn = $('<button>')
.addClass('btn btn-sm btn-primary')
.text('下载')
.click(function() {
window.location.href = '/api/download_file?path=' +
encodeURIComponent(path ? path + '/' + item.name : item.name);
});
actionsCell.append(downloadBtn);
}
row.append(actionsCell);
fileList.append(row);
});
} else { } else {
alert('加载文件列表失败:' + response.message); this.loadFiles(this.currentPath.substring(0, lastSlashIndex));
} }
}) },
.fail(function() { downloadFile(fileName) {
alert('加载文件列表失败'); const filePath = this.currentPath === '/' ? fileName : this.currentPath + '/' + fileName;
}); window.location.href = '/api/download_file?path=' + encodeURIComponent(filePath);
} }
function navigateUp() {
if (!currentPath) return;
const lastSlashIndex = currentPath.lastIndexOf('/');
if (lastSlashIndex === -1) {
loadFiles('');
} else {
loadFiles(currentPath.substring(0, lastSlashIndex));
} }
}
// 页面加载完成后加载根目录
$(document).ready(function() {
loadFiles('');
}); });
</script> </script>
{% endblock %} {% endblock %}