多用户在线 Python 编辑运行和学习示例程序

单位里现在主流的数据处理还都是用excel,但是处理起来费时费力还老出问题,他们也经常找我们技术部门的人来处理数据,也对python这个"高大上"的东西很是好奇,但是又有畏难情绪,其实现在AI发展的这么快,别的不说,搞个python代码处理数据那还真是让AI砸了码农的饭碗了,只有想不到没有做不到。为了让他们初步的接触认识python,我就利用AI开发了这么个基于 Flask 的在线 Python 编辑、运行和学习的平台,提供代码编辑、执行、文件管理、示例代码和练习系统等功能,让大家伙不用自己下载安装python就能在网页上先体验一番,还能循序渐进的了解python,学习python。这次大部分的代码都是AI生成的,我只提供创意,不废话了,直接上菜。

界面是这样的

既然是多用户,必然要有登录界面。

为了大家方便,提供了一个不用注册的账号用于测试,毕竟是学习来的,不用那么麻烦。

主界面就这个样子了,是不是一目了然。

右边放了些python的常用命令。

顶端放了一些主要的学习的功能,一步一步学Python,从基础开始。

看到的代码可以直接点击插入到编辑区,点击执行就可以显示结果。

插入代码

还放了一些精彩示例

同样也可以复制代码在编辑器运行查看结果,轻点几下解决,连代码输入都省了。

还有就是生成了一些python开发的例子,更便于用户理解。

相当于练习考试,还能得分,当然分数计算的准不准确的就不用深究了。

可以查看答案,复制代码,运行代码看结果。

这个程序感觉如何?我已经部署上了,测试了效果还可以,偶尔有点小问题也不用在意。哈哈。

下面是项目文档:

Webpython 是一个基于 Flask 的在线 Python 编辑器和学习平台,提供代码编辑、执行、文件管理、示例代码和练习系统等功能。

主要功能

  • 用户认证与管理(注册、登录、个人资料管理)

  • 在线 Python 代码编辑器(支持代码执行和结果展示)

  • 文件上传与管理(支持多种文件类型)

  • 代码片段保存与管理

  • Python 示例代码库(按分类组织)

  • Python 练习系统(分难度级别)

  • 管理员功能(用户管理、文件管理)

技术特点

  • 模块化设计,易于扩展

  • 安全的代码执行环境

  • 响应式布局,支持多设备访问

  • 完整的用户权限管理

  • 详细的日志记录

2. 技术栈

后端技术

  • **Python 3.8+**:主要开发语言

  • **Flask**:Web 框架

  • **Flask-Login**:用户认证管理

  • **Flask-WTF**:表单处理和 CSRF 保护

  • **Flask-SQLAlchemy**:ORM 数据库工具

  • **SQLite**:轻量级数据库

  • **Werkzeug**:密码加密和安全工具

  • **python-dotenv**:环境变量管理

前端技术

  • **HTML5**:页面结构

  • **CSS3**:样式设计

  • **JavaScript**:客户端交互

  • **Font Awesome**:图标库

  • **ECharts**:数据可视化(可选)

目录结构

```

Webpython/

├── pycache/ # Python 编译缓存

├── build/ # PyInstaller 构建目录

├── dist/ # 打包输出目录

├── logs/ # 日志文件目录

├── routes/ # 路由模块

│ ├── pycache/ # 路由编译缓存

│ ├── init.py # 路由初始化

│ ├── auth.py # 用户认证路由

│ ├── editor.py # 代码编辑器路由

│ ├── examples.py # 示例代码路由

│ ├── files.py # 文件管理路由

│ └── practice.py # 练习系统路由

├── static/ # 静态文件

│ ├── avatars/ # 用户头像

│ ├── css/ # CSS 文件

│ ├── js/ # JavaScript 文件

│ └── webfonts/ # 字体文件

├── templates/ # 模板文件

│ ├── editor.html # 代码编辑器页面

│ ├── login.html # 登录页面

│ ├── register.html # 注册页面

│ ├── profile.html # 个人资料页面

│ ├── python_examples.html # 示例代码索引页面

│ └── practice_*.html # 练习系统相关页面

├── uploads/ # 文件上传目录

├── utils/ # 工具模块

│ ├── pycache/ # 工具编译缓存

│ ├── init.py # 工具初始化

│ ├── code_executor.py # 代码执行器

│ ├── file_utils.py # 文件工具

│ └── logger.py # 日志工具

├── .env # 环境变量配置

├── Python.ico # 应用图标

├── app.py # 主应用文件

├── app.spec # PyInstaller 配置

├── build_exe.py # 构建脚本

├── config.py # 应用配置

├── init_commands.py # 初始化命令数据

├── models.py # 数据库模型

├── requirements.txt # 依赖包列表

├── setup.py # 安装脚本

├── users.db # SQLite 数据库文件

├── webpython.png # 应用图标

└── webpython.spec # PyInstaller 配置

```

代码编辑器模块

**功能**:提供在线 Python 代码编辑和执行环境

**路由**:

  • `/editor` - 代码编辑器页面

  • `/api/save_code` - 保存代码片段

  • `/api/load_code/<id>` - 加载代码片段

  • `/api/delete_code/<id>` - 删除代码片段

  • `/api/update_code/<id>` - 更新代码片段

  • `/api/code_snippets` - 获取代码片段列表

其实主要的代码基本上都是js。

editor.py

python 复制代码
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from flask_login import login_required, current_user
from models import db, File, User, CodeSnippet
from utils import execute_code_safely, setup_logger
import os
import logging

logger = logging.getLogger(__name__)
editor_bp = Blueprint('editor', __name__)


@editor_bp.route('/editor', methods=['GET', 'POST'])
@login_required
def editor():
    output = ''
    code = ''
    
    if request.method == 'POST':
        code = request.form.get('code', '')
        
        if code.strip():
            user_upload_folder = os.path.join('uploads', str(current_user.id))
            output = execute_code_safely(
                code,
                user_upload_folder=user_upload_folder,
                timeout=10,
                max_output_size=1024*1024
            )
            logger.info(f'用户 {current_user.username} 执行了代码')
    
    if current_user.is_admin:
        user_files = File.query.all()
        all_users = User.query.all()
        code_snippets = CodeSnippet.query.all()
    else:
        user_files = File.query.filter_by(user_id=current_user.id).all()
        all_users = None
        code_snippets = CodeSnippet.query.filter_by(user_id=current_user.id).all()
    
    return render_template(
        'editor.html',
        code=code,
        output=output,
        files=user_files,
        all_users=all_users,
        is_admin=current_user.is_admin,
        code_snippets=code_snippets
    )


@editor_bp.route('/api/save_code', methods=['POST'])
@login_required
def save_code():
    data = request.get_json()
    title = data.get('title', '未命名代码')
    code = data.get('code', '')
    description = data.get('description', '')
    is_favorite = data.get('is_favorite', False)
    
    if not code.strip():
        return jsonify({'success': False, 'message': '代码不能为空'}), 400
    
    try:
        snippet = CodeSnippet(
            title=title,
            code=code,
            description=description,
            user_id=current_user.id,
            is_favorite=is_favorite
        )
        db.session.add(snippet)
        db.session.commit()
        
        logger.info(f'用户 {current_user.username} 保存了代码片段: {title}')
        return jsonify({'success': True, 'message': '保存成功', 'id': snippet.id})
    except Exception as e:
        logger.error(f'保存代码失败: {str(e)}')
        db.session.rollback()
        return jsonify({'success': False, 'message': '保存失败'}), 500


@editor_bp.route('/api/load_code/<int:snippet_id>', methods=['GET'])
@login_required
def load_code(snippet_id):
    snippet = CodeSnippet.query.get_or_404(snippet_id)
    
    if snippet.user_id != current_user.id and not current_user.is_admin:
        return jsonify({'success': False, 'message': '无权访问'}), 403
    
    return jsonify({
        'success': True,
        'title': snippet.title,
        'code': snippet.code,
        'description': snippet.description,
        'is_favorite': snippet.is_favorite
    })


@editor_bp.route('/api/delete_code/<int:snippet_id>', methods=['DELETE'])
@login_required
def delete_code(snippet_id):
    snippet = CodeSnippet.query.get_or_404(snippet_id)
    
    if snippet.user_id != current_user.id and not current_user.is_admin:
        return jsonify({'success': False, 'message': '无权删除'}), 403
    
    try:
        db.session.delete(snippet)
        db.session.commit()
        logger.info(f'用户 {current_user.username} 删除了代码片段: {snippet.title}')
        return jsonify({'success': True, 'message': '删除成功'})
    except Exception as e:
        logger.error(f'删除代码失败: {str(e)}')
        db.session.rollback()
        return jsonify({'success': False, 'message': '删除失败'}), 500


@editor_bp.route('/api/update_code/<int:snippet_id>', methods=['PUT'])
@login_required
def update_code(snippet_id):
    snippet = CodeSnippet.query.get_or_404(snippet_id)
    
    if snippet.user_id != current_user.id and not current_user.is_admin:
        return jsonify({'success': False, 'message': '无权修改'}), 403
    
    data = request.get_json()
    
    try:
        if 'title' in data:
            snippet.title = data['title']
        if 'code' in data:
            snippet.code = data['code']
        if 'description' in data:
            snippet.description = data['description']
        if 'is_favorite' in data:
            snippet.is_favorite = data['is_favorite']
        
        db.session.commit()
        logger.info(f'用户 {current_user.username} 更新了代码片段: {snippet.title}')
        return jsonify({'success': True, 'message': '更新成功'})
    except Exception as e:
        logger.error(f'更新代码失败: {str(e)}')
        db.session.rollback()
        return jsonify({'success': False, 'message': '更新失败'}), 500


@editor_bp.route('/api/code_snippets', methods=['GET'])
@login_required
def get_code_snippets():
    if current_user.is_admin:
        snippets = CodeSnippet.query.all()
    else:
        snippets = CodeSnippet.query.filter_by(user_id=current_user.id).all()
    
    snippets_list = []
    for snippet in snippets:
        snippets_list.append({
            'id': snippet.id,
            'title': snippet.title,
            'description': snippet.description,
            'created_at': snippet.created_at.isoformat(),
            'is_favorite': snippet.is_favorite
        })
    
    return jsonify({'success': True, 'snippets': snippets_list})

editor.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>在线Python编辑器</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/all.min.css') }}">
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
            margin: 0;
            padding: 0;
        }
        .header {
            background-color: #333;
            color: white;
            padding: 10px 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .header h1 {
            margin: 0;
            font-size: 20px;
        }
        .header h1 i {
            margin-right: 10px;
            color: #4CAF50;
        }
        .header .user-info {
            display: flex;
            align-items: center;
        }
        .header .user-info span {
            margin-right: 10px;
        }
        .header .user-info a {
            color: white;
            text-decoration: none;
            padding: 5px 10px;
            background-color: #4CAF50;
            border-radius: 4px;
        }
        .header .user-info a:hover {
            background-color: #45a049;
        }
        .header .header-buttons {
            display: flex;
            gap: 10px;
        }
        .header .learn-btn {
            padding: 5px 15px;
            background-color: #2196F3;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        }
        .header .learn-btn:hover {
            background-color: #0b7dda;
        }
        .header .learn-btn i {
            margin-right: 5px;
        }
        .container {
            display: flex;
            flex-wrap: wrap;
            padding: 20px;
            gap: 20px;
        }
        .left-panel {
            flex: 1;
            min-width: 300px;
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        .right-panel {
            flex: 1;
            min-width: 300px;
            display: flex;
            flex-direction: column;
            gap: 20px;
        }
        .code-editor {
            background-color: #f8f9fa;
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 10px;
            height: 400px;
            overflow: auto;
        }
        .code-editor textarea {
            width: 100%;
            height: 100%;
            border: none;
            resize: none;
            font-family: 'Courier New', Courier, monospace;
            font-size: 14px;
        }
        .output {
            background-color: white;
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 10px;
            height: 200px;
            overflow: auto;
            font-family: 'Courier New', Courier, monospace;
            font-size: 14px;
            white-space: pre-wrap;
        }
        .file-upload {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        .file-upload h3 {
            margin-top: 0;
        }
        .file-upload form {
            margin-bottom: 20px;
        }
        .file-upload input[type="file"] {
            margin: 10px 0;
        }
        .file-upload button {
            padding: 5px 10px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .file-upload button:hover {
            background-color: #45a049;
        }
        .file-list {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        .file-list h3 {
            margin-top: 0;
        }
        .file-list ul {
            list-style: none;
            padding: 0;
            margin: 0;
        }
        .file-list li {
            padding: 10px;
            border-bottom: 1px solid #ddd;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .file-list li:last-child {
            border-bottom: none;
        }
        .file-list .file-actions a {
            margin-left: 10px;
            color: #007bff;
            text-decoration: none;
        }
        .file-list .file-actions a:hover {
            text-decoration: underline;
        }
        .file-list .file-actions .delete {
            color: #dc3545;
        }
        .file-owner {
            font-size: 12px;
            color: #6c757d;
            margin-left: 10px;
        }
        .command-reference {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }
        .command-reference h3 {
            margin-top: 0;
        }
        .command-category {
            margin-bottom: 15px;
        }
        .command-category h4 {
            margin: 0 0 10px 0;
            font-size: 14px;
        }
        .command-category button {
            margin-right: 10px;
            margin-bottom: 10px;
            padding: 5px 10px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        }
        .command-category button:hover {
            background-color: #0069d9;
        }
        .command-list {
            margin-top: 15px;
        }
        .command-list ul {
            list-style: none;
            padding: 0;
            margin: 0;
        }
        .command-list li {
            padding: 15px;
            border-bottom: 1px solid #eee;
            margin-bottom: 10px;
            border-radius: 4px;
            background-color: #f8f9fa;
        }
        .command-list li:last-child {
            border-bottom: none;
        }
        .command-name {
            font-weight: bold;
            color: #007bff;
            margin-bottom: 5px;
        }
        .command-desc {
            font-size: 14px;
            color: #6c757d;
            margin-bottom: 5px;
        }
        .command-usage {
            font-size: 13px;
            color: #495057;
            margin-bottom: 5px;
            font-family: 'Courier New', Courier, monospace;
        }
        .command-example {
            font-size: 13px;
            color: #28a745;
            margin-bottom: 10px;
            font-family: 'Courier New', Courier, monospace;
            white-space: pre-wrap;
        }
        .command-list button {
            padding: 3px 8px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        }
        .command-list button:hover {
            background-color: #218838;
        }
        .pagination {
            margin-top: 20px;
            text-align: center;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-wrap: wrap;
            gap: 10px;
        }
        .pagination span {
            font-size: 14px;
            color: #6c757d;
        }
        .pagination button {
            padding: 5px 10px;
            background-color: #f8f9fa;
            color: #007bff;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        }
        .pagination button:hover {
            background-color: #e9ecef;
        }
        .pagination button.active {
            background-color: #007bff;
            color: white;
            border-color: #007bff;
        }
        .pagination button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }
        .flash {
            background-color: #d4edda;
            color: #155724;
            padding: 10px;
            margin: 10px 0;
            border-radius: 4px;
            border: 1px solid #c3e6cb;
        }
        .execute-btn {
            padding: 10px 20px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin-top: 10px;
        }
        .execute-btn:hover {
            background-color: #0069d9;
        }
        .execute-btn i {
            margin-right: 5px;
        }
        .output-container {
            margin-top: 20px;
        }
        .output-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        .output-header h3 {
            margin: 0;
        }
        .output-header button {
            padding: 5px 10px;
            background-color: #6c757d;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        }
        .output-header button:hover {
            background-color: #5a6268;
        }
        .code-editor-container {
            margin-bottom: 20px;
        }
        .code-editor-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        .code-editor-header h3 {
            margin: 0;
        }
        .clear-btn {
            padding: 5px 10px;
            background-color: #dc3545;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        }
        .clear-btn:hover {
            background-color: #c82333;
        }
        .clear-btn i {
            margin-right: 5px;
        }
        .code-snippets {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }
        .code-snippets h3 {
            margin-top: 0;
        }
        .snippet-list {
            list-style: none;
            padding: 0;
            margin: 0;
        }
        .snippet-item {
            padding: 10px;
            border-bottom: 1px solid #ddd;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .snippet-item:last-child {
            border-bottom: none;
        }
        .snippet-info {
            flex: 1;
        }
        .snippet-title {
            font-weight: bold;
            color: #333;
        }
        .snippet-desc {
            font-size: 12px;
            color: #6c757d;
        }
        .snippet-actions {
            display: flex;
            gap: 5px;
        }
        .snippet-actions button {
            padding: 3px 8px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        }
        .snippet-actions button:hover {
            background-color: #0069d9;
        }
        .snippet-actions .delete-btn {
            background-color: #dc3545;
        }
        .snippet-actions .delete-btn:hover {
            background-color: #c82333;
        }
        .save-code-btn {
            padding: 5px 10px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        }
        .save-code-btn:hover {
            background-color: #218838;
        }
        .save-code-btn i {
            margin-right: 5px;
        }
        .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-content {
            background-color: white;
            margin: 10% auto;
            padding: 20px;
            border: 1px solid #888;
            width: 80%;
            max-width: 500px;
            border-radius: 8px;
        }
        .modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        .modal-header h3 {
            margin: 0;
        }
        .close-modal {
            color: #aaa;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
        }
        .close-modal:hover {
            color: black;
        }
        .modal-body input,
        .modal-body textarea {
            width: 100%;
            margin-bottom: 10px;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        .modal-body textarea {
            height: 100px;
            resize: vertical;
        }
        .modal-footer {
            display: flex;
            justify-content: flex-end;
            gap: 10px;
        }
        .modal-footer button {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .modal-footer .cancel-btn {
            background-color: #6c757d;
            color: white;
        }
        .modal-footer .save-btn {
            background-color: #28a745;
            color: white;
        }
    </style>
</head>
<body>
    <div class="header">
        <h1><i class="fas fa-code"></i>在线Python编辑器</h1>
        <div class="header-buttons">
            <button onclick="showPythonCourse()" class="learn-btn"><i class="fas fa-book"></i>一步一步学Python</button>
            <a href="{{ url_for('examples.python_examples') }}" class="learn-btn" style="background-color: #ff9800; text-decoration: none; display: inline-block;"><i class="fas fa-lightbulb"></i>Python精彩示例</a>
            <a href="{{ url_for('practice.practice_home') }}" class="learn-btn" style="background-color: #4CAF50; text-decoration: none; display: inline-block;"><i class="fas fa-pencil-alt"></i>Python练习100例</a>
        </div>
        <div class="user-info">
            <div style="display: flex; align-items: center; gap: 10px;">
                <img src="{{ url_for('static', filename='avatars/' + current_user.avatar + '.svg') }}" alt="头像" style="width: 40px; height: 40px; border-radius: 50%; border: 2px solid #4CAF50;">
                <span>欢迎,{{ current_user.username }}</span>
                <a href="{{ url_for('auth.profile') }}" style="color: white; text-decoration: none; padding: 5px 10px; background-color: #2196F3; border-radius: 4px;"><i class="fas fa-user-cog"></i>个人信息</a>
                <a href="{{ url_for('auth.logout') }}"><i class="fas fa-sign-out-alt"></i>注销</a>
            </div>
        </div>
    </div>
    
    <div class="container">
        <div class="left-panel">
            {% with messages = get_flashed_messages() %}
                {% if messages %}
                    {% for message in messages %}
                        <div class="flash">{{ message }}</div>
                    {% endfor %}
                {% endif %}
            {% endwith %}
            
            <form method="POST">
                <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
                <div class="code-editor-container">
                    <div class="code-editor-header">
                        <h3><i class="fas fa-edit"></i>代码编辑器</h3>
                        <div>
                            <button type="button" onclick="showSaveModal()" class="save-code-btn"><i class="fas fa-save"></i>保存代码</button>
                            <button type="button" onclick="clearCode()" class="clear-btn"><i class="fas fa-trash"></i>清除代码</button>
                        </div>
                    </div>
                    <div class="code-editor">
                        <textarea name="code" placeholder="在此输入Python代码..." id="codeEditor">{{ code }}</textarea>
                    </div>
                </div>
                <button type="submit" class="execute-btn"><i class="fas fa-play"></i>执行代码</button>
            </form>
            
            <div class="output-container">
                <div class="output-header">
                    <h3><i class="fas fa-terminal"></i>执行结果:</h3>
                    <button onclick="clearOutput()"><i class="fas fa-eraser"></i>清除结果</button>
                </div>
                <div class="output" id="outputArea">{{ output|safe }}</div>
            </div>
        </div>
        
        <div class="right-panel">
            <div class="code-snippets">
                <h3><i class="fas fa-code-branch"></i>我的代码片段</h3>
                {% if code_snippets %}
                    <ul class="snippet-list">
                        {% for snippet in code_snippets %}
                            <li class="snippet-item">
                                <div class="snippet-info">
                                    <div class="snippet-title">{{ snippet.title }}</div>
                                    {% if snippet.description %}
                                        <div class="snippet-desc">{{ snippet.description }}</div>
                                    {% endif %}
                                </div>
                                <div class="snippet-actions">
                                    <button onclick="loadCode({{ snippet.id }})"><i class="fas fa-download"></i>加载</button>
                                    <button onclick="deleteCode({{ snippet.id }})" class="delete-btn"><i class="fas fa-trash-alt"></i>删除</button>
                                </div>
                            </li>
                        {% endfor %}
                    </ul>
                {% else %}
                    <p>暂无保存的代码片段</p>
                {% endif %}
            </div>
            
            <div class="file-upload">
                <h3><i class="fas fa-upload"></i>文件上传</h3>
                <form method="POST" action="{{ url_for('files.upload_file') }}" enctype="multipart/form-data">
                    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
                    <input type="file" name="file">
                    <button type="submit"><i class="fas fa-cloud-upload-alt"></i>上传文件</button>
                </form>
            </div>
            
            <div class="command-reference">
                <h3><i class="fas fa-terminal"></i>Python常用命令</h3>
                <div class="command-category">
                    <h4>分类:</h4>
                    <button onclick="showCommands('basic')"><i class="fas fa-code"></i>基础命令</button>
                    <button onclick="showCommands('file')"><i class="fas fa-file"></i>文件操作</button>
                    <button onclick="showCommands('data')"><i class="fas fa-database"></i>数据处理</button>
                    <button onclick="showCommands('excel')"><i class="fas fa-file-excel"></i>Excel处理</button>
                    <button onclick="showCommands('word')"><i class="fas fa-file-word"></i>Word处理</button>
                    <button onclick="showCommands('pdf')"><i class="fas fa-file-pdf"></i>PDF处理</button>
                    <button onclick="showCommands('time')"><i class="fas fa-clock"></i>时间日期</button>
                    <button onclick="showCommands('db')"><i class="fas fa-database"></i>数据库操作</button>
                </div>
                <div class="command-list" id="commandList">
                    <p>请选择一个分类查看命令</p>
                </div>
            </div>
            
            <div class="file-list">
                <h3><i class="fas fa-folder-open"></i>已上传文件</h3>
                {% if files %}
                    <ul>
                        {% for file in files %}
                            <li>
                                <div>
                                    <span>{{ file.filename }}</span>
                                    {% if is_admin %}
                                        {% for user in all_users %}
                                            {% if user.id == file.user_id %}
                                                <span class="file-owner"> (所属用户: {{ user.username }})</span>
                                            {% endif %}
                                        {% endfor %}
                                    {% endif %}
                                </div>
                                <div class="file-actions">
                                    <a href="{{ url_for('files.download_file', file_id=file.id) }}"><i class="fas fa-download"></i>下载</a>
                                    <a href="{{ url_for('files.delete_file', file_id=file.id) }}" class="delete" onclick="return confirm('确定要删除此文件吗?');"><i class="fas fa-trash-alt"></i>删除</a>
                                </div>
                            </li>
                        {% endfor %}
                    </ul>
                {% else %}
                    <p>暂无上传文件</p>
                {% endif %}
            </div>
        </div>
    </div>
    
    <div id="saveModal" class="modal">
        <div class="modal-content">
            <div class="modal-header">
                        <h3><i class="fas fa-save"></i>保存代码片段</h3>
                        <span class="close-modal" onclick="closeSaveModal()">&times;</span>
                    </div>
            <div class="modal-body">
                <input type="text" id="snippetTitle" placeholder="代码标题" required>
                <textarea id="snippetDesc" placeholder="代码描述(可选)"></textarea>
            </div>
            <div class="modal-footer">
                <button class="cancel-btn" onclick="closeSaveModal()">取消</button>
                <button class="save-btn" onclick="saveCode()">保存</button>
            </div>
        </div>
    </div>
    
    {% include 'python_course.html' %}

    <script>
        const commands = {
            basic: [
                {
                    name: "print()",
                    desc: "打印输出",
                    usage: "print(object, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)",
                    example: "print('Hello, World!')",
                    code: "print('Hello, World!')"
                },
                {
                    name: "input()",
                    desc: "获取用户输入",
                    usage: "input([prompt])",
                    example: "name = input('请输入你的名字: ')",
                    code: "name = input('请输入你的名字: '); print(f'你好,{name}!')"
                },
                {
                    name: "len()",
                    desc: "获取长度",
                    usage: "len(s)",
                    example: "len('hello')",
                    code: "print(len('hello')); print(len([1, 2, 3]))"
                },
                {
                    name: "type()",
                    desc: "获取类型",
                    usage: "type(object)",
                    example: "type(42)",
                    code: "print(type(42)); print(type('hello')); print(type([1, 2, 3]))"
                },
                {
                    name: "int()",
                    desc: "转换为整数",
                    usage: "int(x, base=10)",
                    example: "int('42')",
                    code: "print(int('42')); print(int(3.14))"
                },
                {
                    name: "float()",
                    desc: "转换为浮点数",
                    usage: "float(x)",
                    example: "float('3.14')",
                    code: "print(float('3.14')); print(float(42))"
                },
                {
                    name: "str()",
                    desc: "转换为字符串",
                    usage: "str(object='')",
                    example: "str(42)",
                    code: "print(str(42)); print(str(3.14))"
                },
                {
                    name: "bool()",
                    desc: "转换为布尔值",
                    usage: "bool([x])",
                    example: "bool(1)",
                    code: "print(bool(1)); print(bool(0)); print(bool('hello')); print(bool(''))"
                },
                {
                    name: "abs()",
                    desc: "获取绝对值",
                    usage: "abs(x)",
                    example: "abs(-42)",
                    code: "print(abs(-42)); print(abs(3.14))"
                },
                {
                    name: "max()",
                    desc: "获取最大值",
                    usage: "max(iterable, *[, default=obj, key=func]) or max(arg1, arg2, *args, *[, key=func])",
                    example: "max([1, 2, 3])",
                    code: "print(max([1, 2, 3])); print(max(1, 2, 3, 4, 5))"
                },
                {
                    name: "min()",
                    desc: "获取最小值",
                    usage: "min(iterable, *[, default=obj, key=func]) or min(arg1, arg2, *args, *[, key=func])",
                    example: "min([1, 2, 3])",
                    code: "print(min([1, 2, 3])); print(min(1, 2, 3, 4, 5))"
                },
                {
                    name: "sum()",
                    desc: "求和",
                    usage: "sum(iterable, /, start=0)",
                    example: "sum([1, 2, 3])",
                    code: "print(sum([1, 2, 3])); print(sum(range(10))"
                },
                {
                    name: "round()",
                    desc: "四舍五入",
                    usage: "round(number[, ndigits])",
                    example: "round(3.14159)",
                    code: "print(round(3.14159)); print(round(3.14159, 2))"
                },
                {
                    name: "sorted()",
                    desc: "排序",
                    usage: "sorted(iterable, /, *, key=None, reverse=False)",
                    example: "sorted([3, 1, 2])",
                    code: "print(sorted([3, 1, 2])); print(sorted('hello'))"
                }
            ],
            file: [
                {
                    name: "open()",
                    desc: "打开文件",
                    usage: "open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)",
                    example: "open('file.txt', 'r')",
                    code: "# 读取文件\nwith open('file.txt', 'r') as f:\n    content = f.read()\n    print(content)\n\n# 写入文件\nwith open('output.txt', 'w') as f:\n    f.write('Hello, File!')"
                },
                {
                    name: "read()",
                    desc: "读取文件内容",
                    usage: "file.read(size=-1)",
                    example: "f.read()",
                    code: "with open('file.txt', 'r') as f:\n    content = f.read()\n    print(content)"
                },
                {
                    name: "write()",
                    desc: "写入文件内容",
                    usage: "file.write(string)",
                    example: "f.write('Hello')",
                    code: "with open('output.txt', 'w') as f:\n    f.write('Hello, File!')\n    f.write('\\nSecond line.')"
                },
                {
                    name: "readline()",
                    desc: "读取一行",
                    usage: "file.readline(size=-1)",
                    example: "f.readline()",
                    code: "with open('file.txt', 'r') as f:\n    line = f.readline()\n    while line:\n        print(line.strip())\n        line = f.readline()"
                },
                {
                    name: "readlines()",
                    desc: "读取所有行",
                    usage: "file.readlines(hint=-1)",
                    example: "f.readlines()",
                    code: "with open('file.txt', 'r') as f:\n    lines = f.readlines()\n    for line in lines:\n        print(line.strip())"
                },
                {
                    name: "writelines()",
                    desc: "写入多行",
                    usage: "file.writelines(lines)",
                    example: "f.writelines(['Line 1\\n', 'Line 2\\n'])\n",
                    code: "with open('output.txt', 'w') as f:\n    f.writelines(['Line 1\\n', 'Line 2\\n', 'Line 3\\n'])"
                },
                {
                    name: "os.path.exists()",
                    desc: "检查文件是否存在",
                    usage: "os.path.exists(path)",
                    example: "os.path.exists('file.txt')",
                    code: "import os\nprint(os.path.exists('file.txt'))"
                },
                {
                    name: "os.path.join()",
                    desc: "拼接路径",
                    usage: "os.path.join(path, *paths)",
                    example: "os.path.join('dir', 'file.txt')",
                    code: "import os\nprint(os.path.join('dir', 'subdir', 'file.txt'))"
                },
                {
                    name: "shutil.copy()",
                    desc: "复制文件",
                    usage: "shutil.copy(src, dst, *, follow_symlinks=True)",
                    example: "shutil.copy('src.txt', 'dst.txt')",
                    code: "import shutil\nshutil.copy('src.txt', 'dst.txt')\nprint('文件复制成功')"
                },
                {
                    name: "shutil.move()",
                    desc: "移动文件",
                    usage: "shutil.move(src, dst, copy_function=copy2)",
                    example: "shutil.move('src.txt', 'dst.txt')",
                    code: "import shutil\nshutil.move('src.txt', 'dst.txt')\nprint('文件移动成功')"
                }
            ],
            data: [
                {
                    name: "list()",
                    desc: "创建列表",
                    usage: "list([iterable])",
                    example: "list('hello')",
                    code: "print(list('hello')); print(list(range(5))"
                },
                {
                    name: "dict()",
                    desc: "创建字典",
                    usage: "dict(**kwarg) dict(mapping, **kwarg) dict(iterable, **kwarg)",
                    example: "dict(a=1, b=2)",
                    code: "print(dict(a=1, b=2)); print(dict([('a', 1), ('b', 2)])"
                },
                {
                    name: "set()",
                    desc: "创建集合",
                    usage: "set([iterable])",
                    example: "set('hello')",
                    code: "print(set('hello')); print(set([1, 2, 2, 3])"
                },
                {
                    name: "range()",
                    desc: "创建范围",
                    usage: "range(stop) range(start, stop[, step])",
                    example: "range(5)",
                    code: "print(list(range(5))); print(list(range(2, 10, 2))"
                },
                {
                    name: "zip()",
                    desc: "压缩多个可迭代对象",
                    usage: "zip(*iterables, strict=False)",
                    example: "zip([1, 2, 3], ['a', 'b', 'c'])\n",
                    code: "print(list(zip([1, 2, 3], ['a', 'b', 'c']))); print(dict(zip(['a', 'b', 'c'], [1, 2, 3]))"
                },
                {
                    name: "enumerate()",
                    desc: "枚举可迭代对象",
                    usage: "enumerate(iterable, start=0)",
                    example: "enumerate(['a', 'b', 'c'])\n",
                    code: "for i, item in enumerate(['a', 'b', 'c']):\n    print(f'{i}: {item}')"
                },
                {
                    name: "map()",
                    desc: "映射函数到可迭代对象",
                    usage: "map(func, *iterables)",
                    example: "map(str, [1, 2, 3])",
                    code: "print(list(map(str, [1, 2, 3]))); print(list(map(lambda x: x*2, [1, 2, 3]))"
                },
                {
                    name: "filter()",
                    desc: "过滤可迭代对象",
                    usage: "filter(function, iterable)",
                    example: "filter(lambda x: x > 0, [-1, 0, 1, 2])\n",
                    code: "print(list(filter(lambda x: x > 0, [-1, 0, 1, 2]))); print(list(filter(None, [0, 1, '', 'hello']))"
                },
                {
                    name: "列表推导式",
                    desc: "快速创建列表",
                    usage: "[expression for item in iterable if condition]",
                    example: "[x*2 for x in range(5)]",
                    code: "print([x*2 for x in range(5)]); print([x for x in range(10) if x % 2 == 0]"
                },
                {
                    name: "字典推导式",
                    desc: "快速创建字典",
                    usage: "{key: value for item in iterable if condition}",
                    example: "{x: x*2 for x in range(5)}",
                    code: "print({x: x*2 for x in range(5)}); print({k: v for k, v in zip(['a', 'b', 'c'], [1, 2, 3])}"
                },
                {
                    name: "集合推导式",
                    desc: "快速创建集合",
                    usage: "{expression for item in iterable if condition}",
                    example: "{x*2 for x in range(5)}",
                    code: "print({x*2 for x in range(5)}); print({c for c in 'hello'}"
                }
            ],
            time: [
                {
                    name: "time.time()",
                    desc: "获取当前时间戳",
                    usage: "time.time()",
                    example: "time.time()",
                    code: "import time\nprint(time.time())"
                },
                {
                    name: "time.sleep()",
                    desc: "暂停执行",
                    usage: "time.sleep(seconds)",
                    example: "time.sleep(1)",
                    code: "import time\nprint('开始'); time.sleep(2); print('结束')"
                },
                {
                    name: "datetime.datetime.now()",
                    desc: "获取当前日期时间",
                    usage: "datetime.datetime.now(tz=None)",
                    example: "datetime.datetime.now()",
                    code: "from datetime import datetime\nprint(datetime.now())"
                },
                {
                    name: "datetime.date.today()",
                    desc: "获取当前日期",
                    usage: "datetime.date.today()",
                    example: "datetime.date.today()",
                    code: "from datetime import date\nprint(date.today())"
                },
                {
                    name: "strftime()",
                    desc: "格式化日期时间",
                    usage: "datetime.strftime(format)",
                    example: "datetime.now().strftime('%Y-%m-%d %H:%M:%S')",
                    code: "from datetime import datetime\nprint(datetime.now().strftime('%Y-%m-%d %H:%M:%S')); print(datetime.now().strftime('%Y/%m/%d'))"
                },
                {
                    name: "strptime()",
                    desc: "解析日期时间字符串",
                    usage: "datetime.strptime(date_string, format)",
                    example: "datetime.strptime('2023-01-01', '%Y-%m-%d')",
                    code: "from datetime import datetime\nprint(datetime.strptime('2023-01-01', '%Y-%m-%d'))"
                },
                {
                    name: "timedelta()",
                    desc: "表示时间差",
                    usage: "datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)",
                    example: "datetime.timedelta(days=1)",
                    code: "from datetime import datetime, timedelta\ntoday = datetime.now(); tomorrow = today + timedelta(days=1); print(f'Today: {today}'); print(f'Tomorrow: {tomorrow}')"
                }
            ],
            db: [
                {
                    name: "sqlite3.connect()",
                    desc: "连接SQLite数据库",
                    usage: "sqlite3.connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])",
                    example: "sqlite3.connect('example.db')",
                    code: "import sqlite3\n\nconn = sqlite3.connect('example.db'); c = conn.cursor()\n\nc.execute('''CREATE TABLE IF NOT EXISTS users\n             (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)''')\n\nc.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Alice', 30))\n\nconn.commit()\n\nc.execute('SELECT * FROM users'); print(c.fetchall())\n\nconn.close()"
                },
                {
                    name: "execute()",
                    desc: "执行SQL语句",
                    usage: "cursor.execute(sql, parameters=None)",
                    example: "c.execute('SELECT * FROM users')",
                    code: "import sqlite3\n\nconn = sqlite3.connect('example.db'); c = conn.cursor()\n\nc.execute('SELECT * FROM users'); print(c.fetchall())\n\nconn.close()"
                },
                {
                    name: "fetchone()",
                    desc: "获取一条查询结果",
                    usage: "cursor.fetchone()",
                    example: "c.fetchone()",
                    code: "import sqlite3\n\nconn = sqlite3.connect('example.db'); c = conn.cursor()\n\nc.execute('SELECT * FROM users'); print(c.fetchone())\n\nconn.close()"
                },
                {
                    name: "fetchall()",
                    desc: "获取所有查询结果",
                    usage: "cursor.fetchall()",
                    example: "c.fetchall()",
                    code: "import sqlite3\n\nconn = sqlite3.connect('example.db'); c = conn.cursor()\n\nc.execute('SELECT * FROM users'); print(c.fetchall())\n\nconn.close()"
                },
                {
                    name: "commit()",
                    desc: "提交事务",
                    usage: "connection.commit()",
                    example: "conn.commit()",
                    code: "import sqlite3\n\nconn = sqlite3.connect('example.db'); c = conn.cursor()\n\nc.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Bob', 25))\nconn.commit()\n\nc.execute('SELECT * FROM users'); print(c.fetchall())\n\nconn.close()"
                }
            ],
            excel: [
                {
                    name: "pandas.read_excel()",
                    desc: "读取Excel文件",
                    usage: "pandas.read_excel(io, sheet_name=0, header=0, names=None, index_col=None, usecols=None, squeeze=False, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skiprows=None, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, parse_dates=False, date_parser=None, thousands=None, comment=None, skipfooter=0, convert_float=True, mangle_dupe_cols=True, storage_options=None)",
                    example: "pandas.read_excel('data.xlsx')",
                    code: "import pandas as pd\n\ndf = pd.read_excel('data.xlsx'); print(df)\n\ndf.to_excel('output.xlsx', index=False)"
                },
                {
                    name: "pandas.DataFrame()",
                    desc: "创建DataFrame",
                    usage: "pandas.DataFrame(data=None, index=None, columns=None, dtype=None, copy=None)",
                    example: "pandas.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})",
                    code: "import pandas as pd\n\ndf = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}); print(df)"
                },
                {
                    name: "DataFrame.head()",
                    desc: "查看前几行",
                    usage: "DataFrame.head(n=5)",
                    example: "df.head()",
                    code: "import pandas as pd\n\ndf = pd.DataFrame({'A': range(10), 'B': range(10)}); print(df.head()); print(df.head(3))"
                },
                {
                    name: "DataFrame.tail()",
                    desc: "查看后几行",
                    usage: "DataFrame.tail(n=5)",
                    example: "df.tail()",
                    code: "import pandas as pd\n\ndf = pd.DataFrame({'A': range(10), 'B': range(10)}); print(df.tail()); print(df.tail(3))"
                },
                {
                    name: "DataFrame.info()",
                    desc: "查看DataFrame信息",
                    usage: "DataFrame.info(verbose=None, buf=None, max_cols=None, memory_usage=None, show_counts=None, null_counts=None)",
                    example: "df.info()",
                    code: "import pandas as pd\n\ndf = pd.DataFrame({'A': range(10), 'B': range(10)}); df.info()"
                },
                {
                    name: "DataFrame.describe()",
                    desc: "查看统计信息",
                    usage: "DataFrame.describe(percentiles=None, include=None, exclude=None, datetime_is_numeric=False)",
                    example: "df.describe()",
                    code: "import pandas as pd\n\ndf = pd.DataFrame({'A': range(10), 'B': range(10)}); print(df.describe())"
                },
                {
                    name: "DataFrame.loc[]",
                    desc: "按标签选择",
                    usage: "DataFrame.loc[行标签, 列标签]",
                    example: "df.loc[0, 'A']",
                    code: "import pandas as pd\n\ndf = pd.DataFrame({'A': range(5), 'B': range(5)}); print(df.loc[0, 'A']); print(df.loc[:, ['A', 'B']])"
                },
                {
                    name: "DataFrame.iloc[]",
                    desc: "按位置选择",
                    usage: "DataFrame.iloc[行位置, 列位置]",
                    example: "df.iloc[0, 0]",
                    code: "import pandas as pd\n\ndf = pd.DataFrame({'A': range(5), 'B': range(5)}); print(df.iloc[0, 0]); print(df.iloc[:, [0, 1]])"
                }
            ],
            word: [
                {
                    name: "docx.Document()",
                    desc: "创建Word文档",
                    usage: "docx.Document(docx=None)",
                    example: "docx.Document()",
                    code: "from docx import Document\n\ndoc = Document(); doc.add_heading('标题', 0); doc.add_paragraph('这是一个段落。')\n\ndoc.save('example.docx')"
                },
                {
                    name: "add_heading()",
                    desc: "添加标题",
                    usage: "Document.add_heading(text='', level=0)",
                    example: "doc.add_heading('标题', 0)",
                    code: "from docx import Document\n\ndoc = Document()\ndoc.add_heading('一级标题', 0)\ndoc.add_heading('二级标题', 1)\ndoc.add_heading('三级标题', 2)\n\ndoc.save('headings.docx')"
                },
                {
                    name: "add_paragraph()",
                    desc: "添加段落",
                    usage: "Document.add_paragraph(text='', style=None)",
                    example: "doc.add_paragraph('这是一个段落。')",
                    code: "from docx import Document\n\ndoc = Document()\ndoc.add_paragraph('这是第一个段落。')\ndoc.add_paragraph('这是第二个段落。')\n\ndoc.save('paragraphs.docx')"
                },
                {
                    name: "add_table()",
                    desc: "添加表格",
                    usage: "Document.add_table(rows, cols, style=None)",
                    example: "doc.add_table(3, 2)\n",
                    code: "from docx import Document\n\ndoc = Document()\ntable = doc.add_table(3, 2)\n\n# 填充表格\ntable.cell(0, 0).text = '姓名'\ntable.cell(0, 1).text = '年龄'\ntable.cell(1, 0).text = 'Alice'\ntable.cell(1, 1).text = '30'\ntable.cell(2, 0).text = 'Bob'\ntable.cell(2, 1).text = '25'\n\ndoc.save('table.docx')"
                },
                {
                    name: "add_picture()",
                    desc: "添加图片",
                    usage: "Document.add_picture(image_path_or_stream, width=None, height=None)",
                    example: "doc.add_picture('image.jpg')",
                    code: "from docx import Document\nfrom docx.shared import Inches\n\ndoc = Document()\ndoc.add_picture('image.jpg', width=Inches(2), height=Inches(1.5))\n\ndoc.save('picture.docx')"
                }
            ],
            pdf: [
                {
                    name: "PyPDF2.PdfReader()",
                    desc: "读取PDF文件",
                    usage: "PyPDF2.PdfReader(stream, strict=True, password=None)",
                    example: "PyPDF2.PdfReader('example.pdf')",
                    code: "import PyPDF2\n\nwith open('example.pdf', 'rb') as file:\n    reader = PyPDF2.PdfReader(file)\n    num_pages = len(reader.pages)\n    print(f'页数: {num_pages}')\n    \n    page = reader.pages[0]\n    text = page.extract_text()\n    print(text)"
                },
                {
                    name: "PyPDF2.PdfWriter()",
                    desc: "创建PDF文件",
                    usage: "PyPDF2.PdfWriter()",
                    example: "PyPDF2.PdfWriter()",
                    code: "import PyPDF2\n\nwriter = PyPDF2.PdfWriter()\n\n# 这里需要添加页面到writer\n# writer.add_page(page)\n\nwith open('output.pdf', 'wb') as file:\n    writer.write(file)"
                },
                {
                    name: "extract_text()",
                    desc: "提取页面文本",
                    usage: "PageObject.extract_text()",
                    example: "page.extract_text()",
                    code: "import PyPDF2\n\nwith open('example.pdf', 'rb') as file:\n    reader = PyPDF2.PdfReader(file)\n    page = reader.pages[0]\n    text = page.extract_text()\n    print(text)"
                },
                {
                    name: "merge_page()",
                    desc: "合并页面",
                    usage: "PageObject.merge_page(page2)",
                    example: "page1.merge_page(page2)",
                    code: "import PyPDF2\n\nwith open('example1.pdf', 'rb') as file1, open('example2.pdf', 'rb') as file2:\n    reader1 = PyPDF2.PdfReader(file1)\n    reader2 = PyPDF2.PdfReader(file2)\n    \n    writer = PyPDF2.PdfWriter()\n    \n    # 添加第一个PDF的所有页面\n    for page_num in range(len(reader1.pages)):\n        writer.add_page(reader1.pages[page_num])\n    \n    # 添加第二个PDF的所有页面\n    for page_num in range(len(reader2.pages)):\n        writer.add_page(reader2.pages[page_num])\n    \n    with open('merged.pdf', 'wb') as output_file:\n        writer.write(output_file)"
                },
                {
                    name: "rotate()",
                    desc: "旋转页面",
                    usage: "PageObject.rotate(angle)",
                    example: "page.rotate(90)",
                    code: "import PyPDF2\n\nwith open('example.pdf', 'rb') as file:\n    reader = PyPDF2.PdfReader(file)\n    writer = PyPDF2.PdfWriter()\n    \n    for page_num in range(len(reader.pages)):\n        page = reader.pages[page_num]\n        page.rotate(90)  # 旋转90度\n        writer.add_page(page)\n    \n    with open('rotated.pdf', 'wb') as output_file:\n        writer.write(output_file)"
                }
            ]
        };

        function showCommands(category) {
            const commandList = document.getElementById('commandList');
            const commandData = commands[category] || [];
            
            if (commandData.length > 0) {
                let html = '<ul>';
                commandData.forEach((cmd, index) => {
                    html += '<li>';
                    html += '<div class="command-name">' + cmd.name + '</div>';
                    html += '<div class="command-desc">' + cmd.desc + '</div>';
                    html += '<div class="command-usage">用法: ' + cmd.usage + '</div>';
                    html += '<div class="command-example">示例: ' + cmd.example + '</div>';
                    html += '<button onclick="insertCodeAt(' + index + ', \'' + category + '\')">插入代码</button>';
                    html += '</li>';
                });
                html += '</ul>';
                commandList.innerHTML = html;
            } else {
                commandList.innerHTML = '<p>该分类暂无命令</p>';
            }
        }

        function insertCodeAt(index, category) {
            const cmd = commands[category][index];
            const code = cmd.code || cmd.example;
            const textarea = document.getElementById('codeEditor');
            textarea.value = code + '\n';
            textarea.focus();
        }

        function insertCode(code) {
            const textarea = document.getElementById('codeEditor');
            textarea.value = code + '\n';
            textarea.focus();
        }

        function clearCode() {
            document.getElementById('codeEditor').value = '';
            document.getElementById('codeEditor').focus();
        }

        function clearOutput() {
            document.getElementById('outputArea').textContent = '';
        }

        function showSaveModal() {
            const code = document.getElementById('codeEditor').value.trim();
            if (!code) {
                alert('请先输入代码');
                return;
            }
            document.getElementById('saveModal').style.display = 'block';
            document.getElementById('snippetTitle').value = '';
            document.getElementById('snippetDesc').value = '';
        }

        function closeSaveModal() {
            document.getElementById('saveModal').style.display = 'none';
        }

        async function saveCode() {
            const title = document.getElementById('snippetTitle').value.trim();
            const description = document.getElementById('snippetDesc').value.trim();
            const code = document.getElementById('codeEditor').value.trim();

            if (!title) {
                alert('请输入代码标题');
                return;
            }

            try {
                const response = await fetch('/api/save_code', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        title: title,
                        description: description,
                        code: code,
                        is_favorite: false
                    })
                });

                const data = await response.json();
                if (data.success) {
                    alert('保存成功');
                    closeSaveModal();
                    location.reload();
                } else {
                    alert('保存失败: ' + data.message);
                }
            } catch (error) {
                alert('保存失败: ' + error.message);
            }
        }

        async function loadCode(snippetId) {
            try {
                const response = await fetch(`/api/load_code/${snippetId}`);
                const data = await response.json();
                
                if (data.success) {
                    document.getElementById('codeEditor').value = data.code;
                } else {
                    alert('加载失败: ' + data.message);
                }
            } catch (error) {
                alert('加载失败: ' + error.message);
            }
        }

        async function deleteCode(snippetId) {
            if (!confirm('确定要删除此代码片段吗?')) {
                return;
            }

            try {
                const response = await fetch(`/api/delete_code/${snippetId}`, {
                    method: 'DELETE'
                });
                const data = await response.json();
                
                if (data.success) {
                    alert('删除成功');
                    location.reload();
                } else {
                    alert('删除失败: ' + data.message);
                }
            } catch (error) {
                alert('删除失败: ' + error.message);
            }
        }

        function showPythonCourse() {
            document.getElementById('pythonCourseModal').style.display = 'block';
        }

        function closePythonCourse() {
            document.getElementById('pythonCourseModal').style.display = 'none';
        }

        function showCourseLevel(level, element) {
            document.querySelectorAll('.course-level').forEach(el => el.classList.remove('active'));
            element.classList.add('active');
            
            document.querySelectorAll('.course-topics').forEach(el => el.style.display = 'none');
            document.getElementById(level + '-content').style.display = 'block';
        }

        function insertCodeToEditor(code) {
            document.getElementById('codeEditor').value = code + '\n';
            closePythonCourse();
        }

        window.onclick = function(event) {
            const modal = document.getElementById('saveModal');
            const courseModal = document.getElementById('pythonCourseModal');
            if (event.target == modal) {
                modal.style.display = 'none';
            }
            if (event.target == courseModal) {
                courseModal.style.display = 'none';
            }
        }
    </script>

    <footer style="background-color: #333; color: white; padding: 20px; text-align: center; margin-top: 20px;">
        <div style="max-width: 1200px; margin: 0 auto;">
            <div style="font-size: 14px; color: #ccc; margin-bottom: 10px;">
                <span>Python版本: {{ python_version }}</span> | 
                <span>© 202602 半熟的皮皮虾[CSDN/Wechat]</span> | 
                <span>推荐浏览器: Chrome 90+, Firefox 88+, Safari 14+</span> | 
                <span>推荐分辨率: 1920x1080或更高</span>
            </div>
            <div style="font-size: 12px; color: #999;">
                本项目旨在提供一个友好的Python学习和练习环境,欢迎反馈和建议。
            </div>
        </div>
    </footer>
</body>
</html>

看大家喜欢不。

相关推荐
寻寻觅觅☆5 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
YJlio5 小时前
1.7 通过 Sysinternals Live 在线运行工具:不下载也能用的“云端工具箱”
c语言·网络·python·数码相机·ios·django·iphone
l1t5 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
今天只学一颗糖6 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
赶路人儿6 小时前
Jsoniter(java版本)使用介绍
java·开发语言
testpassportcn6 小时前
AWS DOP-C02 認證完整解析|AWS DevOps Engineer Professional 考試
网络·学习·改行学it
ceclar1236 小时前
C++使用format
开发语言·c++·算法
山塘小鱼儿7 小时前
本地Ollama+Agent+LangGraph+LangSmith运行
python·langchain·ollama·langgraph·langsimth
码说AI7 小时前
python快速绘制走势图对比曲线
开发语言·python