diff --git a/assets/images/home-tab-normal-bg.png b/assets/images/home-tab-normal-bg.png new file mode 100644 index 0000000..bb43aa8 Binary files /dev/null and b/assets/images/home-tab-normal-bg.png differ diff --git a/assets/images/login/icon-no-shop-head.png b/assets/images/login/icon-no-shop-head.png new file mode 100644 index 0000000..5aca906 Binary files /dev/null and b/assets/images/login/icon-no-shop-head.png differ diff --git a/lib/api/index.dart b/lib/api/index.dart new file mode 100644 index 0000000..5417092 --- /dev/null +++ b/lib/api/index.dart @@ -0,0 +1,3 @@ +library apipaths; + +export './login.dart'; diff --git a/lib/api/login.dart b/lib/api/login.dart new file mode 100644 index 0000000..15c5bca --- /dev/null +++ b/lib/api/login.dart @@ -0,0 +1,7 @@ +class LoginApi { + // 获取验证码 + static const String getCode = "/getSmsCode"; + + // 手机号登录 + static const String phoneLogin = "/phone/login"; +} diff --git a/lib/global.dart b/lib/global.dart index c56c409..e4a7996 100644 --- a/lib/global.dart +++ b/lib/global.dart @@ -9,9 +9,19 @@ class Global { // 是否 release static bool get isRelease => const bool.fromEnvironment("dart.vm.product"); + static String tokenKey = "token"; + + static String firstOpenKey = "device_first_open"; + // 全局路由观察者 static RouteObserver routeObserver = RouteObserver(); + /// 是否第一次打开 + static bool? isFirstOpen; + + /// 是否离线登录 + static bool isOfflineLogin = true; + static Future init() async { // 运行初始 WidgetsFlutterBinding.ensureInitialized(); @@ -19,6 +29,15 @@ class Global { // 本地存储初始化 await StorageUtil().init(); + // 网络请求初始化 + Request.init(); + + // 读取设备第一次打开 + isFirstOpen = StorageUtil().getBool(firstOpenKey); + var token = StorageUtil().getString(tokenKey); + if (!ToolFn.isBlank(token)) { + isOfflineLogin = false; + } // android 状态栏为透明的沉浸 if (Platform.isAndroid) { SystemUiOverlayStyle systemUiOverlayStyle = const SystemUiOverlayStyle( @@ -27,4 +46,15 @@ class Global { SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); } } + + // 保存用户已打开APP + static saveAlreadyOpen() { + StorageUtil().putBool(firstOpenKey, false); + } + + // 保存token + static saveUserToken(String token) async { + StorageUtil().putJSON(tokenKey, token); + isOfflineLogin = ToolFn.isBlank(token); + } } diff --git a/lib/main.dart b/lib/main.dart index ecf0bcf..d3e054a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,9 +11,7 @@ import 'utils/utils.dart'; import 'initial_binding.dart'; Future main() async { - debugPrint('应用初始化中...'); await Global.init(); - debugPrint('应用初始化完成...'); // 强制竖屏 StatusBarKit.setPortrait().then((_) { runApp(const StatusbarzCapturer( @@ -34,31 +32,31 @@ class MainApp extends StatelessWidget { final botToastBuilder = BotToastInit(); return GetMaterialApp( - debugShowCheckedModeBanner: false, - // 日志 - enableLog: true, - logWriterCallback: Logger.write, - defaultTransition: Transition.cupertino, - // 路由 - getPages: AppPages.routes, - navigatorObservers: [ - Global.routeObserver, - Statusbarz.instance.observer, - BotToastNavigatorObserver() - ], - // 启动页面 - initialRoute: AppPages.initial, - initialBinding: InitialBinding(), - builder: (context, widget) { - widget = MediaQuery( - data: MediaQuery.of(context) - .copyWith(textScaler: const TextScaler.linear(1.0)), - child: FlutterEasyLoading(child: widget), - ); - widget = botToastBuilder(context, widget); - return widget; - }, - ); + debugShowCheckedModeBanner: false, + // 日志 + enableLog: true, + logWriterCallback: Logger.write, + defaultTransition: Transition.cupertino, + // 路由 + getPages: AppPages.routes, + navigatorObservers: [ + Global.routeObserver, + Statusbarz.instance.observer, + BotToastNavigatorObserver() + ], + // 启动页面 + initialRoute: AppPages.initial, + initialBinding: InitialBinding(), + builder: (context, widget) { + widget = MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaler: const TextScaler.linear(1.0)), + child: FlutterEasyLoading(child: widget), + ); + widget = botToastBuilder(context, widget); + return widget; + }, + locale: Get.deviceLocale); }, ); } diff --git a/lib/pages/layout/home/home_binding.dart b/lib/pages/layout/home/home_binding.dart new file mode 100644 index 0000000..f5529e7 --- /dev/null +++ b/lib/pages/layout/home/home_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'home_logic.dart'; + +class HomeBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => HomeLogic()); + } +} diff --git a/lib/pages/layout/home/home_logic.dart b/lib/pages/layout/home/home_logic.dart new file mode 100644 index 0000000..10c1b7f --- /dev/null +++ b/lib/pages/layout/home/home_logic.dart @@ -0,0 +1,19 @@ +import 'package:get/get.dart'; + +import 'home_state.dart'; + +class HomeLogic extends GetxController { + final HomeState state = HomeState(); + + @override + void onReady() { + // TODO: implement onReady + super.onReady(); + } + + @override + void onClose() { + // TODO: implement onClose + super.onClose(); + } +} diff --git a/lib/pages/layout/home/home_state.dart b/lib/pages/layout/home/home_state.dart new file mode 100644 index 0000000..6953451 --- /dev/null +++ b/lib/pages/layout/home/home_state.dart @@ -0,0 +1,5 @@ +class HomeState { + HomeState() { + ///Initialize variables + } +} diff --git a/lib/pages/layout/home/home_view.dart b/lib/pages/layout/home/home_view.dart new file mode 100644 index 0000000..2d81292 --- /dev/null +++ b/lib/pages/layout/home/home_view.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../../utils/utils.dart'; +import 'home_logic.dart'; + +class HomePage extends StatelessWidget { + const HomePage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final logic = Get.find(); + final state = Get.find().state; + + double statusBarHeight = MediaQuery.of(context).padding.top; + + return PopScope( + canPop: false, + onPopInvoked: (_) { + ToolFn().isExit(); + }, + child: Scaffold( + body: Stack( + fit: StackFit.expand, + children: [ + Image.asset( + 'assets/images/home-tab-normal-bg.png', + fit: BoxFit.fitWidth, + ), + Padding( + padding: + EdgeInsets.only(top: statusBarHeight, left: 10, right: 10), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(50), + child: Image.network( + "https://pic.ziyuan.wang/user/guest/2023/12/微信图片_20231109211458_c1a41ab0fd7dd.jpg", + width: 50.0, + )), + const SizedBox( + width: 5, + ), + const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "商家: GAGA酒吧", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500), + ), + Text( + "Huakk,欢迎回来!!!", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ) + ], + ) + ], + ), + const Row( + children: [ + Icon( + TDIcons.search, + size: 35, + ), + Icon( + TDIcons.scan, + size: 35, + ), + Icon( + TDIcons.sound, + size: 35, + ), + ], + ) + ], + ) + ], + )) + ], + ))); + } +} diff --git a/lib/pages/layout/home/index.dart b/lib/pages/layout/home/index.dart new file mode 100644 index 0000000..eb5b612 --- /dev/null +++ b/lib/pages/layout/home/index.dart @@ -0,0 +1,6 @@ +library home; + +export './home_binding.dart'; +export './home_logic.dart'; +export './home_state.dart'; +export './home_view.dart'; \ No newline at end of file diff --git a/lib/pages/layout/index.dart b/lib/pages/layout/index.dart new file mode 100644 index 0000000..2d5df90 --- /dev/null +++ b/lib/pages/layout/index.dart @@ -0,0 +1,4 @@ +library tabbar; + +export './home/index.dart'; +export './user/index.dart'; diff --git a/lib/pages/layout/user/index.dart b/lib/pages/layout/user/index.dart new file mode 100644 index 0000000..399fba9 --- /dev/null +++ b/lib/pages/layout/user/index.dart @@ -0,0 +1,6 @@ +library user; + +export './user_binding.dart'; +export './user_logic.dart'; +export './user_state.dart'; +export './user_view.dart'; diff --git a/lib/pages/layout/user/user_binding.dart b/lib/pages/layout/user/user_binding.dart new file mode 100644 index 0000000..fa61dc1 --- /dev/null +++ b/lib/pages/layout/user/user_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'user_logic.dart'; + +class UserBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => UserLogic()); + } +} diff --git a/lib/pages/layout/user/user_logic.dart b/lib/pages/layout/user/user_logic.dart new file mode 100644 index 0000000..d10d7cc --- /dev/null +++ b/lib/pages/layout/user/user_logic.dart @@ -0,0 +1,19 @@ +import 'package:get/get.dart'; + +import 'user_state.dart'; + +class UserLogic extends GetxController { + final UserState state = UserState(); + + @override + void onReady() { + // TODO: implement onReady + super.onReady(); + } + + @override + void onClose() { + // TODO: implement onClose + super.onClose(); + } +} diff --git a/lib/pages/layout/user/user_state.dart b/lib/pages/layout/user/user_state.dart new file mode 100644 index 0000000..8f24f71 --- /dev/null +++ b/lib/pages/layout/user/user_state.dart @@ -0,0 +1,5 @@ +class UserState { + UserState() { + ///Initialize variables + } +} diff --git a/lib/pages/layout/user/user_view.dart b/lib/pages/layout/user/user_view.dart new file mode 100644 index 0000000..4958e61 --- /dev/null +++ b/lib/pages/layout/user/user_view.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../utils/utils.dart'; +import 'user_logic.dart'; + +class UserPage extends StatelessWidget { + const UserPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final logic = Get.find(); + final state = Get.find().state; + + return PopScope( + canPop: false, + onPopInvoked: (_) { + ToolFn().isExit(); + }, + child: Scaffold( + appBar: AppBar( + title: const Text("商家中心"), + ), + body: const Center( + child: Text("商家中心!!!"), + ), + ), + ); + } +} diff --git a/lib/pages/login/logic.dart b/lib/pages/login/logic.dart index cfd5793..71a620f 100644 --- a/lib/pages/login/logic.dart +++ b/lib/pages/login/logic.dart @@ -1,20 +1,64 @@ import 'dart:io'; import 'package:bot_toast/bot_toast.dart'; +import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'state.dart'; +import '../../utils/utils.dart'; +import '../../api/index.dart'; class LoginLogic extends GetxController { final LoginState state = LoginState(); - void isExit(context) { - if (state.lastPressedAt == null || - DateTime.now().difference(state.lastPressedAt!) > - const Duration(seconds: 1)) { - //两次点击间隔超过1秒则重新计时 - state.lastPressedAt = DateTime.now(); - BotToast.showText(text:"再按一次退出程序"); - return; + void setPhone(String value) => state.phone = value; + + void setSmsCode(String value) => state.code = value; + + // 验证码倒计时 + void startCountdownTimer() { + if (state.countTime > 0) { + state.countTime--; + update(); + Future.delayed(const Duration(seconds: 1), startCountdownTimer); + } else { + state.countTime = 0; + update(); } - exit(0); + } + + // 获取验证码 + Future getCode(context) async { + if (ToolFn.isBlank(state.phone) || ToolFn.checkMobile(state.phone)) { + return ToolFn.tips("请输入正确的手机号"); + } + if (state.countTime == 0) { + final res = + await Request.post>(LoginApi.getCode, data: { + "phone": state.phone, + }); + TDToast.showText(res["msg"].toString(), context: context); + state.countTime = 60; + startCountdownTimer(); + } + } + + // 登录 + Future loginFn(context) async { + if (ToolFn.isBlank(state.phone) || ToolFn.checkMobile(state.phone)) { + return ToolFn.tips("请输入正确的手机号"); + } + if (ToolFn.isBlank(state.code)) { + return ToolFn.tips("请输入正确的验证码"); + } + FocusScope.of(context).unfocus(); + final res = + await Request.post>(LoginApi.phoneLogin, data: { + "Phone": state.phone, + "Code": state.code, + }); + // 保存token + StorageUtil().putString("token", res["data"]["token"]); + // 保存用户信息 + StorageUtil().putJSON("userInfo", res["data"]["data"]); } } diff --git a/lib/pages/login/state.dart b/lib/pages/login/state.dart index 8c67d4e..df2f7c0 100644 --- a/lib/pages/login/state.dart +++ b/lib/pages/login/state.dart @@ -1,7 +1,22 @@ class LoginState { - DateTime? lastPressedAt; //上次点击时间 + // 表单 + late String phone; + + late String code; + + // 验证码倒计时 + late int countTime; + + // 登录按钮状态 + late bool loginBtnStatus; LoginState() { - ///Initialize variables + phone = ''; + + code = ''; + + countTime = 0; + + loginBtnStatus = true; } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 2efd7da..a3b0a30 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../utils/utils.dart'; import 'logic.dart'; class LoginPage extends StatelessWidget { @@ -9,18 +11,123 @@ class LoginPage extends StatelessWidget { final logic = Get.find(); final state = Get.find().state; - DateTime? _lastPressedAt; - @override Widget build(BuildContext context) { return PopScope( canPop: false, onPopInvoked: (_) { - logic.isExit(context); + ToolFn().isExit(); }, - child: const Scaffold( - body: Center( - child: Text("login"), + child: Scaffold( + backgroundColor: Colors.white, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset( + "assets/images/login/icon-no-shop-head.png", + width: double.maxFinite, + ), + Padding( + padding: const EdgeInsets.only(left: 10, right: 10, top: 50), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.only(left: 15), + child: Text("手机登录", + style: TextStyle( + fontSize: 30, + color: Colors.black, + fontWeight: FontWeight.w400))), + const SizedBox(height: 16.0), + Form( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDInput( + maxNum: 11, + leftLabel: '+86', + hintText: '请输入手机号码', + onChanged: (text) { + logic.setPhone(text); + }, + onClearTap: () { + state.phone = ""; + }, + ), + TDInput( + maxNum: 6, + hintText: '请输入验证码', + rightBtn: SizedBox( + width: 100, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(right: 16), + child: Container( + width: 0.5, + height: 24, + color: TDTheme.of(context).grayColor3, + ), + ), + GetBuilder( + builder: (_) { + return state.countTime != 0 + ? TDText( + '重发(${state.countTime}秒)', + textColor: TDTheme.of(context) + .fontGyColor4, + ) + : TDText("发送验证码", + textColor: TDTheme.of(context) + .brandNormalColor); + }, + ), + ], + ), + ), + needClear: false, + onBtnTap: () { + logic.getCode(context); + }, + onChanged: (text) { + logic.setSmsCode(text); + }, + onClearTap: () { + state.phone = ""; + }, + ), + Padding( + padding: const EdgeInsets.all(15), + child: TDText( + "未注册的手机号登录成功后将自动注册", + textColor: TDTheme.of(context).grayColor6, + textAlign: TextAlign.start, + style: const TextStyle(fontSize: 13), + )), + GetBuilder( + builder: (_) { + return TDButton( + isBlock: true, + size: TDButtonSize.large, + theme: TDButtonTheme.primary, + width: double.maxFinite, + text: "立即登录", + disabled: false, + onTap: () { + logic.loginFn(context); + }, + ); + }, + ), + ], + ), + ) + ]), + ) + ], ), )); } diff --git a/lib/pages/splash/splash_logic.dart b/lib/pages/splash/splash_logic.dart index b6c247b..db0c0b6 100644 --- a/lib/pages/splash/splash_logic.dart +++ b/lib/pages/splash/splash_logic.dart @@ -54,10 +54,9 @@ class SplashLogic extends GetxController with GetTickerProviderStateMixin { await Future.delayed(const Duration(seconds: 3), () { final token = StorageUtil().getString("token"); if (!ToolFn.isBlank(token)) { - Get.offAllNamed('/home'); + Get.offAllNamed('/tab'); } else { Get.offAllNamed('/login'); - // Get.toNamed('/login'); } }); } diff --git a/lib/pages/tab/index.dart b/lib/pages/tab/index.dart new file mode 100644 index 0000000..2b0c598 --- /dev/null +++ b/lib/pages/tab/index.dart @@ -0,0 +1,6 @@ +library tab; + +export './tab_logic.dart'; +export './tab_state.dart'; +export './tab_view.dart'; +export './tab_binding.dart'; diff --git a/lib/pages/tab/tab_binding.dart b/lib/pages/tab/tab_binding.dart new file mode 100644 index 0000000..67f81ff --- /dev/null +++ b/lib/pages/tab/tab_binding.dart @@ -0,0 +1,14 @@ +import 'package:flutter_jdt_store/pages/layout/home/index.dart'; +import 'package:flutter_jdt_store/pages/layout/user/index.dart'; +import 'package:get/get.dart'; + +import 'tab_logic.dart'; + +class TabBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => TabLogic()); + Get.lazyPut(() => HomeLogic()); + Get.lazyPut(() => UserLogic()); + } +} diff --git a/lib/pages/tab/tab_logic.dart b/lib/pages/tab/tab_logic.dart new file mode 100644 index 0000000..f35b743 --- /dev/null +++ b/lib/pages/tab/tab_logic.dart @@ -0,0 +1,48 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../global.dart'; +import 'tab_state.dart'; + +class TabLogic extends GetxController { + final TabState state = TabState(); + + @override + void onInit() { + super.onInit(); + state.pageController = PageController(initialPage: state.currentPage); + } + + @override + void onReady() { + // TODO: implement onReady + super.onReady(); + } + + @override + void onClose() { + // TODO: implement onClose + super.onClose(); + } + + @override + void dispose() { + state.pageController!.dispose(); + super.dispose(); + } + + handlePageChanged(int page) { + state.currentPage = page; + update(); + } + + handleNavBarTap(int index) { + if (index == state.currentPage) return; + if (Global.isOfflineLogin && index != 0) { + Get.toNamed('/tab'); + return; + } + state.pageController!.jumpToPage(index); + } +} diff --git a/lib/pages/tab/tab_state.dart b/lib/pages/tab/tab_state.dart new file mode 100644 index 0000000..51a844c --- /dev/null +++ b/lib/pages/tab/tab_state.dart @@ -0,0 +1,10 @@ +import 'package:flutter/cupertino.dart'; + +class TabState { + late int currentPage; + PageController? pageController; + TabState() { + ///Initialize variables + currentPage = 0; + } +} diff --git a/lib/pages/tab/tab_view.dart b/lib/pages/tab/tab_view.dart new file mode 100644 index 0000000..974d471 --- /dev/null +++ b/lib/pages/tab/tab_view.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../utils/utils.dart'; +import '../layout/home/index.dart'; +import '../layout/user/index.dart'; +import 'tab_logic.dart'; + +class TabPage extends StatelessWidget { + const TabPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final logic = Get.find(); + final state = Get.find().state; + + /// 内容页 + Widget buildPageView() { + return GetBuilder(builder: (_) { + return PageView( + physics: const NeverScrollableScrollPhysics(), + controller: state.pageController, + onPageChanged: logic.handlePageChanged, + children: const [ + HomePage(), + UserPage(), + ], + ); + }); + } + + List createBottomItems() { + return [ + const BottomNavigationBarItem( + icon: Icon( + TDIcons.dashboard, + color: Colors.grey, + ), + activeIcon: Icon( + TDIcons.dashboard, + color: Color.fromRGBO(0, 82, 217, 1), + ), + label: "工作台", + ), + const BottomNavigationBarItem( + icon: Icon( + TDIcons.desktop, + color: Colors.grey, + ), + activeIcon: Icon( + TDIcons.desktop, + color: Color.fromRGBO(0, 82, 217, 1), + ), + label: "商家中心", + ), + ]; + } + + /// 底部导航 + Widget buildBottomNavigationBar() { + List bottomItems = createBottomItems(); + return GetBuilder(builder: (_) { + return BottomNavigationBar( + items: bottomItems, + currentIndex: state.currentPage, + backgroundColor: Colors.white, + unselectedItemColor: Colors.grey, + selectedItemColor: const Color.fromRGBO(0, 82, 217, 1), + type: BottomNavigationBarType.fixed, + onTap: logic.handleNavBarTap, + selectedFontSize: 10.sp, + unselectedFontSize: 10.sp, + iconSize: 32.w, + ); + }); + } + + return PopScope( + canPop: false, + onPopInvoked: (_) { + ToolFn().isExit(); + }, + child: Scaffold( + body: buildPageView(), + bottomNavigationBar: buildBottomNavigationBar(), + )); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 1cff134..e2fd6d2 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -1,11 +1,15 @@ import 'package:get/get.dart'; import '../pages/splash/index.dart'; import '../pages/login/index.dart'; +import '../pages/layout/index.dart'; +import '../pages/tab/index.dart'; class AppPages { static const String initial = '/'; static const String login = '/login'; static const String home = '/home'; + static const String user = '/user'; + static const String tab = '/tab'; static final List routes = [ GetPage( @@ -14,6 +18,11 @@ class AppPages { binding: SplashBinding()), GetPage( name: AppPages.login, page: () => LoginPage(), binding: LoginBinding()), - // GetPage(name: AppPages.home, page: () => HomePage()), + // GetPage( + // name: AppPages.home, page: () => HomePage(), binding: HomeBinding()), + // GetPage( + // name: AppPages.user, page: () => UserPage(), binding: UserBinding()), + GetPage( + name: AppPages.tab, page: () => const TabPage(), binding: TabBinding()), ]; } diff --git a/lib/utils/request.dart b/lib/utils/request.dart new file mode 100644 index 0000000..2bfedfe --- /dev/null +++ b/lib/utils/request.dart @@ -0,0 +1,85 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_jdt_store/utils/utils.dart'; + +class Request { + // 构造函数 + Request() { + init(); + } + + static final Dio _dio = Dio(); + + static void init() { + // 基本配置 + _dio.options.baseUrl = + "https://www.wanzhuanyongcheng.cn/app"; // 替换为你的 API 地址 + _dio.options.connectTimeout = const Duration(seconds: 15); // 连接超时时间,单位是毫秒 + _dio.options.receiveTimeout = const Duration(seconds: 15); // 接收超时时间,单位是毫秒 + + _dio.options.headers["Content-Type"] = "application/json; charset=utf-8"; + + _dio.options.headers["token"] = StorageUtil().getString("token"); + + // 添加请求拦截器 + _dio.interceptors.add(InterceptorsWrapper( + onRequest: (options, handler) { + // 在请求被发送之前做一些事情 + return handler.next(options); // 必须返回 options + }, + )); + + // 添加响应拦截器 + _dio.interceptors.add(InterceptorsWrapper( + onResponse: (response, handler) { + // 在响应被处理之前做一些事情 + final Map data = response.data; + // 状态码不等于200时抛出异常 + if (data["code"] != 200) { + ToolFn.tips(data["msg"].toString()); + throw DioException( + requestOptions: response.requestOptions, + response: response, + error: data["msg"], + ); + } + return handler.next(response); // 必须返回 response + }, + onError: (DioException e, handler) { + // 在响应错误被处理之前做一些事情 + return handler.next(e); // 必须返回 error + }, + )); + } + + static Future get(String path, {Map? params}) async { + try { + final Response response = + await _dio.get(path, queryParameters: params); + return response.data!; + } catch (error) { + throw _handleError(error); + } + } + + static Future post(String path, {Map? data}) async { + try { + final Response response = await _dio.post(path, data: data); + return response.data!; + } catch (error) { + throw _handleError(error); + } + } + + // 处理错误 + static Exception _handleError(error) { + if (error is DioException) { + String message = "网络请求失败"; + if (error.response != null && error.response!.data != null) { + message = error.response!.data.toString(); + } + return Exception(message); + } else { + return Exception("未知错误"); + } + } +} diff --git a/lib/utils/tool.dart b/lib/utils/tool.dart index a07b8b3..35c8947 100644 --- a/lib/utils/tool.dart +++ b/lib/utils/tool.dart @@ -1,9 +1,45 @@ +import 'dart:io'; + +import 'package:bot_toast/bot_toast.dart'; +import 'package:flutter/material.dart'; + class ToolFn { + DateTime? lastPressedAt; //上次点击时间 + // 字符串是否为空 - static bool isBlank(String? str) { - if (str == null || str.isEmpty) { + static bool isBlank(String str) { + if (str.isEmpty) { return true; } return false; } + + ///手机号验证(只验证前三位号段) + static bool checkMobile(String str) { + if (isBlank(str)) { + return true; + } + return !RegExp(r"^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}") + .hasMatch(str); + } + + static void tips(String msg) { + BotToast.showText( + text: msg, + contentColor: Colors.black, + textStyle: const TextStyle(fontSize: 17, color: Colors.white)); + } + + // 监听返回键 + void isExit() { + if (lastPressedAt == null || + DateTime.now().difference(lastPressedAt!) > + const Duration(seconds: 1)) { + //两次点击间隔超过1秒则重新计时 + lastPressedAt = DateTime.now(); + ToolFn.tips("再按一次退出程序"); + return; + } + exit(0); + } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 59d82e5..a823bcf 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -3,4 +3,5 @@ library utils; export 'logger.dart'; export 'status_bar.dart'; export 'storage.dart'; -export 'tool.dart'; \ No newline at end of file +export 'tool.dart'; +export 'request.dart'; \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 176c045..f92b8ce 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.4.9" + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.2" async: dependency: transitive description: @@ -174,6 +182,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "5.2.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.9" flutter_swiper_null_safety: dependency: transitive description: @@ -493,6 +509,30 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.3.2" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.9+1" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.9+1" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.9+1" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 21c57b6..43a5237 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: sdk: flutter flutter_easyloading: ^3.0.5 flutter_screenutil: ^5.9.0 + flutter_svg: ^2.0.9 get: ^4.6.6 package_info_plus: ^5.0.1 shared_preferences: ^2.2.2 @@ -29,4 +30,6 @@ dev_dependencies: flutter: uses-material-design: true assets: - - assets/images/logo.png \ No newline at end of file + - assets/images/logo.png + - assets/images/login/icon-no-shop-head.png + - assets/images/home-tab-normal-bg.png \ No newline at end of file