mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-24 23:06:51 +00:00
会话详情wxid支持复制
This commit is contained in:
@@ -2016,12 +2016,43 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
user-select: text;
|
||||||
|
|
||||||
&.highlight {
|
&.highlight {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.copy-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s, color 0.15s, background 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .copy-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-list {
|
.table-list {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'
|
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'
|
||||||
import { Search, MessageSquare, AlertCircle, Loader2, RefreshCw, X, ChevronDown, Info, Calendar, Database, Hash, Play, Pause, Image as ImageIcon, Link, Mic, CheckCircle, XCircle } from 'lucide-react'
|
import { Search, MessageSquare, AlertCircle, Loader2, RefreshCw, X, ChevronDown, Info, Calendar, Database, Hash, Play, Pause, Image as ImageIcon, Link, Mic, CheckCircle, XCircle, Copy, Check } from 'lucide-react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { useChatStore } from '../stores/chatStore'
|
import { useChatStore } from '../stores/chatStore'
|
||||||
import type { ChatSession, Message } from '../types/models'
|
import type { ChatSession, Message } from '../types/models'
|
||||||
@@ -168,6 +168,7 @@ function ChatPage(_props: ChatPageProps) {
|
|||||||
const [showDetailPanel, setShowDetailPanel] = useState(false)
|
const [showDetailPanel, setShowDetailPanel] = useState(false)
|
||||||
const [sessionDetail, setSessionDetail] = useState<SessionDetail | null>(null)
|
const [sessionDetail, setSessionDetail] = useState<SessionDetail | null>(null)
|
||||||
const [isLoadingDetail, setIsLoadingDetail] = useState(false)
|
const [isLoadingDetail, setIsLoadingDetail] = useState(false)
|
||||||
|
const [copiedField, setCopiedField] = useState<string | null>(null)
|
||||||
const [highlightedMessageKeys, setHighlightedMessageKeys] = useState<string[]>([])
|
const [highlightedMessageKeys, setHighlightedMessageKeys] = useState<string[]>([])
|
||||||
const [isRefreshingSessions, setIsRefreshingSessions] = useState(false)
|
const [isRefreshingSessions, setIsRefreshingSessions] = useState(false)
|
||||||
const [hasInitialMessages, setHasInitialMessages] = useState(false)
|
const [hasInitialMessages, setHasInitialMessages] = useState(false)
|
||||||
@@ -243,6 +244,25 @@ function ChatPage(_props: ChatPageProps) {
|
|||||||
setShowDetailPanel(!showDetailPanel)
|
setShowDetailPanel(!showDetailPanel)
|
||||||
}, [showDetailPanel, currentSessionId, loadSessionDetail])
|
}, [showDetailPanel, currentSessionId, loadSessionDetail])
|
||||||
|
|
||||||
|
// 复制字段值到剪贴板
|
||||||
|
const handleCopyField = useCallback(async (text: string, field: string) => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text)
|
||||||
|
setCopiedField(field)
|
||||||
|
setTimeout(() => setCopiedField(null), 1500)
|
||||||
|
} catch {
|
||||||
|
// fallback
|
||||||
|
const textarea = document.createElement('textarea')
|
||||||
|
textarea.value = text
|
||||||
|
document.body.appendChild(textarea)
|
||||||
|
textarea.select()
|
||||||
|
document.execCommand('copy')
|
||||||
|
document.body.removeChild(textarea)
|
||||||
|
setCopiedField(field)
|
||||||
|
setTimeout(() => setCopiedField(null), 1500)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
// 连接数据库
|
// 连接数据库
|
||||||
const connect = useCallback(async () => {
|
const connect = useCallback(async () => {
|
||||||
setConnecting(true)
|
setConnecting(true)
|
||||||
@@ -1601,23 +1621,35 @@ function ChatPage(_props: ChatPageProps) {
|
|||||||
<Hash size={14} />
|
<Hash size={14} />
|
||||||
<span className="label">微信ID</span>
|
<span className="label">微信ID</span>
|
||||||
<span className="value">{sessionDetail.wxid}</span>
|
<span className="value">{sessionDetail.wxid}</span>
|
||||||
|
<button className="copy-btn" title="复制" onClick={() => handleCopyField(sessionDetail.wxid, 'wxid')}>
|
||||||
|
{copiedField === 'wxid' ? <Check size={12} /> : <Copy size={12} />}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{sessionDetail.remark && (
|
{sessionDetail.remark && (
|
||||||
<div className="detail-item">
|
<div className="detail-item">
|
||||||
<span className="label">备注</span>
|
<span className="label">备注</span>
|
||||||
<span className="value">{sessionDetail.remark}</span>
|
<span className="value">{sessionDetail.remark}</span>
|
||||||
|
<button className="copy-btn" title="复制" onClick={() => handleCopyField(sessionDetail.remark!, 'remark')}>
|
||||||
|
{copiedField === 'remark' ? <Check size={12} /> : <Copy size={12} />}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{sessionDetail.nickName && (
|
{sessionDetail.nickName && (
|
||||||
<div className="detail-item">
|
<div className="detail-item">
|
||||||
<span className="label">昵称</span>
|
<span className="label">昵称</span>
|
||||||
<span className="value">{sessionDetail.nickName}</span>
|
<span className="value">{sessionDetail.nickName}</span>
|
||||||
|
<button className="copy-btn" title="复制" onClick={() => handleCopyField(sessionDetail.nickName!, 'nickName')}>
|
||||||
|
{copiedField === 'nickName' ? <Check size={12} /> : <Copy size={12} />}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{sessionDetail.alias && (
|
{sessionDetail.alias && (
|
||||||
<div className="detail-item">
|
<div className="detail-item">
|
||||||
<span className="label">微信号</span>
|
<span className="label">微信号</span>
|
||||||
<span className="value">{sessionDetail.alias}</span>
|
<span className="value">{sessionDetail.alias}</span>
|
||||||
|
<button className="copy-btn" title="复制" onClick={() => handleCopyField(sessionDetail.alias!, 'alias')}>
|
||||||
|
{copiedField === 'alias' ? <Check size={12} /> : <Copy size={12} />}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user