pre / templates /index.html
99i's picture
Create templates/index.html
0e1766d verified
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML文件管理器</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
background-color: #f5f5f5;
color: #333;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header h1 {
color: #2c3e50;
margin-bottom: 10px;
}
.header p {
color: #7f8c8d;
}
.toolbar {
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.btn {
background: #3498db;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.btn:hover {
background: #2980b9;
}
.btn-danger {
background: #e74c3c;
}
.btn-danger:hover {
background: #c0392b;
}
.btn-success {
background: #27ae60;
}
.btn-success:hover {
background: #229954;
}
.file-list {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
}
.file-item {
padding: 15px;
border-bottom: 1px solid #ecf0f1;
display: flex;
justify-content: space-between;
align-items: center;
transition: background-color 0.3s;
}
.file-item:hover {
background-color: #f8f9fa;
}
.file-item:last-child {
border-bottom: none;
}
.file-info {
flex: 1;
}
.file-title {
font-weight: bold;
color: #2c3e50;
margin-bottom: 5px;
}
.file-meta {
font-size: 12px;
color: #7f8c8d;
}
.file-actions {
display: flex;
gap: 10px;
}
.loading {
text-align: center;
padding: 40px;
color: #7f8c8d;
}
.empty {
text-align: center;
padding: 40px;
color: #7f8c8d;
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal.show {
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 8px;
width: 90%;
max-width: 800px;
max-height: 80vh;
overflow: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.close {
font-size: 28px;
font-weight: bold;
cursor: pointer;
color: #aaa;
}
.close:hover {
color: #000;
}
.preview-frame {
width: 100%;
height: 400px;
border: 1px solid #ddd;
border-radius: 4px;
}
.stats {
display: flex;
gap: 20px;
margin-bottom: 15px;
}
.stat-item {
background: #ecf0f1;
padding: 10px 15px;
border-radius: 4px;
text-align: center;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #2c3e50;
}
.stat-label {
font-size: 12px;
color: #7f8c8d;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<h1>HTML文件管理器</h1>
<p>管理和预览已上传的HTML文件</p>
</div>
<div class="toolbar">
<div class="stats">
<div class="stat-item">
<div class="stat-value">{{ files.length }}</div>
<div class="stat-label">文件总数</div>
</div>
</div>
<button class="btn" @click="refreshFiles">刷新列表</button>
</div>
<div class="file-list">
<div v-if="loading" class="loading">
加载中...
</div>
<div v-else-if="files.length === 0" class="empty">
暂无HTML文件
</div>
<div v-else>
<div v-for="file in files" :key="file.filename" class="file-item">
<div class="file-info">
<div class="file-title">
{{ file.title || file.filename }}
</div>
<div class="file-meta">
文件名: {{ file.filename }} | 创建时间: {{ file.created_time }}
</div>
</div>
<div class="file-actions">
<button class="btn btn-success" @click="previewFile(file)">预览</button>
<button class="btn" @click="openFile(file)">打开</button>
<button class="btn btn-danger" @click="deleteFile(file)">删除</button>
</div>
</div>
</div>
</div>
</div>
<!-- 预览模态框 -->
<div class="modal" :class="{ show: showPreview }" @click="closePreview">
<div class="modal-content" @click.stop>
<div class="modal-header">
<h3>{{ currentFile?.title || currentFile?.filename || '预览' }}</h3>
<span class="close" @click="closePreview">&times;</span>
</div>
<iframe v-if="currentFile" :src="currentFile.url" class="preview-frame"></iframe>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
files: [],
loading: false,
showPreview: false,
currentFile: null
};
},
methods: {
async loadFiles() {
this.loading = true;
try {
const response = await axios.get('/api/files');
this.files = response.data.files;
} catch (error) {
console.error('加载文件列表失败:', error);
alert('加载文件列表失败');
} finally {
this.loading = false;
}
},
refreshFiles() {
this.loadFiles();
},
previewFile(file) {
this.currentFile = file;
this.showPreview = true;
},
closePreview() {
this.showPreview = false;
this.currentFile = null;
},
openFile(file) {
window.open(file.url, '_blank');
},
async deleteFile(file) {
if (!confirm(`确定要删除文件 "${file.title || file.filename}" 吗?`)) {
return;
}
try {
await axios.delete(`/api/files/${file.filename}`);
alert('文件删除成功');
this.loadFiles();
} catch (error) {
console.error('删除文件失败:', error);
alert('删除文件失败: ' + (error.response?.data?.detail || error.message));
}
}
},
mounted() {
this.loadFiles();
}
}).mount('#app');
</script>
</body>
</html>