diff --git a/entry/src/main/ets/componets/import/ImportCommon.ets b/entry/src/main/ets/componets/import/ImportCommon.ets index 64e6abe3..00e9bf3c 100644 --- a/entry/src/main/ets/componets/import/ImportCommon.ets +++ b/entry/src/main/ets/componets/import/ImportCommon.ets @@ -10,7 +10,7 @@ import Score from '../common/Score'; import Tag from '../common/Tag'; import BookSourceDao from '../../database/dao/BookSourceDao'; import { BookSource, GroupList, SOURCE_GROUP_MAP } from '../../database/entities/BookSource'; -import SourceGroup from '../ui/sourceGroup'; +import SourceGroup from '../ui/SourceGroup'; @Component export default struct ImportCommon { diff --git a/entry/src/main/ets/componets/ui/sourceGroup.ets b/entry/src/main/ets/componets/ui/SourceGroup.ets similarity index 100% rename from entry/src/main/ets/componets/ui/sourceGroup.ets rename to entry/src/main/ets/componets/ui/SourceGroup.ets diff --git a/entry/src/main/ets/database/dao/BookSourceDao.ets b/entry/src/main/ets/database/dao/BookSourceDao.ets index 69b6341d..d153774e 100644 --- a/entry/src/main/ets/database/dao/BookSourceDao.ets +++ b/entry/src/main/ets/database/dao/BookSourceDao.ets @@ -27,7 +27,8 @@ class BookSourceDao { getFlowColumn() { const column: ColumnInfo[] = AppDatabaseUtil.getAssignColumn(this.TABLE_NAME, ['bookSourceUrl', 'bookSourceName', 'bookSourceGroup', 'bookSourceGrade', 'bookSourceScore', 'customOrder', - 'enabled', 'enabledExplore', 'lastUpdateTime', 'respondTime', 'weight', 'isTop', 'showRecentIcon', 'showExplore']); + 'enabled', 'enabledExplore', 'lastUpdateTime', 'respondTime', 'weight', 'isTop', 'showRecentIcon', + 'showExplore']); column.push(...[ { name: 'hasLoginUrl', @@ -63,6 +64,7 @@ class BookSourceDao { const bookSourceGrade = searchParams?.bookSourceGrade; const hasLoginUrl = searchParams?.hasLoginUrl; const showExplore = searchParams?.showExplore; + const order = searchParams?.order; try { let sql = ` SELECT @@ -117,10 +119,12 @@ class BookSourceDao { sql += ` WHERE ${whereClause.join(' AND ')}`; } - sql += ` - ORDER BY - isTop DESC, customOrder ASC -`; + sql += ` ORDER BY`; + if (order !== undefined) { + sql += ` ${order === 0 ? 'bookSourceGroup' : 'lastUpdateTime'} DESC` + } else { + sql += ` isTop DESC, customOrder ASC`; + } const column: ColumnInfo[] = this.getFlowColumn() return await DbUtil.querySqlForList(sql, column) } catch (err) { diff --git a/entry/src/main/ets/database/types/BookSourceType.ets b/entry/src/main/ets/database/types/BookSourceType.ets index 7e9995ab..e2517299 100644 --- a/entry/src/main/ets/database/types/BookSourceType.ets +++ b/entry/src/main/ets/database/types/BookSourceType.ets @@ -5,7 +5,7 @@ export interface BookSourceSearchParams { type?: number, // 是否启用 enabled?: boolean, - // TODO 排序,还未实现 + // 排序 0 按评分排序 1 按时间排序 order?: number, // 等级 bookSourceGrade?: number, diff --git a/entry/src/main/ets/pages/view/Find/BookSource/components/FilterText.ets b/entry/src/main/ets/pages/view/Find/BookSource/components/FilterText.ets new file mode 100644 index 00000000..92068e0f --- /dev/null +++ b/entry/src/main/ets/pages/view/Find/BookSource/components/FilterText.ets @@ -0,0 +1,44 @@ +@Component +export default struct FilterText { + index: number = 0 + @Prop title: string + @Prop hasActive: boolean = false + isIcon: boolean = true + onChange: (val: number) => void = (_val) => {} + + build() { + Row() { + Text(this.title) + .fontSize(12) + .fontColor(this.hasActive ? '#FF6600' : '#E0000000') + + if (this.isIcon) { + Image($r('app.media.down_arrow')) + .width(8) + .margin({ left: 6 }) + .fillColor(this.hasActive ? '#FF6600' : '#E0000000') + } + if (this.index === 4 && this.hasActive) { + Image($r('app.media.close')) + .width(8) + .margin({ left: 2 }) + .fillColor(this.hasActive ? '#FF6600' : '#E0000000') + } + } + .height(22) + .padding({ + top: 2, + bottom: 2, + left: 4, + right: 4 + }) + .layoutWeight(1) + .backgroundColor(this.hasActive ? 'rgba(255, 102, 0, 0.12)' : Color.White) + .borderRadius(4) + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.Center) + .onClick(() => { + this.onChange(this.index) + }) + } +} diff --git a/entry/src/main/ets/pages/view/Find/BookSource/components/FiltrateOption.ets b/entry/src/main/ets/pages/view/Find/BookSource/components/FiltrateOption.ets new file mode 100644 index 00000000..6b5bd162 --- /dev/null +++ b/entry/src/main/ets/pages/view/Find/BookSource/components/FiltrateOption.ets @@ -0,0 +1,47 @@ +export interface Options { + label: string; + value?: number | string | boolean; +} + + +@Component +export default struct FiltrateOption { + options: Options[] = [] + columns: number = 3 + isAgain: boolean = false + @Prop value?: number | string | boolean + onChange: (value?: number | string | boolean) => void = () => {} + + build() { + Grid() { + ForEach(this.options, (item: Options) => { + GridItem() { + Text(item.label) + .fontColor(this.value === item.value ? '#FF6600' : '#E0000000') + .fontSize(12) + .height(20) + .lineHeight(20) + } + .border({ + radius: 4, + width: 1, + style: BorderStyle.Solid, + color: this.value === item.value ? '#FF6600' : '#0A000000' + }) + .padding({ top: 6, bottom: 6 }) + .backgroundColor(this.value === item.value ? 'rgba(255, 102, 0, 0.12)' : '#0A000000') + .onClick(() => { + if (this.value !== undefined && this.isAgain && this.value === item.value) { + this.onChange(undefined) + return + } + this.onChange(item.value) + }) + }) + } + .columnsTemplate(Array(this.columns).fill('1fr').join(' ')) + .columnsGap(16) + .rowsGap(16) + .padding({ bottom: 12, top: 12 }) + } +} diff --git a/entry/src/main/ets/pages/view/Find/BookSource/components/SourceView.ets b/entry/src/main/ets/pages/view/Find/BookSource/components/SourceView.ets index 630df8ba..0428474c 100644 --- a/entry/src/main/ets/pages/view/Find/BookSource/components/SourceView.ets +++ b/entry/src/main/ets/pages/view/Find/BookSource/components/SourceView.ets @@ -12,7 +12,10 @@ import BookSourceDao from '../../../../../database/dao/BookSourceDao' import { GroupPartList, SOURCE_GROUP_MAP } from '../../../../../database/entities/BookSource' import { BookSourcePart } from '../../../../../database/entities/BookSourcePart' import { JSON } from '@kit.ArkTS' -import SourceGroup from '../../../../../componets/ui/sourceGroup' +import SourceGroup from '../../../../../componets/ui/SourceGroup' +import { BookSourceSearchParams } from '../../../../../database/types/BookSourceType' +import FiltrateOption from './FiltrateOption' +import FilterText from './FilterText' @Component export default struct SourceView { @@ -42,6 +45,13 @@ export default struct SourceView { @State dialogItem: Partial = {} @StorageLink('refreshCount') refreshCount: number = 0 @State isShowGroup: boolean = false + @State currentTitle: number = -1 + @State query: BookSourceSearchParams = { + enabled: undefined, + bookSourceGrade: undefined, + order: undefined, + hasLoginUrl: undefined + } private dialogController: CustomDialogController = new CustomDialogController({ builder: CustomContentDialog({ primaryTitle: '书源评分', @@ -114,7 +124,11 @@ export default struct SourceView { this.refreshCount += 1 BookSourceDao.flowSearch({ type: [0, 2, 1][this.index], - searchKey: this.searchValue + searchKey: this.searchValue, + enabled: this.query.enabled, + bookSourceGrade: this.query.bookSourceGrade, + order: this.query.order, + hasLoginUrl: this.query.hasLoginUrl }).then(data => { const bookSource = data ?? [] console.info('TagInfo', JSON.stringify(bookSource.slice(0, 3))) @@ -187,19 +201,72 @@ export default struct SourceView { // 筛选器 Row() { - this.filterText('全部') + FilterText({ + index: 0, + title: this.query.enabled === undefined ? '全部' : (this.query.enabled ? '启用' : '禁用'), + hasActive: this.query.enabled !== undefined, + onChange: (index) => { + if (this.currentTitle === index) { + this.currentTitle = -1 + return + } + this.currentTitle = index + } + }) Row().width(12) - this.filterText('排序') + FilterText({ + index: 1, + title: this.query.order === undefined ? '排序' : ['按评分', '按时间'][this.query.order], + hasActive: this.query.order !== undefined, + onChange: (index) => { + if (this.currentTitle === index) { + this.currentTitle = -1 + return + } + this.currentTitle = index + } + }) Row().width(12) - this.filterText('校验') + FilterText({ + index: 2, + title: '校验', + hasActive: this.query.verify !== undefined, + onChange: (index) => { + showMessage('校验,开发中') + } + }) Row().width(12) - this.filterText('等级') + FilterText({ + index: 3, + title: this.query.bookSourceGrade === undefined ? '等级' : ['常规', '优质', '精品'][this.query.bookSourceGrade], + hasActive: this.query.bookSourceGrade !== undefined, + onChange: (index) => { + if (this.currentTitle === index) { + this.currentTitle = -1 + return + } + this.currentTitle = index + } + }) Row().width(12) - this.filterText('登录', false) + FilterText({ + index: 4, + title: '登录', + hasActive: this.query.hasLoginUrl !== undefined, + isIcon: false, + onChange: () => { + if (this.query.hasLoginUrl) { + this.query.hasLoginUrl = undefined + } else { + this.query.hasLoginUrl = true + } + this.getList() + } + }) } .padding({ left: 20, @@ -236,79 +303,223 @@ export default struct SourceView { delay: this.isBatch ? 300 : 0 }) - Column() { - // 内容主体 - Row({ space: 12 }) { - SideBar({ - currentIndex: this.currentIndex, - sideBarList: this.groupList, - scroller: this.scroll, - clickAction: (index: number) => { - this.scrollChangeAction(index, true) - }, - isBadge: !this.isBatch, - isDrag: this.isBatch, - onDragChange: async (list) => { - this.groupList = list as GroupPartList[] - const bookSourcePartList = - this.groupList.reduce((acc: BookSourcePart[], item) => acc.concat(item.list), []) - const sortBookSourcePartList = bookSourcePartList.map((item, index) => { - if (index !== 0) { - item.customOrder = bookSourcePartList[index - 1].customOrder + 1 - } - return item - }) - console.info('TagInfo bookSourcePartList:', JSON.stringify(bookSourcePartList)) - await BookSourceDao.batchUpdateFlow(sortBookSourcePartList) - this.refreshCount += 1 + Stack() { + Column() { + Stack() { + Column() { } - }) - .width(88) - - List({ scroller: this.secondScroll }) { - ForEach(this.groupList, (item: GroupPartList, index: number) => { - ListItemGroup({ - header: this.classifyHeader(`${item.title} (${item.list.length})`, index), - space: 0 - }) { - ForEach(item.list, (bookSource: BookSourcePart, j: number) => { - this.CourseItem(bookSource, index, j) + .backgroundColor('#99000000') + .width('100%') + .height('100%') + .opacity(this.currentTitle !== -1 ? 1 : 0) + .animation({ + duration: this.currentTitle !== -1 ? 300 : 0 + }) + + Row() { + if (this.currentTitle === 0) { + FiltrateOption({ + options: [ + { + label: '全部', + value: undefined + }, + { + label: '启用', + value: true + }, + { + label: '禁用', + value: false + } + ], + value: this.query.enabled, + onChange: (value) => { + this.query.enabled = value as boolean + this.getList() + setTimeout(() => { + this.currentTitle = -1 + }, 200) + } + }) + } + if (this.currentTitle === 1) { + FiltrateOption({ + options: [ + { + label: '按评分排序', + value: 0 + }, + { + label: '按时间排序', + value: 1 + } + ], + columns: 2, + isAgain: true, + value: this.query.order, + onChange: (value) => { + this.query.order = value as number + this.getList() + setTimeout(() => { + this.currentTitle = -1 + }, 200) + } + }) + } + if (this.currentTitle === 3) { + FiltrateOption({ + options: [ + { + label: '全部', + value: undefined + }, + { + label: '精选', + value: 2 + }, + { + label: '优质', + value: 1 + }, + { + label: '常规', + value: 0 + } + ], + columns: 4, + value: this.query.bookSourceGrade, + onChange: (value) => { + this.query.bookSourceGrade = value as number + this.getList() + setTimeout(() => { + this.currentTitle = -1 + }, 200) + } }) } + } + .height([0, 1, 3].includes(this.currentTitle) ? 64 : 0) + .backgroundColor('#f5f5f5') + .padding({ left: 20, right: 20, bottom: 8 }) + .borderRadius({ + bottomLeft: 8, + bottomRight: 8 + }) + .translate({ y: this.currentTitle !== -1 ? 0 : -64 }) + .width('100%') + .animation({ + duration: this.currentTitle !== -1 ? 300 : 0 }) } - .padding({ right: 20 }) - .sticky(StickyStyle.Header) - .onScrollIndex((start) => { - this.scrollChangeAction(start, false) - }) + .width('100%') .height('100%') - .layoutWeight(1) - .gesture( - LongPressGesture({ repeat: true, duration: 500 }) - .onAction((event: GestureEvent) => { - if (event && event.repeat) { - if (this.longCount >= 1) { - this.isBatch = true + .alignContent(Alignment.Top) + } + .width('100%') + .height('100%') + .padding({ top: 122 }) + .zIndex(this.currentTitle !== -1 ? 99 : -1) + .onClick(() => { + this.currentTitle = -1 + }) + + Column() { + // 内容主体 + Row({ space: 12 }) { + SideBar({ + currentIndex: this.currentIndex, + sideBarList: this.groupList, + scroller: this.scroll, + clickAction: (index: number) => { + this.scrollChangeAction(index, true) + }, + isBadge: !this.isBatch, + isDrag: this.isBatch, + onDragChange: async (list) => { + this.groupList = list as GroupPartList[] + const bookSourcePartList = + this.groupList.reduce((acc: BookSourcePart[], item) => acc.concat(item.list), []) + const sortBookSourcePartList = bookSourcePartList.map((item, index) => { + if (index !== 0) { + item.customOrder = bookSourcePartList[index - 1].customOrder + 1 } - this.longCount++ + return item + }) + console.info('TagInfo bookSourcePartList:', JSON.stringify(bookSourcePartList)) + await BookSourceDao.batchUpdateFlow(sortBookSourcePartList) + this.refreshCount += 1 + } + }) + .width(88) + + List({ scroller: this.secondScroll }) { + ForEach(this.groupList, (item: GroupPartList, index: number) => { + ListItemGroup({ + header: this.classifyHeader(`${item.title} (${item.list.length})`, index), + space: 0 + }) { + ForEach(item.list, (bookSource: BookSourcePart, j: number) => { + this.CourseItem(bookSource, index, j) + }) } }) - .onActionEnd(() => { - this.longCount = 0 - }) - ) + } + .padding({ right: 20 }) + .sticky(StickyStyle.Header) + .onScrollIndex((start) => { + this.scrollChangeAction(start, false) + }) + .height('100%') + .layoutWeight(1) + .gesture( + LongPressGesture({ repeat: true, duration: 500 }) + .onAction((event: GestureEvent) => { + if (event && event.repeat) { + if (this.longCount >= 1) { + this.isBatch = true + } + this.longCount++ + } + }) + .onActionEnd(() => { + this.longCount = 0 + }) + ) + } + .layoutWeight(1) + .animation({ + duration: 300 + }) } - .layoutWeight(1) + .height('100%') + .padding({ + top: this.isBatch ? 50 + this.topRectHeight : 122, + bottom: this.isBatch ? 68 + this.bottomRectHeight : this.bottomRectHeight + }) + + Column() { + if (this.errorText && !this.sourceList.length) { + Text(this.errorText) + } else { + LoadingProgress() + .color(0xff6600) + .width('50%') + Text('加载中...') + } + } + .justifyContent(FlexAlign.Center) + .zIndex(this.loading || (this.errorText && !this.sourceList.length) ? 9 : -1) + .opacity(this.loading || (this.errorText && !this.sourceList.length) ? 1 : 0) + .width('100%') + .height('100%') + .backgroundColor('#f5f5f5') .animation({ duration: 300 }) } .height('100%') - .padding({ - top: this.isBatch ? 50 + this.topRectHeight : 122, - bottom: this.isBatch ? 68 + this.bottomRectHeight : this.bottomRectHeight - }) + Row() { Row() { @@ -436,25 +647,6 @@ export default struct SourceView { }) .padding({ bottom: this.bottomRectHeight }) - Column() { - if (this.errorText && !this.sourceList.length) { - Text(this.errorText) - } else { - LoadingProgress() - .color(0xff6600) - .width('50%') - Text('加载中...') - } - } - .justifyContent(FlexAlign.Center) - .zIndex(this.loading || (this.errorText && !this.sourceList.length) ? 9 : -1) - .opacity(this.loading || (this.errorText && !this.sourceList.length) ? 1 : 0) - .width('100%') - .height('100%') - .backgroundColor('#f5f5f5') - .animation({ - duration: 300 - }) //分组 Flex() @@ -494,32 +686,6 @@ export default struct SourceView { }) } - @Builder - filterText(title: string, isIcon: boolean = true) { - Row() { - Text(title) - .fontSize(12) - - if (isIcon) { - Image($r('app.media.down_arrow')) - .width(8) - .margin({ left: 6 }) - } - } - .height(22) - .padding({ - top: 2, - bottom: 2, - left: 4, - right: 4 - }) - .layoutWeight(1) - .backgroundColor(Color.White) - .borderRadius(4) - .alignItems(VerticalAlign.Center) - .justifyContent(FlexAlign.Center) - } - @Builder buildContent() { Column() {