diff --git a/lib/base/http/api.dart b/lib/base/http/api.dart index ba38761..c5b0977 100644 --- a/lib/base/http/api.dart +++ b/lib/base/http/api.dart @@ -2,6 +2,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/env/env_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'; @@ -74,9 +75,36 @@ class Api { } static Future> content(String name) async { - return await Http.get(Url.CONFIG_CONTENT+name, null); + return await Http.get(Url.CONFIG_CONTENT + name, null); } - static Future> saveFile(String name,String content) async { - return await Http.post(Url.SAVE_FILE, {"content":content,"name": name}); + + static Future> saveFile(String name, String content) async { + return await Http.post(Url.SAVE_FILE, {"content": content, "name": name}); + } + + static Future>> envs(String search) async { + return await Http.get>(Url.ENVS, {"searchValue": search}); + } + + static Future> enableEnv(String id) async { + return await Http.put(Url.ENABLE_ENVS, [id]); + } + + static Future> disableEnv(String id) async { + return await Http.put(Url.DISABLE_ENVS, [id]); + } + + static Future> delEnv(String id) async { + return await Http.delete(Url.DEL_ENV, [id]); + } + + static Future> addEnv(String value, String name, String remarks, {String? id}) async { + var data = {"value": name, "remarks": remarks, "name": name}; + + if (id != null) { + data["_id"] = id; + return await Http.put(Url.ADD_ENV, data); + } + return await Http.post(Url.ADD_ENV, data); } } diff --git a/lib/base/http/http.dart b/lib/base/http/http.dart index 76eca18..5bc55f7 100644 --- a/lib/base/http/http.dart +++ b/lib/base/http/http.dart @@ -136,6 +136,7 @@ class Http { ); } } catch (e) { + logger.e(e); return HttpResponse( success: false, code: -1000, diff --git a/lib/base/http/url.dart b/lib/base/http/url.dart index 83e3db0..dc497fd 100644 --- a/lib/base/http/url.dart +++ b/lib/base/http/url.dart @@ -12,6 +12,11 @@ class Url { static const FILES = "/api/configs/files"; static const CONFIG_CONTENT = "/api/configs/"; static const SAVE_FILE = "/api/configs/save"; + static const ENVS = "/api/envs"; + static const ADD_ENV = "/api/envs"; + static const DEL_ENV = "/api/envs"; + static const DISABLE_ENVS = "/api/disable"; + static const ENABLE_ENVS = "/api/enable"; static INTIME_LOG(String cronId) { return "/api/crons/$cronId/log"; diff --git a/lib/json.jc.dart b/lib/json.jc.dart index b35a6a3..9200f8e 100644 --- a/lib/json.jc.dart +++ b/lib/json.jc.dart @@ -1,6 +1,7 @@ // GENERATED CODE - DO NOT MODIFY BY HAND import 'package:qinglong_app/module/config/config_bean.dart'; +import 'package:qinglong_app/module/env/env_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'; @@ -24,6 +25,10 @@ class JsonConversion$Json { return ConfigBean.jsonConversion(json) as M; } + if(type == (EnvBean).toString()){ + return EnvBean.jsonConversion(json) as M; + } + if(type == (LoginBean).toString()){ return LoginBean.jsonConversion(json) as M; } @@ -44,6 +49,10 @@ class JsonConversion$Json { return data.map((e) => ConfigBean.jsonConversion(e)).toList() as M; } + if([] is M){ + return data.map((e) => EnvBean.jsonConversion(e)).toList() as M; + } + if([] is M){ return data.map((e) => LoginBean.jsonConversion(e)).toList() as M; } diff --git a/lib/module/env/env_bean.dart b/lib/module/env/env_bean.dart new file mode 100644 index 0000000..53b6e6c --- /dev/null +++ b/lib/module/env/env_bean.dart @@ -0,0 +1,46 @@ +import 'package:json_conversion_annotation/json_conversion_annotation.dart'; + +@JsonConversion() +class EnvBean { + String? value; + String? sId; + int? created; + int? status; + String? timestamp; + String? name; + String? remarks; + + EnvBean( + {this.value, + this.sId, + this.created, + this.status, + this.timestamp, + this.name, + this.remarks}); + + EnvBean.fromJson(Map json) { + value = json['value']; + sId = json['_id']; + created = json['created']; + status = json['status']; + timestamp = json['timestamp']; + name = json['name']; + remarks = json['remarks']; + } + + Map toJson() { + final Map data = new Map(); + data['value'] = this.value; + data['_id'] = this.sId; + data['created'] = this.created; + data['status'] = this.status; + data['timestamp'] = this.timestamp; + data['name'] = this.name; + data['remarks'] = this.remarks; + return data; + } + static EnvBean jsonConversion(Map json) { + return EnvBean.fromJson(json); + } +} \ No newline at end of file diff --git a/lib/module/env/env_page.dart b/lib/module/env/env_page.dart index f6e0592..37e6091 100644 --- a/lib/module/env/env_page.dart +++ b/lib/module/env/env_page.dart @@ -1,5 +1,14 @@ - +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.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/ui/empty_widget.dart'; +import 'package:qinglong_app/base/ui/menu.dart'; +import 'package:qinglong_app/module/env/env_bean.dart'; +import 'package:qinglong_app/module/env/env_viewmodel.dart'; +import 'package:qinglong_app/utils/utils.dart'; class EnvPage extends StatefulWidget { const EnvPage({Key? key}) : super(key: key); @@ -9,8 +18,195 @@ class EnvPage extends StatefulWidget { } class _EnvPageState extends State { + String _searchKey = ""; + @override Widget build(BuildContext context) { - return Scaffold(); + return BaseStateWidget( + builder: (ref, model, child) { + return RefreshIndicator( + color: Theme.of(context).primaryColor, + onRefresh: () async { + return model.loadData(false); + }, + child: model.list.isEmpty + ? const EmptyWidget() + : ListView.builder( + keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, + itemBuilder: (context, index) { + EnvBean item = model.list[index]; + + return EnvItemCell(item, ref); + }, + itemCount: model.list.length, + ), + ); + }, + model: envProvider, + onReady: (viewModel) { + viewModel.loadData(); + }, + ); + } +} + +class EnvItemCell extends StatelessWidget { + final EnvBean bean; + final WidgetRef ref; + + const EnvItemCell(this.bean, this.ref, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return CupertinoContextMenu( + actions: [ + QLCupertinoContextMenuAction( + child: const Text("编辑"), + onPressed: () { + Navigator.of(context).pop(); + Navigator.of(context).pushNamed(Routes.route_AddTask, arguments: bean); + }, + trailingIcon: CupertinoIcons.pencil_outline, + ), + QLCupertinoContextMenuAction( + child: Text(bean.status! == 0 ? "禁用" : "启用"), + onPressed: () { + Navigator.of(context).pop(); + enableEnv(); + }, + isDestructiveAction: true, + trailingIcon: bean.status! == 0 ? Icons.dnd_forwardslash : Icons.check_circle_outline_sharp, + ), + QLCupertinoContextMenuAction( + child: const Text("删除"), + onPressed: () { + Navigator.of(context).pop(); + delEnv(context, ref); + }, + isDestructiveAction: true, + trailingIcon: CupertinoIcons.delete, + ), + ], + previewBuilder: (context, anima, child) { + return IntrinsicWidth( + child: Material( + color: Colors.transparent, + child: Text( + bean.name ?? "", + maxLines: 1, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: bean.status == 0 ? const Color(0xffF85152) : ref.watch(themeProvider).themeColor.taskTitleColor(), + fontSize: 18, + ), + ), + ), + ); + }, + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Material( + color: Colors.transparent, + child: Text( + bean.name ?? "", + maxLines: 1, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: bean.status == 1 ? const Color(0xffF85152) : ref.watch(themeProvider).themeColor.taskTitleColor(), + fontSize: 18, + ), + ), + ), + ), + const SizedBox( + width: 5, + ), + Material( + color: Colors.transparent, + child: Text( + bean.remarks ?? "-", + maxLines: 1, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: ref.watch(themeProvider).themeColor.descColor(), + fontSize: 12, + ), + ), + ), + ], + ), + const SizedBox( + height: 8, + ), + Material( + color: Colors.transparent, + child: Text( + bean.value ?? "", + maxLines: 1, + style: TextStyle( + overflow: TextOverflow.ellipsis, + color: ref.watch(themeProvider).themeColor.descColor(), + fontSize: 12, + ), + ), + ), + ], + ), + ), + const Divider( + height: 1, + indent: 15, + ), + ], + ), + ), + ); + } + + void enableEnv() { + ref.read(envProvider).enableEnv(bean.sId!, bean.status!); + } + + void delEnv(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(envProvider).delEnv(bean.sId!); + }, + ), + ], + ), + ); } } diff --git a/lib/module/env/env_viewmodel.dart b/lib/module/env/env_viewmodel.dart new file mode 100644 index 0000000..c32ea88 --- /dev/null +++ b/lib/module/env/env_viewmodel.dart @@ -0,0 +1,81 @@ +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/env/env_bean.dart'; + +var envProvider = ChangeNotifierProvider((ref) => EnvViewModel()); + +class EnvViewModel extends BaseViewModel { + List list = []; + + Future loadData([isLoading = true]) async { + if (isLoading) { + loading(notify: true); + } + + HttpResponse> result = await Api.envs(""); + + if (result.success && result.bean != null) { + list.clear(); + list.addAll(result.bean!); + sortList(); + success(); + } else { + list.clear(); + failed(result.message, notify: true); + } + } + + void sortList() { + list.sort((a, b) { + return a.status!.compareTo(b.status!); + }); + } + + Future delEnv(String id) async { + HttpResponse result = await Api.delEnv(id); + if (result.success) { + list.removeWhere((element) => element.sId == id); + notifyListeners(); + } else { + failed(result.message, notify: true); + } + } + + void updateEnv(EnvBean result) { + if (result.sId == null) { + loadData(false); + return; + } + EnvBean bean = list.firstWhere((element) => element.sId == result.sId); + bean.name = result.name; + bean.remarks = result.remarks; + bean.value = result.value; + notifyListeners(); + } + + void enableEnv(String sId, int status) async { + if (status == 1) { + HttpResponse response = await Api.enableEnv(sId); + + if (response.success) { + list.firstWhere((element) => element.sId == sId).status = 1; + sortList(); + success(); + } else { + failToast(response.message, notify: true); + } + } else { + HttpResponse response = await Api.disableEnv(sId); + + if (response.success) { + list.firstWhere((element) => element.sId == sId).status = 0; + sortList(); + success(); + } else { + failToast(response.message, notify: true); + } + } + } +} diff --git a/lib/module/task/task_page.dart b/lib/module/task/task_page.dart index 0f4fa4f..b8969d2 100644 --- a/lib/module/task/task_page.dart +++ b/lib/module/task/task_page.dart @@ -1,7 +1,6 @@ import 'package:flutter/cupertino.dart'; 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';