Quellcode durchsuchen

fix: API示例URL自适应服务器地址,修复HTTP剪贴板复制

- ModelPricingDetail.tsx: baseUrl 从硬编码 https://aigc.wangxunai.com 改为 window.location.origin
- 全部页面的 navigator.clipboard.writeText 替换为 copyToClipboard 工具函数
  支持 HTTP 环境下的剪贴板复制(execCommand fallback)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
mengboxin137-blip vor 7 Stunden
Ursprung
Commit
b5abae5acf

+ 6 - 5
frontend/components/LocalModelList.tsx

@@ -8,6 +8,7 @@ import React, { useState, useEffect } from 'react';
 import { Server, Loader2, AlertCircle, X, Copy, CheckCircle2, Code } from 'lucide-react';
 import { localModelApi, LocalModel } from '../services/localModelApi';
 import { useToast } from '../contexts/NotificationContext';
+import { copyToClipboard } from '../utils/clipboard';
 
 interface LocalModelListProps {
   onUseModel?: (modelId: string) => void;
@@ -42,8 +43,8 @@ const LocalModelList: React.FC<LocalModelListProps> = ({ onUseModel }) => {
     setApiExampleModel(model);
   };
 
-  const copyToClipboard = async (text: string, key: string) => {
-    await navigator.clipboard.writeText(text);
+  const handleCopy = async (text: string, key: string) => {
+    await copyToClipboard(text);
     setCopied(key);
     setTimeout(() => setCopied(null), 2000);
   };
@@ -543,7 +544,7 @@ console.log(data.choices[0].message.content);`;
                 <div className="flex items-center justify-between mb-2">
                   <h4 className="text-sm font-medium text-gray-700">cURL</h4>
                   <button
-                    onClick={() => copyToClipboard(generateCurlExample(apiExampleModel), 'curl')}
+                    onClick={() => handleCopy(generateCurlExample(apiExampleModel), 'curl')}
                     className="flex items-center gap-1 text-xs text-gray-500 hover:text-blue-600 transition-colors"
                   >
                     {copied === 'curl' ? (
@@ -569,7 +570,7 @@ console.log(data.choices[0].message.content);`;
                 <div className="flex items-center justify-between mb-2">
                   <h4 className="text-sm font-medium text-gray-700">Python (OpenAI SDK)</h4>
                   <button
-                    onClick={() => copyToClipboard(generatePythonExample(apiExampleModel), 'python')}
+                    onClick={() => handleCopy(generatePythonExample(apiExampleModel), 'python')}
                     className="flex items-center gap-1 text-xs text-gray-500 hover:text-blue-600 transition-colors"
                   >
                     {copied === 'python' ? (
@@ -595,7 +596,7 @@ console.log(data.choices[0].message.content);`;
                 <div className="flex items-center justify-between mb-2">
                   <h4 className="text-sm font-medium text-gray-700">JavaScript (Fetch)</h4>
                   <button
-                    onClick={() => copyToClipboard(generateJsExample(apiExampleModel), 'js')}
+                    onClick={() => handleCopy(generateJsExample(apiExampleModel), 'js')}
                     className="flex items-center gap-1 text-xs text-gray-500 hover:text-blue-600 transition-colors"
                   >
                     {copied === 'js' ? (

+ 2 - 1
frontend/components/MarkdownRenderer.tsx

@@ -1,5 +1,6 @@
 import React, { memo, useMemo } from 'react';
 import ReactMarkdown from 'react-markdown';
+import { copyToClipboard } from '../utils/clipboard';
 import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
 import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
 import remarkGfm from 'remark-gfm';
@@ -49,7 +50,7 @@ const MarkdownRenderer: React.FC<MarkdownRendererProps> = memo(({ content, class
           </SyntaxHighlighter>
           <button
             onClick={() => {
-              navigator.clipboard.writeText(String(children));
+              copyToClipboard(String(children));
             }}
             className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity bg-gray-700 hover:bg-gray-600 text-white text-xs px-2 py-1 rounded"
           >

+ 3 - 2
frontend/pages/ModelPricingDetail.tsx

@@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
 import { useNavigate, useParams } from 'react-router-dom';
 import { getPricingMappingLookup, modelApi, ParsedPricingResponse } from '../services/modelApi';
 import { ArrowLeft, Loader2, Image, Video, Mic, FileText, CheckCircle2, XCircle, Copy } from '../icons/commonIcons';
+import { copyToClipboard } from '../utils/clipboard';
 
 const pricingKeyLabels: Record<string, string> = {
   input: '输入', output: '输出', input_thinking: '输入(思考)', output_thinking: '输出(思考)',
@@ -78,7 +79,7 @@ const getDisplayLabel = (key: string): string => pricingKeyLabels[key] || transl
 
 // API示例代码生成(根据模型分类返回对应示例)
 const generateApiExamples = (modelCode: string, categories: number[] = []) => {
-  const baseUrl = import.meta.env.VITE_OPENAPI_BASE_URL || 'https://aigc.wangxunai.com';
+  const baseUrl = import.meta.env.VITE_OPENAPI_BASE_URL || window.location.origin;
   const api = `${baseUrl}/api/v1`;
 
   // 判断主分类(优先级:图像编辑 > 图像生成 > 视频 > TTS > STT > Embedding > Rerank > LLM)
@@ -518,7 +519,7 @@ const ModelPricingDetail: React.FC = () => {
             <h2 className="text-lg font-semibold text-gray-900">API 调用示例</h2>
             <button
               onClick={() => {
-                navigator.clipboard.writeText(examples[activeTab] || '');
+                copyToClipboard(examples[activeTab] || '');
                 setCopied(true);
                 setTimeout(() => setCopied(false), 2000);
               }}

+ 4 - 3
frontend/pages/OpenPlatform.tsx

@@ -8,6 +8,7 @@ import React, { useState, useEffect } from 'react';
 import { Key, BarChart3, FileText, Plus, Copy, Eye, EyeOff, Trash2, Power, Loader2, CheckCircle2 } from 'lucide-react';
 import { platformApi, ApiKey, ApiKeyCreateResponse, Stats, CallLog, PaginatedResponse, API_BASE_FULL } from '../services/platformApi';
 import { useToast, useConfirm } from '../contexts/NotificationContext';
+import { copyToClipboard } from '../utils/clipboard';
 
 type TabType = 'api-keys' | 'logs';
 
@@ -117,8 +118,8 @@ const OpenPlatform: React.FC = () => {
     }
   };
 
-  const copyToClipboard = async (text: string, keyId: number) => {
-    await navigator.clipboard.writeText(text);
+  const handleCopy = async (text: string, keyId: number) => {
+    await copyToClipboard(text);
     setCopiedKeyId(keyId);
     setTimeout(() => setCopiedKeyId(null), 2000);
   };
@@ -499,7 +500,7 @@ const OpenPlatform: React.FC = () => {
             <div className="flex justify-end gap-3 mt-6">
               <button
                 onClick={() => {
-                  navigator.clipboard.writeText(newlyCreatedKey.api_key);
+                  copyToClipboard(newlyCreatedKey.api_key);
                   showToast('已复制到剪贴板', 'success');
                 }}
                 className="flex items-center gap-2 px-4 py-2 border border-gray-200 rounded-lg hover:bg-gray-50"

+ 2 - 1
frontend/pages/Profile.tsx

@@ -8,6 +8,7 @@ import { ossApi } from '../services/ossApi';
 import { Loader2 } from '../icons/commonIcons';
 import { validateIDCard, validateRealName } from '../utils/idCardValidator';
 import { encryptVerificationData } from '../utils/rsaEncryption';
+import { copyToClipboard } from '../utils/clipboard';
 
 interface EditModalProps {
   isOpen: boolean;
@@ -455,7 +456,7 @@ const Profile: React.FC = () => {
 
   const handleCopyId = () => {
     if (!userInfo?.id) return;
-    navigator.clipboard.writeText(userInfo.id);
+    copyToClipboard(userInfo.id);
     setCopied(true);
     setTimeout(() => setCopied(false), 2000);
   };