generated from dellevin/template
新增影视时长优化界面
This commit is contained in:
@@ -156,7 +156,7 @@ class _BookFormPageState extends State<BookFormPage> {
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
// 书名 + 别名
|
||||
// 第一行:书名 + 别名
|
||||
_halfCard('书名', _titleController.text, Icons.book_outlined, required: true,
|
||||
onTap: () async {
|
||||
final r = await TextInputPanel.show(context: context, title: '书名', initialValue: _titleController.text, hint: '请输入书名');
|
||||
@@ -172,7 +172,7 @@ class _BookFormPageState extends State<BookFormPage> {
|
||||
},
|
||||
),
|
||||
|
||||
// 作者 + 译者
|
||||
// 第二行:作者 + 译者
|
||||
_halfCard('作者', _authors.isEmpty ? '' : '${_authors.length}人:${_authors.join('、')}', Icons.person_outline,
|
||||
onTap: () async {
|
||||
final provider = context.read<AppProvider>();
|
||||
@@ -191,15 +191,8 @@ class _BookFormPageState extends State<BookFormPage> {
|
||||
if (r != null) setState(() => _translators = r);
|
||||
},
|
||||
),
|
||||
_halfCard('出版社', _publisherController.text, Icons.business_outlined,
|
||||
onTap: () async {
|
||||
final r = await TextInputPanel.show(context: context, title: '出版社', initialValue: _publisherController.text, hint: '请输入出版社');
|
||||
if (!mounted) return;
|
||||
if (r != null) setState(() => _publisherController.text = r);
|
||||
},
|
||||
),
|
||||
|
||||
// 类型 + ISBN
|
||||
// 第三行:类型 + 阅读次数
|
||||
_halfCard('类型', _genres.isEmpty ? '' : '${_genres.length}个:${_genres.join('、')}', Icons.category_outlined,
|
||||
onTap: () async {
|
||||
final provider = context.read<AppProvider>();
|
||||
@@ -210,29 +203,23 @@ class _BookFormPageState extends State<BookFormPage> {
|
||||
if (r != null) setState(() => _genres = r);
|
||||
},
|
||||
),
|
||||
_halfCard('ISBN', _isbnController.text, Icons.qr_code_outlined,
|
||||
_halfCard('阅读次数', _readCount > 0 ? '$_readCount 次' : '', Icons.repeat_outlined,
|
||||
onTap: () => _editReadCount(),
|
||||
),
|
||||
|
||||
// 第四行:出版社 + 出版时间
|
||||
_halfCard('出版社', _publisherController.text, Icons.business_outlined,
|
||||
onTap: () async {
|
||||
final r = await TextInputPanel.show(context: context, title: 'ISBN', initialValue: _isbnController.text, hint: '请输入ISBN编号');
|
||||
final r = await TextInputPanel.show(context: context, title: '出版社', initialValue: _publisherController.text, hint: '请输入出版社');
|
||||
if (!mounted) return;
|
||||
if (r != null) setState(() => _isbnController.text = r);
|
||||
if (r != null) setState(() => _publisherController.text = r);
|
||||
},
|
||||
),
|
||||
|
||||
// 出版时间
|
||||
SizedBox(
|
||||
width: double.infinity, height: 90,
|
||||
child: _buildInfoCard(
|
||||
label: '出版时间',
|
||||
value: _publishDate != null ? '${_publishDate!.year}.${_publishDate!.month.toString().padLeft(2, '0')}.${_publishDate!.day.toString().padLeft(2, '0')}' : '',
|
||||
icon: Icons.date_range_outlined,
|
||||
trailing: _publishDate != null
|
||||
? GestureDetector(onTap: () => setState(() => _publishDate = null), child: Icon(Icons.close, size: 16, color: colors.onSurface.withValues(alpha: 0.35)))
|
||||
: null,
|
||||
onTap: () => _selectPublishDate(),
|
||||
),
|
||||
_halfCard('出版时间', _publishDate != null ? '${_publishDate!.year}.${_publishDate!.month.toString().padLeft(2, '0')}.${_publishDate!.day.toString().padLeft(2, '0')}' : '', Icons.date_range_outlined,
|
||||
onTap: () => _selectPublishDate(),
|
||||
),
|
||||
|
||||
// 开始阅读日期 + 读完日期(同一行)
|
||||
// 第五行:开始阅读 + 读完日期
|
||||
_halfCard('开始阅读', _startDate != null ? '${_startDate!.year}.${_startDate!.month.toString().padLeft(2, '0')}.${_startDate!.day.toString().padLeft(2, '0')}' : '', Icons.play_circle_outlined,
|
||||
onTap: () => _selectStartDate(),
|
||||
),
|
||||
@@ -240,12 +227,22 @@ class _BookFormPageState extends State<BookFormPage> {
|
||||
onTap: () => _selectFinishDate(),
|
||||
),
|
||||
|
||||
// 阅读次数
|
||||
_halfCard('阅读次数', _readCount > 0 ? '$_readCount 次' : '', Icons.repeat_outlined,
|
||||
onTap: () => _editReadCount(),
|
||||
// ISBN(独占一行)
|
||||
SizedBox(
|
||||
width: double.infinity, height: 90,
|
||||
child: _buildInfoCard(
|
||||
label: 'ISBN',
|
||||
value: _isbnController.text,
|
||||
icon: Icons.qr_code_outlined,
|
||||
onTap: () async {
|
||||
final r = await TextInputPanel.show(context: context, title: 'ISBN', initialValue: _isbnController.text, hint: '请输入ISBN编号');
|
||||
if (!mounted) return;
|
||||
if (r != null) setState(() => _isbnController.text = r);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
// 书籍简介
|
||||
// 书籍简介(独占一行)
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: _buildInfoCard(label: '书籍简介', value: _summaryController.text, icon: Icons.description_outlined, height: 160, scrollable: true, onTap: () => _editSummary()),
|
||||
|
||||
@@ -263,6 +263,11 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
||||
Text('已观看 ${movie.watchCount} 次',
|
||||
style: TextStyle(fontSize: 14, color: colors.onSurface.withValues(alpha: 0.4))),
|
||||
],
|
||||
if (movie.duration > 0) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(_formatDuration(movie.duration),
|
||||
style: TextStyle(fontSize: 14, color: colors.onSurface.withValues(alpha: 0.4))),
|
||||
],
|
||||
Divider(height: 32, thickness: 0.5, color: colors.outline),
|
||||
// 详细信息
|
||||
if (movie.directors.isNotEmpty) _buildDesktopInfoRow('导演', movie.directors.join(','), colors),
|
||||
@@ -1523,6 +1528,14 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
||||
color: colors.onSurface.withValues(alpha: 0.4),
|
||||
),
|
||||
),
|
||||
if (movie.duration > 0)
|
||||
Text(
|
||||
_formatDuration(movie.duration),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: colors.onSurface.withValues(alpha: 0.4),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -2070,6 +2083,16 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
||||
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
/// 格式化时长:120 -> "2小时0分",0 -> ""
|
||||
String _formatDuration(int minutes) {
|
||||
if (minutes <= 0) return '';
|
||||
final h = minutes ~/ 60;
|
||||
final m = minutes % 60;
|
||||
if (h > 0 && m > 0) return '时长 $h小时$m分';
|
||||
if (h > 0) return '时长 $h小时';
|
||||
return '时长 $m分';
|
||||
}
|
||||
|
||||
void _navigateToEdit(BuildContext context) {
|
||||
final provider = context.read<AppProvider>();
|
||||
Navigator.pushNamed(context, '/movie-form', arguments: widget.movie).then((_) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import '../../utils/toast_util.dart';
|
||||
import '../../utils/image_path_helper.dart';
|
||||
import '../../widgets/genre_selector_page.dart';
|
||||
import '../../widgets/text_input_panel.dart';
|
||||
import '../../widgets/duration_picker.dart';
|
||||
|
||||
/// 从多值字段列表中提取去重排序的唯一值(供 compute 使用)
|
||||
List<String> _collectUnique(List<List<String>> lists) {
|
||||
@@ -54,6 +55,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
||||
DateTime? _releaseDate;
|
||||
DateTime? _watchDate;
|
||||
int _watchCount = 0;
|
||||
int _duration = 0;
|
||||
bool _isDownloading = false;
|
||||
|
||||
@override
|
||||
@@ -91,6 +93,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
||||
_releaseDate = movie.releaseDate;
|
||||
_watchDate = movie.watchDate;
|
||||
_watchCount = movie.watchCount;
|
||||
_duration = movie.duration;
|
||||
} else if (widget.initialStatus != null) {
|
||||
// 添加模式:使用传入的默认状态
|
||||
_status = widget.initialStatus!;
|
||||
@@ -615,6 +618,18 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
||||
),
|
||||
),
|
||||
|
||||
// 影视总时长
|
||||
SizedBox(
|
||||
width: (MediaQuery.of(context).size.width - 52) / 2,
|
||||
height: 90,
|
||||
child: _buildInfoCard(
|
||||
label: '影视总时长',
|
||||
value: _formatDuration(_duration),
|
||||
icon: Icons.schedule_outlined,
|
||||
onTap: () => _editDuration(),
|
||||
),
|
||||
),
|
||||
|
||||
// 第五行:剧情简介(独占一行)
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
@@ -773,6 +788,24 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 编辑影视总时长
|
||||
Future<void> _editDuration() async {
|
||||
final result = await DurationPicker.show(context: context, initialMinutes: _duration);
|
||||
if (result != null) {
|
||||
setState(() => _duration = result);
|
||||
}
|
||||
}
|
||||
|
||||
/// 格式化时长:120 -> "2小时0分",0 -> ""
|
||||
String _formatDuration(int minutes) {
|
||||
if (minutes <= 0) return '';
|
||||
final h = minutes ~/ 60;
|
||||
final m = minutes % 60;
|
||||
if (h > 0 && m > 0) return '$h小时$m分';
|
||||
if (h > 0) return '$h小时';
|
||||
return '$m分';
|
||||
}
|
||||
|
||||
/// 全屏编辑剧情简介
|
||||
Future<void> _editSummary() async {
|
||||
final result = await Navigator.push<String>(
|
||||
@@ -1397,6 +1430,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
||||
category: _category,
|
||||
watchDate: _watchDate,
|
||||
watchCount: _watchCount,
|
||||
duration: _duration,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
);
|
||||
@@ -1419,6 +1453,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
||||
category: _category,
|
||||
watchDate: _watchDate,
|
||||
watchCount: _watchCount,
|
||||
duration: _duration,
|
||||
updatedAt: now,
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user