mirror of
https://github.com/qinglong-app/qinglong_app.git
synced 2025-10-09 16:48:19 +08:00
add config
This commit is contained in:
@@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,6 +40,10 @@ class JsonConversion$Json {
|
||||
}
|
||||
|
||||
static M _getListChildType<M>(List<dynamic> data) {
|
||||
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;
|
||||
}
|
||||
|
||||
25
lib/module/config/config_bean.dart
Normal file
25
lib/module/config/config_bean.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
83
lib/module/config/config_edit_page.dart
Normal file
83
lib/module/config/config_edit_page.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
46
lib/module/config/config_viewmodel.dart
Normal file
46
lib/module/config/config_viewmodel.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
28
pubspec.lock
28
pubspec.lock
@@ -141,6 +141,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
code_editor:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: code_editor
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -223,6 +230,13 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_highlight:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_highlight
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -254,6 +268,13 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
font_awesome_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: font_awesome_flutter
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "9.2.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -275,6 +296,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
highlight:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: highlight
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -22,6 +22,7 @@ dependencies:
|
||||
json_conversion_annotation: ^0.0.4
|
||||
logger: ^1.1.0
|
||||
intl: ^0.17.0
|
||||
code_editor: ^1.3.1
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.0.0
|
||||
|
||||
Reference in New Issue
Block a user