108
108
<!-- 页面内容 -->
109
109
<!-- 有数据 -->
110
110
<div class =" subs-list-wrapper" >
111
- <div v-if =" hasFiles" >
112
- <draggable
113
- v-model =" files"
114
- item-key =" name"
115
- :scroll-sensitivity =" 200"
116
- :force-fallback =" true"
117
- :scroll-speed =" 8"
118
- :scroll =" true"
119
- v-bind =" {
120
- animation: 200,
121
- disabled: false,
122
- delay: 200,
123
- chosenClass: 'chosensub',
124
- handle: 'div',
125
- }"
126
- @change =" changeSort('files', files)"
127
- @start =" handleDragStart(files)"
128
- @end =" handleDragEnd(files)"
129
- >
130
- <template #item =" { element } " >
131
- <div :key =" element.name" class =" draggable-item" >
132
- <FileListItem
133
- :file =" element"
134
- type =" file"
135
- :disabled =" swipeDisabled"
136
- @share =" handleShare"
137
- />
138
- </div >
139
- </template >
140
- </draggable >
111
+ <div v-if =" tags && tags.length > 0" ref =" radioWrapperRef" class =" radio-wrapper" >
112
+ <!-- <nut-radiogroup v-model="tag" direction="horizontal"> -->
113
+ <!-- <nut-radio v-for="i in tags" shape="button" :label="String(i.value)">{{ i.label }}</nut-radio> -->
114
+ <span v-for =" i in tags" class =" tag" :class =" { 'current': i.value === tag }" @click =" setTag(i.value)" >{{ i.label }}</span >
115
+ <!-- </nut-radiogroup> -->
116
+ </div >
117
+ <div class =" subs-list-container" :style =" { paddingTop: `${radioWrapperHeight}px` }" >
118
+ <div v-if =" hasFiles" >
119
+ <draggable
120
+ v-model =" files"
121
+ item-key =" name"
122
+ :scroll-sensitivity =" 200"
123
+ :force-fallback =" true"
124
+ :scroll-speed =" 8"
125
+ :scroll =" true"
126
+ v-bind =" {
127
+ animation: 200,
128
+ disabled: false,
129
+ delay: 200,
130
+ chosenClass: 'chosensub',
131
+ handle: 'div',
132
+ }"
133
+ @change =" changeSort('files', files)"
134
+ @start =" handleDragStart(files)"
135
+ @end =" handleDragEnd(files)"
136
+ >
137
+ <template #item =" { element } " >
138
+ <div v-show =" shouldShowElement(element)" :key =" element.name" class =" draggable-item" >
139
+ <FileListItem
140
+ :file =" element"
141
+ type =" file"
142
+ :disabled =" swipeDisabled"
143
+ @share =" handleShare"
144
+ />
145
+ </div >
146
+ </template >
147
+ </draggable >
148
+ </div >
141
149
</div >
142
150
</div >
143
151
<!-- 没有数据 -->
194
202
195
203
<script lang="ts" setup>
196
204
import { storeToRefs } from " pinia" ;
197
- import { ref , toRaw , onMounted } from " vue" ;
205
+ import { ref , toRaw , onMounted , computed , watch , nextTick } from " vue" ;
198
206
import draggable from " vuedraggable" ;
199
207
import SharePopup from " ./share/SharePopup.vue" ;
200
208
@@ -234,9 +242,6 @@ const editFile = () => {
234
242
addSubBtnIsVisible .value = true ;
235
243
};
236
244
237
- onMounted (() => {
238
- methodStore .registerMethod (" addFile" , editFile );
239
- });
240
245
const { env } = useBackend ();
241
246
const { showNotify } = useAppNotifyStore ();
242
247
const subApi = useSubsApi ();
@@ -263,6 +268,69 @@ const swipeDisabled = ref(false);
263
268
const touchStartY = ref (null );
264
269
const touchStartX = ref (null );
265
270
const sortFailed = ref (false );
271
+ const hasUntagged = ref (false );
272
+ const getTag = () => {
273
+ return localStorage .getItem (' file-tag' ) || ' all' ;
274
+ };
275
+ const tag = ref (getTag ());
276
+ const tags = computed (() => {
277
+ if (! hasFiles .value ) return [];
278
+ const set = new Set ();
279
+ files .value .forEach (sub => {
280
+
281
+ if (Array .isArray (sub .tag ) && sub .tag .length > 0 ) {
282
+ sub .tag .forEach (i => {
283
+ set .add (i );
284
+ });
285
+ } else {
286
+ hasUntagged .value = true ;
287
+ }
288
+ });
289
+
290
+ let tags: any [] = Array .from (set );
291
+ // if(tags.length === 0) return [];
292
+ tags = tags .map (i => ({ label: i , value: i }));
293
+
294
+ const result = [{ label: t (" specificWord.all" ), value: " all" }, ... tags ];
295
+ if (tags .length > 0 && hasUntagged .value ) result .push ({ label: t (" specificWord.untagged" ), value: " untagged" });
296
+
297
+ if (! result .find (i => i .value === tag .value )) {
298
+ tag .value = ' all' ;
299
+ }
300
+ return result ;
301
+ });
302
+ const radioWrapperRef = ref (null );
303
+ const radioWrapperHeight = ref (0 );
304
+
305
+ // 更新标签栏高度
306
+ const updateRadioWrapperHeight = () => {
307
+ nextTick (() => {
308
+ if (radioWrapperRef .value ) {
309
+ radioWrapperHeight .value = radioWrapperRef .value .offsetHeight ;
310
+ } else {
311
+ radioWrapperHeight .value = 0 ;
312
+ }
313
+ });
314
+ };
315
+ const isPWA = ref (
316
+ (window .matchMedia (" (display-mode: standalone)" ).matches &&
317
+ ! / Android/ .test (navigator .userAgent )) ||
318
+ false
319
+ );
320
+ const navBarHeight = computed (() => {
321
+ return isPWA .value ? ` ${44 + 32 + bottomSafeArea .value }px ` : ` ${44 + 12 + bottomSafeArea .value }px ` ;
322
+ });
323
+
324
+ watch (tag , () => {
325
+ updateRadioWrapperHeight ();
326
+ });
327
+
328
+ watch (() => tags .value , () => {
329
+ updateRadioWrapperHeight ();
330
+ }, { deep: true , immediate: true });
331
+ onMounted (() => {
332
+ methodStore .registerMethod (" addFile" , editFile );
333
+ });
266
334
const onTouchStart = (event : TouchEvent ) => {
267
335
touchStartY .value = Math .abs (event .touches [0 ].clientY );
268
336
touchStartX .value = Math .abs (event .touches [0 ].clientX );
@@ -402,6 +470,30 @@ const importTips = () => {
402
470
lockScroll: false ,
403
471
});
404
472
};
473
+ const scrollToTop = () => {
474
+ const scrollPosition = 0 ;
475
+
476
+ window .scrollTo ({
477
+ top: scrollPosition ,
478
+ behavior: " smooth"
479
+ });
480
+ };
481
+
482
+ const setTag = (current ) => {
483
+ tag .value = current ;
484
+ if (current === ' all' ) {
485
+ localStorage .removeItem (' file-tag' );
486
+ } else {
487
+ localStorage .setItem (' file-tag' , current );
488
+ }
489
+ // 增加滚动到顶部
490
+ scrollToTop ();
491
+ };
492
+ const shouldShowElement = (element ) => {
493
+ if (tag .value === ' all' ) return true ;
494
+ if (tag .value === ' untagged' ) return ! Array .isArray (element .tag ) || element .tag .length === 0 ;
495
+ return element .tag .includes (tag .value );
496
+ };
405
497
</script >
406
498
407
499
<style lang="scss" scoped>
@@ -595,5 +687,37 @@ const importTips = () => {
595
687
width : calc (100% - 1.5rem );
596
688
margin-left : auto ;
597
689
margin-right : auto ;
690
+ .radio-wrapper {
691
+ box-sizing : border-box ;
692
+ width : 100% ;
693
+ display : flex ;
694
+ flex-wrap : wrap ;
695
+ position : fixed ;
696
+ padding : 10px ;
697
+ top : v-bind (navBarHeight );
698
+ z-index : 10 ;
699
+ backdrop-filter : blur (var (--nav-bar-blur ));
700
+ -webkit-backdrop-filter : blur (var (--nav-bar-blur ));
701
+ background : var (--nav-bar-color );
702
+ @include centered-fixed-container ;
703
+ @media screen and (min-width : 768px ) {
704
+ border-radius : var (--item-card-radios );
705
+ overflow : hidden ;
706
+ }
707
+ .tag {
708
+ font-size : 12px ;
709
+ color : var (--second-text-color );
710
+ margin : 0px 5px ;
711
+ padding : 7.5px 2.5px 4px ;
712
+ cursor : pointer ;
713
+ -webkit-user-select : none ;
714
+ user-select : none ;
715
+ border-bottom : 1px solid transparent ;
716
+ }
717
+ .current {
718
+ border-bottom : 1px solid var (--primary-color );
719
+ color : var (--primary-color );
720
+ }
721
+ }
598
722
}
599
723
</style >
0 commit comments