mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-05 21:05:39 +08:00
fix: 修复导出日期、语音和联系人名字问题
- 导出的 HTML:点月份先看日期,选中日期后才跳到对应消息。 - 语音转文字:不同账号、不同聊天里的语音不会再串到一起。 - 联系人名字:正常昵称显示不变;昵称异常时使用可见的备注或账号,导出文件名也能正常使用。
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Contact } from '../../../../shared/types'
|
||||
import { displayContactName } from '../../../../shared/contact-name'
|
||||
|
||||
interface ConversationItemProps {
|
||||
contact: Contact
|
||||
@@ -12,10 +13,9 @@ export function ConversationItem({
|
||||
active,
|
||||
onSelect
|
||||
}: ConversationItemProps): React.ReactElement {
|
||||
const nickname = contact.m_nsNickName?.trim()
|
||||
const avatarUsername = contact.m_nsUsrName
|
||||
const internalWxid = contact.wxid || avatarUsername
|
||||
const displayName = nickname || internalWxid || '未命名会话'
|
||||
const displayName = displayContactName(contact, '未命名会话')
|
||||
const initial = (displayName || internalWxid || '?').charAt(0)
|
||||
const [repairedAvatar, setRepairedAvatar] = useState<{
|
||||
username: string
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Contact, Message } from '../../../../shared/types'
|
||||
import type { ExportFormat, GroupMemberName } from './exportTypes'
|
||||
import { resolveMemberName } from '../../../../shared/member-names'
|
||||
import { displayContactName } from '../../../../shared/contact-name'
|
||||
|
||||
export const messageKinds = [
|
||||
['text', '文字'],
|
||||
@@ -24,7 +25,7 @@ export const formatLabels: Record<ExportFormat, { label: string; hint?: string }
|
||||
export const formatOrder: ExportFormat[] = ['csv', 'html', 'json', 'markdown']
|
||||
|
||||
export function displayName(contact: Contact | null): string {
|
||||
return contact?.m_nsNickName || contact?.m_nsUsrName || '未选择会话'
|
||||
return displayContactName(contact, '未选择会话')
|
||||
}
|
||||
|
||||
export function formatPreviewTime(message: Message): string {
|
||||
|
||||
@@ -598,6 +598,8 @@ export function ScheduledReportsWorkspace({
|
||||
const [editingTask, setEditingTask] = React.useState<ScheduledReportTask | null>(null)
|
||||
const [saving, setSaving] = React.useState(false)
|
||||
const [deletingTask, setDeletingTask] = React.useState<ScheduledReportTask | null>(null)
|
||||
const [retryingExecution, setRetryingExecution] =
|
||||
React.useState<ScheduledReportExecution | null>(null)
|
||||
const [busyTaskId, setBusyTaskId] = React.useState<string | null>(null)
|
||||
|
||||
const load = React.useCallback(async (): Promise<void> => {
|
||||
@@ -784,6 +786,13 @@ export function ScheduledReportsWorkspace({
|
||||
}
|
||||
}
|
||||
|
||||
const confirmRetrySend = async (): Promise<void> => {
|
||||
if (!retryingExecution || busyTaskId) return
|
||||
const execution = retryingExecution
|
||||
setRetryingExecution(null)
|
||||
await retrySend(execution)
|
||||
}
|
||||
|
||||
const testErrorNotification = async (task: ScheduledReportTask): Promise<void> => {
|
||||
setBusyTaskId(task.id)
|
||||
try {
|
||||
@@ -1090,7 +1099,7 @@ export function ScheduledReportsWorkspace({
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={busy}
|
||||
onClick={() => void retrySend(execution)}
|
||||
onClick={() => setRetryingExecution(execution)}
|
||||
>
|
||||
{busy ? '发送中…' : '重新发送'}
|
||||
</Button>
|
||||
@@ -1177,6 +1186,44 @@ export function ScheduledReportsWorkspace({
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
<AlertDialog
|
||||
open={Boolean(retryingExecution)}
|
||||
onOpenChange={(open) => !open && setRetryingExecution(null)}
|
||||
>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>将重新发送这份日报?</AlertDialogTitle>
|
||||
<AlertDialogDescription asChild>
|
||||
<div className="grid gap-1">
|
||||
<span>
|
||||
目标:
|
||||
{retryingExecution?.sendTarget ||
|
||||
tasks.find((item) => item.id === retryingExecution?.taskId)?.target ||
|
||||
tasks.find((item) => item.id === retryingExecution?.taskId)?.group ||
|
||||
'未指定'}
|
||||
</span>
|
||||
<span>
|
||||
日报:
|
||||
{tasks.find((item) => item.id === retryingExecution?.taskId)?.name || '定时日报'}
|
||||
</span>
|
||||
<span>日期:{formatDateTime(retryingExecution?.startedAt)}</span>
|
||||
</div>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={Boolean(busyTaskId)}>取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
void confirmRetrySend()
|
||||
}}
|
||||
disabled={Boolean(busyTaskId)}
|
||||
>
|
||||
{busyTaskId ? '发送中…' : '确认重新发送'}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Contact } from '../../../../shared/types'
|
||||
import { displayContactName } from '../../../../shared/contact-name'
|
||||
export type {
|
||||
GeneratedReportRecord,
|
||||
ReportAssetStatus,
|
||||
@@ -12,4 +13,4 @@ export type {
|
||||
export type ReportWorkspaceView = 'configure' | 'result'
|
||||
|
||||
export const contactDisplayName = (contact: Contact | null): string =>
|
||||
contact?.m_nsNickName?.trim() || contact?.m_nsUsrName || '未命名群聊'
|
||||
displayContactName(contact, '未命名群聊')
|
||||
|
||||
@@ -1000,37 +1000,43 @@ export function AISearchWorkspace({
|
||||
<div className="ai-search-insufficient-icon">!</div>
|
||||
<span className="ai-search-kicker">检索反馈</span>
|
||||
<h2>{analysisError || '当前范围没有足够证据'}</h2>
|
||||
<p>可以扩大时间范围、切换群聊,或换一个更具体的问题。</p>
|
||||
<Button
|
||||
size="sm"
|
||||
className="mt-5"
|
||||
onClick={() => {
|
||||
const expandToAll = range === '30d' || range === 'all'
|
||||
setRange(expandToAll ? 'all' : '30d')
|
||||
setTimeRangeOverride(
|
||||
expandToAll
|
||||
? {
|
||||
label: '全部历史',
|
||||
reason: '用户主动扩大到全部历史',
|
||||
source: 'user_retry'
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
skipNextCache()
|
||||
void runAnalysis(undefined, {
|
||||
range: expandToAll ? 'all' : '30d',
|
||||
timeRangeOverride: expandToAll
|
||||
? {
|
||||
label: '全部历史',
|
||||
reason: '用户主动扩大到全部历史',
|
||||
source: 'user_retry'
|
||||
}
|
||||
: undefined
|
||||
})
|
||||
}}
|
||||
>
|
||||
{range === '30d' || range === 'all' ? '搜索全部历史' : '扩大到近 30 天'}
|
||||
</Button>
|
||||
{queryAgentEnabled ? (
|
||||
<p>可以切换聊天范围,或换一个更具体的问题。</p>
|
||||
) : (
|
||||
<>
|
||||
<p>可以扩大时间范围、切换群聊,或换一个更具体的问题。</p>
|
||||
<Button
|
||||
size="sm"
|
||||
className="mt-5"
|
||||
onClick={() => {
|
||||
const expandToAll = range === '30d' || range === 'all'
|
||||
setRange(expandToAll ? 'all' : '30d')
|
||||
setTimeRangeOverride(
|
||||
expandToAll
|
||||
? {
|
||||
label: '全部历史',
|
||||
reason: '用户主动扩大到全部历史',
|
||||
source: 'user_retry'
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
skipNextCache()
|
||||
void runAnalysis(undefined, {
|
||||
range: expandToAll ? 'all' : '30d',
|
||||
timeRangeOverride: expandToAll
|
||||
? {
|
||||
label: '全部历史',
|
||||
reason: '用户主动扩大到全部历史',
|
||||
source: 'user_retry'
|
||||
}
|
||||
: undefined
|
||||
})
|
||||
}}
|
||||
>
|
||||
{range === '30d' || range === 'all' ? '搜索全部历史' : '扩大到近 30 天'}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1400,7 +1406,7 @@ export function AISearchWorkspace({
|
||||
<AISearchComposer
|
||||
query={query}
|
||||
sourceLabel={sourceLabel}
|
||||
rangeLabel={RANGE_LABELS[range]}
|
||||
rangeLabel={queryAgentEnabled ? '时间由问题决定' : RANGE_LABELS[range]}
|
||||
history={history}
|
||||
historyOpen={historyOpen}
|
||||
loading={stage === 'loading'}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { KnowledgeRuntimeStatus } from '../../../../shared/knowledge'
|
||||
import { isKnowledgeFresh } from '../../../../shared/knowledge'
|
||||
import type { Contact } from '../../../../shared/types'
|
||||
import { displayContactName } from '../../../../shared/contact-name'
|
||||
import type { SearchTrace } from './searchTypes'
|
||||
|
||||
export const formatBytes = (bytes: number): string => {
|
||||
@@ -83,11 +84,7 @@ export const formatKnowledgeProcessed = (status: KnowledgeRuntimeStatus): string
|
||||
}
|
||||
|
||||
export const contactLabel = (contact: Contact | null | undefined): string =>
|
||||
contact?.m_nsNickName ||
|
||||
contact?.remark ||
|
||||
contact?.wechatNickname ||
|
||||
contact?.m_nsUsrName ||
|
||||
'未选择会话'
|
||||
displayContactName(contact, '未选择会话')
|
||||
|
||||
export const formatSearchTraceOverview = (
|
||||
trace: SearchTrace
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import type { Contact } from '../../../../../shared/types'
|
||||
import { displayContactName } from '../../../../../shared/contact-name'
|
||||
import type {
|
||||
VoiceBatchConversationSummary,
|
||||
VoiceBatchProgress,
|
||||
@@ -68,7 +69,7 @@ const hasBatchApi = (): boolean =>
|
||||
const BATCH_PAGE_SIZE = 12
|
||||
|
||||
function contactName(contact: Contact): string {
|
||||
return contact.m_nsNickName || contact.remark || contact.wechatNickname || contact.m_nsUsrName
|
||||
return displayContactName(contact)
|
||||
}
|
||||
|
||||
export function VoiceRecognitionPage({
|
||||
@@ -640,7 +641,7 @@ export function VoiceRecognitionPage({
|
||||
{' · '}剩余 {formatTaskDuration(batchProgress.estimatedRemainingMs)}
|
||||
</small>
|
||||
{currentBatchConversation && (
|
||||
<small>当前会话:{currentBatchConversation.m_nsNickName}</small>
|
||||
<small>当前会话:{contactName(currentBatchConversation)}</small>
|
||||
)}
|
||||
</div>
|
||||
<progress value={batchProgress.processed} max={Math.max(1, batchProgress.total)} />
|
||||
|
||||
Reference in New Issue
Block a user