@@ -73,20 +73,35 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
7373 return Math . round ( clamp01 ( done / total ) * 100 )
7474 } )
7575
76- const exportFilteredContacts = computed ( ( ) => {
77- const query = String ( exportSearchQuery . value || '' ) . trim ( ) . toLowerCase ( )
76+ const normalizeExportSelectedUsernames = ( list ) => {
77+ const seen = new Set ( )
78+ return ( Array . isArray ( list ) ? list : [ ] ) . reduce ( ( acc , item ) => {
79+ const username = String ( item || '' ) . trim ( )
80+ if ( ! username || seen . has ( username ) ) return acc
81+ seen . add ( username )
82+ acc . push ( username )
83+ return acc
84+ } , [ ] )
85+ }
86+
87+ const getExportFilteredContacts = ( { tab = exportListTab . value , query = exportSearchQuery . value } = { } ) => {
88+ const normalizedQuery = String ( query || '' ) . trim ( ) . toLowerCase ( )
7889 let list = Array . isArray ( contacts . value ) ? contacts . value : [ ]
7990
80- const tab = String ( exportListTab . value || 'all' )
81- if ( tab === 'groups' ) list = list . filter ( ( contact ) => ! ! contact ?. isGroup )
82- if ( tab === 'singles' ) list = list . filter ( ( contact ) => ! contact ?. isGroup )
91+ const normalizedTab = String ( tab || 'all' )
92+ if ( normalizedTab === 'groups' ) list = list . filter ( ( contact ) => ! ! contact ?. isGroup )
93+ if ( normalizedTab === 'singles' ) list = list . filter ( ( contact ) => ! contact ?. isGroup )
8394
84- if ( ! query ) return list
95+ if ( ! normalizedQuery ) return list
8596 return list . filter ( ( contact ) => {
8697 const name = String ( contact ?. name || '' ) . toLowerCase ( )
8798 const username = String ( contact ?. username || '' ) . toLowerCase ( )
88- return name . includes ( query ) || username . includes ( query )
99+ return name . includes ( normalizedQuery ) || username . includes ( normalizedQuery )
89100 } )
101+ }
102+
103+ const exportFilteredContacts = computed ( ( ) => {
104+ return getExportFilteredContacts ( )
90105 } )
91106
92107 const exportContactCounts = computed ( ( ) => {
@@ -96,6 +111,60 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
96111 return { total, groups, singles : total - groups }
97112 } )
98113
114+ const exportSelectedUsernameSet = computed ( ( ) => {
115+ return new Set ( normalizeExportSelectedUsernames ( exportSelectedUsernames . value ) )
116+ } )
117+
118+ const setExportSelectedUsernames = ( list ) => {
119+ exportSelectedUsernames . value = normalizeExportSelectedUsernames ( list )
120+ }
121+
122+ const getExportFilteredUsernames = ( tab = exportListTab . value ) => {
123+ return getExportFilteredContacts ( { tab } )
124+ . map ( ( contact ) => String ( contact ?. username || '' ) . trim ( ) )
125+ . filter ( Boolean )
126+ }
127+
128+ const selectExportFilteredContacts = ( tab = exportListTab . value ) => {
129+ setExportSelectedUsernames ( getExportFilteredUsernames ( tab ) )
130+ }
131+
132+ const clearExportFilteredContacts = ( ) => {
133+ setExportSelectedUsernames ( [ ] )
134+ }
135+
136+ const areExportFilteredContactsAllSelected = ( tab = exportListTab . value ) => {
137+ const usernames = getExportFilteredUsernames ( tab )
138+ if ( usernames . length !== exportSelectedUsernameSet . value . size ) return false
139+ return usernames . every ( ( username ) => exportSelectedUsernameSet . value . has ( username ) )
140+ }
141+
142+ const onExportListTabClick = ( tab ) => {
143+ const nextTab = String ( tab || 'all' )
144+ const isSameTab = String ( exportListTab . value || 'all' ) === nextTab
145+ exportListTab . value = nextTab
146+
147+ if ( isSameTab ) {
148+ if ( areExportFilteredContactsAllSelected ( nextTab ) ) {
149+ clearExportFilteredContacts ( nextTab )
150+ } else {
151+ selectExportFilteredContacts ( nextTab )
152+ }
153+ return
154+ }
155+
156+ selectExportFilteredContacts ( nextTab )
157+ }
158+
159+ const isExportContactSelected = ( username ) => {
160+ return exportSelectedUsernameSet . value . has ( String ( username || '' ) . trim ( ) )
161+ }
162+
163+ const onExportBatchScopeClick = ( tab ) => {
164+ exportScope . value = 'selected'
165+ onExportListTabClick ( tab )
166+ }
167+
99168 const isDesktopExportRuntime = ( ) => {
100169 return ! ! ( process . client && window ?. wechatDesktop ?. chooseDirectory )
101170 }
@@ -269,12 +338,17 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
269338 exportModalOpen . value = true
270339 exportError . value = ''
271340 exportSaveMsg . value = ''
341+ exportSearchQuery . value = ''
272342 exportListTab . value = 'all'
343+ exportSelectedUsernames . value = [ ]
273344 exportStartLocal . value = ''
274345 exportEndLocal . value = ''
275346 exportMessageTypes . value = exportMessageTypeOptions . map ( ( item ) => item . value )
276347 exportAutoSavedFor . value = ''
277- exportScope . value = selectedContact . value ?. username ? 'current' : 'all'
348+ exportScope . value = selectedContact . value ?. username ? 'current' : 'selected'
349+ if ( ! selectedContact . value ?. username ) {
350+ selectExportFilteredContacts ( 'all' )
351+ }
278352 }
279353
280354 const closeExportModal = ( ) => {
@@ -296,6 +370,12 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
296370 }
297371 } )
298372
373+ watch ( exportScope , ( scope , previousScope ) => {
374+ if ( scope !== 'selected' || previousScope === 'selected' ) return
375+ if ( exportSelectedUsernames . value . length > 0 ) return
376+ selectExportFilteredContacts ( exportListTab . value )
377+ } )
378+
299379 watch (
300380 ( ) => ( {
301381 exportId : String ( exportJob . value ?. exportId || '' ) ,
@@ -447,6 +527,9 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
447527 exportCurrentPercent,
448528 exportFilteredContacts,
449529 exportContactCounts,
530+ onExportBatchScopeClick,
531+ onExportListTabClick,
532+ isExportContactSelected,
450533 hasWebExportFolder,
451534 chooseExportFolder,
452535 getExportDownloadUrl,
0 commit comments