提交 9855a239 authored 作者: maodou's avatar maodou

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

...@@ -229,8 +229,8 @@ abstract class LibraryAPI { ...@@ -229,8 +229,8 @@ abstract class LibraryAPI {
return false; return false;
} }
/// 11、离线下载 /// 11、获取离线书籍下载地址 和 秘钥串
static Future <BookDownloadModel> bookDownload({ static Future <BookDownloadModel> getbookDownloadParam({
required String bookId, required String bookId,
}) async { }) async {
final result = await HttpService.to.post( final result = await HttpService.to.post(
...@@ -243,7 +243,16 @@ abstract class LibraryAPI { ...@@ -243,7 +243,16 @@ abstract class LibraryAPI {
return BookDownloadModel.fromJson(result.data); return BookDownloadModel.fromJson(result.data);
} }
/// 12、离线重连 上传笔记、高亮、划线 /// 12、根据书籍地址下载书籍
static Future <void> downBookByUrl({
required String url,
required String savePath,
ProgressCallback? onReceiveProgress,
}) async {
await HttpService.to.download(url, savePath: savePath,onReceiveProgress: onReceiveProgress);
}
/// 13、离线重连 上传笔记、高亮、划线
static Future <bool> uploadContent({ static Future <bool> uploadContent({
required String bookId, required String bookId,
required String readTypes required String readTypes
...@@ -260,4 +269,36 @@ abstract class LibraryAPI { ...@@ -260,4 +269,36 @@ abstract class LibraryAPI {
return false; return false;
} }
/// 14、添加笔记、高亮、划线的内容
static Future <bool> addNote({
required String bookId,
required String chapterId,
String types = '3',
required String content,
required String isOpen,
String color= '',
required String positioning,
required String noteContent,
}) async {
final result = await HttpService.to.post(
'/v1/book/Information/addNotes',
params: {
'book_id':bookId,
'chapter_id':chapterId,
'types':types,
'content':content,
'is_open':isOpen,
'color':content,
'positioning':positioning,
'note_content':noteContent
},
);
if (result.data is Map && result.data['is_success'] == 1){
return true;
}
return false;
}
} }
\ No newline at end of file
...@@ -17,43 +17,45 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -17,43 +17,45 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
late ToolModel toolModel = tools.first; late ToolModel toolModel = tools.first;
// 输入框窗口是否展示
bool showChat = false; bool showChat = false;
// 0 讨论 1 笔记
late int chatType = 0;
// 笔记是否公开
bool isPublic = false;
// 笔记位置信息
String notePosition = '';
// 话题焦点
final FocusNode discussTitleFocusNode = FocusNode(); final FocusNode discussTitleFocusNode = FocusNode();
// 内容焦点
final FocusNode discussContentFocusNode = FocusNode(); final FocusNode discussContentFocusNode = FocusNode();
late AnimationController _controller;
bool _show = true;
bool get show => _show;
// 讨论添加的图片数组
List <String> discussInputImages= [];
// 讨论话题标题 // 讨论话题标题
final TextEditingController titleInput = TextEditingController(); final TextEditingController titleInput = TextEditingController();
// 讨论内容 // 讨论内容
final TextEditingController contentInput = TextEditingController(); final TextEditingController contentInput = TextEditingController();
// //
// 讨论添加的图片path数组
List <String> discussInputImages= [];
// 讨论添加的语音path数组
List <String> discussInputAudios= [];
// 笔记标题
String noteTitle = '';
bool _show = true;
bool get show => _show;
///------------------------------------------ 页面 生命周期-------------------------------------------------------- ///------------------------------------------ 页面 生命周期--------------------------------------------------------
@override @override
void onInit() { void onInit() {
// pageController = PageController(initialPage: currentPage);
discussTitleFocusNode.addListener(_onCommentFocusChanged); discussTitleFocusNode.addListener(_onCommentFocusChanged);
/// 默认不显示状态栏
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
/// 初始化
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 100),
);
super.onInit(); super.onInit();
} }
@override @override
void onReady() { void onReady() {
// 上报开始阅读时间 // 上报开始阅读时间
print('000000000000000000000--------------------------------$bookId');
_addReadTime(type: 'open'); _addReadTime(type: 'open');
_getChapters(); _getChapters();
super.onReady(); super.onReady();
...@@ -63,7 +65,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -63,7 +65,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
void onClose() { void onClose() {
// 上报阅读结束时间 // 上报阅读结束时间
_addReadTime(type: 'close'); _addReadTime(type: 'close');
_controller.dispose();
discussTitleFocusNode.removeListener(_onCommentFocusChanged); discussTitleFocusNode.removeListener(_onCommentFocusChanged);
discussTitleFocusNode.dispose(); discussTitleFocusNode.dispose();
titleInput.dispose(); titleInput.dispose();
...@@ -74,37 +75,40 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -74,37 +75,40 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
void setShow(bool value) { void setShow(bool value) {
_show = !value; _show = !value;
if (_show) {
/// 显示状态栏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
/// 开启动画
_controller.forward();
}
else {
/// 不显示状态栏
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
/// 收回动画
_controller.reverse();
}
update(); update();
} }
// 添加讨论图片 // 添加讨论图片
void addDiscussInputImages(String path){ void addDiscussInputImages(String path){
discussInputImages.add(path); discussInputImages.add(path);
print('discussInputImages--------------------------------$path'); Console.log('discussInputImages--------------------------------$path');
update(); update();
} }
// 删除讨论图片 // 删除讨论图片
void delDiscussInputImages(String path){ void delDiscussInputImages(String path){
discussInputImages.remove(path); discussInputImages.remove(path);
print('delDiscussInputImages--------------------------------$path'); Console.log('delDiscussInputImages--------------------------------$path');
update(); update();
} }
// 清空讨论图片 // 清空讨论图片
void clearDiscussInputImages(){ void clearDiscussInputImages(){
discussInputImages.clear(); discussInputImages.clear();
print('clearDiscussInputImages--------------------------------'); Console.log('clearDiscussInputImages--------------------------------');
update();
}
// 情况语音
void clearDiscussAudios(){
discussInputAudios.clear();
Console.log('clearDiscussAudios--------------------------------');
update();
}
// 清空所有已经填写的数据
void clearAllDiscussInput(){
discussInputImages.clear();
discussInputAudios.clear();
titleInput.text = '';
contentInput.text = '';
Console.log('clearAllDiscussInput--------------------------------');
update(); update();
} }
...@@ -117,6 +121,81 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -117,6 +121,81 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
return result; return result;
} }
// 提交
Future<bool> submit() async {
// 音频链接数组
List<String> audios = [];
// 图片链接数组
List<String> images = [];
// 循环上传图片获取地址
for(String path in discussInputImages){
final url = await upload(path: path);
images.add(url);
}
// 循环上传音频获取地址
for(String path in discussInputAudios){
final url = await upload(path: path);
audios.add(url);
}
Map<String,dynamic> contentMap = {
'text':contentInput.text,
'audio':audios,
'image':images
};
// 话题
if (chatType == 0){
final result = await addDiscuss();
return result;
}
// 笔记
else if (chatType == 1){
final result = addNote();
return result;
}
return false;
}
// 添加笔记
Future<bool> addNote() async {
// 音频链接数组
List<String> audios = [];
// 图片链接数组
List<String> images = [];
// 循环上传图片获取地址
for(String path in discussInputImages){
final url = await upload(path: path);
images.add(url);
}
// 循环上传音频获取地址
for(String path in discussInputAudios){
final url = await upload(path: path);
audios.add(url);
}
Map<String,dynamic> contentMap = {
'text':contentInput.text,
'audio':audios,
'image':images
};
final result = await LibraryAPI.addNote(
bookId: bookId,
chapterId: chapterId,
content: noteTitle,
isOpen: isPublic?'1':'0',
positioning: notePosition,
noteContent: jsonEncode(contentMap)
);
return result;
}
// 发表评论 // 发表评论
// {String commentId ='0',String quoteContent ='',String title = ''} // {String commentId ='0',String quoteContent ='',String title = ''}
Future<bool> addDiscuss() async{ Future<bool> addDiscuss() async{
...@@ -131,6 +210,12 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -131,6 +210,12 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
images.add(url); images.add(url);
} }
// 循环上传音频获取地址
for(String path in discussInputAudios){
final url = await upload(path: path);
audios.add(url);
}
Map<String,dynamic> contentMap = { Map<String,dynamic> contentMap = {
'text':contentInput.text, 'text':contentInput.text,
'audio':audios, 'audio':audios,
...@@ -177,7 +262,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -177,7 +262,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
} }
AnimationController get controller => _controller; // AnimationController get controller => _controller;
void _onCommentFocusChanged() { void _onCommentFocusChanged() {
if (discussTitleFocusNode.hasFocus || discussContentFocusNode.hasFocus) { if (discussTitleFocusNode.hasFocus || discussContentFocusNode.hasFocus) {
...@@ -187,10 +272,25 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -187,10 +272,25 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
} }
} }
// 展示输入框
void setShowChat(bool value) { void setShowChat(bool value) {
showChat = value; showChat = value;
update(); update();
} }
// 显示输入框类型
void setChatType(int type){
chatType = type;
}
// 设置笔记是否公开
void setIsPublic(){
isPublic = !isPublic;
update();
}
// void setNoteTitle(String title){
// noteTitle = title;
// // update();
// }
...@@ -204,12 +304,32 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -204,12 +304,32 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
void _addReadTime({required type}) async{ void _addReadTime({required type}) async{
final result = await LibraryAPI.addReadTime(bookId: bookId, readTypes: type); final result = await LibraryAPI.addReadTime(bookId: bookId, readTypes: type);
} }
/// 获取离线文件路径
void getBookDown() async{
final result = await LibraryAPI.getbookDownloadParam(bookId: bookId);
Console.log('----------_getBookDown------------------${result.download}');
final String savePath = await _getDocumentsDirectory();
LibraryAPI.downBookByUrl(url: result.download!,savePath: '$savePath$bookId.zip',onReceiveProgress: (int received,int total){
if (total !=-1){
double progress = (received / total) * 100;
Console.log('Download progress: $progress%');
}
});
}
//获取存储目录
Future<String> _getDocumentsDirectory() async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
///------------------------------------------ app 生命周期-------------------------------------------------------- ///------------------------------------------ app 生命周期--------------------------------------------------------
// 当应用程序从后台切换到前台并变为活动状态时调用。这通常在用户从其他应用程序返回到你的应用程序时发生 // 当应用程序从后台切换到前台并变为活动状态时调用。这通常在用户从其他应用程序返回到你的应用程序时发生
void onResumed(){ void onResumed(){
print('onResumed'); Console.log('onResumed');
// open // open
// 上报开始阅读时间 // 上报开始阅读时间
_addReadTime(type: 'open'); _addReadTime(type: 'open');
...@@ -217,20 +337,20 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -217,20 +337,20 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// 当应用程序失去焦点并进入非活动状态时调用。这可能是因为用户切换到其他应用程序或将应用程序最小化 // 当应用程序失去焦点并进入非活动状态时调用。这可能是因为用户切换到其他应用程序或将应用程序最小化
void onPaused(){ void onPaused(){
// close // close
print('onPaused'); Console.log('onPaused');
// 上报阅读结束时间 // 上报阅读结束时间
_addReadTime(type: 'close'); _addReadTime(type: 'close');
} }
// 当应用程序失去焦点但仍然可见时调用。通常,在用户切换到另一个应用程序或显示系统对话框时,应用程序可能会处于非活动状态,但仍然是可见的 // 当应用程序失去焦点但仍然可见时调用。通常,在用户切换到另一个应用程序或显示系统对话框时,应用程序可能会处于非活动状态,但仍然是可见的
void onInactive(){ void onInactive(){
print('onInactive'); Console.log('onInactive');
// close // close
// 上报阅读结束时间 // 上报阅读结束时间
_addReadTime(type: 'close'); _addReadTime(type: 'close');
} }
// 当应用程序被挂起,可能是由于用户关闭应用程序或系统资源不足时调用。在这个状态下,应用程序的代码将不再运行,并且可能被系统终止 // 当应用程序被挂起,可能是由于用户关闭应用程序或系统资源不足时调用。在这个状态下,应用程序的代码将不再运行,并且可能被系统终止
void onDetached(){ void onDetached(){
print('onDetached'); Console.log('onDetached');
// close // close
// 上报阅读结束时间 // 上报阅读结束时间
_addReadTime(type: 'close'); _addReadTime(type: 'close');
......
...@@ -12,6 +12,7 @@ import 'package:flutter_book/utils/index.dart'; ...@@ -12,6 +12,7 @@ import 'package:flutter_book/utils/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';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:path_provider/path_provider.dart';
import '../../apis/index.dart'; import '../../apis/index.dart';
import '../../models/index.dart'; import '../../models/index.dart';
......
...@@ -15,13 +15,13 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> { ...@@ -15,13 +15,13 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
color: Colors.green, color: Colors.white,
margin: EdgeInsets.symmetric(horizontal: 15.w), margin: EdgeInsets.symmetric(horizontal: 15.w),
child: Column( child: Column(
children: [ children: [
Container( Container(
margin: EdgeInsets.symmetric(vertical: 10.w), margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row( child: widget.controller.chatType ==0?Row(
children: [ children: [
Text('话题',style: TextStyle(fontSize: 14.w,color: Colours.c3,height: 1.5,fontWeight: Fonts.medium),), Text('话题',style: TextStyle(fontSize: 14.w,color: Colours.c3,height: 1.5,fontWeight: Fonts.medium),),
Gaps.hGaps5, Gaps.hGaps5,
...@@ -41,11 +41,14 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> { ...@@ -41,11 +41,14 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
filled: true, filled: true,
fillColor: Colours.cF8, fillColor: Colours.cF8,
), ),
onSubmitted: (_){
FocusScope.of(context).requestFocus(widget.controller.discussContentFocusNode);
},
), ),
), ),
), ),
], ],
), ):Text('"${widget.controller.noteTitle}"',style: TextStyle(fontSize: 12.w,height: 1.4,color: Colours.c9),),
), ),
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(4.w), borderRadius: BorderRadius.circular(4.w),
...@@ -59,6 +62,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> { ...@@ -59,6 +62,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
TextField( TextField(
focusNode: widget.controller.discussContentFocusNode, focusNode: widget.controller.discussContentFocusNode,
maxLines: null, maxLines: null,
autofocus: widget.controller.chatType ==0?false:true,
controller: widget.controller.contentInput, controller: widget.controller.contentInput,
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
...@@ -174,11 +178,25 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> { ...@@ -174,11 +178,25 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
}, },
child: Image.asset('assets/images/read_add_audio.png') child: Image.asset('assets/images/read_add_audio.png')
), ),
widget.controller.chatType ==0?const SizedBox():GestureDetector(
onTap: (){
widget.controller.setIsPublic();
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Gaps.hGaps10,
Image.asset(widget.controller.isPublic?'assets/images/pay_check.png':'assets/images/pay_uncheck.png'),
SizedBox(width: 3.w,),
Text('公开',style: TextStyle(fontSize: 13.w,height: 1.3,color: Colours.c9),)
],
),
)
], ],
), ),
GestureDetector( GestureDetector(
onTap: (){ onTap: (){
widget.controller.addDiscuss(); widget.controller.submit();
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
......
...@@ -146,6 +146,7 @@ class HttpService extends GetxService { ...@@ -146,6 +146,7 @@ class HttpService extends GetxService {
try{ try{
final requestOptions = options ?? Options(); final requestOptions = options ?? Options();
requestOptions.headers = _getHeaders(excludeToken: excludeToken,url: url); requestOptions.headers = _getHeaders(excludeToken: excludeToken,url: url);
requestOptions.responseType = ResponseType.bytes;
await _dio.download( await _dio.download(
url, url,
savePath, savePath,
...@@ -153,9 +154,10 @@ class HttpService extends GetxService { ...@@ -153,9 +154,10 @@ class HttpService extends GetxService {
cancelToken: cancelToken, cancelToken: cancelToken,
onReceiveProgress: onReceiveProgress onReceiveProgress: onReceiveProgress
); );
Console.log('Download completed:$savePath'); Console.log('Download completed:$savePath');
}catch(e){ }catch(e){
Console.log(e); Console.log('Error during download: $e');
rethrow; rethrow;
} }
} }
...@@ -167,7 +169,10 @@ class HttpService extends GetxService { ...@@ -167,7 +169,10 @@ 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 {
if (response.data['code'] != 200) { final responseData = response.data;
if (responseData is Map && responseData.containsKey('code')) {
final code = responseData['code'];
if (code != 200) {
handler.reject( handler.reject(
DioException( DioException(
requestOptions: response.requestOptions, requestOptions: response.requestOptions,
...@@ -176,9 +181,24 @@ class _RequestInterceptor extends Interceptor { ...@@ -176,9 +181,24 @@ class _RequestInterceptor extends Interceptor {
), ),
true, true,
); );
}else { return;
super.onResponse(response, handler); }
} 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);
// }
} }
@override @override
...@@ -206,6 +226,7 @@ class _RequestInterceptor extends Interceptor { ...@@ -206,6 +226,7 @@ class _RequestInterceptor extends Interceptor {
var msg = '服务器错误'; var msg = '服务器错误';
switch (statusCode) { switch (statusCode) {
case 403: case 403:
print('-------------------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) {
...@@ -233,6 +254,8 @@ class _RequestInterceptor extends Interceptor { ...@@ -233,6 +254,8 @@ class _RequestInterceptor extends Interceptor {
} else { } else {
UserStore.to.logout(); UserStore.to.logout();
CustomToast.fail('登录已失效,请重新登录'); CustomToast.fail('登录已失效,请重新登录');
print('-------------------access_token-------------------------${UserStore.to.accessToken}--------------------');
} }
break; break;
case 404: case 404:
......
...@@ -19,7 +19,7 @@ class CustomCard extends StatelessWidget { ...@@ -19,7 +19,7 @@ class CustomCard extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.cyan, color: Colors.white,
borderRadius: borderRadius ?? BorderRadius.circular(3), borderRadius: borderRadius ?? BorderRadius.circular(3),
boxShadow: boxShadow ?? [ boxShadow: boxShadow ?? [
BoxShadow( BoxShadow(
...@@ -33,7 +33,7 @@ class CustomCard extends StatelessWidget { ...@@ -33,7 +33,7 @@ class CustomCard extends StatelessWidget {
height: height, height: height,
width: width, width: width,
child: Container( child: Container(
padding: const EdgeInsets.all(1), padding: const EdgeInsets.all(2),
child: CustomImage.network(url: url,placeholder: Image.asset('assets/images/book_placeholder.png'),), child: CustomImage.network(url: url,placeholder: Image.asset('assets/images/book_placeholder.png'),),
), ),
); );
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论