python django orm websocket html 实现deepseek持续聊天对话页面

发布于:2025-03-09 ⋅ 阅读:(10) ⋅ 点赞:(0)

最终效果:

技术栈:

 python django orm websocket html

项目结构:

这里只展示关键代码:

@File: consumers.py
# -*- coding:utf-8 -*-
# @Author: 喵酱
# @time: 2025 - 03 -02
# @File: consumers.py
# desc:
import json
from asgiref.sync import sync_to_async
from .models import Message, Conversation
from channels.generic.websocket import AsyncWebsocketConsumer
import requests


class ChatConsumer(AsyncWebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.message_history = {}  # 用于缓存历史消息

    async def connect(self):
        print("WebSocket scope:", self.scope)
        self.conversation_id = self.scope['url_route']['kwargs']['conversation_id']
        self.room_group_name = f'chat_{self.conversation_id}'

        # 初始化历史消息缓存
        self.message_history[self.conversation_id] = await self.load_message_history()

        # 加入房间组
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        # 离开房间组
        print(f"WebSocket disconnected with code: {close_code}")
        if hasattr(self, 'channel_layer') and self.channel_layer:
            await self.channel_layer.group_discard(
                self.room_group_name,
                self.channel_name
            )

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        print(f"服务端接收到消息: {message}")

        # 保存用户消息到数据库
        await self.save_message_to_db(message, role='user')

        # 将用户消息添加到历史消息缓存
        self.message_history[self.conversation_id].append({"role": "user", "content": message})

        # 调用对话接口处理消息
        processed_message = await self.call_dialogue_service(message)

        # 将 AI 回复添加到历史消息缓存
        self.message_history[self.conversation_id].append({"role": "assistant", "content": processed_message})

        # 保存 AI 回复到数据库
        await self.save_message_to_db(processed_message, role='assistant')

        # 发送处理后的消息到房间组
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': processed_message,
                'role': 'assistant'  # 角色为 assistant
            }
        )

    @sync_to_async
    def save_message_to_db(self, message, role):
        # 保存消息到数据库
        conversation = Conversation.objects.get(id=self.conversation_id)
        Message.objects.create(conversation=conversation, role=role, content=message)

    @sync_to_async
    def load_message_history(self):
        # 从数据库加载历史消息
        conversation = Conversation.objects.get(id=self.conversation_id)
        messages = conversation.messages.all().order_by('created_at')
        return [{"role": msg.role, "content": msg.content} for msg in messages]

    async def call_dialogue_service(self, message):
        # DeepSeek API 的 URL 和 API Key
        DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
        DEEPSEEK_API_KEY = "sk-8b7c9285aac14885a60d610e7fdcedda"

        # 获取当前对话的历史消息
        message_history = self.message_history[self.conversation_id]

        # 构造请求数据,包含历史消息和当前消息
        data = {
            "model": "deepseek-chat",
            "messages": message_history,  # 包含历史消息和当前消息
            "max_tokens": 300  # 根据需要调整
        }

        print("发送请求到 DeepSeek API")
        # 发送请求到 DeepSeek API
        try:
            response = requests.post(
                DEEPSEEK_API_URL,
                headers={
                    "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
                    "Content-Type": "application/json"
                },
                json=data
            )
            response.raise_for_status()  # 检查请求是否成功
            response_data = response.json()
            ai_response = response_data['choices'][0]['message']['content']
            print("发送请求到 DeepSeek API,发送成功")
        except requests.exceptions.RequestException as e:
            # 如果 API 调用失败,返回错误信息
            ai_response = f"Error: {str(e)}"
            print("发送请求到 DeepSeek API,发送失败")

        return ai_response

    async def chat_message(self, event):
        print(f"服务端发送消息: {event['message']}")
        message = event['message']
        role = event.get('role', 'assistant')  # 默认角色为 assistant

        # 发送消息到 WebSocket
        await self.send(text_data=json.dumps({
            'message': message,
            'role': role
        }))
# views.py
# views.py
from django.http import JsonResponse
from django.shortcuts import render

# Create your views here.
import requests


from .forms import UploadFileForm


from django.shortcuts import render, redirect, get_object_or_404
from .models import Conversation, Message
import markdown
import yaml
import json
import os
from django.conf import settings
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt

DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
DEEPSEEK_API_KEY = "sk-8b"



# MAX_FILE_SIZE = 50 * 1024 * 1024  # 50MB
MAX_FILE_SIZE = 100 * 1024 * 1024  # 100MB



def index(request):
    return render(request, 'index.html')

def business_test(request):
    return render(request, 'business_test.html')

def api_test(request):
    return render(request, 'api_test.html')
def data_generator(request):
    return render(request, 'data_generator.html')

def test_report(request):
    return render(request, 'test_report.html')





def chat(request, conversation_id=None):
    # 确保会话已初始化
    if not request.session.session_key:
        request.session.create()

    session_id = request.session.session_key

    # 获取历史对话列表
    history_conversations = Conversation.objects.filter(session_id=session_id).order_by('-created_at')

    # 获取当前对话
    if conversation_id:
        current_conversation = get_object_or_404(Conversation, id=conversation_id, session_id=session_id)
    else:
        # 如果没有传递 conversation_id,则使用最近的对话或创建一个新对话
        current_conversation = history_conversations.first()
        if not current_conversation:
            current_conversation = Conversation.objects.create(session_id=session_id, title="New Conversation")

    if request.method == 'POST':
        user_input = request.POST.get('user_input')
        files = request.FILES.getlist('file')  # 获取上传的文件列表

        # 检查文件大小
        for file in files:
            if file.size > MAX_FILE_SIZE:
                return JsonResponse({'error': f'文件 {file.name} 大小超过 50MB 限制'}, status=400)

        # 保存用户输入
        if user_input:
            print(f"用户输入:{user_input}")
            Message.objects.create(conversation=current_conversation, role='user', content=user_input)

        # 处理上传的文件
        file_contents = []
        for file in files:
            # 保存文件到本地
            file_path = os.path.join(settings.MEDIA_ROOT, file.name)
            with open(file_path, 'wb+') as destination:
                for chunk in file.chunks():
                    destination.write(chunk)

            # 读取文件内容
            file_content = file.read().decode('utf-8')
            file_contents.append(f"文件: {file.name}\n内容:\n{file_content}")

        # 将文件内容与用户输入合并
        full_input = user_input if user_input else ""
        if file_contents:
            full_input += "\n\n上传的文件内容:\n" + "\n".join(file_contents)

        # 调用 DeepSeek API
        try:
            response = requests.post(
                DEEPSEEK_API_URL,
                headers={
                    "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
                    "Content-Type": "application/json"
                },
                json={
                    "model": "deepseek-chat",
                    "messages": [{"role": "user", "content": full_input}],
                    "max_tokens": 150  # 根据需要调整
                }
            )
            response.raise_for_status()  # 检查请求是否成功
            response_data = response.json()
            ai_response = response_data['choices'][0]['message']['content']
        except requests.exceptions.RequestException as e:
            # 如果 API 调用失败,返回错误信息
            ai_response = f"Error: {str(e)}"

        # 保存 AI 回答
        Message.objects.create(conversation=current_conversation, role='assistant', content=ai_response)

        # 通过 WebSocket 发送消息到前端
        channel_layer = get_channel_layer()
        async_to_sync(channel_layer.group_send)(
            f'chat_{current_conversation.id}',
            {
                'type': 'chat_message',
                'message': ai_response,
                'role': 'assistant'
            }
        )

    # 获取当前对话的所有消息
    messages = current_conversation.messages.all().order_by('created_at')

    return render(request, 'chat.html', {
        'current_conversation': current_conversation,
        'history_conversations': history_conversations,
        'messages': messages
    })
def new_conversation(request):
    # 确保会话已初始化
    if not request.session.session_key:
        request.session.create()

    session_id = request.session.session_key
    # 创建新对话
    new_conversation = Conversation.objects.create(session_id=session_id, title="New Conversation")
    return redirect('chat_with_id', conversation_id=new_conversation.id)  # 使用 chat_with_id

models.py

from django.db import models

class Conversation(models.Model):
    session_id = models.CharField(max_length=255)
    title = models.CharField(max_length=255, default="New Conversation")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

class Message(models.Model):
    conversation = models.ForeignKey(Conversation, related_name='messages', on_delete=models.CASCADE)
    role = models.CharField(max_length=10, choices=[('user', 'User'), ('assistant', 'Assistant')])
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.role}: {self.content}"
routing.py
# @File: routing.py
# desc:
from django.urls import re_path
from . import consumers

# 定义 WebSocket 路由
websocket_urlpatterns = [
    re_path(r'ws_chat/(?P<conversation_id>\w+)/$', consumers.ChatConsumer.as_asgi()),
]

 

@File: urls.py
# @File: urls.py
# desc:
from django.urls import path, include
from . import views
from django.conf import settings
from django.conf.urls.static import static
from .routing import websocket_urlpatterns  # 导入 WebSocket 路由

urlpatterns = [
    path('chat/', views.chat, name='chat'),  # 用于没有 conversation_id 的情况
    path('chat/<int:conversation_id>/', views.chat, name='chat_with_id'),  # 用于有 conversation_id 的情况
    path('ws/chat/<int:conversation_id>/', views.chat, name='chat_with_id'),  # 用于有 conversation_id 的情况
    path('ws_chat/', include(websocket_urlpatterns)),  # 这个是关键配置
    path('new-conversation/', views.new_conversation, name='new_conversation'),
    path('upload/', views.upload_file, name='upload'),
    path('autocomplete/', views.autocomplete, name='autocomplete'),
    path('generate-business-test/', views.generate_business_test, name='generate_business_test'),
    path('generate-api-test/', views.generate_api_test, name='generate_api_test'),
    path('', views.index, name='index'),  # 首页
   


] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

# ASGI配置文件
# asgi.py
# ASGI配置文件
# asgi.py



import os
import django
from django.core.asgi import get_asgi_application

# 设置 DJANGO_SETTINGS_MODULE
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'deepseek_project.settings')

# 初始化 Django
django.setup()

# 导入其他模块
from channels.routing import ProtocolTypeRouter, URLRouter
from deepseek_app.routing import websocket_urlpatterns

# 获取 Django 的 ASGI 应用
django_asgi_app = get_asgi_application()

# 配置 ProtocolTypeRouter
application = ProtocolTypeRouter({
    "http": django_asgi_app,  # 处理 HTTP 请求
    # "http": get_asgi_application(),  # 处理 HTTP 请求
    "websocket": URLRouter(
        websocket_urlpatterns  # 处理 WebSocket 请求
    ),
})

settings.py

from pathlib import Path
import os
import pymysql
pymysql.install_as_MySQLdb()
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-ge1ljrz+2^1-f4(y)+!a0n#!(4e)+^z4fx$2$2v7=uf+e$^a&5'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels',  # 添加 channels
    'deepseek_app',

]

ASGI_APPLICATION = 'deepseek_project.asgi.application'

MIDDLEWARE = [
    'whitenoise.middleware.WhiteNoiseMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# 配置静态文件存储
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
ROOT_URLCONF = 'deepseek_project.urls'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'deepseek_project.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases

# DATABASES = {
#     'default': {
#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': BASE_DIR / 'db.sqlite3',
#     }
# }

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 使用 MySQL
        'NAME': 'ds11111',
        'USER': 'root',
        'PASSWORD': '121111',
        'HOST': '11.11.21.11',  # 或者是你 MySQL 的主机地址
        'PORT': '3306',
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')

CORS_ALLOWED_ORIGINS = [
    "http://127.0.0.1:8000",
    "http://localhost:8000",
]



#
# CHANNEL_LAYERS = {
#     "default": {
#         "BACKEND": "channels_redis.core.RedisChannelLayer",
#         "CONFIG": {
#             "hosts": [("11.11.21.11", 6379)],
#             "channel_capacity": {
#                 "http.request": 200,
#                 "http.response!*": 200,
#                 "websocket.send!*": 200,
#             },
#         },
#     },
# }

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels.layers.InMemoryChannelLayer",
    },
}

wsgi.py
import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'deepseek_project.settings')

application = get_wsgi_application()

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI对话</title>
    <!-- 加载 static 标签库 -->
    {% load static %}
    <!-- Bootstrap CSS (本地文件) -->
    <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
    <!-- Font Awesome (本地文件) -->
    <link href="{% static 'css/all.min.css' %}" rel="stylesheet">
    <style>
        /* 自定义样式 */
        body {
            display: flex;
            height: 100vh;
            background-color: #f8f9fa;
            margin: 0;
        }
        .sidebar {
            width: 250px;
            background-color: #2c3e50;
            color: white;
            padding: 20px;
            overflow-y: auto;
        }
        .main-content {
            flex: 1;
            display: flex;
            flex-direction: column;
            background-color: #fff;
        }
        .chat-history {
            flex: 1;
            overflow-y: auto;
            padding: 20px;
            scroll-behavior: smooth; /* 平滑滚动 */
        }
        .chat-input {
            padding: 20px;
            background-color: #fff;
            border-top: 1px solid #ddd;
        }
        .message {
            margin-bottom: 15px;
        }
        .message.user {
            text-align: right;
        }
        .message.assistant {
            text-align: left;
        }
        .message .card {
            max-width: 70%;
            display: inline-block;
        }
        .message.user .card {
            background-color: #007bff;
            color: white;
        }
        .message.assistant .card {
            background-color: #f1f1f1;
            color: black;
        }
        .loading {
            display: none;
            text-align: center;
            margin: 10px 0;
        }
        .loading .spinner-border {
            width: 1.5rem;
            height: 1.5rem;
        }
        .file-preview {
            margin-bottom: 10px;
        }
        .file-info {
            padding: 5px;
            border: 1px solid #ddd;
            border-radius: 5px;
            background-color: #f9f9f9;
        }
    </style>
</head>
<body>
    <!-- 侧边栏 -->
    <div class="sidebar d-none d-md-block">
        <h2>Conversations</h2>
        <ul class="list-unstyled">
            {% for conversation in history_conversations %}
                <li class="mb-2">
                    <a href="{% url 'chat_with_id' conversation.id %}" class="text-white text-decoration-none">
                        <i class="fas fa-comment-dots me-2"></i>{{ conversation.title }}
                    </a>
                </li>
            {% endfor %}
        </ul>
        <button class="btn btn-primary w-100" onclick="location.href='{% url 'new_conversation' %}'">
            <i class="fas fa-plus me-2"></i>新建对话
        </button>
    </div>

    <!-- 主内容区 -->
    <div class="main-content">
        <!-- 对话历史 -->
        <div class="chat-history" id="chat-history">
            <h2>{{ current_conversation.title }}</h2>
            {% for message in messages %}
                <div class="message {% if message.role == 'user' %}user{% else %}assistant{% endif %}">
                    <div class="card">
                        <div class="card-body">
                            <p class="card-text">{{ message.content }}</p>
                            <small class="text-muted">{{ message.created_at|date:"Y-m-d H:i:s" }}</small>
                        </div>
                    </div>
                </div>
            {% endfor %}
        </div>

        <!-- 加载动画 -->
        <div class="loading" id="loading">
            <div class="spinner-border text-primary" role="status">
                <span class="visually-hidden">Loading...</span>
            </div>
        </div>

        <!-- 输入框 -->
        <div class="chat-input">
            <form method="post" class="d-flex flex-column" id="chat-form" enctype="multipart/form-data">
                {% csrf_token %}
                <!-- 文件上传 -->
                <div class="file-upload mb-2">
                    <input type="file" class="form-control" id="file-input" name="file" multiple
                           accept=".txt,.pdf,.docx,.xmind,.jpg,.jpeg,.png,.gif">
                </div>
                <!-- 文件预览 -->
                <div class="file-preview" id="file-preview"></div>
                <!-- 文本输入 -->
                <div class="d-flex">
                    <textarea name="user_input" class="form-control me-2" placeholder="输入你的内容..." rows="1" id="user-input"></textarea>
                    <button type="submit" class="btn btn-primary">
                        <i class="fas fa-paper-plane me-2"></i>提交
                    </button>
                </div>
            </form>
        </div>
    </div>

    <!-- Bootstrap JS (本地文件) -->
    <script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
    <!-- jQuery (本地文件) -->
    <script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
    <!-- WebSocket 支持 -->
    <script>
        // 获取当前对话 ID
        const conversationId = '{{ current_conversation.id }}';
        let chatSocket; // 将 chatSocket 定义在全局作用域

        if (!conversationId) {
            console.error('Conversation ID is missing');
        } else {
            // 创建 WebSocket 连接
            const websocketUrl = 'ws://' + window.location.host + '/ws_chat/' + conversationId + '/';
            console.log('Attempting to establish WebSocket connection to:', websocketUrl);

            chatSocket = new WebSocket(websocketUrl);

            // WebSocket 连接成功
            chatSocket.onopen = function (e) {
                console.log('WebSocket connection opened successfully');
            };

            // 处理接收到的消息
            chatSocket.onmessage = function (e) {
                console.log('Received message from server:', e.data);
                const data = JSON.parse(e.data);
                const chatHistory = document.getElementById('chat-history');

                // 添加新消息到对话历史
                const messageHtml = `
                    <div class="message ${data.role}">
                        <div class="card">
                            <div class="card-body">
                                <p class="card-text">${data.message}</p>
                                <small class="text-muted">${new Date().toLocaleString()}</small>
                            </div>
                        </div>
                    </div>
                `;
                chatHistory.innerHTML += messageHtml;

                // 滚动到底部
                chatHistory.scrollTop = chatHistory.scrollHeight;

                // 隐藏加载动画
                $('#loading').hide();
            };

            // 处理 WebSocket 关闭事件
            chatSocket.onclose = function (e) {
                console.error('WebSocket connection closed:', e);
                alert('WebSocket 连接已关闭,请刷新页面重试。');
            };

            // 处理 WebSocket 错误事件
            chatSocket.onerror = function (e) {
                console.error('WebSocket error:', e);
                alert('WebSocket 连接出错,请检查网络或刷新页面。');
            };
        }

        // 文件选择事件
        $('#file-input').on('change', function () {
            console.log('File input changed');
            const filePreview = $('#file-preview');
            filePreview.empty(); // 清空之前的预览

            const files = this.files;
            for (let i = 0; i < files.length; i++) {
                const file = files[i];
                const fileInfo = `
                    <div class="file-info mb-2">
                        <span>${file.name}</span>
                        <small class="text-muted">(${(file.size / 1024).toFixed(2)} KB)</small>
                    </div>
                `;
                filePreview.append(fileInfo);
            }
        });

        // 发送消息时显示加载动画
        $(document).ready(function () {
    $('#chat-form').on('submit', function (e) {
        e.preventDefault();
        console.log('Form submitted');

        const userInput = $('#user-input').val().trim();
        const fileInput = $('#file-input')[0].files;

        if (!userInput && fileInput.length === 0) {
            console.log('No input or file selected');
            alert('请输入内容或选择文件');
            return;
        }

        $('#loading').show(); // 显示加载动画

        // 手动将用户的消息添加到聊天记录中
        const chatHistory = document.getElementById('chat-history');
        const messageHtml = `
            <div class="message user">
                <div class="card">
                    <div class="card-body">
                        <p class="card-text">${userInput}</p>
                        <small class="text-muted">${new Date().toLocaleString()}</small>
                    </div>
                </div>
            </div>
        `;
        chatHistory.innerHTML += messageHtml;

        // 滚动到底部
        chatHistory.scrollTop = chatHistory.scrollHeight;

        // 如果有文件上传,使用 FormData 发送
        if (fileInput.length > 0) {
            console.log('File upload detected, using AJAX');
            const formData = new FormData();
            formData.append('user_input', userInput);
            for (let i = 0; i < fileInput.length; i++) {
                formData.append('file', fileInput[i]);
            }

            // 使用 AJAX 发送文件
            $.ajax({
                url: '{% url "chat_with_id" current_conversation.id %}',
                method: 'POST',
                data: formData,
                processData: false,
                contentType: false,
                headers: {
                    'X-CSRFToken': '{{ csrf_token }}'
                },
                success: function (response) {
                    console.log('File upload successful:', response);
                    $('#loading').hide();
                    location.reload(); // 刷新页面以显示新消息
                },
                error: function (xhr, status, error) {
                    console.error('File upload failed:', error);
                    $('#loading').hide();
                    alert('文件上传失败,请重试。');
                }
            });
        } else {
            console.log('Sending message via WebSocket:', userInput);
            if (chatSocket.readyState !== WebSocket.OPEN) {
                console.error('WebSocket is not open, readyState:', chatSocket.readyState);
                alert('WebSocket 连接未准备好,请稍后重试。');
                return;
            }

            // 发送消息
            chatSocket.send(JSON.stringify({
                'message': userInput
            }));
            console.log('Message sent via WebSocket:', userInput);
            $('#user-input').val(''); // 清空输入框
        }
    });
});
    </script>
</body>
</html>


网站公告

今日签到

点亮在社区的每一天
去签到