提交 77bf0eee authored 作者: maodou's avatar maodou

Merge remote-tracking branch 'origin/test' into test

# Conflicts: # lib/pages/user_info/view.dart # lib/pages/user_order/view.dart # lib/routes/routes.dart
...@@ -27,6 +27,8 @@ PODS: ...@@ -27,6 +27,8 @@ PODS:
- WechatOpenSDK-XCFramework (~> 2.0.2) - WechatOpenSDK-XCFramework (~> 2.0.2)
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- just_audio (0.0.1):
- Flutter
- OrderedSet (5.0.0) - OrderedSet (5.0.0)
- package_info_plus (0.4.5): - package_info_plus (0.4.5):
- Flutter - Flutter
...@@ -55,6 +57,7 @@ DEPENDENCIES: ...@@ -55,6 +57,7 @@ DEPENDENCIES:
- flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`)
- fluwx (from `.symlinks/plugins/fluwx/ios`) - fluwx (from `.symlinks/plugins/fluwx/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- just_audio (from `.symlinks/plugins/just_audio/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
...@@ -86,6 +89,8 @@ EXTERNAL SOURCES: ...@@ -86,6 +89,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/fluwx/ios" :path: ".symlinks/plugins/fluwx/ios"
image_picker_ios: image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios" :path: ".symlinks/plugins/image_picker_ios/ios"
just_audio:
:path: ".symlinks/plugins/just_audio/ios"
package_info_plus: package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios" :path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation: path_provider_foundation:
...@@ -108,6 +113,7 @@ SPEC CHECKSUMS: ...@@ -108,6 +113,7 @@ SPEC CHECKSUMS:
flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d
fluwx: 3c7b6df42f83d444d4538f3eaeae079f12d30c37 fluwx: 3c7b6df42f83d444d4538f3eaeae079f12d30c37
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425 image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
......
...@@ -165,6 +165,14 @@ abstract class CommonAPI { ...@@ -165,6 +165,14 @@ abstract class CommonAPI {
return result.data['filesUrl']; return result.data['filesUrl'];
} }
/// 11、获取OSS临时token
static Future <OssModel> oss() async {
final result = await HttpService.to.post(
'/v1/common/Alioss/getUploadToken',
);
if (result.data is! Map ) return OssModel();
return OssModel.fromJson(result.data);
}
} }
\ No newline at end of file
...@@ -276,7 +276,7 @@ abstract class LibraryAPI { ...@@ -276,7 +276,7 @@ abstract class LibraryAPI {
String types = '3', String types = '3',
required String content, required String content,
required String isOpen, required String isOpen,
String color= '', String color= '#FF0000',
required String positioning, required String positioning,
required String noteContent, required String noteContent,
}) async { }) async {
...@@ -288,7 +288,7 @@ abstract class LibraryAPI { ...@@ -288,7 +288,7 @@ abstract class LibraryAPI {
'types':types, 'types':types,
'content':content, 'content':content,
'is_open':isOpen, 'is_open':isOpen,
'color':content, 'color':color,
'positioning':positioning, 'positioning':positioning,
'note_content':noteContent 'note_content':noteContent
......
...@@ -215,7 +215,7 @@ abstract class MineAPI { ...@@ -215,7 +215,7 @@ abstract class MineAPI {
}); });
} }
/// 13、笔记详情列表 /// 13、讨论详情列表
/// ///
static Future<List<DiscussModel>> discussList( static Future<List<DiscussModel>> discussList(
{int page = 1, {int page = 1,
...@@ -228,7 +228,7 @@ abstract class MineAPI { ...@@ -228,7 +228,7 @@ abstract class MineAPI {
'page': page, 'page': page,
'page_size': limit, 'page_size': limit,
'book_id': bookId, 'book_id': bookId,
'types': types 'type': types
}, },
); );
if (result.data is! Map && result.data['list'] is! List) return []; if (result.data is! Map && result.data['list'] is! List) return [];
...@@ -413,5 +413,52 @@ abstract class MineAPI { ...@@ -413,5 +413,52 @@ abstract class MineAPI {
return false; return false;
} }
/// 25、删除笔记、高亮、划线的内容
static Future<bool> delNotes({
required String notesId,
required String bookId,
}) async {
final result = await HttpService.to.post(
'/v1/book/Information/delNotes',
params: {
'notes_id': notesId,
'book_id':bookId
},
);
if (result.data is Map && result.data['is_success'] == 1) {
return true;
}
return false;
}
/// 25、修改笔记、高亮、划线的内容
static Future<bool> editNotes({
required String content,
required String notesId,
required String bookId,
required String noteContent,
String isOpen = '0',
String positioning = '',
String color= '#FF0000',
}) async {
final result = await HttpService.to.post(
'/v1/book/Information/editNotes',
params: {
'notes_id': notesId,
'book_id':bookId,
'note_content':noteContent,
'positioning':positioning,
'is_open':isOpen,
'color': color,
'content':content
},
showLoading: true
);
if (result.data is Map && result.data['is_success'] == 1) {
return true;
}
return false;
}
} }
...@@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; ...@@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_book/routes/index.dart'; import 'package:flutter_book/routes/index.dart';
import 'package:flutter_book/store/index.dart'; import 'package:flutter_book/store/index.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_book/widgets/index.dart'; import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
...@@ -21,6 +22,10 @@ void main() { ...@@ -21,6 +22,10 @@ void main() {
Future.wait([ Future.wait([
UserStore.to.profile(), UserStore.to.profile(),
]).whenComplete(() { ]).whenComplete(() {
// String name = EncryptUtil.aesEncrypt('我是谁');
// print('2222222---------$name');
// final result = EncryptUtil.aesDecrypt(name);
// Console.log('解密--------------------------$result');
runApp(const MyApp()); runApp(const MyApp());
//FlutterNativeSplash.remove(); //FlutterNativeSplash.remove();
}); });
......
...@@ -2,7 +2,7 @@ library models; ...@@ -2,7 +2,7 @@ library models;
import 'package:just_audio/just_audio.dart' as just_audio;
part 'response.dart'; part 'response.dart';
part 'course.dart'; part 'course.dart';
......
...@@ -494,4 +494,41 @@ class BookDownloadModel { ...@@ -494,4 +494,41 @@ class BookDownloadModel {
} }
class OssModel {
OssModel({
this.securityToken,
this.accessKeyId,
this.accessKeySecret,
this.expiration,});
OssModel.fromJson(dynamic json) {
securityToken = json['SecurityToken'];
accessKeyId = json['AccessKeyId'];
accessKeySecret = json['AccessKeySecret'];
expiration = json['Expiration'];
}
String? securityToken;
String? accessKeyId;
String? accessKeySecret;
String? expiration;
OssModel copyWith({ String? securityToken,
String? accessKeyId,
String? accessKeySecret,
String? expiration,
}) => OssModel( securityToken: securityToken ?? this.securityToken,
accessKeyId: accessKeyId ?? this.accessKeyId,
accessKeySecret: accessKeySecret ?? this.accessKeySecret,
expiration: expiration ?? this.expiration,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['SecurityToken'] = securityToken;
map['AccessKeyId'] = accessKeyId;
map['AccessKeySecret'] = accessKeySecret;
map['Expiration'] = expiration;
return map;
}
}
...@@ -338,6 +338,7 @@ class UserInfoModel { ...@@ -338,6 +338,7 @@ class UserInfoModel {
/// 笔记详情模型 /// 笔记详情模型
class NoteModel { class NoteModel {
NoteModel({ NoteModel({
this.notesId,
this.types, this.types,
this.chapterId, this.chapterId,
this.content, this.content,
...@@ -348,33 +349,38 @@ class NoteModel { ...@@ -348,33 +349,38 @@ class NoteModel {
}); });
NoteModel.fromJson(dynamic json) { NoteModel.fromJson(dynamic json) {
notesId = json['notes_id'];
types = json['types']; types = json['types'];
chapterId = json['chapter_id']; chapterId = json['chapter_id'];
content = json['content']; content = json['content'];
positioning = json['positioning']; positioning = json['positioning'];
noteContent = json['note_content']; // noteContent = json['note_content'];
noteContent = json['note_content'] != null ? NoteContentModel.fromJson(json['note_content']) : null;
color = json['color']; color = json['color'];
chapterName = json['chapter_name']; chapterName = json['chapter_name'];
} }
num? notesId;
num? types; num? types;
num? chapterId; num? chapterId;
String? content; String? content;
String? positioning; String? positioning;
String? noteContent; // String? noteContent;
NoteContentModel? noteContent;
String? color; String? color;
String? chapterName; String? chapterName;
NoteModel copyWith({ NoteModel copyWith({
num? notesId,
num? types, num? types,
num? chapterId, num? chapterId,
String? content, String? content,
String? positioning, String? positioning,
String? noteContent, NoteContentModel? noteContent,
String? color, String? color,
String? chapterName, String? chapterName,
}) => }) =>
NoteModel( NoteModel(
notesId: notesId ?? this.notesId,
types: types ?? this.types, types: types ?? this.types,
chapterId: chapterId ?? this.chapterId, chapterId: chapterId ?? this.chapterId,
content: content ?? this.content, content: content ?? this.content,
...@@ -386,17 +392,114 @@ class NoteModel { ...@@ -386,17 +392,114 @@ class NoteModel {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final map = <String, dynamic>{}; final map = <String, dynamic>{};
map['notes_id'] = notesId;
map['types'] = types; map['types'] = types;
map['chapter_id'] = chapterId; map['chapter_id'] = chapterId;
map['content'] = content; map['content'] = content;
map['positioning'] = positioning; map['positioning'] = positioning;
map['note_content'] = noteContent; if (noteContent != null) {
map['note_content'] = noteContent?.toJson();
}
map['color'] = color; map['color'] = color;
map['chapter_name'] = chapterName; map['chapter_name'] = chapterName;
return map; return map;
} }
} }
class MediaModel {
MediaModel({
this.privacyStatus,
this.content,
this.id,
this.duration = '',
this.path = '',
this.currentDuration = '0:00:00'
});
MediaModel.fromJson(dynamic json) {
privacyStatus = json['privacy_status'];
content = json['content'];
id = json['id'];
duration = '';
path = '';
currentDuration = '0:00:00';
}
num? privacyStatus;
String? content;
late String path;
num? id;
late String duration;
late String currentDuration;
MediaModel copyWith({
num? privacyStatus,
String? content,
num? id,
String path = '',
}) => MediaModel(
privacyStatus: privacyStatus ?? this.privacyStatus,
content: content ?? this.content,
id: id ?? this.id,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['privacy_status'] = privacyStatus;
map['content'] = content;
map['id'] = id;
return map;
}
}
class NoteContentModel {
NoteContentModel({
this.text,
this.audio,
this.image,});
NoteContentModel.fromJson(dynamic json) {
text = json['text'] != null ? MediaModel.fromJson(json['text']) : null;
if (json['audio'] != null) {
audio = [];
json['audio'].forEach((v) {
audio?.add(MediaModel.fromJson(v));
});
}
if (json['image'] != null) {
image = [];
json['image'].forEach((v) {
image?.add(MediaModel.fromJson(v));
});
}
}
MediaModel? text;
List<MediaModel>? audio;
List<MediaModel>? image;
NoteContentModel copyWith({ MediaModel? text,
List<MediaModel>? audio,
List<MediaModel>? image,
}) => NoteContentModel( text: text ?? this.text,
audio: audio ?? this.audio,
image: image ?? this.image,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
if (text != null) {
map['text'] = text?.toJson();
}
if (audio != null) {
map['audio'] = audio?.map((v) => v.toJson()).toList();
}
if (image != null) {
map['image'] = image?.map((v) => v.toJson()).toList();
}
return map;
}
}
/// 讨论模型 /// 讨论模型
class DiscussModel { class DiscussModel {
DiscussModel({ DiscussModel({
...@@ -662,3 +765,4 @@ class CoinModel { ...@@ -662,3 +765,4 @@ class CoinModel {
} }
} }
...@@ -4,8 +4,10 @@ import 'package:flutter/material.dart'; ...@@ -4,8 +4,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart'; import 'package:flutter_book/utils/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:go_router/go_router.dart';
import '../../models/index.dart'; import '../../models/index.dart';
import '../../routes/index.dart';
part 'view.dart'; part 'view.dart';
part 'widgets/item.dart'; part 'widgets/item.dart';
\ No newline at end of file
...@@ -3,9 +3,13 @@ part of book_category; ...@@ -3,9 +3,13 @@ part of book_category;
class BookCategoryPage extends StatefulWidget { class BookCategoryPage extends StatefulWidget {
final List <ChapterModel> chapters; final List <ChapterModel> chapters;
final String bookId;
final BookDetailModel bookDetails;
const BookCategoryPage({ const BookCategoryPage({
Key? key, Key? key,
required this.chapters required this.chapters,
required this.bookId,
required this.bookDetails
}) : super(key: key); }) : super(key: key);
@override @override
...@@ -20,7 +24,7 @@ class _BookCategoryPageState extends State<BookCategoryPage> { ...@@ -20,7 +24,7 @@ class _BookCategoryPageState extends State<BookCategoryPage> {
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemBuilder:(BuildContext context, int index){ itemBuilder:(BuildContext context, int index){
return BuildItem(model: widget.chapters[index],); return BuildItem(model: widget.chapters[index],bookId: widget.bookId,bookDetails: widget.bookDetails,);
}, },
itemCount: widget.chapters.length, itemCount: widget.chapters.length,
), ),
......
...@@ -2,9 +2,13 @@ part of book_category; ...@@ -2,9 +2,13 @@ part of book_category;
class BuildItem extends StatefulWidget { class BuildItem extends StatefulWidget {
final ChapterModel model; final ChapterModel model;
final String bookId;
final BookDetailModel bookDetails;
const BuildItem({ const BuildItem({
Key? key, Key? key,
required this.model required this.model,
required this.bookId,
required this.bookDetails
}) : super(key: key); }) : super(key: key);
@override @override
...@@ -19,9 +23,13 @@ class _BuildItemState extends State<BuildItem> { ...@@ -19,9 +23,13 @@ class _BuildItemState extends State<BuildItem> {
/// 章节名称容器 /// 章节名称容器
GestureDetector( GestureDetector(
onTap: (){ onTap: (){
setState(() { // 如果章下面没有节 点击才会跳转
widget.model.selected = !widget.model.selected; if (widget.model.children!.isEmpty){
}); context.pushNamed(Routes.web,queryParameters: {'book_id': widget.bookDetails.bookId.toString(),'chapter_id': widget.model.id.toString(),'chapter_name':widget.model.name.toString()},extra: widget.bookDetails);
}
// setState(() {
// widget.model.selected = !widget.model.selected;
// });
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 15.w), padding: EdgeInsets.symmetric(horizontal: 15.w),
...@@ -49,7 +57,19 @@ class _BuildItemState extends State<BuildItem> { ...@@ -49,7 +57,19 @@ class _BuildItemState extends State<BuildItem> {
), ),
Transform.rotate( Transform.rotate(
angle: widget.model.selected?0:-90 * (3.141592653589793 / 180), angle: widget.model.selected?0:-90 * (3.141592653589793 / 180),
child: Image.asset('assets/images/down.png') child: GestureDetector(
onTap: (){
setState(() {
widget.model.selected = !widget.model.selected;
});
},
child: Container(
width: 20,
height: 20,
// color: Colors.red,
child: Image.asset('assets/images/down.png')
),
)
) )
], ],
...@@ -63,7 +83,13 @@ class _BuildItemState extends State<BuildItem> { ...@@ -63,7 +83,13 @@ class _BuildItemState extends State<BuildItem> {
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index){ itemBuilder: (BuildContext context, int index){
return _buildSection(widget.model.children![index]); ChapterModel model = widget.model.children![index];
return GestureDetector(
onTap: (){
context.pushNamed(Routes.web,queryParameters: {'book_id': widget.bookDetails.bookId.toString(),'chapter_id':model.id.toString(),'chapter_name':model.name.toString()},extra: widget.bookDetails);
},
child: _buildSection(model)
);
}, },
itemCount: widget.model.children!.length, itemCount: widget.model.children!.length,
) )
......
...@@ -45,6 +45,7 @@ class BookInfoPage extends StatelessWidget { ...@@ -45,6 +45,7 @@ class BookInfoPage extends StatelessWidget {
Container( Container(
margin: EdgeInsets.symmetric(vertical: 10.w), margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Column( Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
...@@ -71,17 +72,24 @@ class BookInfoPage extends StatelessWidget { ...@@ -71,17 +72,24 @@ class BookInfoPage extends StatelessWidget {
height: 90, height: 90,
// color: Colors.green, // color: Colors.green,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: List.generate(model.ratingList!.length, (index){ mainAxisAlignment: MainAxisAlignment.center,
return _buildProgrss(5- index.toDouble(), model.ratingList![index].toDouble()/model.ratingCount!); children: [
}).toList() Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: List.generate(model.ratingList!.length, (index){
return _buildProgrss(5- index.toDouble(), model.ratingList![index].toDouble()/model.ratingCount!);
}).toList()
),
Text('${model.ratingCount}个评分',style: TextStyle(fontSize: 9.w,height: 1.4,color: Colours.c9),)
],
), ),
), ),
) ),
], ],
), ),
) ),
], ],
), ),
Container(height: 1,width: double.infinity,color: Colours.cF2,), Container(height: 1,width: double.infinity,color: Colours.cF2,),
...@@ -90,19 +98,26 @@ class BookInfoPage extends StatelessWidget { ...@@ -90,19 +98,26 @@ class BookInfoPage extends StatelessWidget {
Column( Column(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: ['书名','作者','分类','出品方','出版社','上架时间'].map((item){ children: [
{'name':'书名','value':model.bookName},
{'name':'作者','value':model.authors},
{'name':'分类','value':model.categoryName},
{'name':'出品方','value':model.producersName},
{'name':'出版社','value':model.pressName},
{'name':'上架时间','value':Tools.dateFromMS(model.onsaleTime!.toInt(),pattern:'yyyy年MM月dd日' )},
].map((item){
return Row( return Row(
children: [ children: [
Container( Container(
// color: Colors.cyan, // color: Colors.cyan,
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
width: 50, width: 50,
child: Text(item,style: const TextStyle(fontSize: 11,height: 2.1,color: Colours.c3),), child: Text(item['name'].toString(),style: const TextStyle(fontSize: 11,height: 2.1,color: Colours.c3),),
), ),
Gaps.hGaps20, Gaps.hGaps20,
Container( Container(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text(item,style: const TextStyle(fontSize: 11,height: 2.1,color: Colours.c9),textAlign: TextAlign.end,), child: Text(item['value'].toString(),style: const TextStyle(fontSize: 11,height: 2.1,color: Colours.c9),textAlign: TextAlign.end,),
), ),
], ],
); );
......
...@@ -60,7 +60,10 @@ class BookPayController extends GetxController { ...@@ -60,7 +60,10 @@ class BookPayController extends GetxController {
/// 支付方式 默认第一个 /// 支付方式 默认第一个
late PayModel _payModel = pays.first; late PayModel _payModel = pays.first;
PayModel get payModel => _payModel; PayModel get payModel {
_payModel.selected = true;
return _payModel;
}
/// 选择积分状态 /// 选择积分状态
void show(){ void show(){
......
...@@ -113,7 +113,7 @@ class _ChangePwdPageState extends State<ChangePwdPage> { ...@@ -113,7 +113,7 @@ class _ChangePwdPageState extends State<ChangePwdPage> {
), ),
Container( Container(
margin: EdgeInsets.only(left: 5.w,top: 5.w), margin: EdgeInsets.only(left: 5.w,top: 5.w),
child: Text('密码必须是6-20个英文字母、数字或符号',style: TextStyle(fontSize: 10.w,color: Colours.c9),), child: Text('密码必须是8-20个英文字母、数字或符号(除空格)',style: TextStyle(fontSize: 10.w,color: Colours.c9),),
), ),
Gaps.vGaps40, Gaps.vGaps40,
CustomGradientButton( CustomGradientButton(
......
...@@ -86,15 +86,15 @@ class CourseController extends GetxController { ...@@ -86,15 +86,15 @@ class CourseController extends GetxController {
} }
} }
void logout(BuildContext context) async { // void logout(BuildContext context) async {
final result = await AccountAPI.logout(); // final result = await AccountAPI.logout();
if (result){ // if (result){
CustomToast.success('退出成功'); // CustomToast.success('退出成功');
await UserStore.to.logout(); // await UserStore.to.logout();
if(context.mounted){ // if(context.mounted){
context.goNamed(Routes.main); // context.goNamed(Routes.main);
} // }
} // }
} // }
} }
\ No newline at end of file
...@@ -42,7 +42,6 @@ class _CoursePageState extends State<CoursePage> { ...@@ -42,7 +42,6 @@ class _CoursePageState extends State<CoursePage> {
if (result == true) { if (result == true) {
controller.getNums(); controller.getNums();
} }
// controller.logout(context);
}, },
child: badges.Badge( child: badges.Badge(
position: badges.BadgePosition.topEnd(top: -5.w, end: 0), position: badges.BadgePosition.topEnd(top: -5.w, end: 0),
......
...@@ -38,10 +38,10 @@ class BuildBanner extends StatelessWidget { ...@@ -38,10 +38,10 @@ class BuildBanner extends StatelessWidget {
// ), // ),
), ),
//CustomImage.network(item.pic??'',)
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(8.w), borderRadius: BorderRadius.circular(8.w),
child: Image.network(item.pic??'',) child: CustomImage.network(url: item.pic??'')
), ),
// child: CustomImage.asset( // child: CustomImage.asset(
// url: 'assets/images/banner.png', // url: 'assets/images/banner.png',
......
part of web; part of web;
class DiscussController extends GetxController { class DiscussController extends GetxController {
final BookDetailModel bookDetailModel;
// 当前的章节id
final String chapterId;
DiscussController({required this.bookDetailModel,required this.chapterId});
List<DiscussModel> discuss = []; List<DiscussModel> discuss = [];
final EasyRefreshController refreshController = EasyRefreshController( final EasyRefreshController refreshController = EasyRefreshController(
controlFinishLoad: true, controlFinishLoad: true,
...@@ -32,8 +36,8 @@ class DiscussController extends GetxController { ...@@ -32,8 +36,8 @@ class DiscussController extends GetxController {
final result = await LibraryAPI.discussList( final result = await LibraryAPI.discussList(
page: _page, page: _page,
limit: _limit, limit: _limit,
bookId: '110', bookId: bookDetailModel.bookId.toString(),
chapterId: '1' chapterId: chapterId
); );
// 如果是刷新 清理数据 // 如果是刷新 清理数据
if (isRefresh) discuss.clear(); if (isRefresh) discuss.clear();
......
...@@ -2,17 +2,29 @@ library web; ...@@ -2,17 +2,29 @@ library web;
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:archive/archive.dart';
import 'package:audio_session/audio_session.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:easy_refresh/easy_refresh.dart'; import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_book/utils/index.dart'; import 'package:flutter_book/utils/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_sound/public/flutter_sound_recorder.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_platform_interface.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_recorder_platform_interface.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:just_audio/just_audio.dart' as just_audio;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import '../../apis/index.dart'; import '../../apis/index.dart';
import '../../models/index.dart'; import '../../models/index.dart';
...@@ -31,4 +43,5 @@ part 'widgets/note.dart'; ...@@ -31,4 +43,5 @@ part 'widgets/note.dart';
part 'widgets/book.dart'; part 'widgets/book.dart';
part 'note_controller.dart'; part 'note_controller.dart';
part 'discuss_controller.dart'; part 'discuss_controller.dart';
part 'widgets/input_discuss.dart'; part 'widgets/input_discuss.dart';
\ No newline at end of file part 'widgets/item.dart';
\ No newline at end of file
part of web; part of web;
class NoteController extends GetxController { class NoteController extends GetxController {
final BookDetailModel bookDetailModel;
// 当前的章节id
final String chapterId;
NoteController({required this.bookDetailModel,required this.chapterId});
List<NoteModel> notes = []; List<NoteModel> notes = [];
final EasyRefreshController refreshController = EasyRefreshController( final EasyRefreshController refreshController = EasyRefreshController(
...@@ -34,8 +37,8 @@ class NoteController extends GetxController { ...@@ -34,8 +37,8 @@ class NoteController extends GetxController {
final result = await LibraryAPI.noteList( final result = await LibraryAPI.noteList(
page: _page, page: _page,
limit: _limit, limit: _limit,
bookId: '110', bookId: bookDetailModel.bookId.toString(),
chapterId: '1' chapterId: chapterId
); );
// 如果是刷新 清理数据 // 如果是刷新 清理数据
if (isRefresh) notes.clear(); if (isRefresh) notes.clear();
......
...@@ -4,11 +4,13 @@ class ReadPage extends StatefulWidget { ...@@ -4,11 +4,13 @@ class ReadPage extends StatefulWidget {
final String bookId; final String bookId;
final String chapterId; final String chapterId;
final String chapterName; final String chapterName;
final BookDetailModel bookDetailModel;
const ReadPage({ const ReadPage({
Key? key, Key? key,
required this.bookId, required this.bookId,
required this.chapterId, required this.chapterId,
required this.chapterName, required this.chapterName,
required this.bookDetailModel
}) : super(key: key); }) : super(key: key);
@override @override
...@@ -24,25 +26,25 @@ class _ReadPageState extends State<ReadPage> { ...@@ -24,25 +26,25 @@ class _ReadPageState extends State<ReadPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<ReadController>( return GetBuilder<ReadController>(
init: ReadController(bookId: widget.bookId, chapterId: widget.chapterId,chapterName: widget.chapterName), init: ReadController(bookId: widget.bookId, chapterId: widget.chapterId,chapterName: widget.chapterName,bookDetailModel: widget.bookDetailModel),
builder: (readController) => Scaffold( builder: (readController) => Scaffold(
appBar: CustomAppBar( appBar: CustomAppBar(
title: Text(widget.chapterName), title: Text(readController.chapterName),
centerTitle: false, centerTitle: false,
actions: [ actions: [
GestureDetector( GestureDetector(
onTap: () { onTap: () {
readController.getBookDown(); readController.getBookDown();
}, },
child: Text( child: Text(
'离线阅读', '离线阅读',
style: TextStyle( style: TextStyle(
fontSize: 14.w, color: Colours.c3), fontSize: 14.w, color: Colours.c3),
)) ))
], ],
), ),
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
floatingActionButton: readController.show?GestureDetector( floatingActionButton: readController.show&& !readController.toolModel.selected?GestureDetector(
onTap: (){ onTap: (){
readController.setShowChat(true); readController.setShowChat(true);
readController.noteTitle = '你好你问你你等您第五年对哦in我ID呢哦win地哦为内地那打卡你打困哪'; readController.noteTitle = '你好你问你你等您第五年对哦in我ID呢哦win地哦为内地那打卡你打困哪';
...@@ -56,25 +58,33 @@ class _ReadPageState extends State<ReadPage> { ...@@ -56,25 +58,33 @@ class _ReadPageState extends State<ReadPage> {
color: Colors.white, color: Colors.white,
child: Stack( child: Stack(
children: [ children: [
Container( // Container(
height: 40, // height: 40,
width: double.infinity, // width: double.infinity,
color: Colors.lightBlue, // color: Colors.lightBlue,
), // ),
InAppWebView( InAppWebView(
initialUrlRequest: URLRequest( initialUrlRequest: URLRequest(
url: Uri.parse('http://192.168.11.46:9200/read.html'), url: Uri.parse('http://150.158.138.40:9200/read.html'),
), ),
contextMenu: ContextMenu( contextMenu: ContextMenu(
options: ContextMenuOptions(hideDefaultSystemContextMenuItems: true), options: ContextMenuOptions(hideDefaultSystemContextMenuItems: true),
), ),
onWebViewCreated: (InAppWebViewController controller){
readController.webViewController = controller;
},
onConsoleMessage: (controller, consoleMessage) {
// 接收从 WebView 发送的消息
print("Received message from WebView: ${consoleMessage.message}");
},
onLoadStop: (controller, url) { onLoadStop: (controller, url) {
// flutter 主动给 js 传参数 // flutter 主动给 js 传参数
Map<String, dynamic> param = { Map<String, dynamic> param = {
'book_id': 110, 'book_id': readController.bookId,
'chapter_id': 1, 'chapter_id': readController.chapterId,
'token':UserStore.to.token 'token':UserStore.to.token
}; };
Console.log('param--------------------------------$param');
controller.evaluateJavascript(source: 'callbackInFlutterComponent("$param");'); controller.evaluateJavascript(source: 'callbackInFlutterComponent("$param");');
// 添加单击事件 // 添加单击事件
...@@ -232,57 +242,56 @@ class _ReadPageState extends State<ReadPage> { ...@@ -232,57 +242,56 @@ class _ReadPageState extends State<ReadPage> {
); );
} }
/// 目录、评论、笔记
Widget detail(ReadController controller,ToolModel model){
if(model.tag == 0){
return ReadCategoryPage(controller: controller,
onTap: (){
controller.chooseTool(model);
},
onTapChapter: (ChapterModel chapterModel){
print('-----------选择的章节-------------${chapterModel.name}--------');
// 配置选择的章节
controller.selectChapter(chapterModel);
// 取消选中 tool
controller.chooseTool(model);
// 选择了新的章节 刷新 webview
controller.webViewController.reload();
},
);
}
else if(model.tag == 1){
return ReadNotePage(onTap: (){
controller.chooseTool(model);
},bookDetailModel: controller.bookDetailModel,chapterId: controller.chapterId,);
}
else if(model.tag == 2){
return ReadDiscussPage(onTap: (){
controller.chooseTool(model);
},bookDetailModel: controller.bookDetailModel,chapterId:controller.chapterId,);
}
return const SizedBox();
}
/// 目录、评论、笔记 背景
Widget _showContent(ReadController controller,ToolModel model) { Widget _showContent(ReadController controller,ToolModel model) {
Console.log('++++++++++++++++++++++++${model.tag}'); Console.log('++++++++++++++++++++++++${model.tag}');
if (controller.show){ if (controller.show){
if (model.tag == 0){ if(model.selected){
return model.selected? Container( return Container(
color: const Color(0xFF000000).withOpacity(0.5), color: const Color(0xFF000000).withOpacity(0.5),
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.2), padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.2),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.only(topRight: Radius.circular(8.w),topLeft: Radius.circular(8.w)), borderRadius: BorderRadius.only(topRight: Radius.circular(8.w),topLeft: Radius.circular(8.w)),
child: Container( child: Container(
color: Colors.white, color: Colors.white,
child: ReadCategoryPage(controller: controller,onTap: (){ child: detail(controller, model)
controller.chooseTool(model);
},),
), ),
), ),
// child: ReadCategoryPage(), // child: ReadCategoryPage(),
):const SizedBox(); );
}
else if (model.tag == 1){
return model.selected? Container(
color: const Color(0xFF000000).withOpacity(0.5),
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.2),
child: ClipRRect(
borderRadius: BorderRadius.only(topRight: Radius.circular(8.w),topLeft: Radius.circular(8.w)),
child: Container(
color: Colors.white,
child: ReadNotePage(onTap: (){
controller.chooseTool(model);
},),
),
),
// child: ReadCategoryPage(),
):const SizedBox();
} }
else if (model.tag == 2){ else{
return model.selected? Container( return const SizedBox();
color: const Color(0xFF000000).withOpacity(0.5),
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.2),
child: ClipRRect(
borderRadius: BorderRadius.only(topRight: Radius.circular(8.w),topLeft: Radius.circular(8.w)),
child: Container(
color: Colors.white,
child: ReadDiscussPage(onTap: (){
controller.chooseTool(model);
},),
),
),
// child: ReadCategoryPage(),
):const SizedBox();
} }
} }
return const SizedBox(); return const SizedBox();
......
part of web; part of web;
class BuildBook extends StatelessWidget { class BuildBook extends StatelessWidget {
const BuildBook({Key? key}) : super(key: key); final BookDetailModel bookDetailModel;
const BuildBook({
Key? key,
required this.bookDetailModel
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -18,7 +22,7 @@ class BuildBook extends StatelessWidget { ...@@ -18,7 +22,7 @@ class BuildBook extends StatelessWidget {
CustomCard( CustomCard(
width: 72.w, width: 72.w,
height: 86.w, height: 86.w,
url: '', url: bookDetailModel.img??'',
), ),
Container( Container(
height: 87.w, height: 87.w,
...@@ -31,8 +35,8 @@ class BuildBook extends StatelessWidget { ...@@ -31,8 +35,8 @@ class BuildBook extends StatelessWidget {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('书名',style: TextStyle(fontSize: 14.w,height: 1.5,fontWeight: Fonts.medium,color: Colours.c3),), Text(bookDetailModel.bookName??'',style: TextStyle(fontSize: 14.w,height: 1.5,fontWeight: Fonts.medium,color: Colours.c3),),
Text('作者',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c6),), Text(bookDetailModel.authors??'',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c6),),
], ],
), ),
], ],
......
...@@ -3,10 +3,12 @@ part of web; ...@@ -3,10 +3,12 @@ part of web;
class ReadCategoryPage extends StatefulWidget { class ReadCategoryPage extends StatefulWidget {
final ReadController controller; final ReadController controller;
final void Function()? onTap; final void Function()? onTap;
final Function(ChapterModel chapterModel) onTapChapter;
const ReadCategoryPage({ const ReadCategoryPage({
Key? key, Key? key,
required this.controller, required this.controller,
required this.onTap, required this.onTap,
required this.onTapChapter
}) : super(key: key); }) : super(key: key);
@override @override
...@@ -60,15 +62,18 @@ class _ReadCategoryPageState extends State<ReadCategoryPage> { ...@@ -60,15 +62,18 @@ class _ReadCategoryPageState extends State<ReadCategoryPage> {
), ),
), ),
), ),
BuildBook(), BuildBook(bookDetailModel: widget.controller.bookDetailModel,),
// Expanded( Expanded(
// child: ListView.builder( child: ListView.builder(
// itemBuilder:(BuildContext context, int index){ itemBuilder:(BuildContext context, int index){
// return BuildItem(model: widget.controller.chapters[index],); return BuildItem(model: widget.controller.chapters[index],onTapChapter:(ChapterModel chapterModel){
// }, widget.onTapChapter(chapterModel);
// itemCount: widget.controller.chapters.length, widget.onTap;
// ), },);
// ), },
itemCount: widget.controller.chapters.length,
),
),
], ],
), ),
); );
......
...@@ -2,9 +2,14 @@ part of web; ...@@ -2,9 +2,14 @@ part of web;
class ReadDiscussPage extends StatefulWidget { class ReadDiscussPage extends StatefulWidget {
final void Function()? onTap; final void Function()? onTap;
final BookDetailModel bookDetailModel;
// 当前的章节id
final String chapterId;
const ReadDiscussPage({ const ReadDiscussPage({
Key? key, Key? key,
required this.onTap, required this.onTap,
required this.bookDetailModel,
required this.chapterId
}) : super(key: key); }) : super(key: key);
@override @override
...@@ -16,7 +21,7 @@ class _ReadDiscussPageState extends State<ReadDiscussPage> { ...@@ -16,7 +21,7 @@ class _ReadDiscussPageState extends State<ReadDiscussPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<DiscussController>( return GetBuilder<DiscussController>(
init: DiscussController(), init: DiscussController(bookDetailModel: widget.bookDetailModel,chapterId: widget.chapterId),
builder:(controller) => Scaffold( builder:(controller) => Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
body: Column( body: Column(
...@@ -58,7 +63,7 @@ class _ReadDiscussPageState extends State<ReadDiscussPage> { ...@@ -58,7 +63,7 @@ class _ReadDiscussPageState extends State<ReadDiscussPage> {
), ),
), ),
), ),
BuildBook(), BuildBook(bookDetailModel: widget.bookDetailModel,),
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemBuilder: (BuildContext context,int index){ itemBuilder: (BuildContext context,int index){
......
...@@ -106,7 +106,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> { ...@@ -106,7 +106,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
onTap: (){ onTap: (){
widget.controller.delDiscussInputImages(widget.controller.discussInputImages[index]); widget.controller.delDiscussInputImages(widget.controller.discussInputImages[index]);
}, },
child: Image.asset('assets/images/del_close.png',width: 12.w,height: 12.w,) child: Image.asset('assets/images/media_del.png',width: 12.w,height: 12.w,)
) )
) )
], ],
...@@ -145,7 +145,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> { ...@@ -145,7 +145,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
), ),
); );
}, },
itemCount: 0, itemCount: widget.controller.discussInputAudios.length,
), ),
), ),
) )
...@@ -174,9 +174,15 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> { ...@@ -174,9 +174,15 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
Gaps.hGaps10, Gaps.hGaps10,
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
if(widget.controller.startRecording){
widget.controller.stopRecorder();
}
else {
widget.controller.record();
}
}, },
child: Image.asset('assets/images/read_add_audio.png') child: Image.asset(widget.controller.startRecording?'assets/images/stop.png':'assets/images/read_add_audio.png')
), ),
widget.controller.chatType ==0?const SizedBox():GestureDetector( widget.controller.chatType ==0?const SizedBox():GestureDetector(
onTap: (){ onTap: (){
......
part of web;
class BuildItem extends StatefulWidget {
final ChapterModel model;
final Function(ChapterModel chapterModel) onTapChapter;
const BuildItem({
Key? key,
required this.model,
required this.onTapChapter
}) : super(key: key);
@override
State<BuildItem> createState() => _BuildItemState();
}
class _BuildItemState extends State<BuildItem> {
@override
Widget build(BuildContext context) {
return Column(
children: [
/// 章节名称容器
GestureDetector(
onTap: (){
if(widget.model.children!.isEmpty){
widget.onTapChapter(widget.model);
}
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 15.w),
height: 30.w,
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(widget.model.name??'',style: TextStyle(fontSize: 14.w,color: widget.model.seen ==0? Colours.c3:Colours.c9,fontWeight: Fonts.medium,height: 2),),
Gaps.hGaps5,
widget.model.isReading == 1? Container(
height: 17,
width: 17,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.5.w),
border: Border.all(width:1,color: AppTheme.primary)
),
child: Text('试',style: TextStyle(fontSize: 12.w,color: AppTheme.primary),),
):const SizedBox(),
],
),
Transform.rotate(
angle: widget.model.selected?0:-90 * (3.141592653589793 / 180),
child: GestureDetector(
onTap: (){
setState(() {
widget.model.selected = !widget.model.selected;
});
},
child: SizedBox(
width: 20,
height: 20,
child: Image.asset('assets/images/down.png')
),
)
)
],
)
),
),
/// 节的名称容器
Visibility(
visible: widget.model.selected,
child: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index){
ChapterModel model = widget.model.children![index];
return GestureDetector(
onTap: (){
widget.onTapChapter(model);
},
child: _buildSection(model)
);
},
itemCount: widget.model.children!.length,
)
)
],
);
}
Widget _buildSection(ChapterModel model){
return Container(
color: Colors.white,
padding: const EdgeInsets.only(left: 60),
child: Text(model.name??'',style:TextStyle(fontSize: 12,color: model.seen ==0? Colours.c3:Colours.c9,height: 2),),
);
}
}
part of web; part of web;
class ReadNotePage extends StatefulWidget { class ReadNotePage extends StatefulWidget {
final BookDetailModel bookDetailModel;
// 当前的章节id
final String chapterId;
final void Function()? onTap; final void Function()? onTap;
const ReadNotePage({ const ReadNotePage({
Key? key, Key? key,
required this.onTap, required this.onTap,
required this.bookDetailModel,
required this.chapterId
}) : super(key: key); }) : super(key: key);
@override @override
...@@ -16,7 +21,7 @@ class _ReadNotePageState extends State<ReadNotePage> { ...@@ -16,7 +21,7 @@ class _ReadNotePageState extends State<ReadNotePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetBuilder<NoteController>( return GetBuilder<NoteController>(
init: NoteController(), init: NoteController(bookDetailModel: widget.bookDetailModel,chapterId: widget.chapterId),
builder:(controller) =>Column( builder:(controller) =>Column(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
...@@ -56,7 +61,7 @@ class _ReadNotePageState extends State<ReadNotePage> { ...@@ -56,7 +61,7 @@ class _ReadNotePageState extends State<ReadNotePage> {
), ),
), ),
), ),
BuildBook(), BuildBook(bookDetailModel: widget.bookDetailModel,),
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemBuilder: (BuildContext context,int index){ itemBuilder: (BuildContext context,int index){
......
...@@ -33,28 +33,29 @@ class _SplashPageState extends State<SplashPage> { ...@@ -33,28 +33,29 @@ class _SplashPageState extends State<SplashPage> {
return Scaffold( return Scaffold(
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
appBar: AppBar(), appBar: AppBar(),
body: const Column( body: Image.asset('assets/images/splash.png',fit: BoxFit.cover,)
crossAxisAlignment: CrossAxisAlignment.stretch, // const Column(
mainAxisAlignment: MainAxisAlignment.center, // crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ // mainAxisAlignment: MainAxisAlignment.center,
Expanded( // children: [
flex: 5, // Expanded(
child: Center( // flex: 5,
child: CustomEmpty( // child: Center(
icon: CustomImage.asset( // child: CustomEmpty(
url: 'assets/images/logo.png', // icon: CustomImage.asset(
fit: BoxFit.contain, // url: 'assets/images/logo.png',
), // fit: BoxFit.contain,
title: Text('紫荆数智学堂'), // ),
), // title: Text('紫荆数智学堂'),
) // ),
), // )
// Expanded( // ),
// flex: 2, // // Expanded(
// child: Center(child: CupertinoActivityIndicator(),), // // flex: 2,
// ) // // child: Center(child: CupertinoActivityIndicator(),),
], // // )
), // ],
// ),
); );
} }
} }
...@@ -40,8 +40,8 @@ class _AboutPageState extends State<AboutPage> { ...@@ -40,8 +40,8 @@ class _AboutPageState extends State<AboutPage> {
margin: EdgeInsets.only(top: 55.w), margin: EdgeInsets.only(top: 55.w),
height: 60.w, height: 60.w,
width: 60.w, width: 60.w,
color: Colors.cyan, // color: Colors.cyan,
child: const CustomImage.asset(url: 'assets/images/banner.png'), child: const CustomImage.asset(url: 'assets/images/icon.png'),
), ),
Gaps.vGaps15, Gaps.vGaps15,
Text('紫荆数智学堂',style: TextStyle(fontSize: 17.w,fontWeight: Fonts.medium,color: Colours.c3),), Text('紫荆数智学堂',style: TextStyle(fontSize: 17.w,fontWeight: Fonts.medium,color: Colours.c3),),
......
...@@ -16,6 +16,7 @@ import 'package:get/get_state_manager/src/simple/get_controllers.dart'; ...@@ -16,6 +16,7 @@ import 'package:get/get_state_manager/src/simple/get_controllers.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:tobias/tobias.dart'; import 'package:tobias/tobias.dart';
import '../../routes/index.dart';
import '../../services/index.dart'; import '../../services/index.dart';
import '../../utils/index.dart'; import '../../utils/index.dart';
import '../../widgets/index.dart'; import '../../widgets/index.dart';
......
...@@ -68,12 +68,17 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA ...@@ -68,12 +68,17 @@ class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepA
), ),
), ),
Gaps.vGaps15, Gaps.vGaps15,
RichText(text: TextSpan( GestureDetector(
children: [ onTap: (){
TextSpan(text: '充值即代表同意',style: TextStyle(fontSize: 13.w,height: 1.5,color: Colours.c9)), context.pushNamed(Routes.terms);
TextSpan(text: '《用户充值协议》',style: TextStyle(fontSize: 13.w,height: 1.5,color: Color(0xFF2A82D9))), },
] child: RichText(text: TextSpan(
)), children: [
TextSpan(text: '充值即代表同意',style: TextStyle(fontSize: 13.w,height: 1.5,color: Colours.c9)),
TextSpan(text: '《用户充值协议》',style: TextStyle(fontSize: 13.w,height: 1.5,color: Color(0xFF2A82D9))),
]
)),
),
Gaps.vGaps15 Gaps.vGaps15
], ],
), ),
......
...@@ -10,7 +10,7 @@ class UserCouponController extends GetxController { ...@@ -10,7 +10,7 @@ class UserCouponController extends GetxController {
// 优惠券 // 优惠券
List <CouponModel> coupons = []; List <CouponModel> coupons = [];
late int type = 1; late int type = 2;
final int _limit = 10; final int _limit = 10;
int _page = 1; int _page = 1;
...@@ -27,7 +27,7 @@ class UserCouponController extends GetxController { ...@@ -27,7 +27,7 @@ class UserCouponController extends GetxController {
void getOverCoupons() { void getOverCoupons() {
_noMore = true; _noMore = true;
_page = 1; _page = 1;
type = 2; type = 1;
_getCoupon(); _getCoupon();
} }
......
...@@ -54,6 +54,7 @@ class UserDiscussDesController extends GetxController { ...@@ -54,6 +54,7 @@ class UserDiscussDesController extends GetxController {
refreshController.finishRefresh(IndicatorResult.success); refreshController.finishRefresh(IndicatorResult.success);
refreshController.resetFooter(); refreshController.resetFooter();
} catch (error) { } catch (error) {
Console.log('------------onRefresh-----------------$error');
refreshController.finishRefresh(IndicatorResult.fail); refreshController.finishRefresh(IndicatorResult.fail);
} }
} }
......
...@@ -22,6 +22,7 @@ class _BuildListPageState extends State<BuildListPage> with AutomaticKeepAliveCl ...@@ -22,6 +22,7 @@ class _BuildListPageState extends State<BuildListPage> with AutomaticKeepAliveCl
init: UserDiscussDesController(widget.tag,widget.model), init: UserDiscussDesController(widget.tag,widget.model),
builder: (controller) =>CustomPullScrollView( builder: (controller) =>CustomPullScrollView(
controller: controller.refreshController, controller: controller.refreshController,
// onRefresh: controller.onRefresh,
onLoading: controller.onLoading, onLoading: controller.onLoading,
child: ListView.builder( child: ListView.builder(
itemBuilder: (BuildContext context,int index){ itemBuilder: (BuildContext context,int index){
......
part of user_edit_note;
class UserEditNoteController extends GetxController {
final NoteModel model;
final String bookId;
late TextEditingController contentInput;
UserEditNoteController(this.model,this.bookId){
_setDuration();
contentInput = TextEditingController(text: model.noteContent?.text?.content);
}
// 录音
final FlutterSoundRecorder _mRecorder = FlutterSoundRecorder();
just_audio.AudioPlayer audioPlayer = just_audio.AudioPlayer();
// 录音开始
bool startRecording = false;
// 笔记是否公开
bool isPublic = false;
bool initRecorder = false;
String currentDuration = '';
late MediaModel currentPlayMediaModel = MediaModel();
@override
void onInit() {
super.onInit();
}
@override
void onClose() {
contentInput.dispose();
audioPlayer.dispose();
super.onClose();
}
// 设置笔记是否公开
void setIsPublic(){
isPublic = !isPublic;
update();
}
// 配置音频时长
void _setDuration() async {
for(MediaModel mediaModel in model.noteContent!.audio!){
Duration? duration = await just_audio.AudioPlayer().setUrl(mediaModel.content??'');
mediaModel.duration = Tools.formatDuration(duration!);
}
update();
}
// 删除图片
void delImage(MediaModel mediaModel){
model.noteContent!.image!.remove(mediaModel);
update();
}
// 删除音频
void delAudio(MediaModel mediaModel){
model.noteContent!.audio!.remove(mediaModel);
// 新加的录音 如果删除要删除源文件
if(mediaModel.id == 0){
}
update();
}
// 添加音频
void addAudio(String path,String duration){
MediaModel mediaModel = MediaModel(path: path,id: 0,duration: duration);
model.noteContent!.audio!.add(mediaModel);
update();
}
// 添加图片
void addImage(String path){
MediaModel mediaModel = MediaModel(path: path,id: 0);
model.noteContent!.image!.add(mediaModel);
update();
}
// 初始化录音组件
Future<void> openTheRecorder() async {
if(!initRecorder){
// 获取权限
if(await Access.microphone()){
await _mRecorder.openRecorder();
final session = await AudioSession.instance;
await session.configure(AudioSessionConfiguration(
avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
avAudioSessionCategoryOptions:
AVAudioSessionCategoryOptions.allowBluetooth |
AVAudioSessionCategoryOptions.defaultToSpeaker,
avAudioSessionMode: AVAudioSessionMode.spokenAudio,
avAudioSessionRouteSharingPolicy:
AVAudioSessionRouteSharingPolicy.defaultPolicy,
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
androidAudioAttributes: const AndroidAudioAttributes(
contentType: AndroidAudioContentType.speech,
flags: AndroidAudioFlags.none,
usage: AndroidAudioUsage.voiceCommunication,
),
androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
androidWillPauseWhenDucked: true,
));
}
// 没有权限
else {
}
}
initRecorder = true;
}
// 开启录音
void record() async {
openTheRecorder();
update();
startRecording = true;
String filePath = await Tools.getDirectory();
String fileName = Tools.generateVoiceFileName();
_mRecorder.startRecorder(
toFile: '$filePath/$fileName',
audioSource: sound_recorder_platform.AudioSource.microphone,
codec: Codec.aacMP4,
);
}
// 停止录音
void stopRecorder() async{
startRecording = false;
final path = await _mRecorder.stopRecorder();
var duration = await audioPlayer.setFilePath(path!);
print('-----duration---------------------$duration------');
// 添加到数组
addAudio(path, Tools.formatDuration(duration!));
}
// 播放音频
void playAudio(MediaModel mediaModel){
if(audioPlayer.playerState.playing){
audioPlayer.stop();
mediaModel.currentDuration = '0:00:00';
// if(currentPlayMediaModel.id == mediaModel.id){
// return;
// }
}
// 本地音频
if (mediaModel.id == 0){
audioPlayer.setFilePath(mediaModel.path);
}
// 远程音频
else {
audioPlayer.setUrl(mediaModel.content??'');
}
audioPlayer.play();
audioPlayer.positionStream.listen((position) {
String temp = Tools.formatDuration(position);
Console.log('播放时间---------------------$temp');
mediaModel.currentDuration = temp;
update();
});
// currentPlayMediaModel = mediaModel;
}
// 上传文件
Future<String> upload({
required String path
}) async {
// String result = await CommonAPI.upload(path:path,fileTypes: 'comment');
// return result;
OssTool tool = OssTool('zxts-comment-file');
final response = await tool.putObjectFile(path);
print('------response--------------------------${response.realUri}');
return response.realUri.toString();
}
// 提交
Future<bool> submit() async {
// 循环上传图片获取地址
for(MediaModel mediaModel in model.noteContent!.image!){
if (mediaModel.id == 0){
final url = await upload(path: mediaModel.path);
mediaModel.content = url;
}
}
// 循环上传音频获取地址
for(MediaModel mediaModel in model.noteContent!.audio!){
if (mediaModel.id == 0){
final url = await upload(path: mediaModel.path);
mediaModel.content = url;
}
}
// 组织图片
List<Map> images = [];
for(MediaModel mediaModel in model.noteContent!.image!){
images.add( mediaModel.toJson());
}
// 组织音频
List<Map> audios = [];
for(MediaModel mediaModel in model.noteContent!.audio!){
audios.add( mediaModel.toJson());
}
model.noteContent!.text!.content = contentInput.text;
Map<String,dynamic> contentMap = {
'text':model.noteContent!.text!.toJson(),
'audio':audios,
'image':images
};
final result = MineAPI.editNotes(
content: model.content??'',
notesId: model.notesId.toString(),
bookId: bookId,
noteContent: jsonEncode(contentMap)
);
return result;
}
}
\ No newline at end of file
library user_edit_note;
import 'dart:convert';
import 'package:audio_session/audio_session.dart';
import 'package:flutter/material.dart';
import 'package:flutter_book/apis/index.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_sound/public/flutter_sound_recorder.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_platform_interface.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_recorder_platform_interface.dart' as sound_recorder_platform;
import 'package:get/get.dart';
import 'package:go_router/go_router.dart';
import 'package:just_audio/just_audio.dart' as just_audio;
import 'package:just_audio/just_audio.dart';
import 'dart:async';
import '../../models/index.dart';
import '../../theme.dart';
import '../../utils/index.dart';
import '../../widgets/index.dart';
part 'view.dart';
part 'controller.dart';
\ No newline at end of file
差异被折叠。
...@@ -9,8 +9,12 @@ class UserInfoController extends GetxController { ...@@ -9,8 +9,12 @@ class UserInfoController extends GetxController {
void upload({ void upload({
required String path required String path
}) async { }) async {
String result = await CommonAPI.upload(path:path,fileTypes: 'user'); // String result = await CommonAPI.upload(path:path,fileTypes: 'user');
_changeInfo(result); OssTool tool = OssTool('zxts-user-file');
print('------path--------------------------$path');
final response = await tool.putObjectFile(path);
print('------response--------------------------${response.realUri}');
_changeInfo(response.realUri.toString());
} }
void show(){ void show(){
......
...@@ -217,6 +217,124 @@ class _UserInfoPageState extends State<UserInfoPage> { ...@@ -217,6 +217,124 @@ class _UserInfoPageState extends State<UserInfoPage> {
), ),
], ],
); );
return WillPopScope(
onWillPop: () async {
context.pop(true);
return false;
},
child: GetBuilder<UserInfoController>(
init: UserInfoController(widget.userInfo),
builder: (controller) =>
Scaffold(
appBar: AppBar(
title: const Text('个人信息'),
centerTitle: true,
),
body: Container(
margin: EdgeInsets.symmetric(
horizontal: AppTheme.margin, vertical: AppTheme.margin),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.w),
color: Colors.white,
boxShadow: [
BoxShadow(
color: const Color(0xFFC7C7C7).withOpacity(0.5),
offset: Offset(3.w, 0),
blurRadius: 10.w,
spreadRadius: 0.w,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.w),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// 头像
Container(
height: 52.w,
padding: EdgeInsets.only(left: 18.w, right: 15.w),
child: GestureDetector(
onTap: () async {
final assets = await AssetsPicker.image(
context: context,
);
controller.upload(path: assets!.path);
},
child: Container(
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'头像',
style: TextStyle(
color: Colours.c3,
fontSize: 14.w,
height: 1.6),
),
Row(
children: [
Container(
width: 33.w,
height: 33.w,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.cyan,
),
child: CustomImage.network(
url: controller.userInfo.headImg ?? '',
radius: 16.5,
),
),
Gaps.hGaps10,
SizedBox(
width: 5.w,
height: 8.w,
child: Image.asset(
'assets/images/right_arrow.png'),
)
],
)
],
),
),
),
),
Container(
color: Colours.cLine,
margin: EdgeInsets.symmetric(horizontal: 15.w),
height: 1.w,
),
GestureDetector(
onTap: () {
context.pushNamed(Routes.nike, extra: controller.userInfo);
},
child: _buildItem(
'昵称',
widget.userInfo.name ?? '',
)),
Container(
color: Colours.cLine,
margin: EdgeInsets.symmetric(horizontal: 15.w),
height: 1.w,
),
GestureDetector(
onTap: () {
context.pushNamed(
Routes.gender, extra: controller.userInfo);
},
child: _buildItem(
'性别',
getGender(widget.userInfo.sex)
// widget.userInfo.sex == 1 ? '男' : '女',
)),
],
),
),
)),),
);
} }
getGender(num? sex) { getGender(num? sex) {
......
...@@ -35,12 +35,24 @@ class MsgPage extends StatelessWidget { ...@@ -35,12 +35,24 @@ class MsgPage extends StatelessWidget {
if(model.type == 1){ if(model.type == 1){
// 1订单支付快要超时(跳转订单详情) // 1订单支付快要超时(跳转订单详情)
final result = await context.pushNamed(Routes.order);
if (result == true){
controller.onRefresh();
}
///TODO: ///TODO:
}else if (model.type == 2){ }else if (model.type == 2){
// 2 购买完成三天未评价(跳转订单列表--已完成) // 2 购买完成三天未评价(跳转订单列表--已完成)
final result = await context.pushNamed(Routes.order);
if (result == true){
controller.onRefresh();
}
///TODO: ///TODO:
}else if (model.type == 3){ }else if (model.type == 3){
// 3 讨论有人回复的时候 (跳转对应书籍的讨论页面) // 3 讨论有人回复的时候 (跳转对应书籍的讨论页面)
final result = await context.pushNamed(Routes.bookDetail,queryParameters: {'book_id':model.urlId?.bookId.toString()});
if (result == true){
controller.onRefresh();
}
} }
else if (model.type == 4){ else if (model.type == 4){
// 4 订单完成后有新的积分增加(跳转用户积分记录页) // 4 订单完成后有新的积分增加(跳转用户积分记录页)
......
...@@ -11,6 +11,8 @@ class UserNotesDesController extends GetxController { ...@@ -11,6 +11,8 @@ class UserNotesDesController extends GetxController {
controlFinishRefresh: true, controlFinishRefresh: true,
); );
just_audio.AudioPlayer audioPlayer = just_audio.AudioPlayer();
final int _limit = 10; final int _limit = 10;
int _page = 1; int _page = 1;
bool _noMore = false; bool _noMore = false;
...@@ -25,9 +27,44 @@ class UserNotesDesController extends GetxController { ...@@ -25,9 +27,44 @@ class UserNotesDesController extends GetxController {
@override @override
void onClose() { void onClose() {
refreshController.dispose(); refreshController.dispose();
audioPlayer.dispose();
super.onClose(); super.onClose();
} }
// 播放音频
void playAudio(MediaModel mediaModel){
if(audioPlayer.playerState.playing){
audioPlayer.stop();
mediaModel.currentDuration = '0:00:00';
// if(currentPlayMediaModel.id == mediaModel.id){
// return;
// }
}
// 本地音频
if (mediaModel.id == 0){
audioPlayer.setFilePath(mediaModel.path);
}
// 远程音频
else {
audioPlayer.setUrl(mediaModel.content??'');
}
audioPlayer.play();
audioPlayer.positionStream.listen((position) {
String temp = Tools.formatDuration(position);
Console.log('播放时间---------------------$temp');
mediaModel.currentDuration = temp;
update();
});
// currentPlayMediaModel = mediaModel;
}
void delNotes({required String notesId,required String bookId}) async {
final result = await MineAPI.delNotes(notesId: notesId, bookId: bookId);
if (result){
Toast.show('删除笔记成功');
onRefresh();
}
}
/// 获取笔记列表 /// 获取笔记列表
Future<void> _getNotes([bool isRefresh = false]) async { Future<void> _getNotes([bool isRefresh = false]) async {
...@@ -39,6 +76,15 @@ class UserNotesDesController extends GetxController { ...@@ -39,6 +76,15 @@ class UserNotesDesController extends GetxController {
bookId: model.bookId.toString(), bookId: model.bookId.toString(),
types: tag types: tag
); );
for(NoteModel noteModel in result){
if(noteModel.noteContent!.audio!.isNotEmpty){
for(MediaModel mediaModel in noteModel.noteContent!.audio!){
Duration? duration = await just_audio.AudioPlayer().setUrl(mediaModel.content??'');
mediaModel.duration = Tools.formatDuration(duration!);
}
}
}
// 如果是刷新 清理数据 // 如果是刷新 清理数据
if (isRefresh) notes.clear(); if (isRefresh) notes.clear();
notes.addAll(result); notes.addAll(result);
...@@ -54,6 +100,7 @@ class UserNotesDesController extends GetxController { ...@@ -54,6 +100,7 @@ class UserNotesDesController extends GetxController {
refreshController.finishRefresh(IndicatorResult.success); refreshController.finishRefresh(IndicatorResult.success);
refreshController.resetFooter(); refreshController.resetFooter();
} catch (error) { } catch (error) {
Console.log('----error--------------------------------------$error--------------');
refreshController.finishRefresh(IndicatorResult.fail); refreshController.finishRefresh(IndicatorResult.fail);
} }
} }
......
...@@ -4,12 +4,16 @@ import 'package:easy_refresh/easy_refresh.dart'; ...@@ -4,12 +4,16 @@ import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_book/widgets/index.dart'; import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:go_router/go_router.dart';
import '../../apis/index.dart'; import '../../apis/index.dart';
import '../../models/index.dart'; import '../../models/index.dart';
import '../../routes/index.dart';
import '../../theme.dart'; import '../../theme.dart';
import '../../utils/index.dart'; import '../../utils/index.dart';
import 'package:just_audio/just_audio.dart' as just_audio;
part 'view.dart'; part 'view.dart';
......
...@@ -2,16 +2,17 @@ part of user_notes_des; ...@@ -2,16 +2,17 @@ part of user_notes_des;
class BuildHigh extends StatelessWidget { class BuildHigh extends StatelessWidget {
final NoteModel model; final NoteModel model;
final void Function()? onTapDel;
const BuildHigh({ const BuildHigh({
Key? key, Key? key,
required this.model required this.model,
this.onTapDel
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w), margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w),
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.w), borderRadius: BorderRadius.circular(4.w),
color: Colors.white, color: Colors.white,
...@@ -24,23 +25,43 @@ class BuildHigh extends StatelessWidget { ...@@ -24,23 +25,43 @@ class BuildHigh extends StatelessWidget {
), ),
], ],
), ),
child: Column( child: Slidable(
crossAxisAlignment: CrossAxisAlignment.start, endActionPane: ActionPane(
children: [ motion: const ScrollMotion(),
Text(model.content??'',style: TextStyle( children: [
fontSize: 14.w, SlidableAction(
height: 1.5, // An action can be bigger than the others.
// color: Colors.red, onPressed: (BuildContext context){
color: Color(int.parse(model.color!.substring(1), radix: 16) + 0xFF000000) onTapDel;
),), },
Gaps.vGaps8, backgroundColor: const Color(0xFFAE1414),
Container( foregroundColor: Colors.white,
width: double.infinity, // icon: Icons.archive,
padding: EdgeInsets.symmetric(vertical: 5.w,horizontal: 10.w), label: '删除',
color: Colours.cF8, ),
child: Text(model.chapterName??'',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9),), ],
) ),
], child: Container(
padding: EdgeInsets.all(10.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(model.content??'',style: TextStyle(
fontSize: 14.w,
height: 1.5,
// color: Colors.red,
color: Color(int.parse(model.color!.substring(1), radix: 16) + 0xFF000000)
),),
Gaps.vGaps8,
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 5.w,horizontal: 10.w),
color: Colours.cF8,
child: Text(model.chapterName??'',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9),),
)
],
),
),
), ),
); );
} }
......
...@@ -2,16 +2,17 @@ part of user_notes_des; ...@@ -2,16 +2,17 @@ part of user_notes_des;
class BuildLine extends StatelessWidget { class BuildLine extends StatelessWidget {
final NoteModel model; final NoteModel model;
final void Function()? onTapDel;
const BuildLine({ const BuildLine({
Key? key, Key? key,
required this.model required this.model,
this.onTapDel
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w), margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w),
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.w), borderRadius: BorderRadius.circular(4.w),
color: Colors.white, color: Colors.white,
...@@ -24,26 +25,46 @@ class BuildLine extends StatelessWidget { ...@@ -24,26 +25,46 @@ class BuildLine extends StatelessWidget {
), ),
], ],
), ),
child: Column( child: Slidable(
crossAxisAlignment: CrossAxisAlignment.start, endActionPane: ActionPane(
children: [ motion: const ScrollMotion(),
Text(model.content??'',style: TextStyle( children: [
fontSize: 14.w, SlidableAction(
height: 1.5, // An action can be bigger than the others.
color: Colours.c3, onPressed: (BuildContext context){
decoration: TextDecoration.underline, onTapDel;
// decorationColor: Colors.red, },
decorationColor:Color(int.parse(model.color!.substring(1), radix: 16) + 0xFF000000), backgroundColor: const Color(0xFFAE1414),
decorationThickness: 2 foregroundColor: Colors.white,
),), // icon: Icons.archive,
Gaps.vGaps8, label: '删除',
Container( ),
width: double.infinity, ],
padding: EdgeInsets.symmetric(vertical: 5.w,horizontal: 10.w), ),
color: Colours.cF8, child: Container(
child: Text(model.chapterName??'',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9),), padding: EdgeInsets.all(10.w),
) child: Column(
], crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(model.content??'',style: TextStyle(
fontSize: 14.w,
height: 1.5,
color: Colours.c3,
decoration: TextDecoration.underline,
// decorationColor: Colors.red,
decorationColor:Color(int.parse(model.color!.substring(1), radix: 16) + 0xFF000000),
decorationThickness: 2
),),
Gaps.vGaps8,
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 5.w,horizontal: 10.w),
color: Colours.cF8,
child: Text(model.chapterName??'',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9),),
)
],
),
),
), ),
); );
} }
......
...@@ -28,16 +28,35 @@ class _BuildListPageState extends State<BuildListPage> with AutomaticKeepAliveCl ...@@ -28,16 +28,35 @@ class _BuildListPageState extends State<BuildListPage> with AutomaticKeepAliveCl
NoteModel model = controller.notes[index]; NoteModel model = controller.notes[index];
// 划线 // 划线
if(model.types == 1){ if(model.types == 1){
return BuildLine(model: model,); return BuildLine(model: model,
onTapDel: (){
controller.delNotes(notesId: model.notesId.toString(), bookId:widget.model.bookId.toString());
},
);
} }
// 高亮 // 高亮
else if(model.types == 2){ else if(model.types == 2){
return BuildHigh(model: model,); return BuildHigh(model: model,
onTapDel: (){
controller.delNotes(notesId: model.notesId.toString(), bookId:widget.model.bookId.toString());
},
);
} }
// 笔记 // 笔记
else if(model.types == 3){ else if(model.types == 3){
return BuildNote(model: model,); return BuildNote(model: model,
onTapDel: (){
controller.delNotes(notesId: model.notesId.toString(), bookId:widget.model.bookId.toString());
},
onTapEdit: (){
context.pushNamed(Routes.editNote,extra: model,queryParameters: {'book_id':widget.model.bookId.toString()});
},
onTapAudio: (mediaModel){
controller.playAudio(mediaModel);
},
);
} }
return null;
}, },
itemCount: controller.notes.length, itemCount: controller.notes.length,
), ),
......
...@@ -2,16 +2,22 @@ part of user_notes_des; ...@@ -2,16 +2,22 @@ part of user_notes_des;
class BuildNote extends StatelessWidget { class BuildNote extends StatelessWidget {
final NoteModel model; final NoteModel model;
final void Function()? onTapDel;
final void Function()? onTapEdit;
final void Function(MediaModel mediaModel)? onTapAudio;
const BuildNote({ const BuildNote({
Key? key, Key? key,
required this.model required this.model,
this.onTapDel,
this.onTapEdit,
this.onTapAudio
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w), margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w),
padding: EdgeInsets.all(10.w), // padding: EdgeInsets.all(10.w),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.w), borderRadius: BorderRadius.circular(4.w),
color: Colors.white, color: Colors.white,
...@@ -24,33 +30,64 @@ class BuildNote extends StatelessWidget { ...@@ -24,33 +30,64 @@ class BuildNote extends StatelessWidget {
), ),
], ],
), ),
child: Column( child: Slidable(
crossAxisAlignment: CrossAxisAlignment.start, endActionPane: ActionPane(
children: [ motion: const ScrollMotion(),
Text('看来谁都不记得自己为何出现在此',style: TextStyle( children: [
fontSize: 14.w, SlidableAction(
height: 1.5, // An action can be bigger than the others.
// color: Colors.red, onPressed: (BuildContext context){
color:Colours.c3 if (onTapEdit !=null) onTapEdit!();
),),
Gaps.vGaps13, },
_buildImageGridView(), backgroundColor: const Color(0xFFEB914A),
Gaps.vGaps13, foregroundColor: Colors.white,
_buildAudioListView(), // icon: Icons.archive,
Gaps.vGaps13, label: '编辑',
Container( ),
width: double.infinity, SlidableAction(
padding: EdgeInsets.symmetric(vertical: 5.w,horizontal: 10.w), // An action can be bigger than the others.
color: Colours.cF8, onPressed: (BuildContext context){
child:Column( if (onTapDel !=null) onTapDel!();
crossAxisAlignment: CrossAxisAlignment.start, },
children: [ backgroundColor: const Color(0xFFAE1414),
Text(model.chapterName??'',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9),), foregroundColor: Colors.white,
Text('内容:${model.content??''}',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9),), // icon: Icons.archive,
], label: '删除',
) ),
) ],
], ),
child: Container(
padding: EdgeInsets.all(10.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(model.noteContent?.text?.content??'',style: TextStyle(
fontSize: 14.w,
height: 1.5,
// color: Colors.red,
color:Colours.c3
),),
Gaps.vGaps13,
_buildImageGridView(),
Gaps.vGaps13,
_buildAudioListView(),
Gaps.vGaps13,
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 5.w,horizontal: 10.w),
color: Colours.cF8,
child:Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(model.chapterName??'',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9),),
Text('内容:${model.content??''}',style: TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9),),
],
)
)
],
),
),
), ),
); );
} }
...@@ -59,7 +96,7 @@ class BuildNote extends StatelessWidget { ...@@ -59,7 +96,7 @@ class BuildNote extends StatelessWidget {
Widget _buildImageGridView(){ Widget _buildImageGridView(){
return GridView.builder( return GridView.builder(
// padding: const EdgeInsets.only(left: 13,top: 10), // padding: const EdgeInsets.only(left: 13,top: 10),
physics: NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 6, crossAxisCount: 6,
...@@ -68,12 +105,9 @@ class BuildNote extends StatelessWidget { ...@@ -68,12 +105,9 @@ class BuildNote extends StatelessWidget {
childAspectRatio: 1 childAspectRatio: 1
), ),
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return Container( return CustomImage.network(url: model.noteContent!.image![index].content??'',fit: BoxFit.cover,);
color: Colors.red,
child: Center(child: Text('图片')),
);
}, },
itemCount: 3, itemCount: model.noteContent?.image?.length,
); );
} }
...@@ -82,6 +116,7 @@ class BuildNote extends StatelessWidget { ...@@ -82,6 +116,7 @@ class BuildNote extends StatelessWidget {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
MediaModel mediaModel = model.noteContent!.audio![index];
return Container( return Container(
height: 20.w, height: 20.w,
margin: EdgeInsets.only(right: 130.w), margin: EdgeInsets.only(right: 130.w),
...@@ -96,34 +131,19 @@ class BuildNote extends StatelessWidget { ...@@ -96,34 +131,19 @@ class BuildNote extends StatelessWidget {
// mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
mainAxisAlignment:MainAxisAlignment.spaceBetween, mainAxisAlignment:MainAxisAlignment.spaceBetween,
children: [ children: [
Image.asset('assets/images/audio.png'), GestureDetector(
Text('0:00/1:52',style: TextStyle(fontSize: 10.w,height: 1.4,color: Colours.c9),) onTap: (){
if (onTapAudio !=null) onTapAudio!(mediaModel);
},
child: Image.asset('assets/images/audio.png')
),
Text('0:00/${mediaModel.duration}',style: TextStyle(fontSize: 10.w,height: 1.4,color: Colours.c9),)
], ],
), ),
), ),
); );
}, },
itemCount: 3, itemCount: model.noteContent?.audio?.length,
); );
} }
// Widget _buildAudioGridView(){
// return GridView.builder(
// physics: const NeverScrollableScrollPhysics(),
// shrinkWrap: true,
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 6,
// crossAxisSpacing: 2.w,
// mainAxisSpacing: 2.w,
// childAspectRatio: 1
// ),
// itemBuilder: (BuildContext context, int index) {
// return Container(
// color: Colors.red,
// child: Center(child: Text('音频')),
// );
// },
// itemCount: 3,
// );
// }
} }
...@@ -45,6 +45,7 @@ import 'package:go_router/go_router.dart'; ...@@ -45,6 +45,7 @@ import 'package:go_router/go_router.dart';
import '../models/index.dart'; import '../models/index.dart';
import '../pages/bai_ke/index.dart'; import '../pages/bai_ke/index.dart';
import '../pages/read_web/index.dart'; import '../pages/read_web/index.dart';
import '../pages/user_edit_note/index.dart';
import '../pages/user_order/index.dart'; import '../pages/user_order/index.dart';
import '../pages/user_order_evaluate/index.dart'; import '../pages/user_order_evaluate/index.dart';
import '../pages/pay_coupon/index.dart'; import '../pages/pay_coupon/index.dart';
......
...@@ -79,6 +79,8 @@ abstract class Routes { ...@@ -79,6 +79,8 @@ abstract class Routes {
static const note = 'note'; static const note = 'note';
// 笔记详情 // 笔记详情
static const noteDes = 'note_des'; static const noteDes = 'note_des';
// 编辑笔记
static const editNote = 'edit_note';
// 错题 // 错题
static const wrong = 'wrong'; static const wrong = 'wrong';
// 讨论 // 讨论
...@@ -189,6 +191,7 @@ abstract class Routes { ...@@ -189,6 +191,7 @@ abstract class Routes {
bookId: state.uri.queryParameters['book_id'].toString(), bookId: state.uri.queryParameters['book_id'].toString(),
chapterId: state.uri.queryParameters['chapter_id'].toString(), chapterId: state.uri.queryParameters['chapter_id'].toString(),
chapterName: state.uri.queryParameters['chapter_name'].toString(), chapterName: state.uri.queryParameters['chapter_name'].toString(),
bookDetailModel: state.extra as BookDetailModel,
) )
) )
), ),
...@@ -553,6 +556,15 @@ abstract class Routes { ...@@ -553,6 +556,15 @@ abstract class Routes {
) )
) )
), ),
GoRoute(
path: '/$editNote',
name: editNote,
pageBuilder: (context, state) =>CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: UserEditNotePage(model: state.extra as NoteModel,bookId: state.uri.queryParameters['book_id'].toString(),)
)
),
GoRoute( // 订单搜索 GoRoute( // 订单搜索
path: '/$orderSearch', path: '/$orderSearch',
name: orderSearch, name: orderSearch,
......
...@@ -19,13 +19,14 @@ class HttpService extends GetxService { ...@@ -19,13 +19,14 @@ class HttpService extends GetxService {
_dio = Dio(options); _dio = Dio(options);
_dio.interceptors.add(_RequestInterceptor()); _dio.interceptors.add(_RequestInterceptor());
_dio.interceptors.add(_CacheInterceptor());
} }
/// 组织 headers 参数 /// 组织 headers 参数
Map<String,dynamic>? _getHeaders({bool excludeToken = false,Map<String,dynamic>? params,String? url}) { Map<String,dynamic>? _getHeaders({bool excludeToken = false,Map<String,dynamic>? params,String? url}) {
final headers = <String, dynamic>{}; final headers = <String, dynamic>{};
headers['appId'] = AppConfig.AppID; headers['appId'] = AppConfig.appID;
headers['appSecret'] = AppConfig.AppSecret; headers['appSecret'] = AppConfig.appSecret;
headers['timestamp'] = (DateTime.now().millisecondsSinceEpoch~/1000).toString(); headers['timestamp'] = (DateTime.now().millisecondsSinceEpoch~/1000).toString();
headers['url'] = kServerUrl + url.toString(); headers['url'] = kServerUrl + url.toString();
...@@ -56,11 +57,18 @@ class HttpService extends GetxService { ...@@ -56,11 +57,18 @@ class HttpService extends GetxService {
CancelToken? cancelToken, CancelToken? cancelToken,
bool excludeToken = false, bool excludeToken = false,
bool showLoading = false, bool showLoading = false,
bool cacheEnabled = false,
}) async { }) async {
if (showLoading) CustomToast.loading(); if (showLoading) CustomToast.loading();
try { try {
final requestOptions = options ?? Options(); final requestOptions = options ?? Options();
requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url); requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);
// 如果启用缓存,将cacheEnabled参数添加到请求选项中
if(cacheEnabled){
requestOptions.extra ??= {};
requestOptions.extra!['cacheEnabled'] = true;
}
final response = await _dio.get( final response = await _dio.get(
url, url,
queryParameters: params, queryParameters: params,
...@@ -83,6 +91,7 @@ class HttpService extends GetxService { ...@@ -83,6 +91,7 @@ class HttpService extends GetxService {
CancelToken? cancelToken, CancelToken? cancelToken,
bool excludeToken = false, bool excludeToken = false,
bool showLoading = false, bool showLoading = false,
bool cacheEnabled = true,
}) async{ }) async{
if (showLoading) CustomToast.loading(); if (showLoading) CustomToast.loading();
try { try {
...@@ -90,6 +99,12 @@ class HttpService extends GetxService { ...@@ -90,6 +99,12 @@ class HttpService extends GetxService {
requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url); requestOptions.headers = _getHeaders(excludeToken: excludeToken,params: params,url: url);
Console.log('----headers------${requestOptions.headers}'); Console.log('----headers------${requestOptions.headers}');
Console.log('----params------$params'); Console.log('----params------$params');
// 如果启用缓存,将cacheEnabled参数添加到请求选项中
if(cacheEnabled){
requestOptions.extra ??= {};
requestOptions.extra!['cacheEnabled'] = false;
}
final response = await _dio.post( final response = await _dio.post(
url, url,
data: params, data: params,
...@@ -169,36 +184,36 @@ class HttpService extends GetxService { ...@@ -169,36 +184,36 @@ class HttpService extends GetxService {
class _RequestInterceptor extends Interceptor { class _RequestInterceptor extends Interceptor {
@override @override
void onResponse(Response response, ResponseInterceptorHandler handler) async { void onResponse(Response response, ResponseInterceptorHandler handler) async {
final responseData = response.data; // final responseData = response.data;
if (responseData is Map && responseData.containsKey('code')) { // if (responseData is Map && responseData.containsKey('code')) {
final code = responseData['code']; // final code = responseData['code'];
if (code != 200) { // if (code != 200) {
handler.reject( // handler.reject(
DioException( // DioException(
requestOptions: response.requestOptions, // requestOptions: response.requestOptions,
response: response, // response: response,
type: DioExceptionType.badResponse, // type: DioExceptionType.badResponse,
), // ),
true, // true,
); // );
return; // return;
} // }
} else if (response.requestOptions.responseType == ResponseType.bytes) { // } else if (response.requestOptions.responseType == ResponseType.bytes) {
//
}
super.onResponse(response, handler);
// if (response.data['code'] != 200) {
// handler.reject(
// DioException(
// requestOptions: response.requestOptions,
// response: response,
// type: DioExceptionType.badResponse,
// ),
// true,
// );
// }else {
// super.onResponse(response, handler);
// } // }
// super.onResponse(response, handler);
if (response.data['code'] != 200) {
handler.reject(
DioException(
requestOptions: response.requestOptions,
response: response,
type: DioExceptionType.badResponse,
),
true,
);
}else {
super.onResponse(response, handler);
}
} }
@override @override
...@@ -226,7 +241,7 @@ class _RequestInterceptor extends Interceptor { ...@@ -226,7 +241,7 @@ class _RequestInterceptor extends Interceptor {
var msg = '服务器错误'; var msg = '服务器错误';
switch (statusCode) { switch (statusCode) {
case 403: case 403:
print('-------------------access_token-------------------------${UserStore.to.accessToken}--------------------'); print('----------403---------access_token-------------------------${UserStore.to.accessToken}--------------------');
msg = '$statusCode - Unauthorized'; msg = '$statusCode - Unauthorized';
final newToken = await refreshToken(); final newToken = await refreshToken();
if (newToken != null) { if (newToken != null) {
...@@ -260,15 +275,15 @@ class _RequestInterceptor extends Interceptor { ...@@ -260,15 +275,15 @@ class _RequestInterceptor extends Interceptor {
break; break;
case 404: case 404:
msg = '$statusCode - Server not found'; msg = '$statusCode - Server not found';
CustomToast.fail(msg); // CustomToast.fail(msg);
break; break;
case 500: case 500:
msg = '$statusCode - Server error'; msg = '$statusCode - Server error';
CustomToast.fail(msg); // CustomToast.fail(msg);
break; break;
case 502: case 502:
msg = '$statusCode - Bad gateway'; msg = '$statusCode - Bad gateway';
CustomToast.fail(msg); // CustomToast.fail(msg);
break; break;
default: default:
// if (code == 901) UserStore.to.logout(); // if (code == 901) UserStore.to.logout();
...@@ -296,7 +311,7 @@ class _RequestInterceptor extends Interceptor { ...@@ -296,7 +311,7 @@ class _RequestInterceptor extends Interceptor {
final result = await HttpService.to.post( final result = await HttpService.to.post(
'/v1/members/login/getToken', '/v1/members/login/getToken',
params: { params: {
'access_token':UserStore.to.accessToken 'access_token':StorageService.to.getString(kLocalAccessToken)
} }
); );
if (result.data is Map) { if (result.data is Map) {
...@@ -313,4 +328,61 @@ class _RequestInterceptor extends Interceptor { ...@@ -313,4 +328,61 @@ class _RequestInterceptor extends Interceptor {
} }
// 缓存拦截器
class _CacheInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
final cacheEnabled = options.extra?['cacheEnabled'] ?? false;
if (cacheEnabled) {
// 在发起请求之前,检查缓存是否存在有效数据
final cachedFile = await DefaultCacheManager().getFileFromCache(options.uri.toString());
if (cachedFile != null && cachedFile.validTill.isAfter(DateTime.now())) {
// 如果缓存有效,直接返回缓存数据
final cachedData = await cachedFile.file.readAsBytes();
final decodedData = utf8.decode(cachedData); // 将字节列表解码为字符串
final jsonData = jsonDecode(decodedData);
handler.resolve(Response(
requestOptions: options,
statusCode: 200,
data: jsonData,
));
return;
}
}
// 如果缓存不存在或已过期,或未启用缓存,继续发起网络请求
handler.next(options);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) async {
// 只缓存状态码为200的响应
if (response.statusCode == 200) {
try {
// 获取请求的 URL
final url = response.requestOptions.uri.toString();
// 获取请求体参数(如果是 POST 请求)
final requestBody = response.requestOptions.data.toString();
// 将 GET 请求的参数和请求体参数拼接成缓存的键
final cacheKey = requestBody.isEmpty ? url : '$url?$requestBody';
// Console.log('----------cacheKey-----------------------$cacheKey');
// 将响应数据转换为字符串,并将其编码为字节列表
List<int> bytes = utf8.encode(jsonEncode(response.data));
Uint8List uint8List = Uint8List.fromList(bytes);
// 创建一个缓存文件并将数据写入其中
DefaultCacheManager().putFile(
cacheKey,
uint8List,
fileExtension: '.json', // 可以根据需求修改文件扩展名
maxAge: const Duration(hours: 1)
);
} catch (e) {
Console.log('Error caching response: $e');
}
}
super.onResponse(response, handler);
}
}
...@@ -2,11 +2,13 @@ library services; ...@@ -2,11 +2,13 @@ library services;
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter_book/store/index.dart'; import 'package:flutter_book/store/index.dart';
import 'package:flutter_book/utils/index.dart'; import 'package:flutter_book/utils/index.dart';
import 'package:flutter_book/widgets/index.dart'; import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:get/get.dart' hide Response, FormData, MultipartFile; import 'package:get/get.dart' hide Response, FormData, MultipartFile;
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
......
...@@ -31,4 +31,10 @@ abstract class Access { ...@@ -31,4 +31,10 @@ abstract class Access {
return result[Permission.storage] == PermissionStatus.granted; return result[Permission.storage] == PermissionStatus.granted;
} }
/// 获取麦克风权限
static Future<bool> microphone() async {
final result = await [Permission.microphone].request();
return result[Permission.microphone] == PermissionStatus.granted;
}
} }
...@@ -2,10 +2,14 @@ part of utils; ...@@ -2,10 +2,14 @@ part of utils;
class AppConfig{ class AppConfig{
//app在服务端注册的id,由后端直接提供 //app在服务端注册的id,由后端直接提供
static const String AppID = 'dkFv6OwWyQhSeaZG'; static const String appID = 'dkFv6OwWyQhSeaZG';
//app在服务端的秘钥串,由后端直接提供 //app在服务端的秘钥串,由后端直接提供
static const String AppSecret = 'd34cea92316d1da113e95158bed9ca97'; static const String appSecret = 'd34cea92316d1da113e95158bed9ca97';
//签名时使用的加密串,由后端直接提供 //签名时使用的加密串,由后端直接提供
static const String AppEncodeKey = '&4F6g4Y6b5L4R9'; static const String appEncodeKey = '&4F6g4Y6b5L4R9';
// 下载需要的key
static const String aesKey = 'e8a83e801cf85d90b69556b0cb1e0c8a';
static const String aesIV = '13e801c8901b6955';
} }
...@@ -14,6 +14,7 @@ const String kFailOrder = 'failOrder'; ...@@ -14,6 +14,7 @@ const String kFailOrder = 'failOrder';
abstract class C { abstract class C {
static const String localAccount = 'storage_account'; static const String localAccount = 'storage_account';
static const String localThemeMode = 'storage_theme_mode'; static const String localThemeMode = 'storage_theme_mode';
......
...@@ -7,4 +7,25 @@ class EncryptUtil { ...@@ -7,4 +7,25 @@ class EncryptUtil {
var digest = md5.convert(content); var digest = md5.convert(content);
return hex.encode(digest.bytes); return hex.encode(digest.bytes);
} }
/// AES 加密
static aesEncrypt(String content,[String aesKey = AppConfig.aesKey,]) {
final key = encrypt.Key.fromUtf8(aesKey);
final iv = encrypt.IV.fromBase64(base64Encode(utf8.encode(AppConfig.aesIV)));
final encryptor = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc),);
final encrypted = encryptor.encrypt(content, iv: iv);
return base64Encode(utf8.encode(encrypted.base64));
}
/// AES 解密
static aesDecrypt(String content,[String aesKey = AppConfig.aesKey,]) {
final key = encrypt.Key.fromUtf8(aesKey);
final iv = encrypt.IV.fromBase64(base64Encode(utf8.encode(AppConfig.aesIV)));
final encryptor = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
final encrypted = encrypt.Encrypted.fromBase64(utf8.decode(base64.decode(content)));
final decrypted = encryptor.decrypt(encrypted, iv: iv);
return decrypted.trimRight();
}
} }
\ No newline at end of file
library utils; library utils;
import 'dart:convert';
import 'dart:convert';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:ui'; import 'dart:ui';
// import 'package:encrypt/encrypt.dart';
import 'package:dio/dio.dart';
import 'package:encrypt/encrypt.dart' as encrypt; // 使用别名 'encrypt'
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_book/apis/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
...@@ -17,6 +23,8 @@ import 'package:oktoast/oktoast.dart'; ...@@ -17,6 +23,8 @@ import 'package:oktoast/oktoast.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_oss_aliyun/flutter_oss_aliyun.dart';
import '../models/index.dart';
import '../widgets/index.dart'; import '../widgets/index.dart';
...@@ -32,4 +40,5 @@ part 'validator.dart'; ...@@ -32,4 +40,5 @@ part 'validator.dart';
part 'toast_utils.dart'; part 'toast_utils.dart';
part 'clear_cache_util.dart'; part 'clear_cache_util.dart';
part 'access.dart'; part 'access.dart';
part 'assets_picker.dart'; part 'assets_picker.dart';
\ No newline at end of file part 'oss.dart';
\ No newline at end of file
part of utils;
//'bucket_comment' => 'zxts-comment-file', 'bucket_user' => 'zxts-user-file', 'endpoint' => 'zijingebook.com',
class OssTool {
late OssModel ossModel;
final String bucketName;
OssTool(this.bucketName){
initOss(bucketName);
}
void initOss(String bucketName) async {
Client.init(ossEndpoint: 'oss-cn-beijing.aliyuncs.com', bucketName:bucketName,authGetter: _authGetter);
}
// 获取临时凭证
Future<Auth> _authGetter() async {
final result = await CommonAPI.oss();
return Auth(
accessKey: result.accessKeyId!,
accessSecret: result.accessKeySecret!,
expire: result.expiration!,
secureToken: result.securityToken!,
);
}
// 本地文件上传
Future<Response<dynamic>> putObjectFile(String filePath) async {
print('------------------222222222222222222222--------------$filePath');
print('------------------222222222222222222222--------------');
List<String> parts = filePath.split('/');
String fileName = parts.last;
final String path = '${DateTime.now().year}/${DateTime.now().month.toString().padLeft(2,'0')}/${DateTime.now().day.toString().padLeft(2,'0')}';
final Response<dynamic> resp = await Client().putObjectFile(
filePath,
fileKey: '$path/$fileName'
);
print('99999999999-------------${resp.realUri}---${resp.extra}--${resp.data}');
return resp;
}
// 批量本地文件上传
Future<List<Response>> putObjectFiles(List<String> filePaths){
return Client().putObjectFiles(
filePaths.map((e){
return AssetFileEntity(
filepath: e
);
}).toList()
);
}
}
\ No newline at end of file
...@@ -8,7 +8,7 @@ class SignTool { ...@@ -8,7 +8,7 @@ class SignTool {
tempArr.add("$key=${tempParams[key]}"); tempArr.add("$key=${tempParams[key]}");
} }
String tempStr = tempArr.join("&"); String tempStr = tempArr.join("&");
String finalString = tempStr + AppConfig.AppEncodeKey; String finalString = tempStr + AppConfig.appEncodeKey;
String finalEncodeStr = EncryptUtil.encodeMd5(EncryptUtil.encodeMd5(finalString)); String finalEncodeStr = EncryptUtil.encodeMd5(EncryptUtil.encodeMd5(finalString));
return finalEncodeStr; return finalEncodeStr;
} }
......
...@@ -5,4 +5,51 @@ abstract class Tools { ...@@ -5,4 +5,51 @@ abstract class Tools {
static void unfocus() { static void unfocus() {
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus(); WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
} }
static String dateFromMS(
int timestamp, {
String pattern = 'yyyy-MM-dd',
bool humanize = false,
}) {
final dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp*1000);
if (humanize) {
final now = DateTime.now();
final difference = now.difference(dateTime);
if (difference.inMinutes < 60) {
if (difference.inMinutes < 1) return '刚刚';
return '${difference.inMinutes}分钟前';
} else if (difference.inHours < 24) {
return '${difference.inHours}小时前';
} else if (difference.inDays < 30) {
return '${difference.inDays}天前';
} else if (now.year == dateTime.year) {
return DateFormat('MM-dd').format(dateTime);
}
}
return DateFormat(pattern).format(dateTime);
}
static String formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${duration.inHours}:$twoDigitMinutes:$twoDigitSeconds";
}
static Future<String> getDirectory() async {
// getTemporaryDirectory
final directory = await getExternalStorageDirectory();
return directory!.path;
}
// 语音文件名称
static String generateVoiceFileName(){
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyyMMddHHmmss').format(now);
return 'voice_$formattedDate.mp4';
}
} }
差异被折叠。
...@@ -103,6 +103,17 @@ dependencies: ...@@ -103,6 +103,17 @@ dependencies:
tobias: ^3.3.0 tobias: ^3.3.0
# 内购 # 内购
flutter_inapp_purchase: ^5.6.1 flutter_inapp_purchase: ^5.6.1
encrypt: ^5.0.3
# 阿里oss直传
flutter_oss_aliyun: ^6.4.1
just_audio: ^0.9.36
# 缓存
flutter_cache_manager: ^3.3.1
# 解压
archive: ^3.1.2
# 判断当前网络情况
connectivity_plus: ^5.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论