mirror of
https://github.com/qinglong-app/qinglong_app.git
synced 2025-10-09 16:48:19 +08:00
add task
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:back_button_interceptor/back_button_interceptor.dart';
|
import 'package:back_button_interceptor/back_button_interceptor.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// @author newtab on 2021/7/16
|
/// @author newtab on 2021/7/16
|
||||||
@@ -13,6 +14,22 @@ class CommonDialog extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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(
|
Widget result = Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: Align(
|
child: Align(
|
||||||
@@ -36,7 +53,7 @@ class CommonDialog extends StatelessWidget {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
size: 40.0,
|
size: 40.0,
|
||||||
),
|
),
|
||||||
child: LoadingIcon()),
|
child: loading),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 15,
|
height: 15,
|
||||||
@@ -132,7 +149,7 @@ HideCallback showCommonDialog(
|
|||||||
}, zIndex: backButtonIndex, name: backButtonName);
|
}, zIndex: backButtonIndex, name: backButtonName);
|
||||||
backButtonIndex++;
|
backButtonIndex++;
|
||||||
|
|
||||||
if (overlay != null) {
|
if (overlay != null && overlay!.mounted) {
|
||||||
overlay?.remove();
|
overlay?.remove();
|
||||||
overlay = null;
|
overlay = null;
|
||||||
}
|
}
|
||||||
@@ -146,11 +163,13 @@ HideCallback showCommonDialog(
|
|||||||
},
|
},
|
||||||
child: CommonDialog(
|
child: CommonDialog(
|
||||||
text: text,
|
text: text,
|
||||||
|
commonDialogState: commonDialogState,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
result.complete(() {
|
result.complete(() {
|
||||||
overlay?.remove();
|
overlay?.remove();
|
||||||
|
overlay = null;
|
||||||
BackButtonInterceptor.removeByName(backButtonName);
|
BackButtonInterceptor.removeByName(backButtonName);
|
||||||
});
|
});
|
||||||
if (overlay != null) {
|
if (overlay != null) {
|
||||||
|
|||||||
@@ -34,8 +34,11 @@ class Api {
|
|||||||
return await Http.get<String>(Url.INTIME_LOG(cron), null);
|
return await Http.get<String>(Url.INTIME_LOG(cron), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Future<HttpResponse<TaskDetailBean>> taskDetail(String cron) async {
|
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});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ class Url {
|
|||||||
static const RUN_TASKS = "/api/crons/run";
|
static const RUN_TASKS = "/api/crons/run";
|
||||||
static const STOP_TASKS = "/api/crons/stop";
|
static const STOP_TASKS = "/api/crons/stop";
|
||||||
static const TASK_DETAIL = "/api/crons/";
|
static const TASK_DETAIL = "/api/crons/";
|
||||||
|
static const ADD_TASK = "/api/crons";
|
||||||
|
|
||||||
static INTIME_LOG(String cronId) {
|
static INTIME_LOG(String cronId) {
|
||||||
return "/api/crons/$cronId/log";
|
return "/api/crons/$cronId/log";
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:qinglong_app/module/home/home_page.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 {
|
class Routes {
|
||||||
static const String route_HomePage = "/home/homepage";
|
static const String route_HomePage = "/home/homepage";
|
||||||
|
static const String route_AddTask = "/task/add";
|
||||||
|
|
||||||
static Route<dynamic>? generateRoute(RouteSettings settings) {
|
static Route<dynamic>? generateRoute(RouteSettings settings) {
|
||||||
switch (settings.name) {
|
switch (settings.name) {
|
||||||
case route_HomePage:
|
case route_HomePage:
|
||||||
return MaterialPageRoute(builder: (context) => const 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;
|
return null;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import 'package:qinglong_app/module/login/login_bean.dart';
|
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_bean.dart';
|
||||||
|
import 'package:qinglong_app/module/task/task_detail/task_detail_bean.dart';
|
||||||
|
|
||||||
|
|
||||||
class JsonConversion$Json {
|
class JsonConversion$Json {
|
||||||
@@ -26,6 +27,10 @@ class JsonConversion$Json {
|
|||||||
return TaskBean.jsonConversion(json) as M;
|
return TaskBean.jsonConversion(json) as M;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(type == (TaskDetailBean).toString()){
|
||||||
|
return TaskDetailBean.jsonConversion(json) as M;
|
||||||
|
}
|
||||||
|
|
||||||
throw Exception("not found");
|
throw Exception("not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +43,10 @@ class JsonConversion$Json {
|
|||||||
return data.map<TaskBean>((e) => TaskBean.jsonConversion(e)).toList() as M;
|
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");
|
throw Exception("not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
201
lib/module/task/add_task_page.dart
Normal file
201
lib/module/task/add_task_page.dart
Normal 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 ??"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,4 +60,8 @@ class TaskDetailBean {
|
|||||||
data['isPinned'] = this.isPinned;
|
data['isPinned'] = this.isPinned;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TaskDetailBean jsonConversion(Map<String, dynamic> json) {
|
||||||
|
return TaskDetailBean.fromJson(json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
import 'package:qinglong_app/base/base_state_widget.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/base/theme.dart';
|
||||||
import 'package:qinglong_app/module/task/intime_log/intime_log_page.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_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/module/task/task_viewmodel.dart';
|
||||||
import 'package:qinglong_app/utils/utils.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;
|
final TaskBean bean;
|
||||||
|
|
||||||
const TaskItemCell(this.bean, {Key? key}) : super(key: key);
|
const TaskItemCell(this.bean, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@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(
|
return Slidable(
|
||||||
key: const ValueKey(0),
|
key: const ValueKey(0),
|
||||||
startActionPane: ActionPane(
|
startActionPane: ActionPane(
|
||||||
@@ -299,6 +314,15 @@ class TaskItemCell extends ConsumerWidget {
|
|||||||
child: const Text('编辑'),
|
child: const Text('编辑'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
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(
|
CupertinoActionSheetAction(
|
||||||
|
|||||||
Reference in New Issue
Block a user