chore: 以本地为准,上传全部并替换 GitHub
This commit is contained in:
617
app/ai-agent/channels/page.tsx
Normal file
617
app/ai-agent/channels/page.tsx
Normal file
@@ -0,0 +1,617 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import {
|
||||
MessageSquare,
|
||||
Plus,
|
||||
Settings,
|
||||
CheckCircle2,
|
||||
XCircle,
|
||||
RefreshCw,
|
||||
Copy,
|
||||
ExternalLink,
|
||||
Webhook,
|
||||
Bot,
|
||||
Loader2,
|
||||
Send,
|
||||
Key,
|
||||
Globe,
|
||||
Smartphone,
|
||||
} from "lucide-react"
|
||||
|
||||
// 渠道接口
|
||||
interface Channel {
|
||||
id: string
|
||||
name: string
|
||||
type: 'feishu' | 'wecom' | 'wechat' | 'api' | 'webhook'
|
||||
status: 'connected' | 'disconnected' | 'pending'
|
||||
config: {
|
||||
appId?: string
|
||||
appSecret?: string
|
||||
webhookUrl?: string
|
||||
token?: string
|
||||
encryptKey?: string
|
||||
}
|
||||
stats: {
|
||||
totalMessages: number
|
||||
todayMessages: number
|
||||
lastMessage?: string
|
||||
}
|
||||
createdAt: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
// 渠道类型配置 - 包含详细配置步骤
|
||||
const CHANNEL_TYPES = {
|
||||
feishu: {
|
||||
name: '飞书',
|
||||
icon: '🪶',
|
||||
color: 'bg-blue-100 text-blue-700',
|
||||
description: '对接飞书机器人,支持私聊和群聊',
|
||||
configFields: ['appId', 'appSecret', 'encryptKey', 'token'],
|
||||
steps: [
|
||||
'1. 登录飞书开放平台 open.feishu.cn',
|
||||
'2. 创建企业自建应用 → 机器人',
|
||||
'3. 获取 App ID 和 App Secret',
|
||||
'4. 配置事件订阅 URL: {apiBaseUrl}/feishu/events',
|
||||
'5. 添加权限: im:message, im:message.group_at_msg',
|
||||
'6. 将机器人添加到目标群聊',
|
||||
'7. @机器人 即可开始对话',
|
||||
],
|
||||
},
|
||||
wecom: {
|
||||
name: '企业微信',
|
||||
icon: '💼',
|
||||
color: 'bg-green-100 text-green-700',
|
||||
description: '对接企业微信应用或机器人',
|
||||
configFields: ['corpId', 'agentId', 'secret', 'token', 'encodingAESKey'],
|
||||
steps: [
|
||||
'1. 登录企业微信管理后台 work.weixin.qq.com',
|
||||
'2. 应用管理 → 自建应用 → 创建应用',
|
||||
'3. 获取 CorpID、AgentId、Secret',
|
||||
'4. 设置接收消息 URL: {apiBaseUrl}/wecom/callback',
|
||||
'5. 配置 Token 和 EncodingAESKey',
|
||||
'6. 开启客户联系、消息推送等权限',
|
||||
'7. 发消息给应用即可触发对话',
|
||||
],
|
||||
},
|
||||
wechat: {
|
||||
name: '微信',
|
||||
icon: '💬',
|
||||
color: 'bg-emerald-100 text-emerald-700',
|
||||
description: '通过存客宝对接微信私域',
|
||||
configFields: ['ckbApiKey', 'ckbSecret'],
|
||||
steps: [
|
||||
'1. 登录存客宝后台 admin.cunkebao.com',
|
||||
'2. 系统设置 → API配置',
|
||||
'3. 获取 API Key 和 Secret',
|
||||
'4. 配置消息回调 URL: {apiBaseUrl}/ckb/webhook',
|
||||
'5. 在卡若AI配置存客宝凭证',
|
||||
'6. 微信好友发消息即可触发AI回复',
|
||||
],
|
||||
},
|
||||
api: {
|
||||
name: 'API接口',
|
||||
icon: '🔌',
|
||||
color: 'bg-purple-100 text-purple-700',
|
||||
description: '标准REST API,供第三方系统调用',
|
||||
configFields: ['apiKey'],
|
||||
steps: [
|
||||
'1. 在本页面生成 API Key',
|
||||
'2. 调用方式: POST {apiBaseUrl}/chat',
|
||||
'3. Header: Authorization: Bearer {API_KEY}',
|
||||
'4. Body: { "message": "你的问题" }',
|
||||
'5. 响应: { "success": true, "response": {...} }',
|
||||
],
|
||||
},
|
||||
webhook: {
|
||||
name: 'Webhook',
|
||||
icon: '🔗',
|
||||
color: 'bg-orange-100 text-orange-700',
|
||||
description: '通用Webhook回调接口',
|
||||
configFields: ['webhookUrl', 'secret'],
|
||||
steps: [
|
||||
'1. 配置接收 Webhook 的目标 URL',
|
||||
'2. 设置签名密钥 (可选)',
|
||||
'3. 神射手会在以下事件时推送:',
|
||||
' - 新用户画像生成',
|
||||
' - 流量包创建完成',
|
||||
' - 报告生成完成',
|
||||
'4. 推送格式: { "event": "...", "data": {...} }',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// 预定义渠道
|
||||
const DEFAULT_CHANNELS: Channel[] = [
|
||||
{
|
||||
id: 'ch_feishu_1',
|
||||
name: '卡若AI飞书机器人',
|
||||
type: 'feishu',
|
||||
status: 'connected',
|
||||
config: {
|
||||
appId: 'cli_a6xxx',
|
||||
appSecret: '********',
|
||||
token: 'karuo_token',
|
||||
},
|
||||
stats: {
|
||||
totalMessages: 12580,
|
||||
todayMessages: 156,
|
||||
lastMessage: '5分钟前',
|
||||
},
|
||||
createdAt: '2024-01-15',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'ch_wecom_1',
|
||||
name: '企业微信-神射手助手',
|
||||
type: 'wecom',
|
||||
status: 'connected',
|
||||
config: {
|
||||
appId: 'ww_xxx',
|
||||
},
|
||||
stats: {
|
||||
totalMessages: 8920,
|
||||
todayMessages: 89,
|
||||
lastMessage: '12分钟前',
|
||||
},
|
||||
createdAt: '2024-02-01',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'ch_wechat_1',
|
||||
name: '存客宝微信通道',
|
||||
type: 'wechat',
|
||||
status: 'connected',
|
||||
config: {},
|
||||
stats: {
|
||||
totalMessages: 45600,
|
||||
todayMessages: 320,
|
||||
lastMessage: '1分钟前',
|
||||
},
|
||||
createdAt: '2024-01-01',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'ch_api_1',
|
||||
name: '神射手API',
|
||||
type: 'api',
|
||||
status: 'connected',
|
||||
config: {
|
||||
apiKey: 'sk-archer-xxx',
|
||||
},
|
||||
stats: {
|
||||
totalMessages: 156000,
|
||||
todayMessages: 2340,
|
||||
lastMessage: '刚刚',
|
||||
},
|
||||
createdAt: '2024-01-01',
|
||||
enabled: true,
|
||||
},
|
||||
]
|
||||
|
||||
export default function ChannelsPage() {
|
||||
const [channels, setChannels] = useState<Channel[]>(DEFAULT_CHANNELS)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [showAddDialog, setShowAddDialog] = useState(false)
|
||||
const [showConfigDialog, setShowConfigDialog] = useState(false)
|
||||
const [selectedChannel, setSelectedChannel] = useState<Channel | null>(null)
|
||||
const [newChannelType, setNewChannelType] = useState<keyof typeof CHANNEL_TYPES>('feishu')
|
||||
const [testLoading, setTestLoading] = useState<string | null>(null)
|
||||
|
||||
// API端点基础URL - 动态获取当前域名
|
||||
const [apiBaseUrl, setApiBaseUrl] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
// 获取当前部署的域名
|
||||
const host = typeof window !== 'undefined' ? window.location.origin : ''
|
||||
setApiBaseUrl(`${host}/api/shensheshou`)
|
||||
}, [])
|
||||
|
||||
const formatNumber = (num: number): string => {
|
||||
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`
|
||||
if (num >= 1000) return `${(num / 1000).toFixed(1)}K`
|
||||
return num.toLocaleString()
|
||||
}
|
||||
|
||||
// 测试连接
|
||||
const testConnection = async (channelId: string) => {
|
||||
setTestLoading(channelId)
|
||||
await new Promise(r => setTimeout(r, 2000))
|
||||
setTestLoading(null)
|
||||
}
|
||||
|
||||
// 切换启用状态
|
||||
const toggleChannel = (channelId: string) => {
|
||||
setChannels(channels.map(ch =>
|
||||
ch.id === channelId ? { ...ch, enabled: !ch.enabled } : ch
|
||||
))
|
||||
}
|
||||
|
||||
// 复制到剪贴板
|
||||
const copyToClipboard = (text: string) => {
|
||||
navigator.clipboard.writeText(text)
|
||||
}
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
switch (status) {
|
||||
case 'connected':
|
||||
return <Badge className="bg-green-100 text-green-700"><CheckCircle2 className="h-3 w-3 mr-1" />已连接</Badge>
|
||||
case 'disconnected':
|
||||
return <Badge className="bg-red-100 text-red-700"><XCircle className="h-3 w-3 mr-1" />未连接</Badge>
|
||||
default:
|
||||
return <Badge className="bg-yellow-100 text-yellow-700"><RefreshCw className="h-3 w-3 mr-1" />配置中</Badge>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50/30 to-purple-50/20">
|
||||
<div className="p-6 space-y-6">
|
||||
{/* 顶部标题 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">渠道配置</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">配置飞书、企微、微信等渠道,让AI助手可通过多渠道访问</p>
|
||||
</div>
|
||||
<Button onClick={() => setShowAddDialog(true)}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
添加渠道
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<Card className="border-0 shadow-sm bg-white/80">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">接入渠道</p>
|
||||
<p className="text-2xl font-bold">{channels.length}</p>
|
||||
</div>
|
||||
<Webhook className="h-8 w-8 text-blue-500 opacity-50" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="border-0 shadow-sm bg-white/80">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">已连接</p>
|
||||
<p className="text-2xl font-bold text-green-600">{channels.filter(c => c.status === 'connected').length}</p>
|
||||
</div>
|
||||
<CheckCircle2 className="h-8 w-8 text-green-500 opacity-50" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="border-0 shadow-sm bg-white/80">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">今日消息</p>
|
||||
<p className="text-2xl font-bold">{formatNumber(channels.reduce((sum, c) => sum + c.stats.todayMessages, 0))}</p>
|
||||
</div>
|
||||
<MessageSquare className="h-8 w-8 text-purple-500 opacity-50" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="border-0 shadow-sm bg-white/80">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">累计消息</p>
|
||||
<p className="text-2xl font-bold">{formatNumber(channels.reduce((sum, c) => sum + c.stats.totalMessages, 0))}</p>
|
||||
</div>
|
||||
<Send className="h-8 w-8 text-orange-500 opacity-50" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* API接入说明 */}
|
||||
<Card className="border-0 shadow-sm bg-gradient-to-r from-blue-50 to-purple-50">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 mb-1">🔗 API接入端点</h3>
|
||||
<p className="text-sm text-gray-600 mb-2">第三方系统可通过以下接口与神射手Skill通信</p>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="bg-white px-3 py-1.5 rounded text-sm font-mono text-purple-600">
|
||||
POST {apiBaseUrl}/chat
|
||||
</code>
|
||||
<Button variant="ghost" size="sm" onClick={() => copyToClipboard(`${apiBaseUrl}/chat`)}>
|
||||
<Copy className="h-3 w-3" />
|
||||
</Button>
|
||||
<span className="text-xs text-gray-500">智能对话</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="bg-white px-3 py-1.5 rounded text-sm font-mono text-purple-600">
|
||||
POST {apiBaseUrl}/query
|
||||
</code>
|
||||
<Button variant="ghost" size="sm" onClick={() => copyToClipboard(`${apiBaseUrl}/query`)}>
|
||||
<Copy className="h-3 w-3" />
|
||||
</Button>
|
||||
<span className="text-xs text-gray-500">用户查询</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="outline" size="sm">
|
||||
<ExternalLink className="h-4 w-4 mr-1" />
|
||||
API文档
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 渠道列表 */}
|
||||
<div className="space-y-3">
|
||||
{channels.map(channel => {
|
||||
const typeConfig = CHANNEL_TYPES[channel.type]
|
||||
|
||||
return (
|
||||
<Card key={channel.id} className="border-0 shadow-sm bg-white/80 hover:shadow-md transition-all">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl ${typeConfig.color.split(' ')[0]} flex items-center justify-center text-2xl`}>
|
||||
{typeConfig.icon}
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-semibold text-gray-900">{channel.name}</h3>
|
||||
<Badge className={typeConfig.color}>{typeConfig.name}</Badge>
|
||||
{getStatusBadge(channel.status)}
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-0.5">{typeConfig.description}</p>
|
||||
<div className="flex items-center gap-4 mt-1 text-xs text-gray-500">
|
||||
<span>今日: {formatNumber(channel.stats.todayMessages)} 条</span>
|
||||
<span>累计: {formatNumber(channel.stats.totalMessages)} 条</span>
|
||||
{channel.stats.lastMessage && (
|
||||
<span>最近消息: {channel.stats.lastMessage}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch
|
||||
checked={channel.enabled}
|
||||
onCheckedChange={() => toggleChannel(channel.id)}
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => testConnection(channel.id)}
|
||||
disabled={testLoading === channel.id}
|
||||
>
|
||||
{testLoading === channel.id ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setSelectedChannel(channel)
|
||||
setShowConfigDialog(true)
|
||||
}}
|
||||
>
|
||||
<Settings className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* 添加渠道弹窗 */}
|
||||
<Dialog open={showAddDialog} onOpenChange={setShowAddDialog}>
|
||||
<DialogContent className="max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>添加渠道</DialogTitle>
|
||||
<DialogDescription>选择要接入的渠道类型</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-3 py-4">
|
||||
{Object.entries(CHANNEL_TYPES).map(([key, config]) => (
|
||||
<div
|
||||
key={key}
|
||||
onClick={() => setNewChannelType(key as keyof typeof CHANNEL_TYPES)}
|
||||
className={`p-4 rounded-xl border-2 cursor-pointer transition-all ${
|
||||
newChannelType === key ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<div className="text-2xl mb-2">{config.icon}</div>
|
||||
<h4 className="font-semibold text-gray-900">{config.name}</h4>
|
||||
<p className="text-xs text-gray-500 mt-1 line-clamp-2">{config.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setShowAddDialog(false)}>取消</Button>
|
||||
<Button onClick={() => {
|
||||
// 跳转到配置页面
|
||||
setShowAddDialog(false)
|
||||
setShowConfigDialog(true)
|
||||
setSelectedChannel({
|
||||
id: `ch_${newChannelType}_new`,
|
||||
name: '',
|
||||
type: newChannelType,
|
||||
status: 'pending',
|
||||
config: {},
|
||||
stats: { totalMessages: 0, todayMessages: 0 },
|
||||
createdAt: new Date().toISOString().split('T')[0],
|
||||
enabled: false,
|
||||
})
|
||||
}}>
|
||||
下一步:配置
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* 配置弹窗 */}
|
||||
<Dialog open={showConfigDialog} onOpenChange={setShowConfigDialog}>
|
||||
<DialogContent className="max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{selectedChannel?.id.includes('new') ? '配置新渠道' : '编辑渠道配置'}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{selectedChannel && CHANNEL_TYPES[selectedChannel.type]?.description}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{selectedChannel && (
|
||||
<div className="space-y-4 py-4">
|
||||
{/* 配置步骤指南 */}
|
||||
<div className="p-3 rounded-lg bg-blue-50 border border-blue-100">
|
||||
<h4 className="font-medium text-blue-900 mb-2 flex items-center gap-2">
|
||||
📋 {CHANNEL_TYPES[selectedChannel.type].name} 配置步骤
|
||||
</h4>
|
||||
<ol className="text-xs text-blue-800 space-y-1">
|
||||
{(CHANNEL_TYPES[selectedChannel.type] as any).steps?.map((step: string, i: number) => (
|
||||
<li key={i} className="flex items-start gap-1">
|
||||
<span>{step.replace('{apiBaseUrl}', apiBaseUrl || 'https://your-domain.com/api/shensheshou')}</span>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>渠道名称</Label>
|
||||
<Input
|
||||
placeholder={`例如:${CHANNEL_TYPES[selectedChannel.type].name}机器人`}
|
||||
defaultValue={selectedChannel.name}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{selectedChannel.type === 'feishu' && (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<Label>App ID</Label>
|
||||
<Input placeholder="cli_xxx" defaultValue={selectedChannel.config.appId} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>App Secret</Label>
|
||||
<Input type="password" placeholder="输入App Secret" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Verification Token</Label>
|
||||
<Input placeholder="用于验证请求来源" defaultValue={selectedChannel.config.token} />
|
||||
</div>
|
||||
<div className="p-3 rounded-lg bg-blue-50 text-sm">
|
||||
<p className="font-medium text-blue-800 mb-1">📋 飞书配置步骤</p>
|
||||
<ol className="text-blue-700 text-xs space-y-1 list-decimal list-inside">
|
||||
<li>在飞书开放平台创建应用</li>
|
||||
<li>添加机器人能力并设置权限</li>
|
||||
<li>配置事件订阅回调地址</li>
|
||||
<li>填入上述配置信息</li>
|
||||
</ol>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedChannel.type === 'wecom' && (
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>企业ID (CorpId)</Label>
|
||||
<Input placeholder="ww_xxx" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>应用ID (AgentId)</Label>
|
||||
<Input placeholder="1000001" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>应用Secret</Label>
|
||||
<Input type="password" placeholder="输入应用Secret" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedChannel.type === 'wechat' && (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<Label>存客宝API Key</Label>
|
||||
<Input placeholder="从存客宝获取" />
|
||||
</div>
|
||||
<div className="p-3 rounded-lg bg-green-50 text-sm">
|
||||
<p className="font-medium text-green-800 mb-1">💡 微信通道说明</p>
|
||||
<p className="text-green-700 text-xs">
|
||||
微信私域通过存客宝SDK接入,需要在存客宝平台配置API密钥
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedChannel.type === 'api' && (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<Label>API Key</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input value="sk-archer-xxx-xxx-xxx" readOnly className="font-mono" />
|
||||
<Button variant="outline" size="icon">
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>调用示例</Label>
|
||||
<pre className="p-3 rounded-lg bg-gray-900 text-green-400 text-xs overflow-x-auto">
|
||||
{`curl -X POST ${apiBaseUrl}/chat \\
|
||||
-H "Authorization: Bearer sk-archer-xxx" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{"message": "查询用户 13800138000"}'`}
|
||||
</pre>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedChannel.type === 'webhook' && (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<Label>Webhook回调URL</Label>
|
||||
<Input placeholder="https://your-server.com/webhook" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>签名密钥(可选)</Label>
|
||||
<Input placeholder="用于验证回调请求" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setShowConfigDialog(false)}>取消</Button>
|
||||
<Button onClick={() => {
|
||||
setShowConfigDialog(false)
|
||||
// 保存配置逻辑
|
||||
}}>
|
||||
保存配置
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user