Files
video/app/lib/screens/profile/edit_video_screen.dart
T
2026-10-15 20:36:59 +08:00

201 lines
6.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/network/api_exception.dart';
import '../../core/theme/app_theme.dart';
import '../../models/video_item.dart';
import '../../providers/profile_provider.dart';
import '../../providers/upload_provider.dart';
import '../../services/video_service.dart';
import '../feed/widgets/feed_overlays.dart';
/// 编辑作品(《03-接口文档》5.12 / 5.13,仅作者):
/// 可改标题/简介/分类/标签;删除为服务端软删除,二次确认后立即下架。
class EditVideoScreen extends ConsumerStatefulWidget {
const EditVideoScreen({super.key, required this.video});
final VideoItem video;
@override
ConsumerState<EditVideoScreen> createState() => _EditVideoScreenState();
}
class _EditVideoScreenState extends ConsumerState<EditVideoScreen> {
late final TextEditingController _titleCtrl;
late final TextEditingController _descCtrl;
late final TextEditingController _tagsCtrl;
late int _categoryId;
bool _submitting = false;
@override
void initState() {
super.initState();
final v = widget.video;
_titleCtrl = TextEditingController(text: v.title);
_descCtrl = TextEditingController(text: v.description);
_tagsCtrl = TextEditingController(text: v.tags.join(' '));
_categoryId = v.categoryId;
}
@override
void dispose() {
_titleCtrl.dispose();
_descCtrl.dispose();
_tagsCtrl.dispose();
super.dispose();
}
/// 空格/逗号/顿号分隔,去重,最多 5 个
List<String> _parseTags() => _tagsCtrl.text
.split(RegExp(r'[\s,,、]+'))
.map((e) => e.trim())
.where((e) => e.isNotEmpty)
.toSet()
.take(5)
.toList();
void _toast(String message) {
if (mounted) showTopToast(context, message);
}
Future<void> _save() async {
final title = _titleCtrl.text.trim();
if (title.isEmpty) {
_toast('请填写标题');
return;
}
setState(() => _submitting = true);
try {
await VideoService.updateVideo(
widget.video.videoId,
title: title,
description: _descCtrl.text.trim(),
categoryId: _categoryId,
tags: _parseTags(),
);
// 刷新我的作品列表,返回后展示最新信息
ref.invalidate(myVideosProvider);
_toast('已保存');
if (mounted) Navigator.of(context).maybePop();
} on ApiException catch (e) {
_toast(e.message);
} catch (_) {
_toast('网络异常,请稍后重试');
} finally {
if (mounted) setState(() => _submitting = false);
}
}
Future<void> _confirmDelete() async {
final confirmed = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
backgroundColor: AppColors.surface,
title: const Text('删除作品'),
content: const Text('删除后作品立即下架且不可恢复,确定删除?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: const Text('取消',
style: TextStyle(color: AppColors.textSecondary)),
),
TextButton(
onPressed: () => Navigator.pop(ctx, true),
child:
const Text('删除', style: TextStyle(color: AppColors.primary)),
),
],
),
);
if (confirmed != true) return;
setState(() => _submitting = true);
try {
await VideoService.deleteVideo(widget.video.videoId);
ref.invalidate(myVideosProvider);
_toast('已删除');
if (mounted) Navigator.of(context).maybePop();
} on ApiException catch (e) {
_toast(e.message);
} catch (_) {
_toast('网络异常,请稍后重试');
} finally {
if (mounted) setState(() => _submitting = false);
}
}
@override
Widget build(BuildContext context) {
final categories = ref.watch(uploadCategoriesProvider);
return Scaffold(
appBar: AppBar(title: const Text('编辑作品')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
TextField(
controller: _titleCtrl,
enabled: !_submitting,
maxLength: 100,
decoration: const InputDecoration(labelText: '标题'),
),
const SizedBox(height: 8),
TextField(
controller: _descCtrl,
enabled: !_submitting,
maxLength: 500,
maxLines: 3,
decoration: const InputDecoration(labelText: '简介'),
),
const SizedBox(height: 8),
const Text('分类', style: TextStyle(color: AppColors.textSecondary)),
const SizedBox(height: 8),
categories.when(
loading: () => const LinearProgressIndicator(),
error: (_, __) => const Text('分类加载失败',
style: TextStyle(color: AppColors.textWeak, fontSize: 12)),
data: (list) => Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final c in list)
ChoiceChip(
label: Text(c.name),
selected: _categoryId == c.categoryId,
onSelected: _submitting
? null
: (_) => setState(() => _categoryId = c.categoryId),
),
],
),
),
const SizedBox(height: 16),
TextField(
controller: _tagsCtrl,
enabled: !_submitting,
decoration: const InputDecoration(
labelText: '标签',
hintText: '空格分隔,最多 5 个',
),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: _submitting ? null : _save,
style: ElevatedButton.styleFrom(minimumSize: const Size(0, 48)),
child: const Text('保存'),
),
const SizedBox(height: 8),
OutlinedButton(
onPressed: _submitting ? null : _confirmDelete,
style: OutlinedButton.styleFrom(
minimumSize: const Size(0, 48),
side: const BorderSide(color: AppColors.primary),
),
child: const Text('删除作品',
style: TextStyle(color: AppColors.primary)),
),
],
),
);
}
}