11import { defineComponent , PropType , ref , computed , watch } from 'vue' ;
2- import { ConfigDto } from "@/client/apiGen" ;
2+ import { ConfigDto , Section } from "@/client/apiGen" ;
33import { TextInput , theme , WhateverNaviBar } from '@munet/ui' ;
44import _ from "lodash" ;
55import configSortStub from './configSort.yaml'
6- import { useMagicKeys , whenever } from "@vueuse/core" ;
6+ import { useMagicKeys , useStorage , whenever } from "@vueuse/core" ;
77import { getBigSectionName } from './utils' ;
88import { useI18n } from 'vue-i18n' ;
99import ConfigSection from './ConfigSection' ;
1010
11+ const FAVORITES_TAB_KEY = '__favorites__' ;
12+
1113export default defineComponent ( {
1214 props : {
1315 config : { type : Object as PropType < ConfigDto > , required : true } ,
@@ -21,6 +23,14 @@ export default defineComponent({
2123 const configSort = computed ( ( ) => props . config ?. configSort || configSortStub )
2224 const communityList = computed ( ( ) => configSort . value [ '社区功能' ] || [ ] ) ;
2325 const { t } = useI18n ( ) ;
26+ const favoriteSectionPaths = useStorage < string [ ] > ( 'aquamai-config-favorite-sections' , [ ] ) ;
27+
28+ const configSections = computed ( ( ) => props . config ?. sections || [ ] ) ;
29+ const favoritePathList = computed ( ( ) => Array . isArray ( favoriteSectionPaths . value ) ? favoriteSectionPaths . value : [ ] ) ;
30+ const favoritePathSet = computed ( ( ) => new Set ( favoritePathList . value ) ) ;
31+ const sectionByPath = computed ( ( ) => new Map ( configSections . value
32+ . filter ( ( section ) : section is Section & { path : string } => ! ! section . path )
33+ . map ( section => [ section . path , section ] ) ) ) ;
2434
2535 const { ctrl_f } = useMagicKeys ( {
2636 passive : false ,
@@ -31,20 +41,41 @@ export default defineComponent({
3141 } )
3242 whenever ( ctrl_f , ( ) => searchRef . value ?. select ( ) ) ;
3343
44+ const sectionMatchesSearch = ( section : Section , keyword : string ) =>
45+ section . path ?. toLowerCase ( ) . includes ( keyword ) ||
46+ section . attribute ?. comment ?. nameZh ?. toLowerCase ( ) . includes ( keyword ) ||
47+ section . attribute ?. comment ?. commentZh ?. toLowerCase ( ) . includes ( keyword ) ||
48+ section . attribute ?. comment ?. commentEn ?. toLowerCase ( ) . includes ( keyword ) ||
49+ section . entries ?. some ( entry => entry . name ?. toLowerCase ( ) . includes ( keyword ) || entry . path ?. toLowerCase ( ) . includes ( keyword ) ||
50+ entry . attribute ?. comment ?. commentZh ?. toLowerCase ( ) . includes ( keyword ) || entry . attribute ?. comment ?. commentEn ?. toLowerCase ( ) . includes ( keyword ) ||
51+ entry . attribute ?. comment ?. nameZh ?. toLowerCase ( ) . includes ( keyword ) ) ;
52+
3453 const filteredSections = computed ( ( ) => {
35- if ( ! search . value ) return props . config ?. sections ;
54+ if ( ! search . value ) return configSections . value ;
3655 const s = search . value . toLowerCase ( ) ;
37- return props . config . sections ?. filter ( it =>
38- it . path ?. toLowerCase ( ) . includes ( s ) ||
39- it . attribute ?. comment ?. nameZh ?. toLowerCase ( ) . includes ( s ) ||
40- it . attribute ?. comment ?. commentZh ?. toLowerCase ( ) . includes ( s ) ||
41- it . attribute ?. comment ?. commentEn ?. toLowerCase ( ) . includes ( s ) ||
42- it . entries ?. some ( entry => entry . name ?. toLowerCase ( ) . includes ( s ) || entry . path ?. toLowerCase ( ) . includes ( s ) ||
43- entry . attribute ?. comment ?. commentZh ?. toLowerCase ( ) . includes ( s ) || entry . attribute ?. comment ?. commentEn ?. toLowerCase ( ) . includes ( s ) ||
44- entry . attribute ?. comment ?. nameZh ?. toLowerCase ( ) . includes ( s ) )
45- ) ;
56+ return configSections . value . filter ( it => sectionMatchesSearch ( it , s ) ) ;
4657 } )
4758
59+ const favoriteSections = computed ( ( ) => {
60+ const seen = new Set < string > ( ) ;
61+ return favoritePathList . value
62+ . filter ( path => {
63+ if ( typeof path !== 'string' || seen . has ( path ) ) return false ;
64+ seen . add ( path ) ;
65+ return true ;
66+ } )
67+ . map ( path => sectionByPath . value . get ( path ) )
68+ . filter ( ( section ) => ! ! section && ! section . attribute ?. exampleHidden ) ;
69+ } ) ;
70+
71+ const toggleFavoriteSection = ( path : string ) => {
72+ if ( favoritePathSet . value . has ( path ) ) {
73+ favoriteSectionPaths . value = favoritePathList . value . filter ( item => item !== path ) ;
74+ return ;
75+ }
76+ favoriteSectionPaths . value = [ ...favoritePathList . value . filter ( item => typeof item === 'string' ) , path ] ;
77+ } ;
78+
4879 const bigSections = computed ( ( ) => {
4980 if ( props . useNewSort ) {
5081 return Object . keys ( configSort . value ) . filter ( it => it !== '社区功能' ) . filter ( it => filteredSections . value ?. some ( s => configSort . value [ it ] . includes ( s . path ! ) ) ?? false ) ;
@@ -61,6 +92,9 @@ export default defineComponent({
6192 // 所有可选 tab,包括 "其他"
6293 const allTabs = computed ( ( ) => {
6394 const tabs = bigSections . value . map ( key => ( { key : key ! , label : getBigSectionName ( key ! ) } ) ) ;
95+ if ( favoriteSections . value . length > 0 ) {
96+ tabs . unshift ( { key : FAVORITES_TAB_KEY , label : t ( 'mod.favorite' ) } ) ;
97+ }
6498 if ( otherSection . value . length > 0 ) {
6599 tabs . push ( { key : '__other__' , label : t ( 'mod.other' ) } ) ;
66100 }
@@ -85,6 +119,7 @@ export default defineComponent({
85119 return filteredSections . value ?. filter ( it => ! it . attribute ?. exampleHidden ) || [ ] ;
86120 }
87121 if ( ! activeTab . value ) return [ ] ;
122+ if ( activeTab . value === FAVORITES_TAB_KEY ) return favoriteSections . value ;
88123 if ( activeTab . value === '__other__' ) return otherSection . value ;
89124 return filteredSections . value ?. filter ( it => {
90125 if ( props . useNewSort ) {
@@ -126,12 +161,20 @@ export default defineComponent({
126161 </ div >
127162 < div ref = { scrollContainerRef } class = "of-y-auto cst flex-1 p-2 pt-0 text-14px" >
128163 < div class = "flex flex-col gap-1" >
129- { currentSections . value . map ( ( section ) =>
130- < ConfigSection key = { section . path ! } section = { section }
131- entryStates = { props . config . entryStates ! }
132- isCommunity = { communityList . value . includes ( section . path ! ) }
133- sectionState = { props . config . sectionStates ! [ section . path ! ] }
134- allSectionStates = { props . config . sectionStates ! } /> ) }
164+ { currentSections . value . map ( ( section ) => {
165+ if ( ! section ) return null ;
166+ const entryStates = props . config ?. entryStates ;
167+ const sectionStates = props . config ?. sectionStates ;
168+ const sectionState = sectionStates ?. [ section . path ! ] ;
169+ if ( ! entryStates || ! sectionStates || ! sectionState ) return null ;
170+ return < ConfigSection key = { section . path ! } section = { section }
171+ entryStates = { entryStates }
172+ isCommunity = { communityList . value . includes ( section . path ! ) }
173+ isFavorite = { favoritePathSet . value . has ( section . path ! ) }
174+ toggleFavorite = { ( ) => toggleFavoriteSection ( section . path ! ) }
175+ sectionState = { sectionState }
176+ allSectionStates = { sectionStates } /> ;
177+ } ) }
135178 </ div >
136179 </ div >
137180 </ div >
0 commit comments