This commit is contained in:
jyuesong
2022-01-13 16:45:44 +08:00
parent d29c9e73f7
commit e8956e5ff1
11 changed files with 156 additions and 33 deletions

View File

@@ -1,5 +1,6 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qinglong_app/base/common_dialog.dart';
import 'base_viewmodel.dart'; import 'base_viewmodel.dart';
class BaseStateWidget<T extends BaseViewModel> extends ConsumerStatefulWidget { class BaseStateWidget<T extends BaseViewModel> extends ConsumerStatefulWidget {
@@ -36,6 +37,12 @@ class _BaseStateWidgetState<T extends BaseViewModel> extends ConsumerState<BaseS
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var viewModel = ref.watch<T>(widget.model); var viewModel = ref.watch<T>(widget.model);
if (viewModel.failReason != null) {
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
failDialog(context, viewModel.failReason!);
viewModel.clearToast();
});
}
if (viewModel.currentState == PageState.CONTENT) { if (viewModel.currentState == PageState.CONTENT) {
return widget.builder(ref, viewModel, widget.child); return widget.builder(ref, viewModel, widget.child);
} }
@@ -50,7 +57,7 @@ class _BaseStateWidgetState<T extends BaseViewModel> extends ConsumerState<BaseS
if (viewModel.currentState == PageState.FAILED) { if (viewModel.currentState == PageState.FAILED) {
return Container( return Container(
alignment: Alignment.center, alignment: Alignment.center,
child: Text(viewModel.failReason ??""), child: Text(viewModel.failReason ?? ""),
); );
} }

View File

@@ -7,6 +7,7 @@ class BaseViewModel extends ViewModel {
String? failReason; String? failReason;
void loading({bool notify = false}) { void loading({bool notify = false}) {
failReason = null;
currentState = PageState.LOADING; currentState = PageState.LOADING;
if (notify) { if (notify) {
notifyListeners(); notifyListeners();
@@ -14,6 +15,7 @@ class BaseViewModel extends ViewModel {
} }
void success({bool notify = true}) { void success({bool notify = true}) {
failReason = null;
currentState = PageState.CONTENT; currentState = PageState.CONTENT;
if (notify) { if (notify) {
notifyListeners(); notifyListeners();
@@ -27,8 +29,20 @@ class BaseViewModel extends ViewModel {
notifyListeners(); notifyListeners();
} }
} }
void failToast(String? reason, {bool notify = false}) {
currentState = PageState.CONTENT;
failReason = reason;
if (notify) {
notifyListeners();
}
}
void clearToast(){
failReason = null;
}
void empty({bool notify = false}) { void empty({bool notify = false}) {
failReason = null;
currentState = PageState.EMPTY; currentState = PageState.EMPTY;
if (notify) { if (notify) {
notifyListeners(); notifyListeners();

View File

@@ -39,6 +39,16 @@ class Api {
} }
static Future<HttpResponse<TaskDetailBean>> addTask(String name, String command, String cron, {String? id}) async { static Future<HttpResponse<TaskDetailBean>> addTask(String name, String command, String cron, {String? id}) async {
return await Http.put<TaskDetailBean>(Url.ADD_TASK, {"name": name, "command": command, "schedule": cron, "_id": id}); var data = {"name": name, "command": command, "schedule": cron};
if (id != null) {
data["_id"] = id;
return await Http.put<TaskDetailBean>(Url.ADD_TASK, data);
}
return await Http.post<TaskDetailBean>(Url.ADD_TASK, data);
}
static Future<HttpResponse<NullResponse>> delTask(String cron) async {
return await Http.delete<NullResponse>(Url.ADD_TASK, [cron]);
} }
} }

View File

@@ -36,31 +36,46 @@ class Http {
} }
static Future<HttpResponse<T>> get<T>(String uri, Map<String, String>? json, {bool compute = true}) async { static Future<HttpResponse<T>> get<T>(String uri, Map<String, String>? json, {bool compute = true}) async {
_init(); try {
var response = await _dio!.get(uri, queryParameters: json); _init();
var response = await _dio!.get(uri, queryParameters: json);
return decodeResponse<T>(response, compute); return decodeResponse<T>(response, compute);
} on DioError catch (e) {
return HttpResponse(success: false, message: e.message, code: 0);
}
} }
static Future<HttpResponse<T>> post<T>(String uri, dynamic json, {bool compute = true}) async { static Future<HttpResponse<T>> post<T>(String uri, dynamic json, {bool compute = true}) async {
_init(); try {
var response = await _dio!.post(uri, data: json); _init();
var response = await _dio!.post(uri, data: json);
return decodeResponse<T>(response, compute); return decodeResponse<T>(response, compute);
} on DioError catch (e) {
return HttpResponse(success: false, message: e.message, code: 0);
}
} }
static Future<HttpResponse<T>> delete<T>(String uri, dynamic json, {bool compute = true}) async { static Future<HttpResponse<T>> delete<T>(String uri, dynamic json, {bool compute = true}) async {
_init(); try {
var response = await _dio!.delete(uri, data: json); _init();
var response = await _dio!.delete(uri, data: json);
return decodeResponse<T>(response, compute); return decodeResponse<T>(response, compute);
} on DioError catch (e) {
return HttpResponse(success: false, message: e.message, code: 0);
}
} }
static Future<HttpResponse<T>> put<T>(String uri, dynamic json, {bool compute = true}) async { static Future<HttpResponse<T>> put<T>(String uri, dynamic json, {bool compute = true}) async {
_init(); try {
var response = await _dio!.put(uri, data: json); _init();
var response = await _dio!.put(uri, data: json);
return decodeResponse<T>(response, compute); return decodeResponse<T>(response, compute);
} on DioError catch (e) {
return HttpResponse(success: false, message: e.message, code: 0);
}
} }
static HttpResponse<T> decodeResponse<T>( static HttpResponse<T> decodeResponse<T>(
@@ -173,4 +188,4 @@ void decode<T>() async {
compute(DeserializeAction.invokeJson, DeserializeAction<T>({})); compute(DeserializeAction.invokeJson, DeserializeAction<T>({}));
} }
class NullResponse{} class NullResponse {}

View File

@@ -7,7 +7,7 @@ class TokenInterceptor extends Interceptor {
if (userInfoViewModel.token != null) { if (userInfoViewModel.token != null) {
options.headers["Authorization"] = "Bearer " + userInfoViewModel.token!; options.headers["Authorization"] = "Bearer " + userInfoViewModel.token!;
} }
options.queryParameters["t"] = DateTime.now().millisecondsSinceEpoch; options.queryParameters["t"] = (DateTime.now().millisecondsSinceEpoch~/1000).toString();
return handler.next(options); return handler.next(options);
} }
} }

View File

@@ -18,6 +18,8 @@ class Routes {
builder: (context) => AddTaskPage( builder: (context) => AddTaskPage(
taskBean: settings.arguments as TaskBean, taskBean: settings.arguments as TaskBean,
)); ));
} else {
return CupertinoPageRoute(builder: (context) => AddTaskPage());
} }
} }

View File

@@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qinglong_app/base/ql_app_bar.dart'; import 'package:qinglong_app/base/ql_app_bar.dart';
import 'package:qinglong_app/base/routes.dart';
import 'package:qinglong_app/base/theme.dart'; import 'package:qinglong_app/base/theme.dart';
import 'package:qinglong_app/module/config/config_page.dart'; import 'package:qinglong_app/module/config/config_page.dart';
import 'package:qinglong_app/module/env/env_page.dart'; import 'package:qinglong_app/module/env/env_page.dart';
@@ -30,20 +31,44 @@ class _HomePageState extends State<HomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<Widget> actions = [];
if (_index == 0) {
actions.add(InkWell(
onTap: () {
Navigator.of(context).pushNamed(
Routes.route_AddTask,
);
},
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 15,
),
child: Center(
child: Icon(
CupertinoIcons.add,
size: 20,
),
),
),
));
}
actions.add(
Consumer(builder: (context, ref, child) {
return InkWell(
onTap: () {
ref.read(themeProvider).changeTheme();
},
child: const Center(child: Text("改变主题")),
);
}),
);
return Scaffold( return Scaffold(
appBar: QlAppBar( appBar: QlAppBar(
canBack: false, canBack: false,
title: _title, title: _title,
actions: [ actions: actions,
Consumer(builder: (context, ref, child) {
return InkWell(
onTap: () {
ref.read(themeProvider).changeTheme();
},
child: const Center(child: Text("改变主题")),
);
}),
],
), ),
body: IndexedStack( body: IndexedStack(
index: _index, index: _index,

View File

@@ -79,7 +79,7 @@ class _AddTaskPageState extends State<AddTaskPage> {
height: 10, height: 10,
), ),
const Text( const Text(
"任务名称:", "名称:",
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@@ -92,7 +92,7 @@ class _AddTaskPageState extends State<AddTaskPage> {
controller: _nameController, controller: _nameController,
decoration: const InputDecoration( decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入任务名称", hintText: "请输入名称",
), ),
autofocus: false, autofocus: false,
), ),

View File

@@ -9,7 +9,7 @@ class TaskBean {
String? schedule; String? schedule;
bool? saved; bool? saved;
String? sId; String? sId;
num? created; int? created;
int? status; int? status;
String? timestamp; String? timestamp;
int? isSystem; int? isSystem;

View File

@@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -31,6 +33,7 @@ class _TaskPageState extends State<TaskPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BaseStateWidget<TaskViewModel>( return BaseStateWidget<TaskViewModel>(
builder: (ref, model, child) { builder: (ref, model, child) {
return RefreshIndicator( return RefreshIndicator(
onRefresh: () async { onRefresh: () async {
return model.loadData(false); return model.loadData(false);
@@ -335,6 +338,7 @@ class _TaskItemCellState extends ConsumerState<TaskItemCell> {
child: const Text('删除'), child: const Text('删除'),
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
delTask(context, ref);
}, },
), ),
CupertinoActionSheetAction( CupertinoActionSheetAction(
@@ -347,4 +351,29 @@ class _TaskItemCellState extends ConsumerState<TaskItemCell> {
), ),
); );
} }
void delTask(BuildContext context, WidgetRef ref) {
showCupertinoDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text("确认删除"),
content: Text("确认删除定时任务 ${bean.name ?? ""}"),
actions: [
CupertinoDialogAction(
child: const Text("取消"),
onPressed: () {
Navigator.of(context).pop();
},
),
CupertinoDialogAction(
child: const Text("确定"),
onPressed: () {
Navigator.of(context).pop();
ref.read(taskProvider).delCron(bean.sId!);
},
),
],
),
);
}
} }

View File

@@ -1,3 +1,6 @@
import 'dart:convert';
import 'dart:math';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qinglong_app/base/base_viewmodel.dart'; import 'package:qinglong_app/base/base_viewmodel.dart';
import 'package:qinglong_app/base/http/api.dart'; import 'package:qinglong_app/base/http/api.dart';
@@ -9,6 +12,8 @@ var taskProvider = ChangeNotifierProvider((ref) => TaskViewModel());
class TaskViewModel extends BaseViewModel { class TaskViewModel extends BaseViewModel {
List<TaskBean> list = []; List<TaskBean> list = [];
String temp = "sadad";
Future<void> loadData([isLoading = true]) async { Future<void> loadData([isLoading = true]) async {
if (isLoading) { if (isLoading) {
loading(notify: true); loading(notify: true);
@@ -19,7 +24,6 @@ class TaskViewModel extends BaseViewModel {
if (result.success && result.bean != null) { if (result.success && result.bean != null) {
list.clear(); list.clear();
list.addAll(result.bean!); list.addAll(result.bean!);
sortList(); sortList();
success(); success();
} else { } else {
@@ -30,11 +34,14 @@ class TaskViewModel extends BaseViewModel {
void sortList() { void sortList() {
list.sort((a, b) { list.sort((a, b) {
return a.isDisabled! - b.isDisabled!; return b.created!.compareTo(a.created!);
});
list.sort((a, b) {
return a.isDisabled!.compareTo(b.isDisabled!);
}); });
list.sort((a, b) { list.sort((a, b) {
return a.status! - b.status!; return a.status!.compareTo(b.status!);
}); });
} }
@@ -43,6 +50,8 @@ class TaskViewModel extends BaseViewModel {
if (result.success) { if (result.success) {
list.firstWhere((element) => element.sId == cron).status = 0; list.firstWhere((element) => element.sId == cron).status = 0;
notifyListeners(); notifyListeners();
} else {
failed(result.message, notify: true);
} }
} }
@@ -51,6 +60,18 @@ class TaskViewModel extends BaseViewModel {
if (result.success) { if (result.success) {
list.firstWhere((element) => element.sId == cron).status = 1; list.firstWhere((element) => element.sId == cron).status = 1;
notifyListeners(); notifyListeners();
} else {
failed(result.message, notify: true);
}
}
Future<void> delCron(String id) async {
HttpResponse<NullResponse> result = await Api.delTask(id);
if (result.success) {
list.removeWhere((element) => element.sId == id);
notifyListeners();
} else {
failed(result.message, notify: true);
} }
} }
} }