diff --git a/lib/base/base_state_widget.dart b/lib/base/base_state_widget.dart index b968c51..bb490c8 100644 --- a/lib/base/base_state_widget.dart +++ b/lib/base/base_state_widget.dart @@ -50,7 +50,7 @@ class _BaseStateWidgetState extends ConsumerState> login(String userName, String passWord) async { + return await Http.post( + Url.LOGIN, + { + "username": userName, + "password": passWord, + }, + ); + } + + static Future>> crons() async { + return await Http.get>(Url.TASKS, {"searchValue": ""}); + } +} diff --git a/lib/base/http/http.dart b/lib/base/http/http.dart index 5b55a59..57b9e68 100644 --- a/lib/base/http/http.dart +++ b/lib/base/http/http.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:ffi'; + import 'package:dio/dio.dart'; import 'package:dio_log/dio_log.dart'; import 'package:flutter/foundation.dart'; @@ -27,48 +30,111 @@ class Http { _dio?.interceptors.add(TokenInterceptor()); } - static Future> post(String uri, Map json, {bool compute = true}) async { - if (userInfoViewModel.host == null || userInfoViewModel.host!.isEmpty) { - return HttpResponse(success: false, code: NOT_LOGIN, message: "未登录"); - } - + static void _init() { if (_dio == null) { initDioConfig(UserInfoViewModel.getInstance().host!); } + } + static Future> get(String uri, Map json, {bool compute = true}) async { + _init(); + var response = await _dio!.get(uri, queryParameters: json); + + return decodeResponse(response, compute); + } + + static Future> post(String uri, dynamic json, {bool compute = true}) async { + _init(); var response = await _dio!.post(uri, data: json); return decodeResponse(response, compute); } + static Future> delete(String uri, dynamic json, {bool compute = true}) async { + _init(); + var response = await _dio!.delete(uri, data: json); + + return decodeResponse(response, compute); + } + + static Future> put(String uri, dynamic json, {bool compute = true}) async { + _init(); + var response = await _dio!.put(uri, data: json); + + return decodeResponse(response, compute); + } + static HttpResponse decodeResponse( Response response, bool compute, ) { - late HttpResponse result; - int code = 0; if (response.statusCode == 200) { - if (compute) { - try { - if (response.data["code"] == 200) { + try { + if (response.data["code"] == 200) { + if (response.data["data"] != null) { + if (T is Void) { + return HttpResponse( + success: true, + code: 200, + ); + } + dynamic data = response.data["data"]; - T bean = DeserializeAction.invokeJson(DeserializeAction(data)); - result = HttpResponse(success: true, code: 200, bean: bean); + T t; + if (T is String) { + if (data is String) { + t = data as T; + } else { + t = jsonEncode(data) as T; + } + return HttpResponse( + success: true, + code: 200, + bean: t, + ); + } else { + T bean; + if (compute) { + bean = DeserializeAction.invokeJson(DeserializeAction(data)); + } else { + bean = JsonConversion$Json.fromJson(data); + } + return HttpResponse( + success: true, + code: 200, + bean: bean, + ); + } } else { - result = HttpResponse(success: false, code: -1000, message: "服务器返回数据异常"); + return HttpResponse( + success: false, + code: 200, + ); } - } catch (e) { - result = HttpResponse(success: false, code: -1000, message: "json解析失败"); + } else { + return HttpResponse( + success: false, + code: -1000, + message: "服务器返回数据异常", + ); } + } catch (e) { + return HttpResponse( + success: false, + code: -1000, + message: "json解析失败", + ); } } else { code = response.statusCode ?? 0; - result = HttpResponse(success: false, code: code, message: response.statusMessage); + return HttpResponse( + success: false, + code: code, + message: response.statusMessage, + ); } - - return result; } } diff --git a/lib/base/http/url.dart b/lib/base/http/url.dart index abfb268..f29b687 100644 --- a/lib/base/http/url.dart +++ b/lib/base/http/url.dart @@ -1,3 +1,4 @@ class Url { - static const LOGIN = "api/user/login"; + static const LOGIN = "/api/user/login"; + static const TASKS = "/api/crons"; } diff --git a/lib/base/theme.dart b/lib/base/theme.dart index 0531b7d..517599d 100644 --- a/lib/base/theme.dart +++ b/lib/base/theme.dart @@ -7,11 +7,15 @@ var themeProvider = ChangeNotifierProvider((ref) => ThemeViewModel()); class ThemeViewModel extends ChangeNotifier { ThemeData currentTheme = lightTheme; + ThemeColors themeColor = LightartThemeColors(); + void changeTheme() { if (currentTheme == darkTheme) { currentTheme = lightTheme; + themeColor = LightartThemeColors(); } else { currentTheme = darkTheme; + themeColor = DartThemeColors(); } notifyListeners(); } @@ -23,3 +27,21 @@ ThemeData darkTheme = ThemeData.dark().copyWith( ThemeData lightTheme = ThemeData.light().copyWith( primaryColor: Color(0xFF0F77FE), ); + +abstract class ThemeColors { + Color taskTitleColor(); +} + +class LightartThemeColors extends ThemeColors { + @override + Color taskTitleColor() { + return Color(0xff333333); + } +} + +class DartThemeColors extends ThemeColors { + @override + Color taskTitleColor() { + return Colors.white; + } +} diff --git a/lib/base/userinfo_viewmodel.dart b/lib/base/userinfo_viewmodel.dart index 061d9f8..ab73b48 100644 --- a/lib/base/userinfo_viewmodel.dart +++ b/lib/base/userinfo_viewmodel.dart @@ -15,7 +15,7 @@ class UserInfoViewModel { UserInfoViewModel._() { String userInfoJson = SpUtil.getString(sp_UserINfo); - _host = SpUtil.getString(sp_Host); + _host = SpUtil.getString(sp_Host,defValue: "http://49.234.59.95:5700"); if (userInfoJson.isNotEmpty) { _token = userInfoJson; } @@ -34,4 +34,8 @@ class UserInfoViewModel { String? get token => _token; String? get host => _host; + + bool isLogined() { + return token != null && token!.isNotEmpty; + } } diff --git a/lib/main.dart b/lib/main.dart index b978dc9..96351a5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,11 +1,13 @@ import 'dart:io'; +import 'package:dio_log/overlay_draggable_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:logger/logger.dart'; import 'package:qinglong_app/base/theme.dart'; import 'package:qinglong_app/module/login/login_page.dart'; +import 'package:qinglong_app/utils/sp_utils.dart'; import 'base/routes.dart'; import 'base/userinfo_viewmodel.dart'; @@ -15,9 +17,9 @@ late UserInfoViewModel userInfoViewModel; var logger = Logger(); -void main() { - +void main() async { WidgetsFlutterBinding.ensureInitialized(); + await SpUtil.getInstance(); userInfoViewModel = UserInfoViewModel.getInstance(); runApp( ProviderScope( @@ -48,8 +50,13 @@ class MyApp extends ConsumerWidget { onGenerateRoute: (setting) { return Routes.generateRoute(setting); }, - // home: ref.read(userInfoProvider).userInfoBean != null ? const HomePage() : LoginPage(), - home: LoginPage(), + home: Builder( + builder: (context) { + showDebugBtn(context, btnColor: Colors.blue); + return userInfoViewModel.isLogined() ? const HomePage() : LoginPage(); + }, + ), + // home: LoginPage(), ), ); } diff --git a/lib/module/login/login_page.dart b/lib/module/login/login_page.dart index e0be585..a3579f7 100644 --- a/lib/module/login/login_page.dart +++ b/lib/module/login/login_page.dart @@ -22,7 +22,7 @@ class _LoginPageState extends State { @override void initState() { super.initState(); - showDebugBtn(context, btnColor: Colors.blue); + } @override diff --git a/lib/module/login/login_viewmodel.dart b/lib/module/login/login_viewmodel.dart index 062fefd..245f1af 100644 --- a/lib/module/login/login_viewmodel.dart +++ b/lib/module/login/login_viewmodel.dart @@ -1,5 +1,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:qinglong_app/base/base_viewmodel.dart'; +import 'package:qinglong_app/base/http/api.dart'; import 'package:qinglong_app/base/http/http.dart'; import 'package:qinglong_app/base/http/url.dart'; import 'package:qinglong_app/main.dart'; @@ -19,13 +20,7 @@ class LoginViewModel extends ViewModel { msg = ""; notifyListeners(); - HttpResponse response = await Http.post( - Url.LOGIN, - { - "username": userName, - "password": password, - }, - ); + HttpResponse response = await Api.login(userName, password); if (response.success) { userInfoViewModel.updateToken(response.bean?.token ?? ""); diff --git a/lib/module/task/task_bean.dart b/lib/module/task/task_bean.dart index ba65f7b..b49a006 100644 --- a/lib/module/task/task_bean.dart +++ b/lib/module/task/task_bean.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:json_conversion_annotation/json_conversion_annotation.dart'; @JsonConversion() @@ -7,14 +9,14 @@ class TaskBean { String? schedule; bool? saved; String? sId; - int? created; + num? created; int? status; String? timestamp; int? isSystem; int? isDisabled; String? logPath; int? isPinned; - int? lastExecutionTime; + num? lastExecutionTime; int? lastRunningTime; String? pid; @@ -36,21 +38,26 @@ class TaskBean { this.pid}); TaskBean.fromJson(Map json) { - name = json['name']; - command = json['command']; - schedule = json['schedule']; - saved = json['saved']; - sId = json['_id']; - created = json['created']; - status = json['status']; - timestamp = json['timestamp']; - isSystem = json['isSystem']; - isDisabled = json['isDisabled']; - logPath = json['log_path']; - isPinned = json['isPinned']; - lastExecutionTime = json['last_execution_time']; - lastRunningTime = json['last_running_time']; - pid = json['pid']; + try { + name = json['name'].toString(); + command = json['command'].toString(); + schedule = json['schedule'].toString(); + saved = json['saved']; + sId = json['_id'].toString(); + created = json['created']; + status = json['status']; + timestamp = json['timestamp'].toString(); + isSystem = json['isSystem']; + isDisabled = json['isDisabled']; + logPath = json['log_path'].toString(); + isPinned = json['isPinned']; + lastExecutionTime = json['last_execution_time']; + lastRunningTime = json['last_running_time']; + pid = json['pid'].toString(); + } catch (e) { + print(jsonEncode(json)); + print(e); + } } Map toJson() { diff --git a/lib/module/task/task_page.dart b/lib/module/task/task_page.dart index 2e1faa1..05b35a8 100644 --- a/lib/module/task/task_page.dart +++ b/lib/module/task/task_page.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:qinglong_app/base/base_state_widget.dart'; +import 'package:qinglong_app/base/theme.dart'; +import 'package:qinglong_app/module/task/task_bean.dart'; import 'package:qinglong_app/module/task/task_viewmodel.dart'; class TaskPage extends StatefulWidget { @@ -19,18 +21,18 @@ class _TaskPageState extends State { builder: (context, model, child) { return RefreshIndicator( onRefresh: () async { - return Future.delayed(Duration(seconds: 1), () {}); + return model.loadData(false); }, child: ListView.separated( - padding: EdgeInsets.symmetric( + padding: const EdgeInsets.symmetric( vertical: 15, ), itemBuilder: (context, index) { - return TaskItemCell(); + return TaskItemCell(model.list[index]); }, itemCount: model.list.length, separatorBuilder: (BuildContext context, int index) { - return SizedBox( + return const SizedBox( height: 10, ); }, @@ -45,11 +47,13 @@ class _TaskPageState extends State { } } -class TaskItemCell extends StatelessWidget { - const TaskItemCell({Key? key}) : super(key: key); +class TaskItemCell extends ConsumerWidget { + final TaskBean bean; + + const TaskItemCell(this.bean, {Key? key}) : super(key: key); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { return Slidable( key: const ValueKey(0), startActionPane: ActionPane( @@ -98,34 +102,49 @@ class TaskItemCell extends StatelessWidget { children: [ Row( mainAxisAlignment: MainAxisAlignment.start, - children: const [ + children: [ Text( - "东东农场", + bean.name ?? "", + maxLines: 1, style: TextStyle( - fontWeight: FontWeight.w500, + overflow: TextOverflow.ellipsis, + color: bean.isDisabled == 1 ? Color(0xffF85152) : ref.watch(themeProvider).themeColor.taskTitleColor(), fontSize: 18, ), ), - SizedBox( + const SizedBox( width: 5, ), - SizedBox( - width: 15, - height: 15, - child: CircularProgressIndicator( - strokeWidth: 2, + bean.status == 1 + ? const SizedBox.shrink() + : const SizedBox( + width: 15, + height: 15, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ), + const Spacer(), + Text( + "${bean.lastRunningTime ?? "-"}", + maxLines: 1, + style: const TextStyle( + overflow: TextOverflow.ellipsis, + color: Color(0xff999999), + fontSize: 12, ), ), - Spacer(), - Text("2020/12/12 12:23"), ], ), const SizedBox( height: 5, ), - const Text( - "12/12/12", - style: TextStyle( + Text( + bean.schedule ?? "", + maxLines: 1, + style: const TextStyle( + overflow: TextOverflow.ellipsis, + color: Color(0xff999999), fontSize: 12, ), ), diff --git a/lib/module/task/task_viewmodel.dart b/lib/module/task/task_viewmodel.dart index 3f63da5..d2afd66 100644 --- a/lib/module/task/task_viewmodel.dart +++ b/lib/module/task/task_viewmodel.dart @@ -1,31 +1,37 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:qinglong_app/base/base_viewmodel.dart'; +import 'package:qinglong_app/base/http/api.dart'; +import 'package:qinglong_app/base/http/http.dart'; +import 'package:qinglong_app/module/task/task_bean.dart'; var taskProvider = ChangeNotifierProvider((ref) => TaskViewModel()); class TaskViewModel extends BaseViewModel { - List list = []; + List list = []; - void loadData() { - loading(notify: true); + Future loadData([isLoading = true]) async { + if (isLoading) { + loading(notify: true); + } + + HttpResponse> result = await Api.crons(); + + if (result.success && result.bean != null) { + list.clear(); + list.addAll(result.bean!); + + list.sort((a, b) { + return a.isDisabled! - b.isDisabled!; + }); + + list.sort((a, b) { + return a.status! - b.status!; + }); - Future.delayed(Duration(seconds: 2), () { - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); - list.add("sdfsf"); success(); - }); + } else { + list.clear(); + failed(result.message, notify: true); + } } } diff --git a/lib/utils/extension.dart b/lib/utils/extension.dart new file mode 100644 index 0000000..bc70745 --- /dev/null +++ b/lib/utils/extension.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +extension ContextExt on BuildContext { + T read(ProviderBase provider) { + return (this as WidgetRef).read(provider); + } + + T watch(ProviderBase provider) { + return (this as WidgetRef).watch(provider); + } +} diff --git a/test/widget_test.dart b/test/widget_test.dart index 51f9fdc..9c4006f 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -16,4 +16,7 @@ import 'package:qinglong_app/module/login/login_viewmodel.dart'; void main() { + testWidgets("", (_){ + + }); }