feat(export): centralize avatar export default

This commit is contained in:
aits2026
2026-03-06 14:11:02 +08:00
parent d2ec9c680d
commit 450e5f7e61
3 changed files with 68 additions and 7 deletions

View File

@@ -13,6 +13,7 @@ import './ExportDefaultsSettingsForm.scss'
export interface ExportDefaultsSettingsPatch { export interface ExportDefaultsSettingsPatch {
format?: string format?: string
avatars?: boolean
dateRange?: ExportDateRangeSelection dateRange?: ExportDateRangeSelection
media?: boolean media?: boolean
voiceAsText?: boolean voiceAsText?: boolean
@@ -59,6 +60,7 @@ export function ExportDefaultsSettingsForm({
const exportExcelColumnsDropdownRef = useRef<HTMLDivElement>(null) const exportExcelColumnsDropdownRef = useRef<HTMLDivElement>(null)
const [exportDefaultFormat, setExportDefaultFormat] = useState('excel') const [exportDefaultFormat, setExportDefaultFormat] = useState('excel')
const [exportDefaultAvatars, setExportDefaultAvatars] = useState(true)
const [exportDefaultDateRange, setExportDefaultDateRange] = useState<ExportDateRangeSelection>(() => createDefaultExportDateRangeSelection()) const [exportDefaultDateRange, setExportDefaultDateRange] = useState<ExportDateRangeSelection>(() => createDefaultExportDateRangeSelection())
const [exportDefaultMedia, setExportDefaultMedia] = useState(false) const [exportDefaultMedia, setExportDefaultMedia] = useState(false)
const [exportDefaultVoiceAsText, setExportDefaultVoiceAsText] = useState(false) const [exportDefaultVoiceAsText, setExportDefaultVoiceAsText] = useState(false)
@@ -68,8 +70,9 @@ export function ExportDefaultsSettingsForm({
useEffect(() => { useEffect(() => {
let cancelled = false let cancelled = false
void (async () => { void (async () => {
const [savedFormat, savedDateRange, savedMedia, savedVoiceAsText, savedExcelCompactColumns, savedConcurrency] = await Promise.all([ const [savedFormat, savedAvatars, savedDateRange, savedMedia, savedVoiceAsText, savedExcelCompactColumns, savedConcurrency] = await Promise.all([
configService.getExportDefaultFormat(), configService.getExportDefaultFormat(),
configService.getExportDefaultAvatars(),
configService.getExportDefaultDateRange(), configService.getExportDefaultDateRange(),
configService.getExportDefaultMedia(), configService.getExportDefaultMedia(),
configService.getExportDefaultVoiceAsText(), configService.getExportDefaultVoiceAsText(),
@@ -80,6 +83,7 @@ export function ExportDefaultsSettingsForm({
if (cancelled) return if (cancelled) return
setExportDefaultFormat(savedFormat || 'excel') setExportDefaultFormat(savedFormat || 'excel')
setExportDefaultAvatars(savedAvatars ?? true)
setExportDefaultDateRange(resolveExportDateRangeConfig(savedDateRange)) setExportDefaultDateRange(resolveExportDateRangeConfig(savedDateRange))
setExportDefaultMedia(savedMedia ?? false) setExportDefaultMedia(savedMedia ?? false)
setExportDefaultVoiceAsText(savedVoiceAsText ?? false) setExportDefaultVoiceAsText(savedVoiceAsText ?? false)
@@ -168,6 +172,34 @@ export function ExportDefaultsSettingsForm({
</div> </div>
</div> </div>
<div className="form-group">
<div className="form-copy">
<label></label>
<span className="form-hint"></span>
</div>
<div className="form-control">
<div className="log-toggle-line">
<span className="log-status">{exportDefaultAvatars ? '已开启' : '已关闭'}</span>
<label className="switch" htmlFor="shared-export-default-avatars">
<input
id="shared-export-default-avatars"
className="switch-input"
type="checkbox"
checked={exportDefaultAvatars}
onChange={async (e) => {
const enabled = e.target.checked
setExportDefaultAvatars(enabled)
await configService.setExportDefaultAvatars(enabled)
onDefaultsChanged?.({ avatars: enabled })
notify(enabled ? '已开启聊天消息导出带头像' : '已关闭聊天消息导出带头像', true)
}}
/>
<span className="switch-slider" />
</label>
</div>
</div>
</div>
<div className="form-group"> <div className="form-group">
<div className="form-copy"> <div className="form-copy">
<label></label> <label></label>

View File

@@ -1286,6 +1286,7 @@ function ExportPage() {
const [isExportDefaultsModalOpen, setIsExportDefaultsModalOpen] = useState(false) const [isExportDefaultsModalOpen, setIsExportDefaultsModalOpen] = useState(false)
const [timeRangeSelection, setTimeRangeSelection] = useState<ExportDateRangeSelection>(() => createDefaultExportDateRangeSelection()) const [timeRangeSelection, setTimeRangeSelection] = useState<ExportDateRangeSelection>(() => createDefaultExportDateRangeSelection())
const [exportDefaultFormat, setExportDefaultFormat] = useState<TextExportFormat>('excel') const [exportDefaultFormat, setExportDefaultFormat] = useState<TextExportFormat>('excel')
const [exportDefaultAvatars, setExportDefaultAvatars] = useState(true)
const [exportDefaultDateRangeSelection, setExportDefaultDateRangeSelection] = useState<ExportDateRangeSelection>(() => createDefaultExportDateRangeSelection()) const [exportDefaultDateRangeSelection, setExportDefaultDateRangeSelection] = useState<ExportDateRangeSelection>(() => createDefaultExportDateRangeSelection())
const [exportDefaultMedia, setExportDefaultMedia] = useState(false) const [exportDefaultMedia, setExportDefaultMedia] = useState(false)
const [exportDefaultVoiceAsText, setExportDefaultVoiceAsText] = useState(false) const [exportDefaultVoiceAsText, setExportDefaultVoiceAsText] = useState(false)
@@ -1792,9 +1793,10 @@ function ExportPage() {
setIsBaseConfigLoading(true) setIsBaseConfigLoading(true)
let isReady = true let isReady = true
try { try {
const [savedPath, savedFormat, savedMedia, savedVoiceAsText, savedExcelCompactColumns, savedTxtColumns, savedConcurrency, savedSessionMap, savedContentMap, savedSessionRecordMap, savedSnsPostCount, savedWriteLayout, savedSessionNameWithTypePrefix, savedDefaultDateRange, exportCacheScope] = await Promise.all([ const [savedPath, savedFormat, savedAvatars, savedMedia, savedVoiceAsText, savedExcelCompactColumns, savedTxtColumns, savedConcurrency, savedSessionMap, savedContentMap, savedSessionRecordMap, savedSnsPostCount, savedWriteLayout, savedSessionNameWithTypePrefix, savedDefaultDateRange, exportCacheScope] = await Promise.all([
configService.getExportPath(), configService.getExportPath(),
configService.getExportDefaultFormat(), configService.getExportDefaultFormat(),
configService.getExportDefaultAvatars(),
configService.getExportDefaultMedia(), configService.getExportDefaultMedia(),
configService.getExportDefaultVoiceAsText(), configService.getExportDefaultVoiceAsText(),
configService.getExportDefaultExcelCompactColumns(), configService.getExportDefaultExcelCompactColumns(),
@@ -1826,6 +1828,7 @@ function ExportPage() {
setExportRecordsBySession(savedSessionRecordMap) setExportRecordsBySession(savedSessionRecordMap)
setLastSnsExportPostCount(savedSnsPostCount) setLastSnsExportPostCount(savedSnsPostCount)
setExportDefaultFormat((savedFormat as TextExportFormat) || 'excel') setExportDefaultFormat((savedFormat as TextExportFormat) || 'excel')
setExportDefaultAvatars(savedAvatars ?? true)
setExportDefaultMedia(savedMedia ?? false) setExportDefaultMedia(savedMedia ?? false)
setExportDefaultVoiceAsText(savedVoiceAsText ?? false) setExportDefaultVoiceAsText(savedVoiceAsText ?? false)
setExportDefaultExcelCompactColumns(savedExcelCompactColumns ?? true) setExportDefaultExcelCompactColumns(savedExcelCompactColumns ?? true)
@@ -1848,6 +1851,7 @@ function ExportPage() {
setOptions(prev => ({ setOptions(prev => ({
...prev, ...prev,
format: ((savedFormat as TextExportFormat) || 'excel'), format: ((savedFormat as TextExportFormat) || 'excel'),
exportAvatars: savedAvatars ?? true,
exportMedia: savedMedia ?? prev.exportMedia, exportMedia: savedMedia ?? prev.exportMedia,
exportVoiceAsText: savedVoiceAsText ?? prev.exportVoiceAsText, exportVoiceAsText: savedVoiceAsText ?? prev.exportVoiceAsText,
excelCompactColumns: savedExcelCompactColumns ?? prev.excelCompactColumns, excelCompactColumns: savedExcelCompactColumns ?? prev.excelCompactColumns,
@@ -3205,6 +3209,7 @@ function ExportPage() {
const next: ExportOptions = { const next: ExportOptions = {
...prev, ...prev,
format: exportDefaultFormat, format: exportDefaultFormat,
exportAvatars: exportDefaultAvatars,
useAllTime: exportDefaultDateRangeSelection.useAllTime, useAllTime: exportDefaultDateRangeSelection.useAllTime,
dateRange: nextDateRange, dateRange: nextDateRange,
exportMedia: exportDefaultMedia, exportMedia: exportDefaultMedia,
@@ -3224,7 +3229,6 @@ function ExportPage() {
next.exportVoices = false next.exportVoices = false
next.exportVideos = false next.exportVideos = false
next.exportEmojis = false next.exportEmojis = false
next.exportAvatars = true
} else { } else {
next.exportMedia = true next.exportMedia = true
next.exportImages = payload.contentType === 'image' next.exportImages = payload.contentType === 'image'
@@ -3241,6 +3245,7 @@ function ExportPage() {
exportDefaultDateRangeSelection, exportDefaultDateRangeSelection,
exportDefaultExcelCompactColumns, exportDefaultExcelCompactColumns,
exportDefaultFormat, exportDefaultFormat,
exportDefaultAvatars,
exportDefaultMedia, exportDefaultMedia,
exportDefaultVoiceAsText, exportDefaultVoiceAsText,
exportDefaultConcurrency exportDefaultConcurrency
@@ -3351,7 +3356,7 @@ function ExportPage() {
format: fastTextFormat, format: fastTextFormat,
contentType, contentType,
exportConcurrency: textExportConcurrency, exportConcurrency: textExportConcurrency,
exportAvatars: true, exportAvatars: base.exportAvatars,
exportMedia: false, exportMedia: false,
exportImages: false, exportImages: false,
exportVoices: false, exportVoices: false,
@@ -3764,6 +3769,7 @@ function ExportPage() {
closeExportDialog() closeExportDialog()
await configService.setExportDefaultFormat(options.format) await configService.setExportDefaultFormat(options.format)
await configService.setExportDefaultAvatars(options.exportAvatars)
await configService.setExportDefaultMedia(Boolean(options.exportImages || options.exportVoices || options.exportVideos || options.exportEmojis)) await configService.setExportDefaultMedia(Boolean(options.exportImages || options.exportVoices || options.exportVideos || options.exportEmojis))
await configService.setExportDefaultVoiceAsText(options.exportVoiceAsText) await configService.setExportDefaultVoiceAsText(options.exportVoiceAsText)
await configService.setExportDefaultExcelCompactColumns(options.excelCompactColumns) await configService.setExportDefaultExcelCompactColumns(options.excelCompactColumns)
@@ -4892,6 +4898,10 @@ function ExportPage() {
const isContentTextDialog = isContentScopeDialog && exportDialog.contentType === 'text' const isContentTextDialog = isContentScopeDialog && exportDialog.contentType === 'text'
const shouldShowFormatSection = !isContentScopeDialog || isContentTextDialog const shouldShowFormatSection = !isContentScopeDialog || isContentTextDialog
const shouldShowMediaSection = !isContentScopeDialog const shouldShowMediaSection = !isContentScopeDialog
const avatarExportStatusLabel = options.exportAvatars ? '已开启聊天消息导出带头像' : '已关闭聊天消息导出带头像'
const textContentFormatNote = options.exportAvatars
? '此模式包含用户头像,不导出图片语音视频表情包等多媒体内容'
: '此模式不包含用户头像,不导出图片语音视频表情包等多媒体内容'
const shouldShowDisplayNameSection = !( const shouldShowDisplayNameSection = !(
exportDialog.scope === 'sns' || exportDialog.scope === 'sns' ||
( (
@@ -5175,6 +5185,10 @@ function ExportPage() {
if (patch.format) { if (patch.format) {
setExportDefaultFormat(patch.format as TextExportFormat) setExportDefaultFormat(patch.format as TextExportFormat)
} }
if (typeof patch.avatars === 'boolean') {
setExportDefaultAvatars(patch.avatars)
setOptions(prev => ({ ...prev, exportAvatars: patch.avatars! }))
}
if (patch.dateRange) { if (patch.dateRange) {
setExportDefaultDateRangeSelection(patch.dateRange) setExportDefaultDateRangeSelection(patch.dateRange)
} }
@@ -6044,8 +6058,11 @@ function ExportPage() {
{shouldShowFormatSection && ( {shouldShowFormatSection && (
<div className="dialog-section"> <div className="dialog-section">
<h4>{exportDialog.scope === 'sns' ? '朋友圈导出格式选择' : '对话文本导出格式选择'}</h4> <h4>{exportDialog.scope === 'sns' ? '朋友圈导出格式选择' : '对话文本导出格式选择'}</h4>
{!isContentScopeDialog && exportDialog.scope !== 'sns' && (
<div className="format-note">{avatarExportStatusLabel}</div>
)}
{isContentTextDialog && ( {isContentTextDialog && (
<div className="format-note"></div> <div className="format-note">{textContentFormatNote}</div>
)} )}
<div className="format-grid"> <div className="format-grid">
{formatCandidateOptions.map(option => ( {formatCandidateOptions.map(option => (
@@ -6086,7 +6103,7 @@ function ExportPage() {
{shouldShowMediaSection && ( {shouldShowMediaSection && (
<div className="dialog-section"> <div className="dialog-section">
<h4>{exportDialog.scope === 'sns' ? '媒体文件(可多选)' : '媒体与头像'}</h4> <h4>{exportDialog.scope === 'sns' ? '媒体文件(可多选)' : '媒体内容'}</h4>
<div className="media-check-grid"> <div className="media-check-grid">
{exportDialog.scope === 'sns' ? ( {exportDialog.scope === 'sns' ? (
<> <>
@@ -6101,7 +6118,6 @@ function ExportPage() {
<label><input type="checkbox" checked={options.exportVideos} onChange={event => setOptions(prev => ({ ...prev, exportVideos: event.target.checked }))} /> </label> <label><input type="checkbox" checked={options.exportVideos} onChange={event => setOptions(prev => ({ ...prev, exportVideos: event.target.checked }))} /> </label>
<label><input type="checkbox" checked={options.exportEmojis} onChange={event => setOptions(prev => ({ ...prev, exportEmojis: event.target.checked }))} /> </label> <label><input type="checkbox" checked={options.exportEmojis} onChange={event => setOptions(prev => ({ ...prev, exportEmojis: event.target.checked }))} /> </label>
<label><input type="checkbox" checked={options.exportVoiceAsText} onChange={event => setOptions(prev => ({ ...prev, exportVoiceAsText: event.target.checked }))} /> </label> <label><input type="checkbox" checked={options.exportVoiceAsText} onChange={event => setOptions(prev => ({ ...prev, exportVoiceAsText: event.target.checked }))} /> </label>
<label><input type="checkbox" checked={options.exportAvatars} onChange={event => setOptions(prev => ({ ...prev, exportAvatars: event.target.checked }))} /> </label>
</> </>
)} )}
</div> </div>

View File

@@ -27,6 +27,7 @@ export const CONFIG_KEYS = {
AUTO_TRANSCRIBE_VOICE: 'autoTranscribeVoice', AUTO_TRANSCRIBE_VOICE: 'autoTranscribeVoice',
TRANSCRIBE_LANGUAGES: 'transcribeLanguages', TRANSCRIBE_LANGUAGES: 'transcribeLanguages',
EXPORT_DEFAULT_FORMAT: 'exportDefaultFormat', EXPORT_DEFAULT_FORMAT: 'exportDefaultFormat',
EXPORT_DEFAULT_AVATARS: 'exportDefaultAvatars',
EXPORT_DEFAULT_DATE_RANGE: 'exportDefaultDateRange', EXPORT_DEFAULT_DATE_RANGE: 'exportDefaultDateRange',
EXPORT_DEFAULT_MEDIA: 'exportDefaultMedia', EXPORT_DEFAULT_MEDIA: 'exportDefaultMedia',
EXPORT_DEFAULT_VOICE_AS_TEXT: 'exportDefaultVoiceAsText', EXPORT_DEFAULT_VOICE_AS_TEXT: 'exportDefaultVoiceAsText',
@@ -335,6 +336,18 @@ export async function setExportDefaultFormat(format: string): Promise<void> {
await config.set(CONFIG_KEYS.EXPORT_DEFAULT_FORMAT, format) await config.set(CONFIG_KEYS.EXPORT_DEFAULT_FORMAT, format)
} }
// 获取导出默认头像设置
export async function getExportDefaultAvatars(): Promise<boolean | null> {
const value = await config.get(CONFIG_KEYS.EXPORT_DEFAULT_AVATARS)
if (typeof value === 'boolean') return value
return null
}
// 设置导出默认头像设置
export async function setExportDefaultAvatars(enabled: boolean): Promise<void> {
await config.set(CONFIG_KEYS.EXPORT_DEFAULT_AVATARS, enabled)
}
// 获取导出默认时间范围 // 获取导出默认时间范围
export async function getExportDefaultDateRange(): Promise<ExportDefaultDateRangeConfig | string | null> { export async function getExportDefaultDateRange(): Promise<ExportDefaultDateRangeConfig | string | null> {
const value = await config.get(CONFIG_KEYS.EXPORT_DEFAULT_DATE_RANGE) const value = await config.get(CONFIG_KEYS.EXPORT_DEFAULT_DATE_RANGE)