This commit is contained in:
jyuesong
2022-01-13 14:52:44 +08:00
parent 80670821cd
commit d29c9e73f7
8 changed files with 277 additions and 6 deletions

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:back_button_interceptor/back_button_interceptor.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// @author newtab on 2021/7/16
@@ -13,6 +14,22 @@ class CommonDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget loading;
if (commonDialogState == CommonDialogState.FAILED) {
loading = const Icon(
CupertinoIcons.clear_circled,
size: 50,
);
} else if (commonDialogState == CommonDialogState.SUCCESS) {
loading = const Icon(
CupertinoIcons.checkmark_alt,
size: 50,
);
} else {
loading = LoadingIcon();
}
Widget result = Material(
color: Colors.transparent,
child: Align(
@@ -36,7 +53,7 @@ class CommonDialog extends StatelessWidget {
color: Colors.white,
size: 40.0,
),
child: LoadingIcon()),
child: loading),
),
const SizedBox(
height: 15,
@@ -132,7 +149,7 @@ HideCallback showCommonDialog(
}, zIndex: backButtonIndex, name: backButtonName);
backButtonIndex++;
if (overlay != null) {
if (overlay != null && overlay!.mounted) {
overlay?.remove();
overlay = null;
}
@@ -146,11 +163,13 @@ HideCallback showCommonDialog(
},
child: CommonDialog(
text: text,
commonDialogState: commonDialogState,
),
),
);
result.complete(() {
overlay?.remove();
overlay = null;
BackButtonInterceptor.removeByName(backButtonName);
});
if (overlay != null) {

View File

@@ -34,8 +34,11 @@ class Api {
return await Http.get<String>(Url.INTIME_LOG(cron), null);
}
static Future<HttpResponse<TaskDetailBean>> taskDetail(String cron) async {
return await Http.get<TaskDetailBean>(Url.TASK_DETAIL+cron, null);
return await Http.get<TaskDetailBean>(Url.TASK_DETAIL + cron, null);
}
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});
}
}

View File

@@ -4,6 +4,7 @@ class Url {
static const RUN_TASKS = "/api/crons/run";
static const STOP_TASKS = "/api/crons/stop";
static const TASK_DETAIL = "/api/crons/";
static const ADD_TASK = "/api/crons";
static INTIME_LOG(String cronId) {
return "/api/crons/$cronId/log";

View File

@@ -1,14 +1,24 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:qinglong_app/module/home/home_page.dart';
import 'package:qinglong_app/module/task/add_task_page.dart';
import 'package:qinglong_app/module/task/task_bean.dart';
class Routes {
static const String route_HomePage = "/home/homepage";
static const String route_AddTask = "/task/add";
static Route<dynamic>? generateRoute(RouteSettings settings) {
switch (settings.name) {
case route_HomePage:
return MaterialPageRoute(builder: (context) => const HomePage());
case route_AddTask:
if (settings.arguments != null) {
return CupertinoPageRoute(
builder: (context) => AddTaskPage(
taskBean: settings.arguments as TaskBean,
));
}
}
return null;

View File

@@ -2,6 +2,7 @@
import 'package:qinglong_app/module/login/login_bean.dart';
import 'package:qinglong_app/module/task/task_bean.dart';
import 'package:qinglong_app/module/task/task_detail/task_detail_bean.dart';
class JsonConversion$Json {
@@ -26,6 +27,10 @@ class JsonConversion$Json {
return TaskBean.jsonConversion(json) as M;
}
if(type == (TaskDetailBean).toString()){
return TaskDetailBean.jsonConversion(json) as M;
}
throw Exception("not found");
}
@@ -38,6 +43,10 @@ class JsonConversion$Json {
return data.map<TaskBean>((e) => TaskBean.jsonConversion(e)).toList() as M;
}
if(<TaskDetailBean>[] is M){
return data.map<TaskDetailBean>((e) => TaskDetailBean.jsonConversion(e)).toList() as M;
}
throw Exception("not found");
}
}

View File

@@ -0,0 +1,201 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:qinglong_app/base/common_dialog.dart';
import 'package:qinglong_app/base/http/api.dart';
import 'package:qinglong_app/base/http/http.dart';
import 'package:qinglong_app/base/ql_app_bar.dart';
import 'package:qinglong_app/module/task/task_bean.dart';
import 'package:qinglong_app/module/task/task_detail/task_detail_bean.dart';
class AddTaskPage extends StatefulWidget {
final TaskBean? taskBean;
const AddTaskPage({Key? key, this.taskBean}) : super(key: key);
@override
State<AddTaskPage> createState() => _AddTaskPageState();
}
class _AddTaskPageState extends State<AddTaskPage> {
late TaskBean taskBean;
final TextEditingController _nameController = TextEditingController();
final TextEditingController _commandController = TextEditingController();
final TextEditingController _cronController = TextEditingController();
@override
void initState() {
super.initState();
if (widget.taskBean != null) {
taskBean = widget.taskBean!;
_nameController.text = taskBean.name ?? "";
_commandController.text = taskBean.command ?? "";
_cronController.text = taskBean.schedule ?? "";
} else {
taskBean = TaskBean();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: QlAppBar(
canBack: true,
backCall: () {
Navigator.of(context).pop();
},
title: taskBean.name == null ? "新增任务" : "编辑任务",
actions: [
InkWell(
onTap: () {
submit();
},
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 15,
),
child: Center(
child: Text("提交"),
),
),
)
],
),
body: Container(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 10,
),
const Text(
"任务名称:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(
height: 10,
),
TextField(
controller: _nameController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入任务名称",
),
autofocus: false,
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 10,
),
const Text(
"命令:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(
height: 10,
),
TextField(
controller: _commandController,
maxLines: 4,
minLines: 1,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入命令",
),
autofocus: false,
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 10,
),
const Text(
"定时:",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(
height: 10,
),
TextField(
controller: _cronController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5),
hintText: "请输入定时",
),
autofocus: false,
),
],
),
),
],
),
),
);
}
void submit() async {
if (_nameController.text.isEmpty) {
failDialog(context, "任务名称不能为空");
return;
}
if (_commandController.text.isEmpty) {
failDialog(context, "命令不能为空");
return;
}
if (_cronController.text.isEmpty) {
failDialog(context, "定时不能为空");
return;
}
taskBean.name = _nameController.text;
taskBean.command = _commandController.text;
taskBean.schedule = _cronController.text;
HttpResponse<TaskDetailBean> response = await Api.addTask(_nameController.text, _commandController.text, _cronController.text, id: taskBean.sId);
if (response.success) {
successDialog(context, "操作成功").then((value) {
Navigator.of(context).pop(response.bean);
});
} else {
failDialog(context, response.message ??"");
}
}
}

View File

@@ -60,4 +60,8 @@ class TaskDetailBean {
data['isPinned'] = this.isPinned;
return data;
}
static TaskDetailBean jsonConversion(Map<String, dynamic> json) {
return TaskDetailBean.fromJson(json);
}
}

View File

@@ -3,9 +3,11 @@ 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/routes.dart';
import 'package:qinglong_app/base/theme.dart';
import 'package:qinglong_app/module/task/intime_log/intime_log_page.dart';
import 'package:qinglong_app/module/task/task_bean.dart';
import 'package:qinglong_app/module/task/task_detail/task_detail_bean.dart';
import 'package:qinglong_app/module/task/task_viewmodel.dart';
import 'package:qinglong_app/utils/utils.dart';
@@ -97,13 +99,26 @@ class _TaskPageState extends State<TaskPage> {
}
}
class TaskItemCell extends ConsumerWidget {
class TaskItemCell extends ConsumerStatefulWidget {
final TaskBean bean;
const TaskItemCell(this.bean, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
_TaskItemCellState createState() => _TaskItemCellState();
}
class _TaskItemCellState extends ConsumerState<TaskItemCell> {
late TaskBean bean;
@override
void initState() {
bean = widget.bean;
super.initState();
}
@override
Widget build(BuildContext context) {
return Slidable(
key: const ValueKey(0),
startActionPane: ActionPane(
@@ -299,6 +314,15 @@ class TaskItemCell extends ConsumerWidget {
child: const Text('编辑'),
onPressed: () {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.route_AddTask, arguments: bean).then((value) {
if (value != null) {
var result = value as TaskDetailBean;
bean.name = result.name;
bean.schedule = result.schedule;
bean.command = result.command;
setState(() {});
}
});
},
),
CupertinoActionSheetAction(