feat(app): 搜索改为调用后端并渲染卡片结果

This commit is contained in:
Zhou Weidong committed 2026-10-20 04:34:12 +08:00
1 parent 730e38622f
commit ade0cc3566
13 files changed
+424 -20

No files matched your search

+6
View File
@@ -65,6 +65,7 @@ class Drama {
this.freeEpisodes = 0,
this.isHot = false,
this.viewCount = 0,
this.language = '',
this.detailLoaded = false,
});
@@ -80,6 +81,9 @@ class Drama {
final bool isHot;
final int viewCount;
/// 卡片语种(如 zh-CN / id),用于剧场语言筛选。
final String language;
/// 详情接口是否已经取过完整剧集列表。
/// 列表卡片只带一集可播预览,需要完整剧集时再进详情。
final bool detailLoaded;
@@ -104,6 +108,7 @@ class Drama {
isHot: json['isHot'] == true,
// viewCount 后端是 Long,按字符串下发(见 json_numbers.dart)
viewCount: jsonToInt(json['viewCount']),
language: json['language'] as String? ?? '',
);
}
@@ -120,6 +125,7 @@ class Drama {
freeEpisodes: (json['freeEpisodes'] as num?)?.toInt() ?? 0,
isHot: json['isHot'] == true,
viewCount: jsonToInt(json['viewCount']),
language: json['language'] as String? ?? '',
detailLoaded: true,
episodes: (json['episodes'] as List? ?? const [])
.whereType<Map<String, dynamic>>()
+22
View File
@@ -0,0 +1,22 @@
// Author: fruit.com.cn
import 'drama.dart';
import 'json_numbers.dart';
/// 搜索分页结果:`GET /app/search` 的 `{total, list}`。
class SearchResult {
const SearchResult({this.total = 0, this.list = const []});
final int total;
final List<Drama> list;
factory SearchResult.fromJson(Map<String, dynamic> json) {
return SearchResult(
total: jsonToInt(json['total']),
list: (json['list'] as List? ?? const [])
.whereType<Map<String, dynamic>>()
.map(Drama.fromCardJson)
.toList(),
);
}
}
+18
View File
@@ -1,5 +1,6 @@
import '../models/drama.dart';
import '../models/home_feed.dart';
import '../models/search_result.dart';
import 'api_client.dart';
/// 客户端目录接口,对应后端 `/app/**`。
@@ -16,6 +17,23 @@ class CatalogApi {
return HomeFeed.fromJson(_asMap(await _client.get('/app/discover/feed')));
}
/// 搜索已上线短剧:按标题/简介模糊匹配,分页返回卡片列表。
Future<SearchResult> search(
String keyword, {
int pageIndex = 1,
int pageSize = 20,
}) async {
final data = await _client.get(
'/app/search',
query: {
'keyword': keyword,
'pageIndex': '$pageIndex',
'pageSize': '$pageSize',
},
);
return SearchResult.fromJson(_asMap(data));
}
Future<Drama> dramaDetail(String dramaId) async {
return Drama.fromDetailJson(
_asMap(await _client.get('/app/dramas/$dramaId')),
@@ -0,0 +1,26 @@
// Author: fruit.com.cn
import '../models/search_result.dart';
import '../remote/catalog_api.dart';
/// 搜索数据来源:只调用后端 `/app/search`,不在客户端做本地过滤。
abstract interface class SearchRepository {
Future<SearchResult> search(
String keyword, {
int pageIndex = 1,
int pageSize = 20,
});
}
class HttpSearchRepository implements SearchRepository {
const HttpSearchRepository(this._api);
final CatalogApi _api;
@override
Future<SearchResult> search(
String keyword, {
int pageIndex = 1,
int pageSize = 20,
}) => _api.search(keyword, pageIndex: pageIndex, pageSize: pageSize);
}