feat: 导出页面新增群昵称 备注等选择

This commit is contained in:
xuncha
2026-01-25 10:45:38 +08:00
parent 9181ac5d34
commit 4e9c81a93d
4 changed files with 241 additions and 10 deletions

View File

@@ -396,6 +396,99 @@
}
}
.select-field {
position: relative;
}
.select-trigger {
width: 100%;
padding: 10px 16px;
border: 1px solid var(--border-color);
border-radius: 9999px;
font-size: 14px;
background: var(--bg-primary);
color: var(--text-primary);
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
cursor: pointer;
transition: all 0.2s;
&:hover {
border-color: var(--text-tertiary);
}
&.open {
border-color: var(--primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent);
}
}
.select-value {
flex: 1;
min-width: 0;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.select-dropdown {
position: absolute;
top: calc(100% + 6px);
left: 0;
right: 0;
background: color-mix(in srgb, var(--bg-primary) 85%, var(--bg-secondary));
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 6px;
box-shadow: var(--shadow-md);
z-index: 20;
max-height: 260px;
overflow-y: auto;
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
}
.select-option {
width: 100%;
text-align: left;
display: flex;
flex-direction: column;
gap: 4px;
padding: 10px 12px;
border: none;
border-radius: 10px;
background: transparent;
cursor: pointer;
transition: all 0.15s;
color: var(--text-primary);
font-size: 14px;
&:hover {
background: var(--bg-tertiary);
}
&.active {
background: color-mix(in srgb, var(--primary) 12%, transparent);
color: var(--primary);
}
}
.option-label {
font-weight: 500;
}
.option-desc {
font-size: 12px;
color: var(--text-tertiary);
}
.select-option.active .option-desc {
color: var(--primary);
}
.media-options {
display: flex;
flex-wrap: wrap;
@@ -1130,11 +1223,11 @@
}
}
input:checked + .slider {
input:checked+.slider {
background-color: var(--primary);
}
input:checked + .slider::before {
input:checked+.slider::before {
transform: translateX(20px);
}
}

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react'
import { useState, useEffect, useCallback, useRef } from 'react'
import { Search, Download, FolderOpen, RefreshCw, Check, Calendar, FileJson, FileText, Table, Loader2, X, ChevronDown, ChevronLeft, ChevronRight, FileSpreadsheet, Database, FileCode, CheckCircle, XCircle, ExternalLink } from 'lucide-react'
import * as configService from '../services/config'
import './ExportPage.scss'
@@ -23,6 +23,7 @@ interface ExportOptions {
exportVoiceAsText: boolean
excelCompactColumns: boolean
txtColumns: string[]
displayNamePreference: 'group-nickname' | 'remark' | 'nickname'
}
interface ExportResult {
@@ -49,6 +50,8 @@ function ExportPage() {
const [calendarDate, setCalendarDate] = useState(new Date())
const [selectingStart, setSelectingStart] = useState(true)
const [showMediaLayoutPrompt, setShowMediaLayoutPrompt] = useState(false)
const [showDisplayNameSelect, setShowDisplayNameSelect] = useState(false)
const displayNameDropdownRef = useRef<HTMLDivElement>(null)
const [options, setOptions] = useState<ExportOptions>({
format: 'excel',
@@ -64,7 +67,8 @@ function ExportPage() {
exportEmojis: true,
exportVoiceAsText: true,
excelCompactColumns: true,
txtColumns: defaultTxtColumns
txtColumns: defaultTxtColumns,
displayNamePreference: 'remark'
})
const buildDateRangeFromPreset = (preset: string) => {
@@ -189,6 +193,16 @@ function ExportPage() {
removeListener?.()
}
}, [])
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as Node
if (showDisplayNameSelect && displayNameDropdownRef.current && !displayNameDropdownRef.current.contains(target)) {
setShowDisplayNameSelect(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [showDisplayNameSelect])
useEffect(() => {
if (!searchKeyword.trim()) {
@@ -271,6 +285,7 @@ function ExportPage() {
exportVoiceAsText: options.exportVoiceAsText, // 即使不导出媒体,也可以导出语音转文字内容
excelCompactColumns: options.excelCompactColumns,
txtColumns: options.txtColumns,
displayNamePreference: options.displayNamePreference,
sessionLayout,
dateRange: options.useAllTime ? null : options.dateRange ? {
start: Math.floor(options.dateRange.start.getTime() / 1000),
@@ -402,6 +417,25 @@ function ExportPage() {
{ value: 'excel', label: 'Excel', icon: FileSpreadsheet, desc: '电子表格,适合统计分析' },
{ value: 'sql', label: 'PostgreSQL', icon: Database, desc: '数据库脚本,便于导入到数据库' }
]
const displayNameOptions = [
{
value: 'group-nickname',
label: '群昵称优先',
desc: '仅群聊有效,私聊显示备注/昵称'
},
{
value: 'remark',
label: '备注优先',
desc: '有备注显示备注,否则显示昵称'
},
{
value: 'nickname',
label: '微信昵称',
desc: '始终显示微信昵称'
}
]
const displayNameOption = displayNameOptions.find(option => option.value === options.displayNamePreference)
const displayNameLabel = displayNameOption?.label || '备注优先'
return (
<div className="export-page">
@@ -516,6 +550,44 @@ function ExportPage() {
</div>
</div>
{/* 发送者名称显示偏好 */}
{(options.format === 'html' || options.format === 'json' || options.format === 'txt') && (
<div className="setting-section">
<h3></h3>
<p className="setting-subtitle"></p>
<div className="select-field" ref={displayNameDropdownRef}>
<button
type="button"
className={`select-trigger ${showDisplayNameSelect ? 'open' : ''}`}
onClick={() => setShowDisplayNameSelect(!showDisplayNameSelect)}
>
<span className="select-value">{displayNameLabel}</span>
<ChevronDown size={16} />
</button>
{showDisplayNameSelect && (
<div className="select-dropdown">
{displayNameOptions.map(option => (
<button
key={option.value}
type="button"
className={`select-option ${options.displayNamePreference === option.value ? 'active' : ''}`}
onClick={() => {
setOptions({
...options,
displayNamePreference: option.value as ExportOptions['displayNamePreference']
})
setShowDisplayNameSelect(false)
}}
>
<span className="option-label">{option.label}</span>
<span className="option-desc">{option.desc}</span>
</button>
))}
</div>
)}
</div>
</div>
)}
<div className="setting-section">
<h3></h3>
<p className="setting-subtitle">//</p>

View File

@@ -354,6 +354,7 @@ export interface ExportOptions {
excelCompactColumns?: boolean
txtColumns?: string[]
sessionLayout?: 'shared' | 'per-session'
displayNamePreference?: 'group-nickname' | 'remark' | 'nickname'
}
export interface ExportProgress {