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 {
format?: string
avatars?: boolean
dateRange?: ExportDateRangeSelection
media?: boolean
voiceAsText?: boolean
@@ -59,6 +60,7 @@ export function ExportDefaultsSettingsForm({
const exportExcelColumnsDropdownRef = useRef<HTMLDivElement>(null)
const [exportDefaultFormat, setExportDefaultFormat] = useState('excel')
const [exportDefaultAvatars, setExportDefaultAvatars] = useState(true)
const [exportDefaultDateRange, setExportDefaultDateRange] = useState<ExportDateRangeSelection>(() => createDefaultExportDateRangeSelection())
const [exportDefaultMedia, setExportDefaultMedia] = useState(false)
const [exportDefaultVoiceAsText, setExportDefaultVoiceAsText] = useState(false)
@@ -68,8 +70,9 @@ export function ExportDefaultsSettingsForm({
useEffect(() => {
let cancelled = false
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.getExportDefaultAvatars(),
configService.getExportDefaultDateRange(),
configService.getExportDefaultMedia(),
configService.getExportDefaultVoiceAsText(),
@@ -80,6 +83,7 @@ export function ExportDefaultsSettingsForm({
if (cancelled) return
setExportDefaultFormat(savedFormat || 'excel')
setExportDefaultAvatars(savedAvatars ?? true)
setExportDefaultDateRange(resolveExportDateRangeConfig(savedDateRange))
setExportDefaultMedia(savedMedia ?? false)
setExportDefaultVoiceAsText(savedVoiceAsText ?? false)
@@ -168,6 +172,34 @@ export function ExportDefaultsSettingsForm({
</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-copy">
<label></label>

View File

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

View File

@@ -27,6 +27,7 @@ export const CONFIG_KEYS = {
AUTO_TRANSCRIBE_VOICE: 'autoTranscribeVoice',
TRANSCRIBE_LANGUAGES: 'transcribeLanguages',
EXPORT_DEFAULT_FORMAT: 'exportDefaultFormat',
EXPORT_DEFAULT_AVATARS: 'exportDefaultAvatars',
EXPORT_DEFAULT_DATE_RANGE: 'exportDefaultDateRange',
EXPORT_DEFAULT_MEDIA: 'exportDefaultMedia',
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)
}
// 获取导出默认头像设置
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> {
const value = await config.get(CONFIG_KEYS.EXPORT_DEFAULT_DATE_RANGE)