Files
zjpb.net/templates/admin/batch_import.html
Jowe 2eefaa8cc9 feat: v3.2 - 用户管理功能和后台菜单统一
新增功能:
- 用户管理列表页面(搜索、分页)
- 用户详情页面(基本信息、收藏统计)
- 管理员重置用户密码功能
- 管理员修改用户昵称功能
- 管理后台首页添加用户统计卡片

优化改进:
- 统一后台菜单结构,创建可复用的 sidebar 组件
- 所有后台页面使用统一菜单,避免硬编码
- 优化权限配置文件,清理冗余规则

技术文档:
- 添加任务分解规则文档
- 添加后台菜单统一规则文档
- 添加数据库字段修复脚本

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 23:20:35 +08:00

283 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>批量导入 - ZJPB - 自己品吧</title>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Noto+Sans:wght@400;500;700&display=swap" rel="stylesheet">
<!-- Google Material Symbols -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom Admin Theme -->
<link href="{{ url_for('static', filename='css/admin-sidebar.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/admin-actions.css') }}" rel="stylesheet">
</head>
<body class="admin-sidebar-layout">
{% set active_page = 'batch_import' %}
{% include 'admin/components/sidebar.html' %}
<!-- 右侧主内容区 -->
<div class="admin-main">
<!-- 顶部导航栏 -->
<header class="admin-header">
<div class="header-breadcrumb">
<a href="{{ url_for('admin.index') }}" class="breadcrumb-link">控制台</a>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-current">批量导入</span>
</div>
<div class="header-actions">
<div class="search-box">
<span class="material-symbols-outlined search-icon">search</span>
<input type="text" placeholder="全局搜索..." class="search-input">
</div>
<button class="header-btn">
<span class="material-symbols-outlined">notifications</span>
</button>
<button class="header-btn">
<span class="material-symbols-outlined">settings</span>
</button>
</div>
</header>
<!-- 页面内容 -->
<main class="admin-content">
<div class="page-header">
<div>
<h1 class="page-title">批量导入网站</h1>
<p class="page-description">支持通过URL列表或Chrome书签文件批量导入网站</p>
</div>
</div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'success' if category == 'success' else 'danger' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="close" data-dismiss="alert">
<span>&times;</span>
</button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="card">
<div class="card-body">
<ul class="nav nav-tabs mb-4" role="tablist">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#url-list">URL列表导入</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#bookmark-file">Chrome书签导入</a>
</li>
</ul>
<div class="tab-content">
<!-- URL列表导入 -->
<div class="tab-pane fade show active" id="url-list">
<form method="POST" action="{{ url_for('batch_import') }}">
<input type="hidden" name="import_type" value="url_list">
<div class="form-group">
<label for="url-textarea">网站URL列表</label>
<textarea class="form-control" id="url-textarea" name="url_list" rows="10"
placeholder="每行一个URL例如&#10;https://www.example.com&#10;https://www.google.com&#10;https://github.com"></textarea>
<small class="form-text text-muted">
每行输入一个网站URL系统将自动抓取网站名称、描述和Logo
</small>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="auto-activate" name="auto_activate" checked>
<label class="custom-control-label" for="auto-activate">自动启用导入的网站</label>
</div>
</div>
<button type="submit" class="btn btn-primary">
<span class="material-symbols-outlined" style="font-size: 18px; vertical-align: middle;">upload_file</span>
开始导入
</button>
</form>
</div>
<!-- Chrome书签导入 -->
<div class="tab-pane fade" id="bookmark-file">
<form method="POST" action="{{ url_for('batch_import') }}" enctype="multipart/form-data">
<input type="hidden" name="import_type" value="bookmark_file">
<div class="alert alert-info">
<strong>如何导出Chrome书签</strong>
<ol class="mb-0 mt-2">
<li>打开Chrome浏览器</li>
<li><kbd>Ctrl + Shift + O</kbd> 打开书签管理器</li>
<li>点击右上角的 <strong></strong> 菜单</li>
<li>选择 <strong>导出书签</strong></li>
<li>保存为HTML文件</li>
</ol>
</div>
<div class="form-group">
<label for="bookmark-file-input">选择书签文件</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="bookmark-file-input"
name="bookmark_file" accept=".html,.htm" required>
<label class="custom-file-label" for="bookmark-file-input">选择文件...</label>
</div>
<small class="form-text text-muted">
仅支持HTML格式的Chrome书签导出文件
</small>
</div>
<div class="form-group">
<label for="folder-filter">筛选文件夹(可选)</label>
<input type="text" class="form-control" id="folder-filter" name="folder_filter"
placeholder="例如AI工具">
<small class="form-text text-muted">
留空则导入所有书签,填写文件夹名称则只导入该文件夹下的书签
</small>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="auto-activate-2" name="auto_activate" checked>
<label class="custom-control-label" for="auto-activate-2">自动启用导入的网站</label>
</div>
</div>
<button type="submit" class="btn btn-primary">
<span class="material-symbols-outlined" style="font-size: 18px; vertical-align: middle;">upload_file</span>
开始导入
</button>
</form>
</div>
</div>
</div>
</div>
{% if results %}
<div class="card mt-4">
<div class="card-header">
<h5 class="mb-0">导入结果</h5>
</div>
<div class="card-body">
<div class="alert alert-success">
<strong>导入完成!</strong>
成功: {{ results.success_count }},
失败: {{ results.failed_count }},
总计: {{ results.total_count }}
</div>
{% if results.success_list %}
<h6>成功导入 ({{ results.success_count }})</h6>
<ul class="list-group mb-3">
{% for item in results.success_list %}
<li class="list-group-item">
<span class="material-symbols-outlined text-success" style="font-size: 18px; vertical-align: middle;">check_circle</span>
<strong>{{ item.name }}</strong> - {{ item.url }}
</li>
{% endfor %}
</ul>
{% endif %}
{% if results.failed_list %}
<h6 class="text-danger">导入失败 ({{ results.failed_count }})</h6>
<div class="alert alert-warning">
<small><strong>提示:</strong>失败的URL不会影响其他URL的导入您可以稍后手动添加这些网站。</small>
</div>
<div class="table-responsive">
<table class="table table-sm table-hover">
<thead class="thead-light">
<tr>
<th style="width: 40px;">#</th>
<th style="width: 30%;">网站名称</th>
<th style="width: 35%;">URL</th>
<th style="width: 35%;">失败原因</th>
</tr>
</thead>
<tbody>
{% for item in results.failed_list %}
<tr>
<td>
<span class="material-symbols-outlined text-danger" style="font-size: 20px;">cancel</span>
</td>
<td>
<strong>{{ item.name or '未知' }}</strong>
</td>
<td>
<small class="text-muted" style="word-break: break-all;">{{ item.url }}</small>
</td>
<td>
<span class="badge badge-danger">{{ item.error }}</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
</div>
{% endif %}
</main>
</div>
<!-- Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"></script>
<style>
.page-title {
font-size: 24px;
font-weight: 600;
margin-bottom: 8px;
}
.page-description {
color: #666;
margin-bottom: 24px;
}
.card {
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid #e0e0e0;
}
.card-header {
background: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
kbd {
background-color: #f7f7f7;
border: 1px solid #ccc;
border-radius: 3px;
box-shadow: 0 1px 0 rgba(0,0,0,0.2);
color: #333;
display: inline-block;
font-family: monospace;
font-size: 12px;
padding: 2px 6px;
}
</style>
<script>
// 文件选择器显示文件名
document.querySelector('.custom-file-input').addEventListener('change', function(e) {
var fileName = e.target.files[0].name;
var label = e.target.nextElementSibling;
label.textContent = fileName;
});
</script>
</body>
</html>