Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
book-app
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
book-app
Commits
883b112b
提交
883b112b
authored
12月 30, 2025
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: update
上级
f3019c68
显示空白字符变更
内嵌
并排
正在显示
16 个修改的文件
包含
1211 行增加
和
71 行删除
+1211
-71
settings.json
.vscode/settings.json
+3
-0
common.css
assets/html/assets/css/common.css
+0
-0
github.css
assets/html/assets/css/github.css
+0
-0
Podfile.lock
ios/Podfile.lock
+29
-34
controller.dart
lib/pages/login/controller.dart
+6
-3
vip.dart
lib/pages/user_coin_recharge/vip.dart
+1123
-0
user.dart
lib/store/user.dart
+13
-10
assets_picker.dart
lib/utils/assets_picker.dart
+1
-1
console.dart
lib/utils/console.dart
+1
-0
constants.dart
lib/utils/constants.dart
+1
-1
oss.dart
lib/utils/oss.dart
+3
-3
sign.dart
lib/utils/sign.dart
+1
-0
sql.dart
lib/utils/sql.dart
+15
-15
toast_utils.dart
lib/utils/toast_utils.dart
+2
-0
tools.dart
lib/utils/tools.dart
+7
-2
validator.dart
lib/utils/validator.dart
+6
-2
没有找到文件。
.vscode/settings.json
0 → 100644
浏览文件 @
883b112b
{
"dart.flutterSdkPath"
:
"~/flutter"
}
assets/html/assets/css/common.css
浏览文件 @
883b112b
assets/html/assets/css/github.css
浏览文件 @
883b112b
ios/Podfile.lock
浏览文件 @
883b112b
PODS:
- audio_session (0.0.1):
- Flutter
- Bugly (2.6.1)
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
...
...
@@ -9,6 +10,9 @@ PODS:
- Flutter (1.0.0)
- flutter_app_update (0.0.1):
- Flutter
- flutter_bugly (0.0.1):
- Bugly
- Flutter
- flutter_inapp_purchase (0.0.1):
- Flutter
- flutter_inappwebview_ios (0.0.1):
...
...
@@ -47,13 +51,6 @@ PODS:
- Flutter
- ScreenProtectorKit (~> 1.3.1)
- ScreenProtectorKit (1.3.1)
- Sentry/HybridSDK (8.21.0):
- SentryPrivate (= 8.21.0)
- sentry_flutter (7.19.0):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (= 8.21.0)
- SentryPrivate (8.21.0)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
...
...
@@ -73,6 +70,7 @@ DEPENDENCIES:
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
- flutter_bugly (from `.symlinks/plugins/flutter_bugly/ios`)
- flutter_inapp_purchase (from `.symlinks/plugins/flutter_inapp_purchase/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_sound (from `.symlinks/plugins/flutter_sound/ios`)
...
...
@@ -84,19 +82,17 @@ DEPENDENCIES:
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- screen_protector (from `.symlinks/plugins/screen_protector/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
- tobias (from `.symlinks/plugins/tobias/ios`)
SPEC REPOS:
trunk:
- Bugly
- flutter_sound_core
- OrderedSet
- ReachabilitySwift
- ScreenProtectorKit
- Sentry
- SentryPrivate
- WechatOpenSDK-XCFramework
EXTERNAL SOURCES:
...
...
@@ -110,6 +106,8 @@ EXTERNAL SOURCES:
:path: Flutter
flutter_app_update:
:path: ".symlinks/plugins/flutter_app_update/ios"
flutter_bugly:
:path: ".symlinks/plugins/flutter_bugly/ios"
flutter_inapp_purchase:
:path: ".symlinks/plugins/flutter_inapp_purchase/ios"
flutter_inappwebview_ios:
...
...
@@ -132,8 +130,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/permission_handler_apple/ios"
screen_protector:
:path: ".symlinks/plugins/screen_protector/ios"
sentry_flutter:
:path: ".symlinks/plugins/sentry_flutter/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite:
...
...
@@ -142,34 +138,33 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/tobias/ios"
SPEC CHECKSUMS:
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
audio_session: 9bdd3bf46960d4322cb8c3cb6138295dcfe84eee
Bugly: 217ac2ce5f0f2626d43dbaa4f70764c953a26a31
connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_app_update: 65f61da626cb111d1b24674abc4b01728d7723bc
flutter_inapp_purchase: 5c6a1ac3f11b11d0c8c0321c0c41c1f05805e4c8
flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
flutter_sound: c60effa2a350fb977885f0db2fbc4c1ad5160900
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
flutter_bugly: 12197049262a692eab0bd2834d465a5647d920c2
flutter_inapp_purchase: 722da12971a50f306f37e62fc1aaf576b1cbecf6
flutter_inappwebview_ios: 25b61a1b550d1068e4ddaf490fc1d03c2ce6828d
flutter_sound: 49be32081884d275fe91d48262f4b1fcd86e10d3
flutter_sound_core: 26c10e5832e76aaacfae252d8925232281c486ae
flutter_tts:
0f492aab6accf87059b72354fcb4ba934304771d
fluwx:
3c7b6df42f83d444d4538f3eaeae079f12d30c37
image_picker_ios:
99dfe1854b4fa34d0364e74a78448a0151025425
just_audio:
baa7252489dbcf47a4c7cc9ca663e9661c99aafa
flutter_tts:
b88dbc8655d3dc961bc4a796e4e16a4cc1795833
fluwx:
70e62e0330c46b988a1c79ce9731f0d4bc4b26fb
image_picker_ios:
85f2b3c9fb98c09d63725c4d12ebd585b56ec35d
just_audio:
6c031bb61297cf218b4462be616638e81c058e97
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus:
115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation:
3784922295ac71e43754bd15e0653ccfd36a147c
permission_handler_apple:
e76247795d700c14ea09e3a2d8855d41ee80a2e6
package_info_plus:
566e1b7a2f3900e4b0020914ad3fc051dcc95596
path_provider_foundation:
608fcb11be570ce83519b076ab6a1fffe2474f05
permission_handler_apple:
3787117e48f80715ff04a3830ca039283d6a4f29
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
screen_protector:
6f92086bd2f2f4b54f54913289b9d1310610140b
screen_protector:
3d90d44ac886b25335aebd93230b454aef45794a
ScreenProtectorKit: 83a6281b02c7a5902ee6eac4f5045f674e902ae4
Sentry: ebc12276bd17613a114ab359074096b6b3725203
sentry_flutter: 88ebea3f595b0bc16acc5bedacafe6d60c12dcd5
SentryPrivate: d651efb234cf385ec9a1cdd3eff94b5e78a0e0fe
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
tobias: 22f69183b14af82a2ac8ff698af084c352744e5f
shared_preferences_foundation: 0b09b969fb36da5551c0bc4a2dbd9d0ff9387478
sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3
tobias: 50b529c2501d277c83fef9976803a001eb58a057
WechatOpenSDK-XCFramework: acdeeda129efbef9532bca8a10c24e1b4b8c7d69
PODFILE CHECKSUM: 293dca100fbb3eb6a866f640cf85127844630abc
COCOAPODS: 1.1
1
.2
COCOAPODS: 1.1
6
.2
lib/pages/login/controller.dart
浏览文件 @
883b112b
...
...
@@ -37,8 +37,10 @@ class LoginController extends GetxController {
/// 测试账号
if
(
kDebugMode
)
{
phoneInput
.
text
=
'18810760819'
;
passwordInput
.
text
=
'1234567m'
;
phoneInput
.
text
=
'13811534871'
;
passwordInput
.
text
=
'wpf123456*'
;
// phoneInput.text = '18810760819';
// passwordInput.text = '1234567m';
// phoneInput.text = '17311837355';
// passwordInput.text = '12345678';
}
...
...
@@ -128,7 +130,8 @@ class LoginController extends GetxController {
}
final
result
=
await
AccountAPI
.
login
(
phone:
phoneInput
.
text
,
password:
EncryptUtil
.
encodeMd5
(
EncryptUtil
.
encodeMd5
(
passwordInput
.
text
)),
password:
EncryptUtil
.
encodeMd5
(
EncryptUtil
.
encodeMd5
(
passwordInput
.
text
)),
code:
codeInput
.
text
,
type:
type
);
...
...
lib/pages/user_coin_recharge/vip.dart
0 → 100644
浏览文件 @
883b112b
/*
import 'dart:async';
import 'dart:io';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluwx/fluwx.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:tobias/tobias.dart' as tobias;
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:ace_word/utils/http.dart';
import 'package:ace_word/utils/global_data.dart' as globals;
import 'package:ace_word/pages/browser.dart';
import 'package:ace_word/components/ace/navibar/index.dart';
import 'package:tobias/tobias.dart';
class Vip extends StatefulWidget {
const Vip({Key? key}) : super(key: key);
@override
State<Vip> createState() => _VipState();
}
/// 去除ListView的顶部阴影
class MyBehavior extends ScrollBehavior {
@override
Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
return child;
}
}
class _VipState extends State<Vip> {
int chosenIndex = 3;
Map settings = {};
List payInfoList = [];
String orderNo = '';
String payMethod = 'wechat-pay';
String? userID;
var inAppPurchaseInstance = InAppPurchase.instance;
late StreamSubscription<List<PurchaseDetails>> _subscription;
/// 初始化事件
Future<void> init() async {
// setData: userID & settings
settings = await HTTP(url: '/settings').request();
userID = await HTTP(url: '/user/id').request();
setState(() {
settings = settings;
userID = userID;
});
// print(settings);
// print(userID);
// setData: userID & settings
payInfoList = await HTTP(url: '/pay/info').request();
setState(() {
payInfoList = payInfoList;
});
print(payInfoList);
final Stream<List<PurchaseDetails>> purchaseUpdated = inAppPurchaseInstance.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
_listenToPurchaseUpdated(purchaseDetailsList);
}, onDone: () {
_subscription.cancel();
}, onError: (error) {
// handle error here.
});
// 监听微信支付回调
weChatResponseEventHandler.listen((event) async {
// 支付成功
if (event.errCode == 0) {
try {
var res = await HTTP(url: '/pay/status').request();
EasyLoading.showSuccess('支付成功');
globals.needRefreshMinePage = true;
setState(() {
settings['vipExpireDate'] = res['expireDate'];
});
} catch (err) {
var errMap = err as Map;
if (errMap['errcode'] == 419) {
EasyLoading.showError('支付未完成');
}
}
} else {
if (event.errCode == -2) {
EasyLoading.showError('取消支付');
}
}
});
}
// 要把localVerificationData发到服务端,验证
Future<bool> _verifyPurchase(PurchaseDetails purchaseDetails) async {
try {
var result = await HTTP(url: '/pay/ios/notify', method: 'POST', data: {
'orderNo': orderNo,
'receipt': purchaseDetails.verificationData.serverVerificationData,
}).request();
return result['errcode'] == 0;
} catch (e) {
return false;
}
}
// iOS支付监听
Future<void> _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) async {
for (var purchaseDetails in purchaseDetailsList) {
// 支付进行中
if (purchaseDetails.status == PurchaseStatus.pending) {
// _showPendingUI();
} else {
if (purchaseDetails.status == PurchaseStatus.error) {
EasyLoading.showError('发生错误,请重试');
} else if (purchaseDetails.status == PurchaseStatus.canceled) {
EasyLoading.showError('支付取消');
} else if (purchaseDetails.status == PurchaseStatus.restored) {
} else if (purchaseDetails.status == PurchaseStatus.purchased) {
inAppPurchaseInstance.completePurchase(purchaseDetails);
// 1. 用户购买成功,苹果服务器向用户手机发送令牌以后
// tips: 如果关机,信号差...需要本地持久化这个令牌,每次启动,检查本地是否有该令牌,如果有,向服务器发送开通会员请求,删掉
// 2. 用户拿着令牌,向我们自己服务器发送,并开通会员
bool valid = await _verifyPurchase(purchaseDetails);
if (valid) {
try {
Future.delayed(const Duration(milliseconds: 500));
var res = await HTTP(url: '/pay/status').request();
EasyLoading.showSuccess('支付成功');
globals.needRefreshMinePage = true;
setState(() {
settings['vipExpireDate'] = res['expireDate'];
});
} catch (err) {
var errMap = err as Map;
if (errMap['errcode'] == 419) {
EasyLoading.showError('支付未完成');
}
}
} else {
EasyLoading.showError('发生错误,请联系客服');
// _handleInvalidPurchase(purchaseDetails);
}
}
if (purchaseDetails.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchaseDetails);
}
}
}
}
@override
void initState() {
super.initState();
init();
}
String _formatCreateDate(createDate) {
return createDate == null ? '' : createDate.substring(0, 4) + '.' + createDate.substring(4, 6) + '.' + createDate.substring(6);
}
@override
Widget build(BuildContext context) {
if (Theme.of(context).platform == TargetPlatform.android) {
SystemUiOverlayStyle style = const SystemUiOverlayStyle(statusBarColor: Colors.transparent, statusBarIconBrightness: Brightness.light);
SystemChrome.setSystemUIOverlayStyle(style);
}
return ScrollConfiguration(
behavior: MyBehavior(),
child: Scaffold(
body: Stack(children: [
Container(
decoration: const BoxDecoration(
color: Color(0xFF161A27),
),
),
Container(
width: double.infinity,
height: double.infinity,
margin: const EdgeInsets.only(top: 54),
decoration: const BoxDecoration(
color: Color(0xFF161A27),
),
),
Column(
children: [
SizedBox(height: MediaQuery.of(context).padding.top + 54 + 16),
Expanded(
child: Stack(children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Image.asset(
'images/vip/background.png',
width: MediaQuery.of(context).size.width - 32,
height: (MediaQuery.of(context).size.width - 32) * 162 / 343,
),
),
Positioned(
top: 46,
left: 16 + 16,
child: Row(
children: [
Container(
padding: const EdgeInsets.all(1), // Border width
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(100)),
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: SizedBox.fromSize(
size: const Size.fromRadius(26), // Image radius
child: globals.settings['avatarUrl'] == null
? const CircleAvatar(
radius: 1000,
backgroundColor: Colors.white,
backgroundImage: AssetImage('images/icons/avatar.png'),
)
: Image.asset(
'images/vip/avatar.png',
),
),
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${(settings['nickName'] == null || (userID == null)) ? '...' : settings['nickName'].isEmpty ? '词友_${userID!.substring(0, 6)}' : settings['nickName']}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Color(0xFF72522D),
),
),
const SizedBox(height: 2),
Text(
settings['vipExpireDate'] == '00000000' || int.parse(globals.settings['vipExpireDate']) < int.parse(globals.todayDate)
? '尚未开通会员'
: '有效期至${_formatCreateDate(settings['vipExpireDate'])}',
style: const TextStyle(
fontSize: 12,
color: Color(0xFFB58A5A),
),
),
],
),
],
),
),
Positioned(
top: 120,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - (MediaQuery.of(context).padding.top + 54 + 16 + 120),
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: const BoxDecoration(
color: Color(0xFFFFFFFF),
borderRadius: BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0)),
),
child: ListView(
padding: const EdgeInsets.only(top: 24),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'解锁会员',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Color(0xFF1D2129),
),
),
const SizedBox(height: 20),
SizedBox(
width: MediaQuery.of(context).size.width - 32,
height: 92,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
setState(() {
chosenIndex = 3;
});
},
child: Container(
width: MediaQuery.of(context).size.width - 32,
height: 92,
decoration: BoxDecoration(
color: chosenIndex == 3 ? const Color(0xFFFEF9E9) : const Color(0xFFFFFFFF),
border: Border.all(
color: chosenIndex == 3 ? const Color(0xFFAC884E) : const Color(0xFFE2E2E2),
width: 1.0,
),
borderRadius: const BorderRadius.all(Radius.circular(14.0)),
),
child: Stack(children: [
if (chosenIndex == 3)
Positioned(
top: 0,
right: 0,
child: Image.asset(
'images/vip/check.png',
width: 24,
height: 20,
),
),
SizedBox(
height: 92,
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [
Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'终身会员',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF434346),
),
),
SizedBox(height: 4),
Text(
'您将永久解锁本软件的所有会员权益',
style: TextStyle(
fontSize: 11,
color: Color(0xFF86909C),
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'images/vip/rmb.png',
width: 16,
height: 14,
),
Text(
'${payInfoList.isEmpty ? '...' : payInfoList[3]['money'] ~/ 100}',
style: const TextStyle(
fontFamily: 'SFProDisplay',
fontSize: 28,
fontWeight: FontWeight.w700,
color: Color(0xFFBB8247),
),
),
const SizedBox(width: 4),
],
),
const Text(
'¥168',
style: TextStyle(
fontSize: 12,
color: Color(0xFF939495),
decoration: TextDecoration.lineThrough,
),
),
],
),
),
]),
),
]),
),
),
],
),
),
const SizedBox(height: 16),
SizedBox(
width: MediaQuery.of(context).size.width - 32,
height: 138,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
setState(() {
chosenIndex = 0;
});
},
child: Container(
width: (MediaQuery.of(context).size.width - 32 - 30) / 3,
decoration: BoxDecoration(
color: chosenIndex == 0 ? const Color(0xFFFEF9E9) : const Color(0xFFFFFFFF),
border: Border.all(
color: chosenIndex == 0 ? const Color(0xFFAC884E) : const Color(0xFFE2E2E2),
width: 1.0,
),
borderRadius: const BorderRadius.all(Radius.circular(14.0) // <--- border radius here
),
),
child: Stack(children: [
if (chosenIndex == 0)
Positioned(
top: 0,
right: 0,
child: Image.asset(
'images/vip/check.png',
width: 24,
height: 20,
),
),
Positioned(
top: 20,
child: SizedBox(
width: (MediaQuery.of(context).size.width - 32 - 30) / 3,
child: Column(children: [
const Text(
'12个月',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF434346),
),
),
const SizedBox(height: 6),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'images/vip/rmb.png',
width: 16,
height: 14,
),
Text(
'${payInfoList.isEmpty ? '...' : payInfoList[0]['money'] ~/ 100}',
style: const TextStyle(
fontFamily: 'SFProDisplay',
fontSize: 28,
fontWeight: FontWeight.w700,
color: Color(0xFFBB8247),
),
),
const SizedBox(width: 4),
],
),
const SizedBox(height: 6),
const Text(
'¥98',
style: TextStyle(
fontSize: 12,
color: Color(0xFF939495),
decoration: TextDecoration.lineThrough,
),
),
]),
),
),
Positioned(
bottom: 0,
child: Container(
width: (MediaQuery.of(context).size.width - 32 - 30) / 3 - 2,
height: 24,
decoration: BoxDecoration(
color: const Color(0xFFAC884E),
border: Border.all(
color: const Color(0xFFAC884E),
width: 1.0,
),
borderRadius: const BorderRadius.vertical(
bottom: Radius.circular(12.0),
),
),
child: const Center(
child: Text(
'优惠 30%',
style: TextStyle(
fontSize: 12,
color: Color(0xFFFFFFFF),
),
),
),
),
),
]),
),
),
GestureDetector(
onTap: () {
setState(() {
chosenIndex = 1;
});
},
child: Container(
width: (MediaQuery.of(context).size.width - 32 - 30) / 3,
decoration: BoxDecoration(
color: chosenIndex == 1 ? const Color(0xFFFEF9E9) : const Color(0xFFFFFFFF),
border: Border.all(
color: chosenIndex == 1 ? const Color(0xFFAC884E) : const Color(0xFFE2E2E2),
width: 1.0,
),
borderRadius: const BorderRadius.all(Radius.circular(14.0) // <--- border radius here
),
),
child: Stack(children: [
if (chosenIndex == 1)
Positioned(
top: 0,
right: 0,
child: Image.asset(
'images/vip/check.png',
width: 24,
height: 20,
),
),
Positioned(
top: 20,
child: SizedBox(
width: (MediaQuery.of(context).size.width - 32 - 30) / 3,
child: Column(children: [
const Text(
'3个月',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF434346),
),
),
const SizedBox(height: 6),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'images/vip/rmb.png',
width: 16,
height: 14,
),
Text(
'${payInfoList.isEmpty ? '...' : payInfoList[1]['money'] ~/ 100}',
style: const TextStyle(
fontFamily: 'SFProDisplay',
fontSize: 28,
fontWeight: FontWeight.w700,
color: Color(0xFFBB8247),
),
),
const SizedBox(width: 4),
],
),
const SizedBox(height: 6),
const Text(
'¥36',
style: TextStyle(
fontSize: 12,
color: Color(0xFF939495),
decoration: TextDecoration.lineThrough,
),
),
]),
),
),
Positioned(
bottom: 0,
child: Container(
width: (MediaQuery.of(context).size.width - 32 - 30) / 3 - 2,
height: 24,
decoration: BoxDecoration(
color: const Color(0xFFAC884E),
border: Border.all(
color: const Color(0xFFAC884E),
width: 1.0,
),
borderRadius: const BorderRadius.vertical(
bottom: Radius.circular(12.0),
),
),
child: const Center(
child: Text(
'优惠 16%',
style: TextStyle(
fontSize: 12,
color: Color(0xFFFFFFFF),
),
),
),
),
),
]),
),
),
GestureDetector(
onTap: () {
setState(() {
chosenIndex = 2;
});
},
child: Container(
width: (MediaQuery.of(context).size.width - 32 - 30) / 3,
decoration: BoxDecoration(
color: chosenIndex == 2 ? const Color(0xFFFEF9E9) : const Color(0xFFFFFFFF),
border: Border.all(
color: chosenIndex == 2 ? const Color(0xFFAC884E) : const Color(0xFFE2E2E2),
width: 1.0,
),
borderRadius: const BorderRadius.all(Radius.circular(14.0) // <--- border radius here
),
),
child: Stack(children: [
if (chosenIndex == 2)
Positioned(
top: 0,
right: 0,
child: Image.asset(
'images/vip/check.png',
width: 24,
height: 20,
),
),
Positioned(
top: 20,
child: SizedBox(
width: (MediaQuery.of(context).size.width - 32 - 30) / 3,
child: Column(children: [
const Text(
'1个月',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF434346),
),
),
const SizedBox(height: 6),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'images/vip/rmb.png',
width: 16,
height: 14,
),
Text(
'${payInfoList.isEmpty ? '...' : payInfoList[2]['money'] ~/ 100}',
style: const TextStyle(
fontFamily: 'SFProDisplay',
fontSize: 28,
fontWeight: FontWeight.w700,
color: Color(0xFFBB8247),
),
),
const SizedBox(width: 4),
],
),
const SizedBox(height: 6),
const Text(
'无优惠',
style: TextStyle(
fontSize: 12,
color: Color(0xFF939495),
// decoration: TextDecoration.lineThrough,
),
),
]),
),
),
]),
),
),
],
),
),
if (!Platform.isIOS) const SizedBox(height: 20),
if (!Platform.isIOS)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () async {
setState(() {
payMethod = 'wechat-pay';
});
},
child: Container(
width: (MediaQuery.of(context).size.width - 48 - 10) / 2,
height: 50,
padding: const EdgeInsets.fromLTRB(8, 0, 12, 0),
decoration: const BoxDecoration(
color: Color(0xFFF7F8FA),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: SizedBox(
width: (MediaQuery.of(context).size.width - 48 - 10) / 2,
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
'images/vip/wechat-pay-fill.png',
width: 24,
height: 24,
),
const SizedBox(width: 6),
const Text(
'微信支付',
style: TextStyle(
fontSize: 14,
color: Color(0xFF1E2128),
),
),
],
),
Image.asset(
'images/vip/check-circle${payMethod == 'wechat-pay' ? '-fill' : ''}.png',
width: 16,
height: 16,
),
],
),
),
),
),
GestureDetector(
onTap: () async {
setState(() {
payMethod = 'alipay';
});
},
child: Container(
width: (MediaQuery.of(context).size.width - 48 - 10) / 2,
height: 50,
padding: const EdgeInsets.fromLTRB(8, 0, 12, 0),
decoration: const BoxDecoration(
color: Color(0xFFF7F8FA),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: SizedBox(
width: (MediaQuery.of(context).size.width - 48 - 10) / 2,
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
'images/vip/alipay-fill.png',
width: 24,
height: 24,
),
const SizedBox(width: 6),
const Text(
'支付宝支付',
style: TextStyle(
fontSize: 14,
color: Color(0xFF1E2128),
),
),
],
),
Image.asset(
'images/vip/check-circle${payMethod == 'alipay' ? '-fill' : ''}.png',
width: 16,
height: 16,
),
],
),
),
),
),
],
),
const SizedBox(height: 24),
GestureDetector(
onTap: () async {
var isIOS = Platform.isIOS || false;
if (isIOS) {
EasyLoading.show(status: "正在与Apple服务器通信");
// 0. 调用服务器下单接口 /pay/ios
var result = await HTTP(url: '/pay/1.0/ios?payment-id=${payInfoList[chosenIndex]['id']}').request();
orderNo = result['orderNo'];
var productSet = <String>{result['productID']};
ProductDetailsResponse response = await inAppPurchaseInstance.queryProductDetails(productSet);
// 只需要填写商品ID即可
final ProductDetails productDetail = response.productDetails.first;
// 5. 创建购买参数
final PurchaseParam purchaseParam = PurchaseParam(productDetails: productDetail);
// 6. 发起购买,这里要注意避免重复购买
inAppPurchaseInstance.restorePurchases(applicationUserName: result['productID']);
try {
InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam, autoConsume: true);
} catch (e) {
EasyLoading.showError("购买失败");
}
} else if (payMethod == 'wechat-pay') {
EasyLoading.show();
// 微信支付
var result = await HTTP(url: '/pay/1.0/wechat?payment-id=${payInfoList[chosenIndex]['id']}').request();
// print(result);
payWithWeChat(
appId: result['appid'],
partnerId: result['partnerid'],
prepayId: result['prepayid'],
packageValue: result['package'],
nonceStr: result['noncestr'],
timeStamp: int.parse(result['timestamp']),
sign: result['sign'],
);
} else if (payMethod == 'alipay') {
// 支付宝支付
EasyLoading.show();
var str = await HTTP(url: '/pay/1.0/alipay?payment-id=${payInfoList[chosenIndex]['id']}').request();
// print(str);
try {
await tobias.aliPay(str, evn: AliPayEvn.ONLINE);
var res = await HTTP(url: '/pay/status').request();
EasyLoading.showSuccess('支付成功');
setState(() {
settings['vipExpireDate'] = res['expireDate'];
});
} catch (err) {
// print(err);
var errMap = err as Map;
if (errMap['errcode'] == 419) {
EasyLoading.showError('支付未完成');
}
}
}
},
child: Container(
width: MediaQuery.of(context).size.width,
height: 52,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
stops: [-0.16, 1.36],
colors: [
Color(0xFFFAD79D),
Color(0xFFFFC96F),
],
),
borderRadius: BorderRadius.all(Radius.circular(14.0)),
),
child: Center(
child: Text(
'立即以${chosenIndex == 0 ? 68 : chosenIndex == 1 ? 25 : chosenIndex == 2 ? 12 : 128}元开通',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF7D6032),
),
),
),
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RichText(
textAlign: TextAlign.center,
text: TextSpan(
children: [
const TextSpan(style: TextStyle(color: Color(0xFF787878), fontSize: 14), text: '开通代表接受'),
TextSpan(
text: '《AceWord会员协议》',
style: const TextStyle(color: Color(0xFFBB8247), fontSize: 14),
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const Browser(
url:
'https://mp.weixin.qq.com/s?__biz=MzkzNDQwMzgxNw==&mid=2247483703&idx=1&sn=43cc958a0373ddf44ca7706ac2a9db05&chksm=c2bc8ab1f5cb03a789c1d1ff8be6f2d76796859b7a7b55aad2f196f63b349f49579690794232&token=430278592&lang=zh_CN#rd',
title: '会员协议'),
),
);
},
),
],
),
),
],
),
const SizedBox(height: 24),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'会员特权',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Color(0xFF1D2129),
),
),
const SizedBox(height: 20),
Row(
children: [
Image.asset(
'images/vip/feature-1.png',
width: 40,
height: 40,
),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'无限卡片用量',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF1D2129),
),
),
SizedBox(height: 2),
Text(
'每日新建的卡片数量无上限',
style: TextStyle(
fontSize: 13,
color: Color(0xFF929499),
),
),
],
),
],
),
const SizedBox(height: 24),
Row(
children: [
Image.asset(
'images/vip/feature-2.png',
width: 40,
height: 40,
),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'无限单词本、自定义词书用量',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF1D2129),
),
),
SizedBox(height: 2),
Text(
'创建的单词本、自定义词书数量均无上限',
style: TextStyle(
fontSize: 13,
color: Color(0xFF929499),
),
),
],
),
],
),
const SizedBox(height: 24),
Row(
children: [
Image.asset(
'images/vip/feature-3.png',
width: 40,
height: 40,
),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'批量导入自定义释义',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF1D2129),
),
),
SizedBox(height: 2),
Text(
'可以通过自定义词书批量导入自定义释义',
style: TextStyle(
fontSize: 13,
color: Color(0xFF929499),
),
),
],
),
],
),
const SizedBox(height: 24),
Row(
children: [
Image.asset(
'images/vip/feature-4.png',
width: 40,
height: 40,
),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'自定义复习节奏(倒计时)',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF1D2129),
),
),
SizedBox(height: 2),
Text(
'可以调整复习节奏更紧凑,或更松弛',
style: TextStyle(
fontSize: 13,
color: Color(0xFF929499),
),
),
],
),
],
),
const SizedBox(height: 24),
Row(
children: [
Image.asset(
'images/vip/feature-4.png',
width: 40,
height: 40,
),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'丰富的卡片英文字体',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF1D2129),
),
),
SizedBox(height: 2),
Text(
'可以切换八种不同的卡片英文字体',
style: TextStyle(
fontSize: 13,
color: Color(0xFF929499),
),
),
],
),
],
),
const SizedBox(height: 24),
Row(
children: [
Image.asset(
'images/vip/feature-6.png',
width: 40,
height: 40,
),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'丰富的单词背景色',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Color(0xFF1D2129),
),
),
SizedBox(height: 2),
Text(
'可以给卡片单词标记多种背景色',
style: TextStyle(
fontSize: 13,
color: Color(0xFF929499),
),
),
],
),
],
),
const SizedBox(height: 24),
],
),
],
)),
),
]),
),
],
),
Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).viewPadding.top),
child: const NaviBar(type: 'back', showDivider: false, showVantDivider: false, title: '开通会员', backgroundColor: Color(0xFF161A27), titleTextColor: Color(0xFFFFDB93))),
]),
),
);
}
}
*/
lib/store/user.dart
浏览文件 @
883b112b
...
...
@@ -6,21 +6,21 @@ class UserStore extends GetxController {
// 是否登录
bool
_isLogin
=
false
;
// token
String
_token
=
''
;
// 获取token
String
get
token
=>
_token
;
// 刷新token
String
_accessToken
=
''
;
// 获取刷新token
String
get
accessToken
=>
_accessToken
;
// 是否登录
bool
get
isLogin
=>
_isLogin
;
// 是否有token
bool
get
hasToken
=>
_token
.
isNotEmpty
;
// 用户信息模型
UserModel
_info
=
UserModel
();
// 获取用户信息模型
UserModel
get
info
=>
_info
;
// 是否需要登录
...
...
@@ -34,6 +34,7 @@ class UserStore extends GetxController {
_token
=
StorageService
.
to
.
getString
(
kLocalToken
);
}
/// 登录
Future
<
void
>
login
(
UserModel
value
)
async
{
_token
=
value
.
token
??
''
;
_accessToken
=
value
.
accessToken
??
''
;
...
...
@@ -48,16 +49,18 @@ class UserStore extends GetxController {
update
();
}
/// 设置token
Future
<
void
>
setToken
(
String
value
)
async
{
await
StorageService
.
to
.
setString
(
kLocalToken
,
value
);
_token
=
value
;
}
/// 设置刷新token
Future
<
void
>
setAccessToken
(
String
value
)
async
{
await
StorageService
.
to
.
setString
(
kLocalAccessToken
,
value
);
_accessToken
=
value
;
}
/// 设置用户信息
Future
<
void
>
setInfo
(
UserModel
value
)
async
{
await
StorageService
.
to
.
setString
(
kLocalUserInfo
,
jsonEncode
(
value
.
toJson
()));
...
...
@@ -65,7 +68,7 @@ class UserStore extends GetxController {
_info
=
value
;
}
// 登出
//
/
登出
Future
<
void
>
logout
()
async
{
await
StorageService
.
to
.
remove
(
kLocalToken
);
await
StorageService
.
to
.
remove
(
kLocalAccessToken
);
...
...
lib/utils/assets_picker.dart
浏览文件 @
883b112b
...
...
@@ -50,7 +50,7 @@ abstract class AssetsPicker {
// maxHeight: maxHeight,
);
}
/// 展示设置弹窗
static
_showSettingDialog
(
BuildContext
context
,
String
content
)
{
showDialog
(
context:
context
,
...
...
lib/utils/console.dart
浏览文件 @
883b112b
part of
utils
;
/// 打印类
abstract
class
Console
{
static
final
_log
=
Logger
(
filter:
_LogFilter
());
...
...
lib/utils/constants.dart
浏览文件 @
883b112b
...
...
@@ -3,7 +3,7 @@ part of utils;
// 服务器地址
// const String kServerUrl = 'http://192.168.11.88:81';
// const String kServerUrl = 'http://8.141.148.247:7421';
const
String
kServerUrl
=
'http
://1507.superge.cn:81
'
;
const
String
kServerUrl
=
'http
s://ebook-app.ezijing.com
'
;
const
String
kHtmlBaseServer
=
'http://150.158.138.40:9200'
;
const
String
kLocalToken
=
'local_token'
;
const
String
kLocalAccessToken
=
'local_access_token'
;
...
...
lib/utils/oss.dart
浏览文件 @
883b112b
...
...
@@ -15,7 +15,7 @@ class OssTool {
Client
.
init
(
ossEndpoint:
'oss-cn-beijing.aliyuncs.com'
,
bucketName:
bucketName
,
authGetter:
_authGetter
);
}
// 获取临时凭证
//
/
获取临时凭证
Future
<
Auth
>
_authGetter
()
async
{
final
result
=
await
CommonAPI
.
oss
();
return
Auth
(
...
...
@@ -26,7 +26,7 @@ class OssTool {
);
}
// 本地文件上传
//
/
本地文件上传
Future
<
Response
<
dynamic
>>
putObjectFile
(
String
filePath
)
async
{
List
<
String
>
parts
=
filePath
.
split
(
'/'
);
String
fileName
=
parts
.
last
;
...
...
@@ -38,7 +38,7 @@ class OssTool {
return
resp
;
}
// 批量本地文件上传
//
/
批量本地文件上传
Future
<
List
<
Response
>>
putObjectFiles
(
List
<
String
>
filePaths
){
return
Client
().
putObjectFiles
(
filePaths
.
map
((
e
){
...
...
lib/utils/sign.dart
浏览文件 @
883b112b
part of
utils
;
class
SignTool
{
/// 创建签名
static
String
createSign
(
Map
<
String
,
dynamic
>
params
)
{
Map
<
String
,
dynamic
>
tempParams
=
keySort
(
params
);
List
<
String
>
tempArr
=
[];
...
...
lib/utils/sql.dart
浏览文件 @
883b112b
...
...
@@ -58,13 +58,13 @@ class SqlManager {
return
_database
;
}
// 关闭数据库
//
/
关闭数据库
static
Future
<
void
>
closeDatabase
()
async
{
await
_database
?.
close
();
}
// 查询划线高亮笔记
//
/
查询划线高亮笔记
static
Future
<
Map
<
String
,
dynamic
>>
queryLocalNote
({
required
int
bookId
,
required
int
chapterId
})
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
...
...
@@ -89,7 +89,7 @@ class SqlManager {
return
returnMap
;
}
// 添加划线高亮笔记
//
/
添加划线高亮笔记
static
Future
<
int
>
addLocalNote
(
Map
<
String
,
dynamic
>
data
)
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
final
result
=
await
db
?.
insert
(
...
...
@@ -100,7 +100,7 @@ class SqlManager {
return
result
??
0
;
}
// 删除划线高亮笔记
//
/
删除划线高亮笔记
static
Future
<
int
>
delLocalNote
({
required
int
noteId
,
required
int
id
})
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
if
(
noteId
==
0
){
...
...
@@ -124,7 +124,7 @@ class SqlManager {
}
// 修改划线高亮笔记
//
/
修改划线高亮笔记
static
Future
<
int
>
updateLocalNote
({
required
int
notesId
,
required
int
id
,
required
Map
<
String
,
dynamic
>
data
})
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
if
(
notesId
==
0
){
...
...
@@ -148,7 +148,7 @@ class SqlManager {
}
// 查询所有没有上传的数据
//
/
查询所有没有上传的数据
static
Future
<
List
<
Map
<
String
,
dynamic
>>>
queryNoUploadData
()
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
List
<
Map
<
String
,
dynamic
>>?
results
=
await
db
?.
query
(
...
...
@@ -164,7 +164,7 @@ class SqlManager {
return
results
??[];
}
// 将所有 upload 为 0 的数据的 upload 字段值更新为 1
//
/
将所有 upload 为 0 的数据的 upload 字段值更新为 1
static
Future
<
void
>
updateUploadStatus
()
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
final
result
=
await
db
?.
update
(
...
...
@@ -175,7 +175,7 @@ class SqlManager {
);
Console
.
log
(
'Sql---------------更新数据----------------
$result
'
);
}
// 将上传成功的id数据的notes_id 更新为服务器对应数值 并设置upload 为1
//
/
将上传成功的id数据的notes_id 更新为服务器对应数值 并设置upload 为1
static
Future
<
void
>
updateNotesId
(
List
<
Map
<
String
,
dynamic
>>
data
)
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
// 构建批量更新的SQL语句
...
...
@@ -205,7 +205,7 @@ class SqlManager {
db
?.
delete
(
kNoteTable
);
db
?.
delete
(
kReadTable
);
}
// 根据 book_id 查询当前读到的 章节
//
/
根据 book_id 查询当前读到的 章节
static
Future
<
String
>
queryReadHistoryByBookId
(
int
bookId
)
async
{
try
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
...
...
@@ -226,7 +226,7 @@ class SqlManager {
}
// 根据 book_id 写入当前读到的 章节
//
/
根据 book_id 写入当前读到的 章节
static
Future
<
int
>
updateReadHistoryByBookId
(
int
bookId
,
int
chapterId
)
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
...
...
@@ -256,7 +256,7 @@ class SqlManager {
// 插入数据
//
/
插入数据
static
Future
<
bool
>
insertData
(
Map
<
String
,
dynamic
>
data
)
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
final
result
=
await
db
?.
insert
(
...
...
@@ -271,13 +271,13 @@ class SqlManager {
}
// 查询所有数据
//
/
查询所有数据
static
Future
<
List
<
Map
<
String
,
dynamic
>>?>
queryAllData
()
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
return
await
db
?.
query
(
'members_book_notes'
);
}
// 根据 ID 查询数据
//
/
根据 ID 查询数据
static
Future
<
Map
<
String
,
dynamic
>?>
queryDataById
(
int
id
)
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
List
<
Map
<
String
,
dynamic
>>?
results
=
await
db
?.
query
(
...
...
@@ -288,7 +288,7 @@ class SqlManager {
return
results
?.
first
??{};
}
// 更新数据
//
/
更新数据
static
Future
<
void
>
updateData
(
int
id
,
Map
<
String
,
dynamic
>
newData
)
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
await
db
?.
update
(
...
...
@@ -299,7 +299,7 @@ class SqlManager {
);
}
// 删除数据
//
/
删除数据
static
Future
<
void
>
deleteData
(
int
id
)
async
{
Database
?
db
=
await
SqlManager
.
getCurrentDatabase
();
await
db
?.
delete
(
...
...
lib/utils/toast_utils.dart
浏览文件 @
883b112b
...
...
@@ -2,6 +2,7 @@ part of utils;
/// Toast工具类
class
Toast
{
/// 展示toast
static
void
show
(
String
msg
,
{
int
duration
=
2000
})
{
if
(
msg
==
null
)
{
return
;
...
...
@@ -13,6 +14,7 @@ class Toast {
);
}
/// 取消toast
static
void
cancelToast
()
{
dismissAllToast
();
}
...
...
lib/utils/tools.dart
浏览文件 @
883b112b
...
...
@@ -2,10 +2,12 @@ part of utils;
abstract
class
Tools
{
/// 取消焦点
static
void
unfocus
()
{
WidgetsBinding
.
instance
.
focusManager
.
primaryFocus
?.
unfocus
();
}
/// 格式化日期
static
String
dateFromMS
(
int
timestamp
,
{
String
pattern
=
'yyyy-MM-dd'
,
...
...
@@ -29,6 +31,7 @@ abstract class Tools {
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
));
...
...
@@ -36,26 +39,28 @@ abstract class Tools {
return
"
${duration.inHours}
:
$twoDigitMinutes
:
$twoDigitSeconds
"
;
}
/// 获取存储目录
static
Future
<
String
>
getDirectory
()
async
{
// getTemporaryDirectory
final
directory
=
await
getTemporaryDirectory
();
return
directory
!.
path
;
}
// 语音文件名称
//
/
语音文件名称
static
String
generateVoiceFileName
(){
DateTime
now
=
DateTime
.
now
();
String
formattedDate
=
DateFormat
(
'yyyyMMddHHmmss'
).
format
(
now
);
return
'voice_
$formattedDate
.mp4'
;
}
/// 清理数据
static
Future
<
void
>
clearData
()
async
{
await
SqlManager
.
clear
();
ClearCache
().
clearApplicationCache
();
Console
.
log
(
'-------------清除数据-------------------'
);
}
// 判断当前网络状态
//
/
判断当前网络状态
static
Future
<
bool
>
checkCurrentNetStatus
()
async
{
final
connectivityResult
=
await
(
Connectivity
().
checkConnectivity
());
if
(
connectivityResult
==
ConnectivityResult
.
none
){
...
...
lib/utils/validator.dart
浏览文件 @
883b112b
...
...
@@ -29,6 +29,7 @@ class RequiredValidator extends Validator<String?> {
bool
isValid
(
String
?
value
)
=>
(
value
??
''
).
isNotEmpty
;
}
/// 邮箱验证
class
EmailValidator
extends
Validator
<
String
?>
{
final
Pattern
_pattern
=
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'
;
...
...
@@ -42,6 +43,7 @@ class EmailValidator extends Validator<String?> {
}
}
/// 密码验证
class
PasswordValidator
extends
Validator
<
String
?>
{
final
Pattern
_pattern
=
r'(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$'
;
...
...
@@ -55,6 +57,7 @@ class PasswordValidator extends Validator<String?> {
}
}
/// 是否一致
class
EqualValidator
extends
Validator
<
String
?>
{
final
TextEditingController
input
;
...
...
@@ -83,19 +86,20 @@ class AmountValidator extends Validator<String?> {
class
ValidatorTool
{
// 验证手机号
//
/
验证手机号
static
bool
isValidPhoneNumber
(
String
value
)
{
RegExp
phonePattern
=
RegExp
(
r'^1[3-9]\d{9}$'
);
return
phonePattern
.
hasMatch
(
value
);
}
// 密码
//
/
密码
static
bool
isValidPassword
(
String
value
)
{
RegExp
passwordPattern
=
RegExp
(
r'^((?=.*\d)(?=.*[A-Z])(?=.*[a-z]))|((?=.*\d)(?=.*[A-Z])(?=.*[\W_]))|((?=.*\d)(?=.*[a-z])(?=.*[\W_]))|((?=.*[A-Z])(?=.*[a-z])(?=.*[\W_]))[a-zA-Z\d\W_]{8,12}$'
);
// 密码必须是数字、字母、特殊符号三种及以上8-12位的组合
return
passwordPattern
.
hasMatch
(
value
);
}
/// 验证码位数
static
bool
isValidCode
(
String
value
)
{
RegExp
passwordPattern
=
RegExp
(
r'^\d{6}$'
);
return
passwordPattern
.
hasMatch
(
value
);
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论