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