mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: create/edit/del fav folder
This commit is contained in:
384
lib/pages/video/detail/introduction/widgets/create_fav_page.dart
Normal file
384
lib/pages/video/detail/introduction/widgets/create_fav_page.dart
Normal file
@@ -0,0 +1,384 @@
|
||||
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
||||
import 'package:PiliPalaX/http/msg.dart';
|
||||
import 'package:PiliPalaX/http/user.dart';
|
||||
import 'package:PiliPalaX/utils/utils.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
class CreateFavPage extends StatefulWidget {
|
||||
const CreateFavPage({super.key});
|
||||
|
||||
@override
|
||||
State<CreateFavPage> createState() => _CreateFavPageState();
|
||||
}
|
||||
|
||||
class _CreateFavPageState extends State<CreateFavPage> {
|
||||
dynamic _mediaId;
|
||||
late final _titleController = TextEditingController();
|
||||
late final _introController = TextEditingController();
|
||||
String? _cover;
|
||||
bool _isPublic = true;
|
||||
late final _imagePicker = ImagePicker();
|
||||
String? _errMsg;
|
||||
int? _attr;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_mediaId = Get.parameters['mediaId'];
|
||||
if (_mediaId != null) {
|
||||
_getFolderInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void _getFolderInfo() {
|
||||
UserHttp.folderInfo(mediaId: _mediaId).then((data) {
|
||||
if (data['status']) {
|
||||
_titleController.text = data['data']['title'];
|
||||
_introController.text = data['data']['intro'];
|
||||
_isPublic = Utils.isPublic(data['data']['attr']);
|
||||
_cover = data['data']['cover'];
|
||||
_attr = data['data']['attr'];
|
||||
} else {
|
||||
_errMsg = data['msg'];
|
||||
}
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_titleController.dispose();
|
||||
_introController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_mediaId != null ? '编辑' : '创建'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
if (_titleController.text.isEmpty) {
|
||||
SmartDialog.showToast('名称不能为空');
|
||||
return;
|
||||
}
|
||||
UserHttp.addOrEditFolder(
|
||||
isAdd: _mediaId == null,
|
||||
mediaId: _mediaId,
|
||||
title: _titleController.text,
|
||||
privacy: _isPublic ? 0 : 1,
|
||||
cover: _cover ?? '',
|
||||
intro: _introController.text,
|
||||
).then((data) {
|
||||
if (data['status']) {
|
||||
Get.back(result: data['data']);
|
||||
SmartDialog.showToast('${_mediaId != null ? '编辑' : '创建'}成功');
|
||||
} else {
|
||||
SmartDialog.showToast(data['msg']);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: const Text('完成'),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
),
|
||||
body: _mediaId != null
|
||||
? _titleController.text.isNotEmpty
|
||||
? _buildBody
|
||||
: _errMsg?.isNotEmpty == true
|
||||
? Center(
|
||||
child: CustomScrollView(
|
||||
shrinkWrap: true,
|
||||
slivers: [
|
||||
HttpError(
|
||||
errMsg: _errMsg,
|
||||
fn: _getFolderInfo,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Center(child: CircularProgressIndicator())
|
||||
: _buildBody,
|
||||
);
|
||||
}
|
||||
|
||||
void _pickImg() async {
|
||||
XFile? pickedFile = await _imagePicker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
imageQuality: 100,
|
||||
);
|
||||
if (pickedFile != null && mounted) {
|
||||
CroppedFile? croppedFile = await ImageCropper().cropImage(
|
||||
sourcePath: pickedFile.path,
|
||||
uiSettings: [
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: '裁剪',
|
||||
toolbarColor: Theme.of(context).colorScheme.primary,
|
||||
toolbarWidgetColor: Colors.white,
|
||||
aspectRatioPresets: [
|
||||
CropAspectRatioPreset.ratio16x9,
|
||||
],
|
||||
lockAspectRatio: true,
|
||||
hideBottomControls: true,
|
||||
initAspectRatio: CropAspectRatioPreset.ratio16x9,
|
||||
),
|
||||
IOSUiSettings(
|
||||
title: '裁剪',
|
||||
aspectRatioPresets: [
|
||||
CropAspectRatioPreset.ratio16x9,
|
||||
],
|
||||
aspectRatioLockEnabled: true,
|
||||
resetAspectRatioEnabled: false,
|
||||
aspectRatioPickerButtonHidden: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (croppedFile != null) {
|
||||
MsgHttp.uploadImage(
|
||||
path: croppedFile.path,
|
||||
bucket: 'medialist',
|
||||
dir: 'cover',
|
||||
).then((data) {
|
||||
if (data['status']) {
|
||||
_cover = data['data']['location'];
|
||||
setState(() {});
|
||||
} else {
|
||||
SmartDialog.showToast(data['msg']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic leadingStyle = TextStyle(fontSize: 14);
|
||||
|
||||
Widget get _buildBody => SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
if (_attr == null || !Utils.isDefault(_attr!)) ...[
|
||||
ListTile(
|
||||
tileColor: Theme.of(context).colorScheme.onInverseSurface,
|
||||
onTap: () {
|
||||
EasyThrottle.throttle(
|
||||
'imagePicker', const Duration(milliseconds: 500),
|
||||
() async {
|
||||
if (_cover?.isNotEmpty == true) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
contentPadding:
|
||||
const EdgeInsets.fromLTRB(0, 12, 0, 12),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
onTap: () {
|
||||
Get.back();
|
||||
_pickImg();
|
||||
},
|
||||
title: const Text(
|
||||
'替换封面',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
onTap: () {
|
||||
Get.back();
|
||||
_cover = null;
|
||||
setState(() {});
|
||||
},
|
||||
title: const Text(
|
||||
'移除封面',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
_pickImg();
|
||||
}
|
||||
});
|
||||
},
|
||||
leading: Text(
|
||||
'封面',
|
||||
style: leadingStyle,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (_cover?.isNotEmpty == true)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: Image.network(
|
||||
_cover!,
|
||||
height: constraints.maxHeight,
|
||||
width: constraints.maxHeight * 16 / 9,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Icon(
|
||||
Icons.keyboard_arrow_right,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
ListTile(
|
||||
tileColor: Theme.of(context).colorScheme.onInverseSurface,
|
||||
leading: Text.rich(
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 14,
|
||||
),
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '*',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: '名称',
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: TextField(
|
||||
readOnly: _attr != null && Utils.isDefault(_attr!),
|
||||
controller: _titleController,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: _attr != null && Utils.isDefault(_attr!)
|
||||
? Theme.of(context).colorScheme.outline
|
||||
: null,
|
||||
),
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(21),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: '名称',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
gapPadding: 0,
|
||||
),
|
||||
contentPadding: EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (_attr == null || !Utils.isDefault(_attr!)) ...[
|
||||
ListTile(
|
||||
tileColor: Theme.of(context).colorScheme.onInverseSurface,
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '简介',
|
||||
style: leadingStyle,
|
||||
),
|
||||
TextSpan(
|
||||
text: '*',
|
||||
style: TextStyle(color: Colors.transparent),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
minLines: 6,
|
||||
maxLines: 6,
|
||||
controller: _introController,
|
||||
style: TextStyle(fontSize: 14),
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(201),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: '简介',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
gapPadding: 0,
|
||||
),
|
||||
contentPadding: EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
ListTile(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isPublic = !_isPublic;
|
||||
});
|
||||
},
|
||||
tileColor: Theme.of(context).colorScheme.onInverseSurface,
|
||||
leading: Text(
|
||||
'公开',
|
||||
style: leadingStyle,
|
||||
),
|
||||
trailing: Transform.scale(
|
||||
alignment: Alignment.centerRight,
|
||||
scale: 0.8,
|
||||
child: Switch(
|
||||
value: _isPublic,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_isPublic = value;
|
||||
});
|
||||
}),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user