AI问答应用-我全都要V1.0版本

前几天看到一个AI工具觉得挺有意思了,琢磨了下也弄了个大概的样子

就是一次提问多个AI回答,APIKEY用的是第三方的,找个服务器部署下直接就可以用了,页面

目前集成了:deepseek,通义千问,文心一言,腾讯混元大模型

使用的是JQuery,php,html5,css3实现,直接在各大AI平台申请密钥就可以使用了

目录结构:

贴上源码:

api:

get_config.php

<?php
header('Content-Type: application/json');

// 获取配置
$config = include '../config.php';

// 只返回前端需要的配置
echo json_encode([
    'ty_enabled' => $config['ty_enabled'],
    'tx_enabled' => $config['tx_enabled'],
    'dp_enabled' => $config['dp_enabled'],
    'wx_enabled' => $config['wx_enabled'],
    'ty_api_url' => $config['ty_api_url'],
    'tx_api_url' => $config['tx_api_url'],
    'dp_api_url' => $config['dp_api_url'],
    'wx_api_url' => $config['wx_api_url'],
    'ty_model' => $config['ty_model'],
    'dp_model' => $config['dp_model']
]);

send_to_ai.php

<?php
header('Content-Type: application/json');

// 启用错误日志
ini_set('display_errors', 1);
ini_set('log_errors', 1);
ini_set('error_log', '../error.log');

// 检查请求方法
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode(['success' => false, 'error' => '请使用POST方法']);
    exit;
}

// 获取用户消息
$userMessage = isset($_POST['message']) ? $_POST['message'] : '';
if (empty($userMessage)) {
    echo json_encode(['success' => false, 'error' => '消息不能为空']);
    exit;
}

// 获取配置
$config = include '../config.php';

// 初始化回复数组
$replies = [
    'ty' => null,
    'tx' => null,
    'dp' => null,
    'wx' => null
];

// 处理通义千问请求
if ($config['ty_enabled'] === '1' && !empty($config['ty_api_key'])) {
    $replies['ty'] = callTongyi($userMessage, $config);
}

// 处理腾讯元宝请求
if ($config['tx_enabled'] === '1' && !empty($config['tx_api_key'])) {
    $replies['tx'] = callTencent($userMessage, $config);
}
// 处理DeepSeek请求
if ($config['dp_enabled'] === '1' && !empty($config['dp_api_key'])) {
    $replies['dp'] = callDeepSeek($userMessage, $config);
}

// 处理文心一言请求
if ($config['wx_enabled'] === '1' && !empty($config['wx_api_key']) && !empty($config['wx_secret_key'])) {
    $replies['wx'] = callWenxin($userMessage, $config);
}

// 返回结果
echo json_encode([
    'success' => true,
    'replies' => $replies
]);

// 通义千问API调用
// 通义千问API调用
function callTongyi($message, $config) {
    try {
        $url = $config['ty_api_url'];
        $apiKey = $config['ty_api_key'];
        $model = $config['ty_model'];
        
        $data = [
            'model' => $model,
            'messages' => [
                [
                    'role' => 'system',
                    'content' => '你是一个有帮助的助手。'
                ],
                [
                    'role' => 'user',
                    'content' => $message
                ]
            ]
        ];
        
        // 记录请求
        error_log("通义千问请求: " . json_encode($data));
        
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证对等证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证主机名
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $apiKey,
            'Content-Type: application/json'
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        // 记录响应
        error_log("通义千问响应: " . $response);
        
        if ($error) {
            error_log("通义千问CURL错误: " . $error);
            return '通义千问请求错误: ' . $error;
        }
        
        if ($httpCode === 200) {
            $result = json_decode($response, true);
            if (isset($result['choices'][0]['message']['content'])) {
                return $result['choices'][0]['message']['content'];
            } else {
                error_log("通义千问响应格式错误: " . $response);
                return '通义千问响应格式错误,请检查API文档';
            }
        } else {
            // 解析错误响应
            $errorData = json_decode($response, true);
            $errorMsg = '';
            
            // 尝试从不同的错误格式中提取错误信息
            if (isset($errorData['error']['message'])) {
                $errorMsg = $errorData['error']['message'];
            } elseif (isset($errorData['message'])) {
                $errorMsg = $errorData['message'];
            } elseif (isset($errorData['error'])) {
                $errorMsg = is_string($errorData['error']) ? $errorData['error'] : json_encode($errorData['error']);
            }
            
            // 针对400错误提供更具体的提示
            if ($httpCode === 400) {
                $errorMsg .= ". 这可能是因为请求参数格式不正确,请检查API密钥和请求格式。";
            }
            
            $errorMessage = "通义千问HTTP错误: " . $httpCode . ($errorMsg ? ", 错误信息: " . $errorMsg : "");
            error_log($errorMessage);
            return $errorMessage;
        }
    } catch (Exception $e) {
        error_log("通义千问异常: " . $e->getMessage());
        return '通义千问异常: ' . $e->getMessage();
    }
}

// 腾讯元宝API调用
function callTencent($message, $config) {
    try {
        $apiKey = $config['tx_api_key']; // 使用API密钥
        $endpoint = isset($config['tx_api_url']) ? $config['tx_api_url'] : 'api.hunyuan.cloud.tencent.com/v1/chat/completions';
        $model = isset($config['tx_model']) ? $config['tx_model'] : 'hunyuan-turbo'; // 默认使用hunyuan-turbo模型
        
        // 构建请求参数 - 按照新的API格式
        $data = [
            'model' => $model,
            'messages' => [
                [
                    'role' => 'user',
                    'content' => $message
                ]
            ],
            'enable_enhancement' => true
        ];
        
        // 记录请求
        error_log("腾讯元宝请求: " . json_encode($data));
        
        // 发送请求
        $ch = curl_init("https://" . $endpoint);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证对等证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证主机名
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $apiKey
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        // 记录响应
        error_log("腾讯元宝响应: " . $response);
        
        if ($error) {
            error_log("腾讯元宝CURL错误: " . $error);
            return '腾讯元宝请求错误: ' . $error;
        }
        
        if ($httpCode === 200) {
            $result = json_decode($response, true);
            
            // 详细记录响应结构,帮助调试
            error_log("腾讯元宝响应结构: " . print_r($result, true));
            
            // 检查响应中是否包含错误信息
            if (isset($result['error'])) {
                $errorMsg = $result['error']['message'] ?? '未知错误';
                error_log("腾讯元宝API错误: " . $errorMsg);
                return "腾讯元宝API错误: " . $errorMsg;
            }
            
            // 尝试获取不同的响应格式
            if (isset($result['choices'][0]['message']['content'])) {
                return $result['choices'][0]['message']['content'];
            } elseif (isset($result['response'])) {
                return $result['response'];
            } elseif (isset($result['output'])) {
                return $result['output'];
            } else {
                // 输出完整响应结构以便调试
                error_log("腾讯元宝响应格式错误,完整响应: " . json_encode($result));
                return '腾讯元宝响应格式错误,请检查API文档。响应结构: ' . json_encode($result);
            }
        } else {
            // 尝试解析错误响应
            $errorInfo = json_decode($response, true);
            $errorMsg = '';
            
            if (isset($errorInfo['error']['message'])) {
                $errorMsg = $errorInfo['error']['message'];
            }
            
            error_log("腾讯元宝HTTP错误: " . $httpCode . ", 响应: " . $response . ", " . $errorMsg);
            return "腾讯元宝HTTP错误: {$httpCode}" . ($errorMsg ? ", 错误信息: {$errorMsg}" : "");
        }
    } catch (Exception $e) {
        error_log("腾讯元宝异常: " . $e->getMessage());
        return '腾讯元宝异常: ' . $e->getMessage();
    }
}

// DeepSeek API调用
function callDeepSeek($message, $config) {
    try {
        $url = $config['dp_api_url'];
        $apiKey = $config['dp_api_key'];
        $model = $config['dp_model'];
        
        $data = [
            'model' => $model,
            'messages' => [
                [
                    'role' => 'system',
                    'content' => '你是一个有帮助的助手。'
                ],
                [
                    'role' => 'user',
                    'content' => $message
                ]
            ],
            'stream' => false
        ];
        
        // 记录请求
        error_log("DeepSeek请求: " . json_encode($data));
        
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证对等证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证主机名
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $apiKey,
            'Content-Type: application/json'
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        // 记录响应
        error_log("DeepSeek响应: " . $response);
        
        if ($error) {
            error_log("DeepSeek CURL错误: " . $error);
            return 'DeepSeek请求错误: ' . $error;
        }
        
        if ($httpCode === 200) {
            $result = json_decode($response, true);
            if (isset($result['choices'][0]['message']['content'])) {
                return $result['choices'][0]['message']['content'];
            } else {
                error_log("DeepSeek响应格式错误: " . $response);
                return 'DeepSeek响应格式错误,请检查API文档';
            }
        } else {
            // 针对特定错误码提供更详细的错误信息
            $errorMessage = "DeepSeek HTTP错误: " . $httpCode;
            
            // 解析错误响应
            $errorData = json_decode($response, true);
            if (isset($errorData['error']['message'])) {
                $errorMessage .= ", 错误信息: " . $errorData['error']['message'];
            }
            
            // 针对402错误提供更具体的提示
            if ($httpCode === 402) {
                $errorMessage .= ". 这可能是因为API密钥余额不足或计费问题,请检查您的DeepSeek账户余额和计费状态。";
            }
            
            error_log($errorMessage);
            return $errorMessage;
        }
    } catch (Exception $e) {
        error_log("DeepSeek异常: " . $e->getMessage());
        return 'DeepSeek异常: ' . $e->getMessage();
    }
}

// ... 现有代码 ...

// 文心一言API调用
function callWenxin($message, $config) {
    try {
        $apiKey = $config['wx_api_key'];
        $secretKey = $config['wx_secret_key'];
        
        // 记录API密钥信息(不记录完整密钥)
        error_log("文心一言API密钥长度: " . strlen($apiKey) . ", 密钥是否为空: " . (empty($apiKey) ? "是" : "否"));
        error_log("文心一言Secret密钥长度: " . strlen($secretKey) . ", 密钥是否为空: " . (empty($secretKey) ? "是" : "否"));
        
        // 获取访问令牌
        $tokenUrl = "https://aip.baidubce.com/oauth/2.0/token";
        $tokenParams = [
            'grant_type' => 'client_credentials',
            'client_id' => $apiKey,
            'client_secret' => $secretKey
        ];
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $tokenUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($tokenParams));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/x-www-form-urlencoded',
            'Accept: application/json'
        ]);
        
        $tokenResponse = curl_exec($ch);
        $tokenHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $tokenError = curl_error($ch);
        curl_close($ch);
        
        // 记录令牌响应
        error_log("文心一言令牌请求URL: " . $tokenUrl);
        error_log("文心一言令牌请求参数: grant_type=client_credentials&client_id=[隐藏]&client_secret=[隐藏]");
        error_log("文心一言令牌响应状态码: " . $tokenHttpCode);
        error_log("文心一言令牌响应: " . $tokenResponse);
        
        if ($tokenError) {
            error_log("文心一言令牌CURL错误: " . $tokenError);
            return '文心一言令牌请求错误: ' . $tokenError;
        }
        
        if ($tokenHttpCode !== 200) {
            $tokenErrorInfo = json_decode($tokenResponse, true);
            $errorMsg = isset($tokenErrorInfo['error_description']) ? $tokenErrorInfo['error_description'] : '未知错误';
            error_log("文心一言令牌HTTP错误: " . $tokenHttpCode . ", 错误信息: " . $errorMsg);
            return "文心一言授权失败: {$errorMsg} (HTTP {$tokenHttpCode})";
        }
        
        $tokenResult = json_decode($tokenResponse, true);
        if (!isset($tokenResult['access_token'])) {
            error_log("文心一言令牌响应格式错误: " . $tokenResponse);
            return '文心一言授权失败,响应中没有access_token字段。';
        }
        
        $accessToken = $tokenResult['access_token'];
        error_log("文心一言获取到的访问令牌: " . substr($accessToken, 0, 10) . "...");
        
        // 使用访问令牌调用API
        $url = $config['wx_api_url'] . "?access_token={$accessToken}";
        
        $data = [
            'messages' => [
                [
                    'role' => 'user',
                    'content' => $message
                ]
            ],
            'temperature' => 0.8,
            'top_p' => 0.8
        ];
        
        // 记录请求
        error_log("文心一言请求URL: " . $url);
        error_log("文心一言请求内容: " . json_encode($data));
        
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json'
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        // 记录响应
        error_log("文心一言响应状态码: " . $httpCode);
        error_log("文心一言响应: " . $response);
        
        if ($error) {
            error_log("文心一言CURL错误: " . $error);
            return '文心一言请求错误: ' . $error;
        }
        
        if ($httpCode === 200) {
            $result = json_decode($response, true);
            if (isset($result['result'])) {
                return $result['result'];
            } elseif (isset($result['results'][0]['content'])) {
                return $result['results'][0]['content'];
            } else {
                error_log("文心一言响应格式错误: " . $response);
                return '文心一言响应格式错误,完整响应: ' . $response;
            }
        } else {
            $errorData = json_decode($response, true);
            $errorMsg = '';
            
            if (isset($errorData['error_msg'])) {
                $errorMsg = $errorData['error_msg'];
            } elseif (isset($errorData['error_description'])) {
                $errorMsg = $errorData['error_description'];
            }
            
            error_log("文心一言HTTP错误: " . $httpCode . ", 响应: " . $response);
            return "文心一言HTTP错误: {$httpCode}" . ($errorMsg ? ", 错误信息: {$errorMsg}" : "");
        }
    } catch (Exception $e) {
        error_log("文心一言异常: " . $e->getMessage());
        return '文心一言异常: ' . $e->getMessage();
    }
}

css

style.less(请自行编译成css代码)

// 变量定义
@primary-color: #1890ff;
@background-color: #f5f5f5;
@text-color: #333;
@border-color: #e8e8e8;
@success-color: #52c41a;
@warning-color: #faad14;
@error-color: #f5222d;

// 全局样式
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: 'Microsoft YaHei', Arial, sans-serif;
  background-color: @background-color;
  color: @text-color;
  line-height: 1.6;
}

// 聊天容器
.chat-container {
  max-width: 800px;
  margin: 20px auto;
  background: white;
  border-radius: 2px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  height: calc(100vh - 40px);
}

// 聊天头部
.chat-header {
  padding: 15px;
  border-bottom: 1px solid @border-color;
  display: flex;
  justify-content: space-between;
  align-items: center;
  
  h1 {
    font-size: 18px;
    color: @primary-color;
    margin: 0;
  }
  
  .header-actions {
    a {
      color: @primary-color;
      text-decoration: none;
      margin-left: 15px;
      font-size: 14px;
      
      &:hover {
        text-decoration: underline;
      }
    }
  }
}

// 聊天消息区域
.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 15px;
  background-color: #f9f9f9;
  background-image: url('../images/chat-bg.png');
  background-size: 100px;
}

// 消息样式
.message {
  display: flex;
  margin-bottom: 15px;
  position: relative;
  
  .avatar {
    width: 40px;
    height: 40px;
    border-radius: 4px;
    overflow: hidden;
    margin-right: 10px;
    flex-shrink: 0;
    
    img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  }
  
  .message-content {
    position: relative;
    max-width: calc(100% - 50px);
    padding-bottom: 25px;
    
    .message-text {
      background-color: white;
      padding: 10px 15px;
      border-radius: 4px;
      box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
      word-break: break-word;
      
      p {
        margin-bottom: 10px;
        
        &:last-child {
          margin-bottom: 0;
        }
      }
      
      pre {
        background-color: #f5f5f5;
        padding: 10px;
        border-radius: 4px;
        overflow-x: auto;
        margin: 10px 0;
        font-family: Consolas, Monaco, 'Andale Mono', monospace;
        font-size: 14px;
      }
      
      code {
        font-family: Consolas, Monaco, 'Andale Mono', monospace;
        background-color: #f5f5f5;
        padding: 2px 4px;
        border-radius: 3px;
        font-size: 14px;
      }
    }
    
    .message-time {
      font-size: 12px;
      color: #999;
      margin-top: 5px;
    }
  }
  
  &.user-message {
    flex-direction: row-reverse;
    
    .avatar {
      margin-right: 0;
      margin-left: 10px;
    }
    
    .message-content {
      .message-text {
        background-color: @primary-color;
        color: white;
        
        code {
          background-color: rgba(255, 255, 255, 0.2);
          color: white;
        }
      }
      
      .message-time {
        text-align: right;
      }
    }
  }
}

// 消息操作按钮样式
.message-actions {
  display: none;
  position: absolute;
  left: 0;  
  min-width: 95px;
  bottom: -10px;
  transition: opacity 0.3s ease;
  opacity: 0;
  background-color: rgba(255, 255, 255, 0.9);
  border-radius: 4px;
  padding: 2px 5px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  z-index: 10;
}

.message:hover .message-actions {
  display: block;
  opacity: 1;
}

.copy-btn, .delete-btn {
  background: none;
  border: none;
  color: #666;
  cursor: pointer;
  font-size: 12px;
  padding: 2px 5px;
  margin: 0 2px;
}

.copy-btn:hover, .delete-btn:hover {
  color: @primary-color;
  background-color: #f0f0f0;
  border-radius: 3px;
}

// 聊天输入区域
.chat-input {
  padding: 15px;
  border-top: 1px solid @border-color;
  display: flex;
  align-items: center;
  
  textarea {
    flex: 1;
    border: 1px solid @border-color;
    border-radius: 4px;
    padding: 10px;
    resize: none;
    height: 60px;
    font-family: inherit;
    font-size: 14px;
    
    &:focus {
      outline: none;
      border-color: @primary-color;
    }
  }
  
  button {
    background-color: @primary-color;
    color: white;
    border: none;
    border-radius: 4px;
    padding: 10px 20px;
    margin-left: 10px;
    cursor: pointer;
    height: 60px;
    
    &:hover {
      background-color: darken(@primary-color, 10%);
    }
    
    &:disabled {
      background-color: #ccc;
      cursor: not-allowed;
    }
  }
}

// AI选择器
.ai-selector {
  display: flex;
  padding: 10px 15px;
  border-top: 1px solid @border-color;
  
  .ai-option {
    display: flex;
    align-items: center;
    margin-right: 15px;
    cursor: pointer;
    
    input {
      margin-right: 5px;
    }
    
    img {
      width: 20px;
      height: 20px;
      border-radius: 3px;
      margin-right: 5px;
    }
    
    span {
      font-size: 14px;
    }
  }
}

// 加载动画
.loading {
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 2px solid rgba(0, 0, 0, 0.1);
  border-radius: 50%;
  border-top-color: @primary-color;
  animation: spin 1s ease-in-out infinite;
  margin-right: 10px;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

// 复制成功提示
.copy-toast {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 8px 16px;
  border-radius: 4px;
  font-size: 14px;
  z-index: 1000;
  opacity: 0;
  transition: opacity 0.3s ease;
  
  &.show {
    opacity: 1;
  }
}

// 响应式设计
@media (max-width: 768px) {
  .chat-container {
    margin: 0;
    height: 100vh;
    border-radius: 0;
  }
  
  .message .message-content {
    max-width: calc(100% - 40px);
  }
  
  .chat-input {
    padding: 10px;
    
    textarea {
      height: 50px;
    }
    
    button {
      padding: 10px 15px;
      height: 50px;
    }
  }
  
  .ai-selector {
    flex-wrap: wrap;
    
    .ai-option {
      margin-bottom: 5px;
    }
  }
}

// 代码高亮
.hljs {
  display: block;
  overflow-x: auto;
  padding: 0.5em;
  background: #f0f0f0;
  color: #444;
}

.hljs-keyword,
.hljs-selector-tag,
.hljs-literal,
.hljs-section,
.hljs-link {
  color: #aa0d91;
}

.hljs-string {
  color: #008000;
}

.hljs-title,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
  color: #900;
}

.hljs-comment,
.hljs-quote {
  color: #998;
  font-style: italic;
}

.hljs-number {
  color: #099;
}

.hljs-regexp,
.hljs-template-variable {
  color: #009926;
}

.hljs-variable,
.hljs-template-tag {
  color: #905;
}

.hljs-attr,
.hljs-attribute {
  color: #0086b3;
}

.hljs-built_in {
  color: #0086b3;
}

.hljs-symbol,
.hljs-bullet {
  color: #990073;
}

.hljs-meta {
  color: #999;
  font-weight: bold;
}

.hljs-deletion {
  background: #fdd;
}

.hljs-addition {
  background: #dfd;
}

.hljs-emphasis {
  font-style: italic;
}

.hljs-strong {
  font-weight: bold;
}

images

js

chat.js

// 初始化变量
let chatHistory = [];
let aiConfig = {};

// 页面加载完成后执行
$(document).ready(function() {
    // 加载配置
    loadConfig();

    // 加载聊天历史
    loadChatHistory();

    // 绑定发送按钮点击事件
    $('#sendBtn').click(sendMessage);

    // 绑定输入框回车事件
    $('#userInput').keypress(function(e) {
        if (e.which === 13 && !e.shiftKey) {
            e.preventDefault();
            sendMessage();
        }
    });

    // 绑定复制和删除按钮事件
    $(document).on('click', '.copy-btn', function(e) {
        e.stopPropagation();
        const text = $(this).closest('.message-content').find('.message-text').text();
        copyToClipboard(text);
    });

    $(document).on('click', '.delete-btn', function(e) {
        e.stopPropagation();
        $(this).closest('.message').remove();
        saveChatHistory();
    });
});

// 加载配置
function loadConfig() {
    $.getJSON('api/get_config.php', function(data) {
        aiConfig = data;

        // 生成AI选择器
        let selectorHtml = '';

        if (aiConfig.ty_enabled === '1') {
            selectorHtml += createAiOption('ty', '通义千问');
        }

        if (aiConfig.tx_enabled === '1') {
            selectorHtml += createAiOption('tx', '腾讯元宝');
        }

        if (aiConfig.dp_enabled === '1') {
            selectorHtml += createAiOption('dp', 'DeepSeek');
        }

        if (aiConfig.wx_enabled === '1') {
            selectorHtml += createAiOption('wx', '文心一言');
        }

        $('#aiSelector').html(selectorHtml);

        // 默认选中所有AI
        $('.ai-checkbox').prop('checked', true);
    });
}

// 创建AI选项
function createAiOption(type, name) {
    return `
        <div class="ai-option">
            <input type="checkbox" id="${type}Checkbox" class="ai-checkbox" data-type="${type}">
            <img src="images/${type}.png" alt="${name}">
            <span>${name}</span>
        </div>
    `;
}

// 加载聊天历史
function loadChatHistory() {
    const savedHistory = localStorage.getItem('chatHistory');
    if (savedHistory) {
        chatHistory = JSON.parse(savedHistory);

        // 显示历史消息
        chatHistory.forEach(item => {
            if (item.sender === 'user') {
                addMessage('user', item.text);
            } else {
                addMessage('ai', item.text, item.aiType);
            }
        });

        // 滚动到底部
        scrollToBottom();
    }
}

// 保存聊天历史
function saveChatHistory() {
    // 重新从DOM中获取聊天记录
    chatHistory = [];
    $('.message').each(function() {
        const sender = $(this).data('sender');
        const text = $(this).find('.message-text').text();
        const aiType = $(this).data('ai-type') || '';

        chatHistory.push({
            sender: sender,
            text: text,
            aiType: aiType
        });
    });

    localStorage.setItem('chatHistory', JSON.stringify(chatHistory));
}

// 发送消息
function sendMessage() {
    const userMessage = $('#userInput').val().trim();

    if (userMessage === '') {
        return;
    }

    // 添加用户消息到聊天区域
    addMessage('user', userMessage);

    // 清空输入框
    $('#userInput').val('');

    // 保存聊天历史
    saveChatHistory();

    // 获取选中的AI
    const selectedAIs = [];
    $('.ai-checkbox:checked').each(function() {
        selectedAIs.push($(this).data('type'));
    });

    if (selectedAIs.length === 0) {
        addMessage('ai', '请至少选择一个AI助手', 'system');
        return;
    }

    // 为每个选中的AI添加思考中的消息
    selectedAIs.forEach(aiType => {
        const aiName = getAiName(aiType);
        addThinkingMessage(aiType, aiName);
    });

    // 发送请求到服务器
    $.ajax({
        url: 'api/send_to_ai.php',
        type: 'POST',
        data: {
            message: userMessage
        },
        success: function(response) {
            // 移除所有思考中的消息
            $('.thinking-message').remove();

            if (response.success) {
                // 逐个显示AI回复,每个AI之间有延迟
                let delay = 0;

                selectedAIs.forEach(aiType => {
                    if (response.replies[aiType]) {
                        setTimeout(function() {
                            addMessage('ai', response.replies[aiType], aiType);
                            saveChatHistory();
                            scrollToBottom();
                        }, delay);

                        // 每个AI回复之间间隔1秒
                        delay += 1000;
                    }
                });
            } else {
                addMessage('ai', '发生错误: ' + response.error, 'system');
                saveChatHistory();
            }
        },
        error: function() {
            // 移除所有思考中的消息
            $('.thinking-message').remove();

            addMessage('ai', '网络错误,请稍后再试', 'system');
            saveChatHistory();
        }
    });
}

// 添加思考中的消息
function addThinkingMessage(aiType, aiName) {
    const avatar = `images/${aiType}.png`;
    const time = new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });

    const thinkingHtml = `
        <div class="message thinking-message" data-ai-type="${aiType}">
            <div class="avatar">
                <img src="${avatar}" alt="${aiName}">
            </div>
            <div class="message-content">
                <div class="message-text">
                    <div class="loading"></div>
                    <span>正在思考中...</span>
                </div>
                <div class="message-time">${aiName} · ${time}</div>
            </div>
        </div>
    `;

    $('#chatMessages').append(thinkingHtml);
    scrollToBottom();
}

// 添加消息到聊天区域
function addMessage(sender, text, aiType = '') {
    let messageClass = '';
    let avatar = '';
    let senderName = '';

    if (sender === 'user') {
        messageClass = 'user-message';
        avatar = 'images/user.png';
        senderName = '我';
    } else {
        messageClass = 'ai-message';

        switch (aiType) {
            case 'ty':
                avatar = 'images/ty.png';
                senderName = '通义千问';
                break;
            case 'tx':
                avatar = 'images/tx.png';
                senderName = '腾讯元宝';
                break;
            case 'dp':
                avatar = 'images/dp.png';
                senderName = 'DeepSeek';
                break;
            case 'wx':
                avatar = 'images/wx.png';
                senderName = '文心一言';
                break;
            default:
                avatar = 'images/system.png';
                senderName = '系统';
                break;
        }
    }

    const time = new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });

    const messageHtml = `
        <div class="message ${messageClass}" data-sender="${sender}" data-ai-type="${aiType}">
            <div class="avatar">
                <img src="${avatar}" alt="${senderName}">
            </div>
            <div class="message-content">
                <div class="message-text">${formatMessage(text)}</div>
                <div class="message-time">${senderName} · ${time}</div>
                <div class="message-actions">
                    <button class="copy-btn">复制</button>
                    <button class="delete-btn">删除</button>
                </div>
            </div>
        </div>
    `;

    $('#chatMessages').append(messageHtml);
    scrollToBottom();
}

// 格式化消息文本
function formatMessage(text) {
    // 处理换行
    text = text.replace(/\n/g, '<br>');

    // 处理代码块
    text = text.replace(/```([\s\S]*?)```/g, function(match, code) {
        return '<pre><code>' + code + '</code></pre>';
    });

    // 处理行内代码
    text = text.replace(/`([^`]+)`/g, '<code>$1</code>');

    return text;
}

// 滚动到底部
function scrollToBottom() {
    const chatMessages = document.getElementById('chatMessages');
    chatMessages.scrollTop = chatMessages.scrollHeight;
}

// 复制到剪贴板
function copyToClipboard(text) {
    const textarea = document.createElement('textarea');
    textarea.value = text;
    document.body.appendChild(textarea);
    textarea.select();

    try {
        const successful = document.execCommand('copy');
        showCopyToast(successful ? '复制成功' : '复制失败');
    } catch (err) {
        showCopyToast('复制失败');
    }

    document.body.removeChild(textarea);
}

// 显示复制成功提示
function showCopyToast(message) {
    const toast = document.getElementById('copyToast');
    toast.textContent = message;
    toast.classList.add('show');

    setTimeout(function() {
        toast.classList.remove('show');
    }, 2000);
}

// 获取AI名称
function getAiName(aiType) {
    switch (aiType) {
        case 'ty':
            return '通义千问';
        case 'tx':
            return '腾讯元宝';
        case 'dp':
            return 'DeepSeek';
        case 'wx':
            return '文心一言';
        default:
            return '系统';
    }
}

jquery-3.6.0.min.js(自行网上下载)

admin.php

<?php
// 检查是否提交了表单
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 获取当前配置
    $config = include 'config.php';
    
    // 更新配置
    $config['ty_enabled'] = isset($_POST['ty_enabled']) ? '1' : '0';
    $config['ty_api_key'] = $_POST['ty_api_key'];
    
    $config['tx_enabled'] = isset($_POST['tx_enabled']) ? '1' : '0';
    $config['tx_api_key'] = $_POST['tx_api_key']; // 修改为tx_api_key
    
    $config['dp_enabled'] = isset($_POST['dp_enabled']) ? '1' : '0';
    $config['dp_api_key'] = $_POST['dp_api_key'];
    
    $config['wx_enabled'] = isset($_POST['wx_enabled']) ? '1' : '0';
    $config['wx_api_key'] = $_POST['wx_api_key'];
    $config['wx_secret_key'] = $_POST['wx_secret_key'];
    
    // 保存配置
    file_put_contents('config.php', '<?php return ' . var_export($config, true) . ';');
    
    $message = '配置已成功保存!';
}

// 获取当前配置
$config = include 'config.php';
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI助手配置</title>
    <link rel="stylesheet" href="css/style.css">
    <style>
        body {
            background-color: #f5f5f5;
            padding: 20px;
        }
        
        .admin-container {
            max-width: 800px;
            margin: 0 auto;
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            padding: 20px;
        }
        
        h1 {
            text-align: center;
            margin-bottom: 30px;
            color: #1890ff;
        }
        
        .form-group {
            margin-bottom: 20px;
        }
        
        .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        
        .form-group input[type="text"],
        .form-group input[type="password"] {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        
        .form-check {
            margin-bottom: 10px;
        }
        
        .form-check label {
            margin-left: 5px;
        }
        
        .ai-section {
            border: 1px solid #eee;
            border-radius: 8px;
            padding: 15px;
            margin-bottom: 20px;
        }
        
        .ai-section h2 {
            margin-top: 0;
            font-size: 1.2rem;
            color: #1890ff;
        }
        
        .submit-btn {
            background: #1890ff;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 1rem;
        }
        
        .submit-btn:hover {
            background: #40a9ff;
        }
        
        .alert {
            padding: 10px 15px;
            border-radius: 4px;
            margin-bottom: 20px;
        }
        
        .alert-success {
            background-color: #f6ffed;
            border: 1px solid #b7eb8f;
            color: #52c41a;
        }
    </style>
</head>
<body>
    <div class="admin-container">
        <h1>AI助手配置</h1>
        
        <?php if (isset($message)): ?>
        <div class="alert alert-success">
            <?php echo $message; ?>
        </div>
        <?php endif; ?>
        
        <form method="post">
            <!-- 通义千问配置 -->
            <div class="ai-section">
                <h2>通义千问配置</h2>
                <div class="form-check">
                    <input type="checkbox" id="ty_enabled" name="ty_enabled" <?php echo $config['ty_enabled'] === '1' ? 'checked' : ''; ?>>
                    <label for="ty_enabled">启用通义千问</label>
                </div>
                <div class="form-group">
                    <label for="ty_api_key">API密钥</label>
                    <input type="text" id="ty_api_key" name="ty_api_key" value="<?php echo htmlspecialchars($config['ty_api_key']); ?>">
                </div>
            </div>
            
            <!-- 腾讯元宝配置 -->
            <div class="ai-section">
                <h2>腾讯元宝配置</h2>
                <div class="form-check">
                    <input type="checkbox" id="tx_enabled" name="tx_enabled" <?php echo $config['tx_enabled'] === '1' ? 'checked' : ''; ?>>
                    <label for="tx_enabled">启用腾讯元宝</label>
                </div>
                <div class="form-group">
                    <label for="tx_api_key">API密钥</label>
                    <input type="text" id="tx_api_key" name="tx_api_key" value="<?php echo htmlspecialchars($config['tx_api_key'] ?? ''); ?>">
                </div>
            </div>
            
            <!-- DeepSeek配置 -->
            <div class="ai-section">
                <h2>DeepSeek配置</h2>
                <div class="form-check">
                    <input type="checkbox" id="dp_enabled" name="dp_enabled" <?php echo $config['dp_enabled'] === '1' ? 'checked' : ''; ?>>
                    <label for="dp_enabled">启用DeepSeek</label>
                </div>
                <div class="form-group">
                    <label for="dp_api_key">API密钥</label>
                    <input type="text" id="dp_api_key" name="dp_api_key" value="<?php echo htmlspecialchars($config['dp_api_key']); ?>">
                </div>
            </div>
            
            <!-- 文心一言配置 -->
            <div class="ai-section">
                <h2>文心一言配置</h2>
                <div class="form-check">
                    <input type="checkbox" id="wx_enabled" name="wx_enabled" <?php echo $config['wx_enabled'] === '1' ? 'checked' : ''; ?>>
                    <label for="wx_enabled">启用文心一言</label>
                </div>
                <div class="form-group">
                    <label for="wx_api_key">API密钥</label>
                    <input type="text" id="wx_api_key" name="wx_api_key" value="<?php echo htmlspecialchars($config['wx_api_key']); ?>">
                </div>
                <div class="form-group">
                    <label for="wx_secret_key">Secret Key</label>
                    <input type="password" id="wx_secret_key" name="wx_secret_key" value="<?php echo htmlspecialchars($config['wx_secret_key']); ?>">
                </div>
            </div>
            
            <button type="submit" class="submit-btn">保存配置</button>
        </form>
        
        <p style="margin-top: 20px; text-align: center;">
            <a href="index.html" style="color: #1890ff; text-decoration: none;">返回聊天界面</a>
        </p>
    </div>
</body>
</html>

config.php

<?php return array (
  'ty_enabled' => '1',
  'ty_api_key' => '密钥',
  'ty_api_url' => 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions',
  'ty_model' => 'qwen-plus', 
  
  'tx_enabled' => '1',
  'tx_api_key' => '密钥,
  'tx_api_url' => 'api.hunyuan.cloud.tencent.com/v1/chat/completions',
  'tx_model' => 'hunyuan-turbo',
  'tx_api_version' => '2023-09-01',
  
  'dp_enabled' => '1',
  'dp_api_key' => '密钥',
  'dp_api_url' => 'https://api.deepseek.com/chat/completions',
  'dp_model' => 'deepseek-chat',
  
  'wx_enabled' => '1',
  'wx_api_key' => '密钥',
  'wx_secret_key' => '密钥',
  'wx_api_url' => 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions',
  'wx_model' => 'ernie-bot',
);

index.html

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI助手聊天工具</title>
    <link rel="stylesheet" href="css/style.css">
    <script src="./js/jquery-3.6.0.min.js"></script>
</head>

<body>
    <div class="chat-container">
        <div class="chat-header">
            <h1>AI助手聊天工具</h1>
            <div class="header-actions">
                <a href="admin.php">管理</a>
            </div>
        </div>

        <div class="chat-messages" id="chatMessages">
            <!-- 聊天记录将在这里显示 -->
        </div>

        <div class="ai-selector" id="aiSelector">
            <!-- AI选择器将在这里动态生成 -->
        </div>

        <div class="chat-input">
            <textarea id="userInput" placeholder="请输入您的问题..."></textarea>
            <button id="sendBtn">发送</button>
        </div>
    </div>

    <!-- 复制成功提示 -->
    <div class="copy-toast" id="copyToast"></div>

    <script src="js/chat.js"></script>
</body>

</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值