1. 新增工作台三栏布局模式,适配宽屏设备 2. 添加跨平台系统托盘支持,新增托盘图标资源 3. 修复工作台模式下导航返回异常问题 4. 统一JSON类型安全解析,替换硬类型转换 5. 增加macOS深度链接支持,统一渠道分发信息 6. 优化部分页面生命周期和状态加载逻辑 7. 移除废弃的nearby_connections依赖
208 lines
7.1 KiB
Dart
208 lines
7.1 KiB
Dart
/// ============================================================
|
||
/// 闲言APP — 壁纸模板数据模型
|
||
/// 创建时间: 2026-05-01
|
||
/// 更新时间: 2026-06-19
|
||
// 作用: 壁纸图库数据结构 — 壁纸项/分类/来源/模板
|
||
// 上次更新: 修复筛选类型与实际不一致——不支持分类的源跳过category参数
|
||
/// ============================================================
|
||
|
||
// ============================================================
|
||
// 壁纸来源
|
||
// ============================================================
|
||
|
||
enum WallpaperSource {
|
||
unsplash('api_unsplash', '📷', 'photo', 'Unsplash', 138),
|
||
wallstreet('api_wallstreet', '🏙', 'square_grid_2x2', 'WallSt', 140),
|
||
pexels('api_pexels', '🎞', 'photo', 'Pexels', 136),
|
||
pixabay('api_pixabay', '🔍', 'circle_grid_3x3', 'Pixabay', 140),
|
||
bing360('api_360_bing', '🖼', 'photo', '360壁纸', 264),
|
||
bing('api_360_bing', '🌍', 'globe', '必应壁纸', 245),
|
||
nasa('api_nasa', '🚀', 'sparkles', 'NASA', 133),
|
||
haoWallpaper('api_hao_wallpaper', '🏔', 'square_grid_2x2', '精选壁纸', 345),
|
||
bizhiduoduo('api_bizhiduoduo', '🎲', 'square_grid_2x2', '多多壁纸', 2102),
|
||
bingEnhanced('api_bing_enhanced', '🔮', 'sparkles', '增强必应', 371),
|
||
animePictures('api_anime_pictures', '🎨', 'paintpalette', '动漫插画', 2432),
|
||
dmoe('api_dmoe', '🌸', 'sparkles', '二次元', 2807);
|
||
|
||
const WallpaperSource(
|
||
this.path,
|
||
this.emoji,
|
||
this.iconName,
|
||
this.label,
|
||
this.avgMs,
|
||
);
|
||
final String path;
|
||
final String emoji;
|
||
final String iconName;
|
||
final String label;
|
||
final int avgMs;
|
||
|
||
bool get isFast => avgMs < 500;
|
||
|
||
/// 是否支持分类筛选
|
||
///
|
||
/// 以下源不支持分类筛选(返回随机或固定内容):
|
||
/// - bing360 / bing: 必应每日壁纸,无分类
|
||
/// - nasa: NASA每日天文图片,无分类
|
||
/// - dmoe: 随机二次元壁纸,无分类
|
||
/// - animePictures: 随机动漫插画,无分类
|
||
/// - bizhiduoduo: action=random 模式,category 无效
|
||
bool get supportsCategory {
|
||
switch (this) {
|
||
case WallpaperSource.bing360:
|
||
case WallpaperSource.bing:
|
||
case WallpaperSource.nasa:
|
||
case WallpaperSource.dmoe:
|
||
case WallpaperSource.animePictures:
|
||
case WallpaperSource.bizhiduoduo:
|
||
return false;
|
||
case WallpaperSource.unsplash:
|
||
case WallpaperSource.wallstreet:
|
||
case WallpaperSource.pexels:
|
||
case WallpaperSource.pixabay:
|
||
case WallpaperSource.haoWallpaper:
|
||
case WallpaperSource.bingEnhanced:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
String get baseUrl => 'http://bz.wktyl.com';
|
||
|
||
String buildUrl({
|
||
int limit = 20,
|
||
int page = 1,
|
||
String? category,
|
||
String sort = 'hot',
|
||
String? search,
|
||
}) {
|
||
final params = <String, String>{'limit': '$limit', 'page': '$page'};
|
||
if (this == WallpaperSource.bing360) {
|
||
params['source'] = '360';
|
||
} else if (this == WallpaperSource.bing) {
|
||
params['source'] = 'bing';
|
||
} else if (this == WallpaperSource.bizhiduoduo) {
|
||
params['action'] = 'random';
|
||
}
|
||
// 仅对支持分类筛选的源传 category 参数
|
||
if (category != null &&
|
||
category != 'all' &&
|
||
supportsCategory) {
|
||
params['category'] = category;
|
||
}
|
||
if (sort != 'hot') params['sort'] = sort;
|
||
if (search != null && search.isNotEmpty) params['search'] = search;
|
||
|
||
final query = params.entries.map((e) => '${e.key}=${e.value}').join('&');
|
||
return '$baseUrl/$path.php?$query';
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 壁纸数据项
|
||
// ============================================================
|
||
|
||
class WallpaperItem {
|
||
const WallpaperItem({
|
||
required this.id,
|
||
required this.title,
|
||
required this.imageUrl,
|
||
required this.thumbnailUrl,
|
||
required this.previewUrl,
|
||
this.downloadUrl,
|
||
this.width = 0,
|
||
this.height = 0,
|
||
this.resolution = '',
|
||
this.format = '',
|
||
this.fileSize = 0,
|
||
this.views = 0,
|
||
this.downloads = 0,
|
||
this.likes = 0,
|
||
this.popularityScore = 0.0,
|
||
this.tags = const [],
|
||
this.primaryCategory = '',
|
||
this.source = '',
|
||
this.sourceType = '',
|
||
});
|
||
|
||
final String id;
|
||
final String title;
|
||
final String imageUrl;
|
||
final String thumbnailUrl;
|
||
final String previewUrl;
|
||
final String? downloadUrl;
|
||
final int width;
|
||
final int height;
|
||
final String resolution;
|
||
final String format;
|
||
final int fileSize;
|
||
final int views;
|
||
final int downloads;
|
||
final int likes;
|
||
final double popularityScore;
|
||
final List<String> tags;
|
||
final String primaryCategory;
|
||
final String source;
|
||
final String sourceType;
|
||
|
||
double get aspectRatio => width > 0 && height > 0 ? width / height : 9 / 16;
|
||
|
||
factory WallpaperItem.fromApi(Map<String, dynamic> json) {
|
||
final resourceInfo = json['resource_info'] as Map<String, dynamic>? ?? {};
|
||
final media = json['media'] as Map<String, dynamic>? ?? {};
|
||
final classification =
|
||
json['classification'] as Map<String, dynamic>? ?? {};
|
||
final statistics = json['statistics'] as Map<String, dynamic>? ?? {};
|
||
final metadata = json['metadata'] as Map<String, dynamic>? ?? {};
|
||
|
||
return WallpaperItem(
|
||
id: json['id']?.toString() ?? '',
|
||
title: resourceInfo['title'] as String? ?? '',
|
||
imageUrl: media['image_url'] as String? ?? '',
|
||
thumbnailUrl: media['thumbnail_url'] as String? ?? '',
|
||
previewUrl: media['preview_url'] as String? ?? '',
|
||
downloadUrl: media['download_url'] as String?,
|
||
width: (media['width'] as num?)?.toInt() ?? 0,
|
||
height: (media['height'] as num?)?.toInt() ?? 0,
|
||
resolution: media['resolution'] as String? ?? '',
|
||
format: media['format'] as String? ?? '',
|
||
fileSize: (media['file_size'] as num?)?.toInt() ?? 0,
|
||
views: (statistics['views'] as num?)?.toInt() ?? 0,
|
||
downloads: (statistics['downloads'] as num?)?.toInt() ?? 0,
|
||
likes: (statistics['likes'] as num?)?.toInt() ?? 0,
|
||
popularityScore:
|
||
(statistics['popularity_score'] as num?)?.toDouble() ?? 0.0,
|
||
tags: (classification['tags'] is List<dynamic>)
|
||
? (classification['tags'] as List<dynamic>)
|
||
.map((e) => e.toString())
|
||
.toList()
|
||
: [],
|
||
primaryCategory: classification['primary_category'] as String? ?? '',
|
||
source: metadata['source'] as String? ?? '',
|
||
sourceType: resourceInfo['source_type'] as String? ?? '',
|
||
);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 壁纸分类
|
||
// ============================================================
|
||
|
||
enum WallpaperCategory {
|
||
all('all', '🌐', 'globe', '全部'),
|
||
nature('nature', '🌿', 'circle', '自然'),
|
||
animal('animal', '🐾', 'smiley', '动物'),
|
||
architecture('architecture', '🏛', 'square_grid_2x2', '建筑'),
|
||
technology('technology', '💻', 'pencil_ellipsis', '科技'),
|
||
abstract('abstract', '🎨', 'paintpalette', '抽象'),
|
||
people('people', '👤', 'smiley', '人物'),
|
||
travel('travel', '✈️', 'globe', '旅行'),
|
||
food('food', '🍜', 'drop', '美食'),
|
||
anime('anime', '🌸', 'sparkles', '动漫');
|
||
|
||
const WallpaperCategory(this.id, this.emoji, this.iconName, this.label);
|
||
final String id;
|
||
final String emoji;
|
||
final String iconName;
|
||
final String label;
|
||
}
|