add config

This commit is contained in:
jyuesong
2022-01-13 19:41:46 +08:00
parent 51b745af19
commit fb62bea465
11 changed files with 292 additions and 8 deletions

View File

@@ -1,6 +1,7 @@
import 'dart:ffi';
import 'package:qinglong_app/base/http/http.dart';
import 'package:qinglong_app/module/config/config_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_detail/task_detail_bean.dart';
@@ -67,4 +68,15 @@ class Api {
static Future<HttpResponse<NullResponse>> disableTask(String cron) async {
return await Http.put<NullResponse>(Url.DISABLE_TASK, [cron]);
}
static Future<HttpResponse<List<ConfigBean>>> files() async {
return await Http.get<List<ConfigBean>>(Url.FILES, null);
}
static Future<HttpResponse<String>> content(String name) async {
return await Http.get<String>(Url.CONFIG_CONTENT+name, null);
}
static Future<HttpResponse<NullResponse>> saveFile(String name,String content) async {
return await Http.post<NullResponse>(Url.SAVE_FILE, {"content":content,"name": name});
}
}

View File

@@ -9,6 +9,9 @@ class Url {
static const UNPIN_TASK = "/api/crons/unpin";
static const ENABLE_TASK = "/api/crons/enable";
static const DISABLE_TASK = "/api/crons/disable";
static const FILES = "/api/configs/files";
static const CONFIG_CONTENT = "/api/configs/";
static const SAVE_FILE = "/api/configs/save";
static INTIME_LOG(String cronId) {
return "/api/crons/$cronId/log";

View File

@@ -1,5 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:qinglong_app/module/config/config_edit_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';
@@ -7,6 +8,7 @@ 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 const String route_ConfigEdit = "/config/edit";
static Route<dynamic>? generateRoute(RouteSettings settings) {
switch (settings.name) {
@@ -19,8 +21,15 @@ class Routes {
taskBean: settings.arguments as TaskBean,
));
} else {
return CupertinoPageRoute(builder: (context) => AddTaskPage());
return CupertinoPageRoute(builder: (context) => const AddTaskPage());
}
case route_ConfigEdit:
return CupertinoPageRoute(
builder: (context) => ConfigEditPage(
(settings.arguments as Map)["title"],
(settings.arguments as Map)["content"],
),
);
}
return null;

View File

@@ -1,5 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
import 'package:qinglong_app/module/config/config_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_detail/task_detail_bean.dart';
@@ -19,6 +20,10 @@ class JsonConversion$Json {
String type = M.toString();
if(type == (ConfigBean).toString()){
return ConfigBean.jsonConversion(json) as M;
}
if(type == (LoginBean).toString()){
return LoginBean.jsonConversion(json) as M;
}
@@ -35,7 +40,11 @@ class JsonConversion$Json {
}
static M _getListChildType<M>(List<dynamic> data) {
if(<LoginBean>[] is M){
if(<ConfigBean>[] is M){
return data.map<ConfigBean>((e) => ConfigBean.jsonConversion(e)).toList() as M;
}
if(<LoginBean>[] is M){
return data.map<LoginBean>((e) => LoginBean.jsonConversion(e)).toList() as M;
}

View File

@@ -0,0 +1,25 @@
import 'package:json_conversion_annotation/json_conversion_annotation.dart';
@JsonConversion()
class ConfigBean {
String? title;
String? value;
ConfigBean({this.title, this.value});
ConfigBean.fromJson(Map<String, dynamic> json) {
title = json['title'];
value = json['value'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['title'] = this.title;
data['value'] = this.value;
return data;
}
static ConfigBean jsonConversion(Map<String, dynamic> json) {
return ConfigBean.fromJson(json);
}
}

View File

@@ -0,0 +1,83 @@
import 'package:code_editor/code_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.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/config/config_viewmodel.dart';
class ConfigEditPage extends ConsumerStatefulWidget {
final String content;
final String title;
const ConfigEditPage(this.title, this.content, {Key? key}) : super(key: key);
@override
_ConfigEditPageState createState() => _ConfigEditPageState();
}
class _ConfigEditPageState extends ConsumerState<ConfigEditPage> {
String? value;
@override
Widget build(BuildContext context) {
List<FileEditor> files = [
FileEditor(
name: widget.title,
language: "sh",
code: widget.content, // [code] needs a string
),
];
EditorModel editMode = EditorModel(
files: files,
styleOptions: EditorModelStyleOptions(
fontSize: 13,
),
);
return Scaffold(
appBar: QlAppBar(
canBack: true,
backCall: () {
Navigator.of(context).pop();
},
title: '编辑文件',
actions: [
InkWell(
onTap: () async {
if (value == null) {
failDialog(context, "请先点击保存");
return;
}
HttpResponse<NullResponse> response = await Api.saveFile(widget.title, value!);
if (response.success) {
ref.read(configProvider).loadContent(widget.title);
Navigator.of(context).pop();
} else {
failDialog(context, response.message ?? "");
}
},
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 15,
),
child: Center(
child: Text("提交"),
),
),
)
],
),
body: Container(
child: CodeEditor(
model: editMode,
edit: true,
onSubmit: (title, v) {
value = v;
},
disableNavigationbar: false,
),
),
);
}
}

View File

@@ -1,4 +1,11 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:qinglong_app/base/base_state_widget.dart';
import 'package:code_editor/code_editor.dart';
import 'package:qinglong_app/base/routes.dart';
import 'package:qinglong_app/base/theme.dart';
import 'config_viewmodel.dart';
class ConfigPage extends StatefulWidget {
const ConfigPage({Key? key}) : super(key: key);
@@ -8,8 +15,72 @@ class ConfigPage extends StatefulWidget {
}
class _ConfigPageState extends State<ConfigPage> {
final myController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold();
return BaseStateWidget<ConfigViewModel>(
builder: (ref, model, child) {
List<FileEditor> files = [
FileEditor(
name: model.title,
language: "sh",
code: model.content, // [code] needs a string
),
];
EditorModel editMode = EditorModel(
files: files,
styleOptions: EditorModelStyleOptions(
fontSize: 13,
heightOfContainer: MediaQuery.of(context).size.height - kToolbarHeight - kBottomNavigationBarHeight - 150,
),
);
myController.text = model.content;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
DropdownButton<String>(
value: model.title,
items: model.list
.map(
(e) => DropdownMenuItem<String>(
value: e.value ?? "",
child: Text(e.value ?? ""),
),
)
.toList(),
onChanged: (value) {
model.loadContent(value!);
},
),
const Spacer(),
CupertinoButton(
child: const Text("编辑"),
onPressed: () {
Navigator.of(context).pushNamed(Routes.route_ConfigEdit, arguments: {
"title": model.title,
"content": model.content,
});
}),
],
),
Expanded(
child: CodeEditor(
model: editMode,
edit: false,
textEditingController: myController,
disableNavigationbar: false,
),
),
],
);
},
model: configProvider,
onReady: (viewModel) {
viewModel.loadData();
},
);
}
}

View File

@@ -0,0 +1,46 @@
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/config/config_bean.dart';
var configProvider = ChangeNotifierProvider((ref) => ConfigViewModel());
class ConfigViewModel extends BaseViewModel {
List<ConfigBean> list = [];
String content = "";
String title = "";
Future<void> loadData([isLoading = true]) async {
if (isLoading) {
loading(notify: true);
}
HttpResponse<List<ConfigBean>> result = await Api.files();
if (result.success && result.bean != null) {
list.clear();
list.addAll(result.bean!);
title = list[0].value!;
success();
loadContent(list[0].value!);
} else {
list.clear();
failed(result.message, notify: true);
}
}
Future<void> loadContent(String name) async {
title = name;
notifyListeners();
HttpResponse<String> result = await Api.content(name);
if (result.success && result.bean != null) {
content = result.bean!;
success();
} else {
failToast(result.message, notify: false);
}
}
}

View File

@@ -1,5 +1,3 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -9,7 +7,6 @@ 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';
@@ -21,8 +18,8 @@ class TaskPage extends StatefulWidget {
}
class _TaskPageState extends State<TaskPage> {
String? _searchKey = null;
TextEditingController _searchController = TextEditingController();
String? _searchKey;
final TextEditingController _searchController = TextEditingController();
@override
void initState() {