提交 d9047aec authored 作者: maodou's avatar maodou

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

# Conflicts: # lib/pages/book_pay/controller.dart # pubspec.yaml
......@@ -4,6 +4,8 @@ PODS:
- Flutter (1.0.0)
- flutter_app_update (0.0.1):
- Flutter
- flutter_inapp_purchase (0.0.1):
- Flutter
- flutter_inappwebview (0.0.1):
- Flutter
- flutter_inappwebview/Core (= 0.0.1)
......@@ -36,12 +38,18 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- tobias (0.0.1):
- Flutter
- tobias/normal (= 0.0.1)
- tobias/normal (0.0.1):
- Flutter
- WechatOpenSDK-XCFramework (2.0.2)
DEPENDENCIES:
- audio_session (from `.symlinks/plugins/audio_session/ios`)
- Flutter (from `Flutter`)
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
- flutter_inapp_purchase (from `.symlinks/plugins/flutter_inapp_purchase/ios`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
- flutter_sound (from `.symlinks/plugins/flutter_sound/ios`)
- flutter_tts (from `.symlinks/plugins/flutter_tts/ios`)
......@@ -51,6 +59,7 @@ DEPENDENCIES:
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- tobias (from `.symlinks/plugins/tobias/ios`)
SPEC REPOS:
trunk:
......@@ -65,6 +74,8 @@ EXTERNAL SOURCES:
:path: Flutter
flutter_app_update:
:path: ".symlinks/plugins/flutter_app_update/ios"
flutter_inapp_purchase:
:path: ".symlinks/plugins/flutter_inapp_purchase/ios"
flutter_inappwebview:
:path: ".symlinks/plugins/flutter_inappwebview/ios"
flutter_sound:
......@@ -83,11 +94,14 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/permission_handler_apple/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
tobias:
:path: ".symlinks/plugins/tobias/ios"
SPEC CHECKSUMS:
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_app_update: 65f61da626cb111d1b24674abc4b01728d7723bc
flutter_inapp_purchase: 5c6a1ac3f11b11d0c8c0321c0c41c1f05805e4c8
flutter_inappwebview: 3d32228f1304635e7c028b0d4252937730bbc6cf
flutter_sound: c60effa2a350fb977885f0db2fbc4c1ad5160900
flutter_sound_core: 26c10e5832e76aaacfae252d8925232281c486ae
......@@ -99,6 +113,7 @@ SPEC CHECKSUMS:
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
tobias: 22f69183b14af82a2ac8ff698af084c352744e5f
WechatOpenSDK-XCFramework: acdeeda129efbef9532bca8a10c24e1b4b8c7d69
PODFILE CHECKSUM: d88b567994d32cfdf2f8417e66666d472921c85e
......
......@@ -153,11 +153,13 @@ abstract class CommonAPI {
/// 10、上传文件
static Future <String> upload({
required String path
required String path,
required String fileTypes
}) async {
final result = await HttpService.to.upload(
'/v1/members/Information/uploadFile',
path: path
path: path,
fileTypes: fileTypes
);
if (result.data is! Map) return '';
return result.data['filesUrl'];
......
......@@ -366,5 +366,23 @@ abstract class MineAPI {
}
return false;
}
/// 20、紫荆币充值创建订单
static Future<PayOrderModel> createRechargeOrder(
{
required String identifying,
required String type,
}) async {
final result = await HttpService.to.post(
'/v1/orders/Orders/createBeanorders',
params: {
'type': type,
'identifying': identifying,
},
);
if (result.data is! Map) return PayOrderModel();
return PayOrderModel.fromJson(result.data);
}
}
......@@ -134,19 +134,19 @@ abstract class ShopAPI {
}
/// 9、获取订单状态
static Future <bool> orderStatus({
static Future <OrderStatusModel> orderStatus({
required String orderNumber,
required String receipt
}) async {
final result = await HttpService.to.post(
'/v1/orders/Orders/getOrdersStatus',
params: {
'ordersnum':orderNumber,
'receipt':receipt
},
);
if (result.data is Map && result.data['pay_success'] == 1){
return true;
}
return false;
if (result.data is! Map ) return OrderStatusModel();
return OrderStatusModel.fromJson(result.data);
}
......
......@@ -237,3 +237,29 @@ class PayOrderModel {
}
}
/// 订单状态模型
class OrderStatusModel {
OrderStatusModel({
this.paySuccess,
this.ordersnum,});
OrderStatusModel.fromJson(dynamic json) {
paySuccess = json['pay_success'];
ordersnum = json['ordersnum'];
}
num? paySuccess;
String? ordersnum;
OrderStatusModel copyWith({ num? paySuccess,
String? ordersnum,
}) => OrderStatusModel( paySuccess: paySuccess ?? this.paySuccess,
ordersnum: ordersnum ?? this.ordersnum,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['pay_success'] = paySuccess;
map['ordersnum'] = ordersnum;
return map;
}
}
\ No newline at end of file
......@@ -27,7 +27,7 @@ class BookDetailController extends GetxController with GetSingleTickerProviderSt
@override
void onReady() {
_getBookDetails();
getBookDetails();
_getChapters();
super.onReady();
}
......@@ -54,7 +54,7 @@ class BookDetailController extends GetxController with GetSingleTickerProviderSt
}
/// 获取图书详细信息
void _getBookDetails() async {
void getBookDetails() async {
bookDetails = await LibraryAPI.details(bookId:bookId);
update();
}
......@@ -73,7 +73,7 @@ class BookDetailController extends GetxController with GetSingleTickerProviderSt
bookId: bookId, love: isCollection.toString());
print('================================$result');
if (result) {
_getBookDetails();
getBookDetails();
}
}
......
......@@ -124,7 +124,7 @@ class _BookDetailPageState extends State<BookDetailPage> with SingleTickerProvid
Gaps.hGaps10,
Expanded(
child: GestureDetector(
onTap: (){
onTap: () async{
// context.pushNamed(Routes.creditPoints).then((value){
// print('---------------------$value');
// });
......@@ -143,7 +143,11 @@ class _BookDetailPageState extends State<BookDetailPage> with SingleTickerProvid
selected: true
);
buy.add(model);
context.pushNamed(Routes.bookPay,extra: buy);
final result = await context.pushNamed(Routes.bookPay,extra: buy);
if (result == true) {
controller.getBookDetails();
}
}
else {
context.pushNamed(Routes.web);
......
part of book_pay;
class BookPayController extends GetxController {
final BuildContext context;
// 购物车选中的书籍列表
final List<CourseModel> buy;
BookPayController({required this.buy});
BookPayController({required this.buy,required this.context});
// 积分模型
CreditPointModel creditPointModel = CreditPointModel(deductibleIntegral: 0);
// 是否展示优惠券和积分模型
......@@ -22,13 +23,14 @@ class BookPayController extends GetxController {
List<PayModel> pays = Platform.isIOS ?[
PayModel(type: 3, name: '紫荆币', icon: 'assets/images/pay_coin.png', selected: true),
]:[
PayModel(type: 1, name: '微信', icon: 'assets/images/pay_wechat.png', selected: true),
// PayModel(type: 1, name: '微信', icon: 'assets/images/pay_wechat.png', selected: true),
PayModel(type: 2, name: '支付宝', icon: 'assets/images/pay_ali.png', selected: false),
PayModel(type: 3, name: '紫荆币', icon: 'assets/images/pay_coin.png', selected: false),
];
// 支付宝
// Tobias tobias = Tobias();
Tobias tobias = Tobias();
// 微信
Fluwx fluwx = Fluwx();
// 时候展示底部视图
......@@ -162,11 +164,11 @@ class BookPayController extends GetxController {
}
// 支付宝
if (payModel.type == 2){
// final result = await tobias.isAliPayInstalled;
// if(!result){
// Toast.show('请先安装支付宝');
// return;
// }
final result = await tobias.isAliPayInstalled;
if(!result){
Toast.show('请先安装支付宝');
return;
}
}
// 微信
if (payModel.type ==1){
......@@ -213,13 +215,13 @@ class BookPayController extends GetxController {
void requestAliPay(){
print('================================================================${payOrderModel.encryptionOrder!}');
// tobias.pay(payOrderModel.encryptionOrder!).then((payResult){
// if (payResult['resultStatus'] == '9000') {
// requestOrderStatus();
// } else {
// Toast.show(payResult['memo'].toString());
// }
// });
tobias.pay(payOrderModel.encryptionOrder!).then((payResult){
if (payResult['resultStatus'] == '9000') {
requestOrderStatus();
} else {
Toast.show(payResult['memo'].toString());
}
});
}
// 微信支付
......@@ -245,12 +247,16 @@ class BookPayController extends GetxController {
}
void requestOrderStatus() async {
final result = await ShopAPI.orderStatus(orderNumber: payOrderModel.ordersnum??'');
if (result){
final result = await ShopAPI.orderStatus(orderNumber: payOrderModel.ordersnum??'',receipt: '');
if (result.paySuccess == 1){
Toast.show('订单支付完成');
if(context.mounted){
context.pop(true);
}
}
}
......
......@@ -16,7 +16,7 @@ class _BookPayPageState extends State<BookPayPage> {
@override
Widget build(BuildContext context) {
return GetBuilder<BookPayController>(
init: BookPayController(buy: widget.buy),
init: BookPayController(buy: widget.buy,context: context),
builder: (controller) => Stack(
children: [
Scaffold(
......
......@@ -15,7 +15,7 @@ class BuildPayCount extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetBuilder<BookPayController>(
init: BookPayController(buy: []),
init: BookPayController(buy: [],context: context),
builder:(controller)=> Container(
padding: EdgeInsets.symmetric(horizontal: 15.w),
height: 55.w,
......
......@@ -143,7 +143,7 @@ class BookshopController extends GetxController {
}
/// 获取课程内图书列表
Future<void> _getCart([bool isRefresh = false]) async {
Future<void> getCart([bool isRefresh = false]) async {
if (isRefresh) _page = 1;
// 网路请求
final result = await ShopAPI.cart(
......@@ -163,7 +163,7 @@ class BookshopController extends GetxController {
void onRefresh() async {
try {
await _getCart(true);
await getCart(true);
refreshController.finishRefresh(IndicatorResult.success);
refreshController.resetFooter();
} catch (error) {
......@@ -177,7 +177,7 @@ class BookshopController extends GetxController {
return;
}
try {
await _getCart();
await getCart();
refreshController.finishLoad();
} catch (error) {
refreshController.finishLoad(IndicatorResult.fail);
......
......@@ -50,9 +50,12 @@ class BuildCounter extends StatelessWidget {
],
),
GestureDetector(
onTap: (){
onTap: () async {
if (controller.num > 0){
context.pushNamed(Routes.bookPay,extra: controller.buy);
final result = await context.pushNamed(Routes.bookPay,extra: controller.buy);
if (result == true) {
controller.getCart();
}
}
},
child: Container(
......
......@@ -32,7 +32,9 @@ class _CoursePageState extends State<CoursePage> {
icon: Image.asset(
'assets/images/read_history.png',
),
onPressed: () => context.pushNamed(Routes.studyHistory),
onPressed: () async {
context.pushNamed(Routes.studyHistory);
},
),
GestureDetector(
onTap: () async{
......
......@@ -30,16 +30,19 @@ class BuildBanner extends StatelessWidget {
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.w),
color: Color.fromRGBO(
random.nextInt(256), // Red
random.nextInt(256), // Green
random.nextInt(256), // Blue
1.0, // Alpha (opacity)
),
// color: Color.fromRGBO(
// random.nextInt(256), // Red
// random.nextInt(256), // Green
// random.nextInt(256), // Blue
// 1.0, // Alpha (opacity)
// ),
),
child: Image.network(item.pic??''),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.w),
child: Image.network(item.pic??'',)
),
// child: CustomImage.asset(
// url: 'assets/images/banner.png',
// width: 130.w,
......
......@@ -13,7 +13,7 @@ class CreditPointsPage extends StatelessWidget {
return false;
},
child: GetBuilder<BookPayController>(
init: BookPayController(buy: []),
init: BookPayController(buy: [],context: context),
builder:(controller)=> Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
......
......@@ -58,10 +58,10 @@ class ForgetPwdController extends GetxController {
// 定时器回调
});
/// 测试账号
if (kDebugMode) {
phoneInput.text = '13521054068';
codeInput.text = '123456';
}
// if (kDebugMode) {
// phoneInput.text = '13521054068';
// codeInput.text = '123456';
// }
super.onInit();
}
......
......@@ -76,14 +76,15 @@ class _LibraryPageState extends State<LibraryPage> {
child: Expanded(
child: CustomPullScrollView(
controller: controller.refreshController,
// onRefresh: controller.onRefresh,
onRefresh: controller.onRefresh,
onLoading: controller.onLoading,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
controller.ads.isNotEmpty?Container(
color: Colors.cyan,
color: Colors.transparent,
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: BuildBanner(items:controller.ads)
):const SizedBox(),
......@@ -136,7 +137,7 @@ class _LibraryPageState extends State<LibraryPage> {
Widget _buildCategory(){
return Container(
height: 38,
height: 38.w,
child: GetBuilder<LibraryController>(
init: LibraryController(),
builder: (controller) =>ListView.builder(
......
......@@ -39,7 +39,7 @@ class LibraryCell extends StatelessWidget {
Container(
width: 100.w,
height: 120.w,
color: Colors.red,
color: Colors.white,
),
/// 右侧
Expanded(
......@@ -89,7 +89,7 @@ class LibraryCell extends StatelessWidget {
top: 0,
bottom: 18,
child: CustomCard(
width: 100,
width: 100.w,
url: model.img??'',
)
),
......
......@@ -87,10 +87,10 @@ class LoginController extends GetxController {
// 定时器回调
});
/// 测试账号
// if (kDebugMode) {
// phoneInput.text = '13521054068';
// passwordInput.text = '123456';
// }
if (kDebugMode) {
phoneInput.text = '13521054068';
passwordInput.text = '123456';
}
super.onInit();
}
......
......@@ -70,8 +70,8 @@ class MineController extends GetxController {
ReadModel(
name: '订单',
value: userInfo.ordersNums.toString(),
// link: Routes.order,
link: Routes.orderRefunded,
link: Routes.order,
// link: Routes.orderRefunded,
icon: 'assets/images/order.png')
];
update();
......
......@@ -102,7 +102,12 @@ class _MinePageState extends State<MinePage> {
child: BuildBanner(items:controller.ads),
):const SizedBox(),
Gaps.vGaps5,
BuildAccount(items:controller.accounts,),
BuildAccount(items:controller.accounts,onTap: (ReadModel model) async{
final result = await context.pushNamed(model.link??'');
if (result == true){
controller.getInfo();
}
},),
Gaps.vGaps10,
Container(
margin: EdgeInsets.symmetric(horizontal: AppTheme.margin),
......
......@@ -2,7 +2,7 @@ part of mine;
class BuildAccount extends StatelessWidget {
final List<ReadModel> items ;
final void Function()? onTap;
final void Function(ReadModel model)? onTap;
const BuildAccount({
super.key,
this.onTap,
......@@ -41,10 +41,16 @@ class BuildAccount extends StatelessWidget {
child: GestureDetector(
onTap: (){
if (model.link != null){
if (model.link != Routes.love){
context.pushNamed(model.link!);
if (onTap !=null) onTap;
}
else{
if (onTap !=null) onTap!(model);
}
}
},
child: Container(
color: Colors.white,
child: Column(
children: [
SizedBox(
......@@ -65,6 +71,7 @@ class BuildAccount extends StatelessWidget {
],
),
),
),
);
} ).toList(),
),
......
......@@ -68,7 +68,7 @@ class _ResetPwdPageState extends State<ResetPwdPage> {
),
Container(
margin: EdgeInsets.only(left: 5.w,top: 5.w),
child: Text('密码必须是8-20个英文字母、数字或符号',style: TextStyle(fontSize: 10.w,color: Colours.c9),),
child: Text('密码必须是8-20个英文字母、数字或符号(除空格)',style: TextStyle(fontSize: 10.w,color: Colours.c9),),
),
Gaps.vGaps40,
CustomGradientButton(
......
......@@ -21,7 +21,7 @@ class UserCoinController extends GetxController {
/// 获取紫金币记录
Future<void> _getCoin([bool isRefresh = false]) async {
Future<void> getCoin([bool isRefresh = false]) async {
if (isRefresh) _page = 1;
// 网路请求
final result = await MineAPI.coin(
......@@ -39,7 +39,7 @@ class UserCoinController extends GetxController {
void onRefresh() async {
try {
await _getCoin(true);
await getCoin(true);
refreshController.finishRefresh(IndicatorResult.success);
refreshController.resetFooter();
} catch (error) {
......@@ -53,7 +53,7 @@ class UserCoinController extends GetxController {
return;
}
try {
await _getCoin();
await getCoin();
refreshController.finishLoad();
} catch (error) {
refreshController.finishLoad(IndicatorResult.fail);
......
library user_coin;
import 'dart:core';
import 'dart:io';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
import 'package:flutter_inapp_purchase/modules.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluwx/fluwx.dart';
import 'package:get/get.dart';
import 'package:tobias/tobias.dart';
import '../../apis/index.dart';
import '../../models/index.dart';
import '../../services/index.dart';
import '../user_coin_recharge/index.dart';
part 'view.dart';
......
......@@ -24,10 +24,10 @@ class _UserCoinPageState extends State<UserCoinPage> {
borderRadius: BorderRadius.vertical(top: Radius.circular(8.w)),
),
builder: (BuildContext context) {
return CoinRechargePage();
return const CoinRechargePage();
},
).then((value) {
print('================================================================');
controller.getCoin();
});
},
child: Text('充值',style: TextStyle(color: Colours.c3,fontSize: 14.w,height: 1.5,fontWeight: Fonts.medium),)
......
part of recharge;
class CoinRechargeController extends GetxController {
final BuildContext context;
CoinRechargeController({required this.context});
dynamic _purchaseUpdatedSubscription;
dynamic _purchaseErrorSubscription;
dynamic _connectionSubscription;
PurchasedItem? _resultItem;
List<IAPItem> _items = [];
List<PurchasedItem> _purchases = [];
///未完成的订单
List<PurchasedItem> _pendingPurchases = [];
// 支付宝
Tobias tobias = Tobias();
// 微信
Fluwx fluwx = Fluwx();
// 创建订单模型
late PayOrderModel payOrderModel;
List<PayModel> pays = Platform.isIOS ?[
]:[
PayModel(type: 1, name: '微信', icon: 'assets/images/pay_wechat.png', selected: true),
// PayModel(type: 1, name: '微信', icon: 'assets/images/pay_wechat.png', selected: true),
PayModel(type: 2, name: '支付宝', icon: 'assets/images/pay_ali.png', selected: false),
];
List<CoinModel> data = [];
......@@ -16,12 +36,36 @@ class CoinRechargeController extends GetxController {
late PayModel _payModel = pays.first;
PayModel get payModel => _payModel;
@override
void onInit() {
initPlatformState();
_getPendingPurchase();
super.onInit();
}
@override
void onReady() {
_getlist();
super.onReady();
}
@override
void onClose() {
if (_connectionSubscription != null) {
_connectionSubscription.cancel();
_connectionSubscription = null;
}
if (_purchaseUpdatedSubscription != null) {
_purchaseUpdatedSubscription.cancel();
_purchaseUpdatedSubscription = null;
}
if (_purchaseErrorSubscription != null) {
_purchaseErrorSubscription.cancel();
_purchaseErrorSubscription = null;
}
super.onClose();
}
void setPayModel(PayModel payModel){
for (PayModel model in pays) {
......@@ -45,7 +89,7 @@ class CoinRechargeController extends GetxController {
}
/// 获取充值列表
void _getlist()async{
String type = '';
if(Platform.isIOS){
......@@ -56,8 +100,199 @@ class CoinRechargeController extends GetxController {
}
final result = await MineAPI.coinsRechargeList(type: type);
data = result;
if(Platform.isIOS){
_getProduct();
}
update();
}
Future<void> initPlatformState() async{
var result = await FlutterInappPurchase.instance.initialize();
print('--------------initPlatformState-------------------------$result');
_connectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) {
print('connected: $connected');
});
_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((productItem) {
CustomToast.dismiss();
if(productItem != null){
_resultItem = productItem;
_requestOrderStatus();
}
});
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) {
CustomToast.dismiss();
Toast.show(purchaseError!.message.toString());
});
}
/// 获取商品列表
Future _getProduct() async {
List<String> productList = data
.where((coinModel) => coinModel.identifying != null)
.map((coinModel) => coinModel.identifying!)
.toList();
print('-------------productList-------------------$productList');
List<IAPItem> items = await FlutterInappPurchase.instance.getProducts(productList);
for (var item in items) {
_items.add(item);
}
print('-------------_items-------------------$items');
_items = items;
_purchases = [];
// update();
}
/// 创建订单
void createRechargeOrder() async {
String type = '';
if(Platform.isIOS){
type = '3';
}
else{
type = payModel.type.toString();
}
final result = await MineAPI.createRechargeOrder(identifying: rechargeModel.identifying??'', type: type);
print('-----------------------------------${result.ordersnum}--------------------------------${result.msg}');
payOrderModel = result;
// 创建订单成功开启支付流程
if (payOrderModel.ordersnum !=null){
if(payOrderModel.ordersnum!.isNotEmpty){
// 苹果
if(Platform.isIOS){
// 发起苹果支付
IAPItem targetItem = _items.firstWhere(
(item) => item.productId == rechargeModel.identifying,
);
_requestPurchase(targetItem);
}
// 安卓
else if(Platform.isAndroid){
// 发起支付宝支付
if (payModel.type == 2){
requestAliPay();
}
else if (payModel.type == 1){
requestWechat();
}
}
}
}
}
/// 获取后台关于当前单号的结算结果
void _requestOrderStatus() async {
final result = await ShopAPI.orderStatus(orderNumber: payOrderModel.ordersnum??'',receipt:_resultItem != null?_resultItem!.transactionReceipt.toString():'');
// 查询订单支付成功
if (result.paySuccess == 1){
Toast.show('购买紫荆币成功');
if(context.mounted){
context.pop(true);
}
if (Platform.isIOS){
// 清除
if (StorageService.to.getObject(kFailOrder) != null){
List<Map<String,String>> failOrderList = StorageService.to.getObject(kFailOrder) as List<Map<String, String>>;
List<Map<String,String>> temp = [];
temp.addAll(failOrderList);
for (var element in failOrderList) {
if(element['orderNum'] == result.ordersnum){
temp.remove(element);
}
}
StorageService.to.setObject(kFailOrder, temp);
}
FlutterInappPurchase.instance.finishTransactionIOS(_resultItem!.transactionId!);
}
}
// 查询订单支付失败
else {
Toast.show('购买紫荆币失败');
if(Platform.isIOS){
// 保存未支付的订单
Map<String,String> failOrder = {
'orderNum':payOrderModel.ordersnum??'',
'transactionReceipt':_resultItem!.transactionReceipt.toString(),
'transactionId':_resultItem!.transactionId.toString()
};
List<Map> failOrderList = [];
failOrderList.add(failOrder);
StorageService.to.setObject(kFailOrder, failOrderList);
}
else {
}
}
}
/// 苹果支付
void _requestPurchase(IAPItem item) {
CustomToast.loading();
FlutterInappPurchase.instance
.requestPurchase(item.productId!);
}
/// 支付宝支付
void requestAliPay(){
tobias.pay(payOrderModel.encryptionOrder!).then((payResult){
if (payResult['resultStatus'] == '9000') {
_requestOrderStatus();
} else {
Toast.show(payResult['memo'].toString());
}
});
}
/// 微信支付
void requestWechat(){
Payment payment = Payment(
appId: payOrderModel.appid??'',
partnerId: payOrderModel.partnerid??'',
prepayId: payOrderModel.prepayid??'',
packageValue: payOrderModel.package??'',
nonceStr: payOrderModel.noncestr??'',
timestamp: payOrderModel.timestamp!.toInt(),
sign: payOrderModel.sign??''
);
fluwx.pay(which: payment);
fluwx.addSubscriber((response) {
if (response.errCode == 0) {
_requestOrderStatus();
}
else{
Toast.show('${response.errStr}');
}
});
}
///获取未完成的购买
Future _getPendingPurchase() async {
List<PurchasedItem>? items = await FlutterInappPurchase.instance.getPurchaseHistory();
for (var item in items!) {
_pendingPurchases.add(item);
}
if(StorageService.to.getObject(kFailOrder) !=null){
List<Map<String,String>> failOrderList = StorageService.to.getObject(kFailOrder) as List<Map<String, String>>;
for (var item in _pendingPurchases) {
for (var element in failOrderList) {
if(element['transactionId'] == item.transactionId){
_requestOrderStatus();
}
}
}
}
}
}
\ No newline at end of file
library recharge;
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_book/apis/index.dart';
import 'package:flutter_book/models/index.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
import 'package:flutter_inapp_purchase/modules.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluwx/fluwx.dart';
import 'package:get/get.dart';
import 'package:get/get_state_manager/src/simple/get_controllers.dart';
import 'package:go_router/go_router.dart';
import 'package:tobias/tobias.dart';
import '../../services/index.dart';
import '../../utils/index.dart';
import '../../widgets/index.dart';
import '../book_pay/index.dart';
import 'dart:io';
import '../user_coin/index.dart';
part 'view.dart';
part 'controller.dart';
\ No newline at end of file
......@@ -9,11 +9,11 @@ class CoinRechargePage extends StatefulWidget {
State<CoinRechargePage> createState() => _CoinRechargePageState();
}
class _CoinRechargePageState extends State<CoinRechargePage> {
class _CoinRechargePageState extends State<CoinRechargePage> with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
return GetBuilder<CoinRechargeController>(
init: CoinRechargeController(),
init: CoinRechargeController(context: context),
builder:(controller) => Column(
mainAxisSize: MainAxisSize.min,
children: [
......@@ -63,7 +63,7 @@ class _CoinRechargePageState extends State<CoinRechargePage> {
text: '立即充值${controller.rechargeModel.priceName??''}',
isEnabled: true,
onPressed: () {
// context.goNamed(Routes.login);
controller.createRechargeOrder();
},
),
),
......@@ -133,4 +133,8 @@ class _CoinRechargePageState extends State<CoinRechargePage> {
itemCount: controller.pays.length,
);
}
@override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
......@@ -8,7 +8,7 @@ class UserInfoController extends GetxController {
void upload({
required String path
}) async {
String result = await CommonAPI.upload(path:path);
String result = await CommonAPI.upload(path:path,fileTypes: 'user');
_changeInfo(result);
}
......
......@@ -49,9 +49,6 @@ class _UserInfoPageState extends State<UserInfoPage> {
onTap: () async {
final assets = await AssetsPicker.image(
context: context,
source: ImageSource.gallery,
maxWidth: 512.w,
maxHeight: 512.w,
);
controller.upload(path: assets!.path);
},
......
......@@ -4,6 +4,13 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// 目录
List <ChapterModel> chapters = [];
List <ToolModel> tools = [
ToolModel(tag: 0,name: '目录',selected: false,icon: 'assets/images/category_unselect.png',activeIcon: 'assets/images/category_select.png'),
ToolModel(tag: 1,name: '笔记',selected: false,icon: 'assets/images/note_unselect.png',activeIcon:'assets/images/note_select.png'),
ToolModel(tag: 2,name: '讨论',selected: false,icon: 'assets/images/discuss_unselect.png',activeIcon:'assets/images/discuss_select.png'),
];
late ToolModel toolModel = tools.first;
// late final PageController pageController;
//
......@@ -33,6 +40,22 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
}
void chooseTool(ToolModel selectedModel){
for (var model in tools) {
// 如果当前遍历到的工具是选中的,并且不是点击的工具,则取消选中
if (model.selected && model != selectedModel) {
model.selected = false;
}
// 如果当前遍历到的工具是点击的工具,切换选中状态
else if (model == selectedModel) {
model.selected = !model.selected;
}
}
toolModel = selectedModel;
update();
}
AnimationController get controller => _controller;
......@@ -89,3 +112,12 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
update();
}
}
class ToolModel {
ToolModel({required this.tag, required this.selected, required this.name,required this.icon,required this.activeIcon});
int tag;
String name;
bool selected;
String icon;
String activeIcon;
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ library web;
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_book/theme.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
......
......@@ -69,7 +69,7 @@ class _ReadPageState extends State<ReadPage> {
right: 0,
top: 0,
bottom: 49 + MediaQuery.of(context).viewInsets.bottom,
child: _showContent(readController,currentIndex)
child: _showContent(readController,readController.toolModel)
),
AnimatedPositioned(
duration: readController.controller.duration!,
......@@ -81,98 +81,102 @@ class _ReadPageState extends State<ReadPage> {
child: Container(
color: Colors.limeAccent,
alignment: Alignment.center,
child: BottomNavigationBar(
currentIndex: currentIndex,
onTap: (index){
setState(() {
currentIndex = index;
});
// _showBottomSheet(context, index);
},
items: [
BottomNavigationBarItem(
label: '目录',
icon: Image.asset(
'assets/images/category_unselect.png',
),
activeIcon: Image.asset(
'assets/images/category_select.png',
),
),
BottomNavigationBarItem(
label: '笔记',
icon: Image.asset(
'assets/images/note_unselect.png',
),
activeIcon: Image.asset(
'assets/images/note_select.png',
)
),
BottomNavigationBarItem(
label: '讨论',
icon: Image.asset(
'assets/images/discuss_unselect.png',
child: _createToolBar(readController)
),
activeIcon: Image.asset(
'assets/images/discuss_select.png',
)
),
],
)
),
),
),
);
}
Widget _createToolBar(ReadController controller){
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: controller.tools.map((model){
return Expanded(
child: GestureDetector(
onTap: (){
controller.chooseTool(model);
},
child: Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
SizedBox(
width: 17,
height: 17,
child: Image.asset(model.selected?model.activeIcon:model.icon)
),
// SizedBox(height: 2.5.w,),
model.selected?Text(model.name,style: TextStyle(fontSize: 10.w,height: 1.4,fontWeight: Fonts.medium,color: AppTheme.primary),)
:Text(model.name,style: TextStyle(fontSize: 10.w,height: 1.4,fontWeight: Fonts.medium,color: Colours.c6))
],
),
),
),
);
}).toList()
),
);
}
Widget _showContent(ReadController controller,int index) {
print('++++++++++++++++++++++++$index');
Widget _showContent(ReadController controller,ToolModel model) {
print('++++++++++++++++++++++++${model.tag}');
if (controller.show){
if (index == 0){
return Container(
if (model.tag == 0){
return model.selected? Container(
color: 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: ReadCategoryPage(controller: controller,),
child: ReadCategoryPage(controller: controller,onTap: (){
controller.chooseTool(model);
},),
),
),
// child: ReadCategoryPage(),
);
):const SizedBox();
}
else if (index == 1){
return Container(
else if (model.tag == 1){
return model.selected? Container(
color: 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(),
child: ReadNotePage(onTap: (){
controller.chooseTool(model);
},),
),
),
// child: ReadCategoryPage(),
);
):const SizedBox();
}
else if (index == 2){
return Container(
else if (model.tag == 2){
return model.selected? Container(
color: 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(),
child: ReadDiscussPage(onTap: (){
controller.chooseTool(model);
},),
),
),
// child: ReadCategoryPage(),
);
):const SizedBox();
}
}
return const SizedBox();
......
......@@ -2,9 +2,11 @@ part of web;
class ReadCategoryPage extends StatefulWidget {
final ReadController controller;
final void Function()? onTap;
const ReadCategoryPage({
Key? key,
required this.controller,
required this.onTap,
}) : super(key: key);
@override
......@@ -24,7 +26,10 @@ class _ReadCategoryPageState extends State<ReadCategoryPage> {
children: [
Container(
margin: EdgeInsets.only(left: 15.w,right: 15.w,top: 15.w,bottom: 15.w),
child: Image.asset('assets/images/close.png')
child: GestureDetector(
onTap:widget.onTap,
child: Image.asset('assets/images/close.png'),
)
),
Container(
margin: EdgeInsets.only(left: 15.w,right: 15.w,),
......@@ -45,7 +50,7 @@ class _ReadCategoryPageState extends State<ReadCategoryPage> {
child: CustomInputSearch(
controller: searchController,
readOnly: false,
hintText: '请输入书籍名称',
hintText: '请输入书籍名称分类',
onTap: () {
// context.pushNamed(Routes.msgs);
},
......
part of web;
class ReadDiscussPage extends StatefulWidget {
const ReadDiscussPage({Key? key}) : super(key: key);
final void Function()? onTap;
const ReadDiscussPage({
Key? key,
required this.onTap,
}) : super(key: key);
@override
State<ReadDiscussPage> createState() => _ReadDiscussPageState();
......@@ -20,7 +24,10 @@ class _ReadDiscussPageState extends State<ReadDiscussPage> {
children: [
Container(
margin: EdgeInsets.only(left: 15.w,right: 15.w,top: 15.w,bottom: 15.w),
child: GestureDetector(
onTap: widget.onTap,
child: Image.asset('assets/images/close.png')
)
),
Container(
margin: EdgeInsets.only(left: 15.w,right: 15.w,),
......@@ -41,7 +48,7 @@ class _ReadDiscussPageState extends State<ReadDiscussPage> {
child: CustomInputSearch(
controller: searchController,
readOnly: false,
hintText: '请输入书籍名称',
hintText: '请输入书籍名称讨论',
onTap: () {
// context.pushNamed(Routes.msgs);
},
......@@ -52,12 +59,14 @@ class _ReadDiscussPageState extends State<ReadDiscussPage> {
),
),
BuildBook(),
ListView.builder(
Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context,int index){
DiscussModel model = controller.discuss[index];
return BuildDiscuss(model: model,);
},
itemCount: controller.discuss.length,
),
)
],
......
part of web;
class ReadNotePage extends StatefulWidget {
const ReadNotePage({Key? key}) : super(key: key);
final void Function()? onTap;
const ReadNotePage({
Key? key,
required this.onTap,
}) : super(key: key);
@override
State<ReadNotePage> createState() => _ReadNotePageState();
......@@ -18,7 +22,10 @@ class _ReadNotePageState extends State<ReadNotePage> {
children: [
Container(
margin: EdgeInsets.only(left: 15.w,right: 15.w,top: 15.w,bottom: 15.w),
child: GestureDetector(
onTap: widget.onTap,
child: Image.asset('assets/images/close.png')
)
),
Container(
margin: EdgeInsets.only(left: 15.w,right: 15.w,),
......@@ -39,7 +46,7 @@ class _ReadNotePageState extends State<ReadNotePage> {
child: CustomInputSearch(
controller: searchController,
readOnly: false,
hintText: '请输入书籍名称',
hintText: '请输入书籍名称笔记',
onTap: () {
// context.pushNamed(Routes.msgs);
},
......
......@@ -109,6 +109,7 @@ class HttpService extends GetxService {
Future<ResponseModel> upload(
String url, {
required String path,
required fileTypes,
Options? options,
CancelToken? cancelToken,
bool excludeToken = false,
......@@ -118,7 +119,10 @@ class HttpService extends GetxService {
requestOptions.headers = _getHeaders(excludeToken: excludeToken,url:url);
final name = path.substring(path.lastIndexOf('/') + 1,path.length);
final image = await MultipartFile.fromFile(path, filename: name);
final formData = FormData.fromMap({'files':image});
final formData = FormData.fromMap({
'files':image,
'file_types':fileTypes
});
final response = await _dio.post(
url,
data:formData,
......@@ -162,7 +166,7 @@ class HttpService extends GetxService {
class _RequestInterceptor extends Interceptor {
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
void onResponse(Response response, ResponseInterceptorHandler handler) async {
if (response.data['code'] != 200) {
handler.reject(
DioException(
......@@ -172,7 +176,7 @@ class _RequestInterceptor extends Interceptor {
),
true,
);
} else {
}else {
super.onResponse(response, handler);
}
}
......@@ -198,7 +202,6 @@ class _RequestInterceptor extends Interceptor {
final statusCode = response?.statusCode;
print('************* ${response?.data}');
// Console.log(response?.data);
final code = int.tryParse(response?.data['code']?.toString() ?? '');
var msg = '服务器错误';
switch (statusCode) {
case 401:
......@@ -233,20 +236,23 @@ class _RequestInterceptor extends Interceptor {
break;
case 404:
msg = '$statusCode - Server not found';
CustomToast.fail(msg);
break;
case 500:
msg = '$statusCode - Server error';
CustomToast.fail(msg);
break;
case 502:
msg = '$statusCode - Bad gateway';
CustomToast.fail(msg);
break;
default:
// if (code == 901) UserStore.to.logout();
// msg = response?.data?['msg']?.toString() ?? msg;
msg = response?.data?['message']?.toString() ?? msg;
CustomToast.fail(msg);
break;
}
CustomToast.fail(msg);
break;
case DioExceptionType.cancel:
Console.log('请求取消');
......@@ -262,10 +268,14 @@ class _RequestInterceptor extends Interceptor {
}
Future<String?> refreshToken() async {
final result = await HttpService.to.post(
'/v1/members/login/getToken',
params: {
'access_token':UserStore.to.accessToken
}
);
if (result.data is! Map) {
if (result.data is Map) {
await Future.wait([
UserStore.to.setToken(result.data['token']),
UserStore.to.setAccessToken(result.data['access_token']),
......
library services;
import 'dart:async';
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter_book/store/index.dart';
......
......@@ -26,6 +26,11 @@ class StorageService extends GetxService {
return await _prefs.setStringList(key, value);
}
///存储Object
Future<bool>? setObject(String key, Object value) {
return _prefs.setString(key, json.encode(value));
}
///获取字符串
String getString(String key) {
return _prefs.getString(key) ?? '';
......@@ -40,6 +45,11 @@ class StorageService extends GetxService {
List<String> getList(String key) {
return _prefs.getStringList(key) ?? [];
}
/// 获取object
Map? getObject(String key) {
String? data = _prefs.getString(key);
return (data == null || data.isEmpty) ? null : json.decode(data);
}
///移除key
Future<bool> remove(String key) async {
......
......@@ -16,6 +16,12 @@ abstract class Access {
return false;
}
/// 相机权限
static Future<bool> camera() async {
final result = await [Permission.camera].request();
return result[Permission.camera] == PermissionStatus.granted;
}
/// 打开设置
static Future<void> setting() async => await openAppSettings();
......
......@@ -3,6 +3,7 @@ part of utils;
abstract class AssetsPicker {
static final ImagePicker _imagePicker = ImagePicker();
/// 获取图库
static Future<XFile?> image({
required BuildContext context,
ImageSource source = ImageSource.gallery,
......@@ -24,4 +25,27 @@ abstract class AssetsPicker {
maxHeight: maxHeight,
);
}
/// 拍照
static Future<XFile?> camera({
required BuildContext context,
ImageSource source = ImageSource.camera,
double maxWidth = 512,
double maxHeight = 512,
}) async {
if (!(await Access.photos())) {
if (context.mounted) {
CustomDialog.showAccess(
context: context,
content: const Text('Requires access to your photo gallery'),
);
}
return null;
}
return _imagePicker.pickImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
);
}
}
......@@ -2,14 +2,15 @@ part of utils;
// 服务器地址
// const String kServerUrl = 'https://app.vning.com';
const String kServerUrl = 'http://192.168.11.88:81';
// const String kServerUrl = 'http://192.168.11.88:81';
const String kServerUrl = 'http://1507.superge.cn:81';
const String kLocalToken = 'local_token';
const String kLocalAccessToken = 'local_access_token';
const String kLocalAccount = 'local_account';
const String kLocalPassword = 'local_password';
const String kLocalUserInfo = 'local_user_info';
const String kSearchHistory = 'search_history';
const String kFailOrder = 'failOrder';
......
......@@ -65,6 +65,7 @@ class Gaps {
class Fonts {
static const medium = FontWeight.w500;
static const regular = FontWeight.w400;
static const bold = FontWeight.bold;
static const boldSemi = FontWeight.w600;
}
......
......@@ -34,7 +34,7 @@ class CustomCard extends StatelessWidget {
width: width,
child: Container(
padding: const EdgeInsets.all(1),
child: CustomImage.network(url: url),
child: CustomImage.network(url: url,placeholder: Image.asset('assets/images/book_placeholder.png'),),
),
);
}
......
......@@ -82,6 +82,9 @@ class CustomDialog extends StatelessWidget {
));
}
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.w), // 调整这里的数值以设置圆角大小
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
......
......@@ -190,6 +190,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.5"
flutter_inapp_purchase:
dependency: "direct main"
description:
name: flutter_inapp_purchase
sha256: d67544e703439feefe91cab9cb4920cffc0f1576f7ce51a78e3a0a3c5f7d3f1e
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.6.1"
flutter_inappwebview:
dependency: "direct main"
description:
......
......@@ -100,7 +100,9 @@ dependencies:
# 能指定滑动位置的listView
azlistview: ^2.0.0
# 支付宝
# tobias: ^3.3.0
tobias: ^3.3.0
# 内购
flutter_inapp_purchase: ^5.6.1
dev_dependencies:
flutter_test:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论