mirror of
https://github.com/qinglong-app/qinglong_app.git
synced 2025-10-09 16:48:19 +08:00
Compare commits
2 Commits
7e3a63169e
...
558b780235
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
558b780235 | ||
|
|
1cd3724805 |
30
.metadata
30
.metadata
@@ -1,10 +1,36 @@
|
|||||||
# This file tracks properties of this Flutter project.
|
# This file tracks properties of this Flutter project.
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
#
|
#
|
||||||
# This file should be version controlled and should not be manually edited.
|
# This file should be version controlled.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b
|
revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268
|
||||||
channel: stable
|
channel: stable
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268
|
||||||
|
base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268
|
||||||
|
- platform: linux
|
||||||
|
create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268
|
||||||
|
base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268
|
||||||
|
- platform: macos
|
||||||
|
create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268
|
||||||
|
base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268
|
||||||
|
- platform: windows
|
||||||
|
create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268
|
||||||
|
base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:qinglong_app/module/login/user_bean.dart';
|
|||||||
import 'package:qinglong_app/module/others/dependencies/dependency_bean.dart';
|
import 'package:qinglong_app/module/others/dependencies/dependency_bean.dart';
|
||||||
import 'package:qinglong_app/module/others/login_log/login_log_bean.dart';
|
import 'package:qinglong_app/module/others/login_log/login_log_bean.dart';
|
||||||
import 'package:qinglong_app/module/others/scripts/script_bean.dart';
|
import 'package:qinglong_app/module/others/scripts/script_bean.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_bean.dart';
|
||||||
import 'package:qinglong_app/module/others/task_log/task_log_bean.dart';
|
import 'package:qinglong_app/module/others/task_log/task_log_bean.dart';
|
||||||
import 'package:qinglong_app/module/task/task_bean.dart';
|
import 'package:qinglong_app/module/task/task_bean.dart';
|
||||||
import 'package:qinglong_app/utils/utils.dart';
|
import 'package:qinglong_app/utils/utils.dart';
|
||||||
@@ -377,4 +378,89 @@ class Api {
|
|||||||
[id],
|
[id],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 获取订阅
|
||||||
|
static Future<HttpResponse<List<Subscription>>> getSubscription()async{
|
||||||
|
return await Http.get<List<Subscription>>(
|
||||||
|
Url.subscriptions,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增订阅
|
||||||
|
static Future<HttpResponse<Subscription>> postSubscription(Subscription subscription)async{
|
||||||
|
var data = subscription.toJson();
|
||||||
|
data.remove("id");
|
||||||
|
data.remove("status");
|
||||||
|
data.remove("pid");
|
||||||
|
data.remove("log_path");
|
||||||
|
data.remove("is_disabled");
|
||||||
|
data.remove("createdAt");
|
||||||
|
data.remove("updatedAt");
|
||||||
|
data.remove("pullType");
|
||||||
|
data.remove("pullOption");
|
||||||
|
return await Http.post<Subscription>(
|
||||||
|
Url.subscriptions,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改订阅
|
||||||
|
static Future<HttpResponse<Subscription>> putSubscription(Subscription subscription)async{
|
||||||
|
return await Http.put<Subscription>(
|
||||||
|
Url.subscriptions,
|
||||||
|
subscription.toJson()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除订阅 参数未一个id数组
|
||||||
|
static Future<HttpResponse<NullResponse>> deleteSubscription(List<int> ids)async{
|
||||||
|
return await Http.delete<NullResponse>(
|
||||||
|
Url.subscriptions,
|
||||||
|
ids
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除订阅 参数未一个id数组
|
||||||
|
static Future<HttpResponse<NullResponse>> disableSubscription(List<int> ids)async{
|
||||||
|
return await Http.put<NullResponse>(
|
||||||
|
Url.disableSubscriptions,
|
||||||
|
ids
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除订阅 参数未一个id数组
|
||||||
|
static Future<HttpResponse<NullResponse>> enableSubscription(List<int> ids)async{
|
||||||
|
return await Http.put<NullResponse>(
|
||||||
|
Url.enableSubscriptions,
|
||||||
|
ids
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始执行订阅
|
||||||
|
static Future<HttpResponse<NullResponse>> startSubscription(
|
||||||
|
List<int> subscriptions) async {
|
||||||
|
return await Http.put<NullResponse>(
|
||||||
|
Url.runSubscriptions,
|
||||||
|
subscriptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暂停订阅执行
|
||||||
|
static Future<HttpResponse<NullResponse>> stopSubscription(
|
||||||
|
List<int> subscriptions) async {
|
||||||
|
return await Http.put<NullResponse>(
|
||||||
|
Url.stopSubscriptions,
|
||||||
|
subscriptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取订阅的日志
|
||||||
|
static Future<HttpResponse<String>> subTimeLog(int subId) async {
|
||||||
|
return await Http.get<String>(
|
||||||
|
Url.subtimeLog(subId),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,38 @@ class Url {
|
|||||||
: "/api/envs/$envId/move";
|
: "/api/envs/$envId/move";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 运行订阅
|
||||||
|
static get runSubscriptions => getIt<UserInfoViewModel>().useSecretLogined
|
||||||
|
? "/open/subscriptions/run"
|
||||||
|
: "/api/subscriptions/run";
|
||||||
|
|
||||||
|
// 停止订阅
|
||||||
|
static get stopSubscriptions => getIt<UserInfoViewModel>().useSecretLogined
|
||||||
|
? "/open/subscriptions/stop"
|
||||||
|
: "/api/subscriptions/stop";
|
||||||
|
|
||||||
|
// 启用订阅
|
||||||
|
static get enableSubscriptions => getIt<UserInfoViewModel>().useSecretLogined
|
||||||
|
? "/open/subscriptions/enable"
|
||||||
|
: "/api/subscriptions/enable";
|
||||||
|
|
||||||
|
// 禁用订阅
|
||||||
|
static get disableSubscriptions => getIt<UserInfoViewModel>().useSecretLogined
|
||||||
|
? "/open/subscriptions/disable"
|
||||||
|
: "/api/subscriptions/disable";
|
||||||
|
|
||||||
|
// GET 获取订阅 POST 提交订阅 PUT 修改订阅 DELETE 删除订阅
|
||||||
|
static get subscriptions => getIt<UserInfoViewModel>().useSecretLogined
|
||||||
|
? "/open/subscriptions"
|
||||||
|
: "/api/subscriptions";
|
||||||
|
|
||||||
|
// 获取订阅日志
|
||||||
|
static subtimeLog(int cronId) {
|
||||||
|
return getIt<UserInfoViewModel>().useSecretLogined
|
||||||
|
? "/open/subscriptions/$cronId/log"
|
||||||
|
: "/api/subscriptions/$cronId/log";
|
||||||
|
}
|
||||||
|
|
||||||
static bool inWhiteList(String path) {
|
static bool inWhiteList(String path) {
|
||||||
if (path == login ||
|
if (path == login ||
|
||||||
path == loginByClientId ||
|
path == loginByClientId ||
|
||||||
|
|||||||
@@ -8,12 +8,18 @@ import 'package:qinglong_app/module/env/env_detail_page.dart';
|
|||||||
import 'package:qinglong_app/module/home/home_page.dart';
|
import 'package:qinglong_app/module/home/home_page.dart';
|
||||||
import 'package:qinglong_app/module/login/login_page.dart';
|
import 'package:qinglong_app/module/login/login_page.dart';
|
||||||
import 'package:qinglong_app/module/others/about_page.dart';
|
import 'package:qinglong_app/module/others/about_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/backup/backup_page.dart';
|
||||||
import 'package:qinglong_app/module/others/dependencies/add_dependency_page.dart';
|
import 'package:qinglong_app/module/others/dependencies/add_dependency_page.dart';
|
||||||
import 'package:qinglong_app/module/others/dependencies/dependency_page.dart';
|
import 'package:qinglong_app/module/others/dependencies/dependency_page.dart';
|
||||||
import 'package:qinglong_app/module/others/login_log/login_log_page.dart';
|
import 'package:qinglong_app/module/others/login_log/login_log_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/scripts/acript_add_page.dart';
|
||||||
import 'package:qinglong_app/module/others/scripts/script_detail_page.dart';
|
import 'package:qinglong_app/module/others/scripts/script_detail_page.dart';
|
||||||
import 'package:qinglong_app/module/others/scripts/script_edit_page.dart';
|
import 'package:qinglong_app/module/others/scripts/script_edit_page.dart';
|
||||||
import 'package:qinglong_app/module/others/scripts/script_page.dart';
|
import 'package:qinglong_app/module/others/scripts/script_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_add_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_bean.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_detail_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_page.dart';
|
||||||
import 'package:qinglong_app/module/others/task_log/task_log_detail_page.dart';
|
import 'package:qinglong_app/module/others/task_log/task_log_detail_page.dart';
|
||||||
import 'package:qinglong_app/module/others/task_log/task_log_page.dart';
|
import 'package:qinglong_app/module/others/task_log/task_log_page.dart';
|
||||||
import 'package:qinglong_app/module/others/theme_page.dart';
|
import 'package:qinglong_app/module/others/theme_page.dart';
|
||||||
@@ -43,6 +49,12 @@ class Routes {
|
|||||||
static const String routeAbout = "/about";
|
static const String routeAbout = "/about";
|
||||||
static const String routeTheme = "/theme";
|
static const String routeTheme = "/theme";
|
||||||
static const String routeChangeAccount = "/changeAccount";
|
static const String routeChangeAccount = "/changeAccount";
|
||||||
|
static const String routerAddScript = "/script/add";
|
||||||
|
static const String routerSubscription = "/subscription";
|
||||||
|
static const String routerSubscriptionDetail = "/subscription/detail";
|
||||||
|
static const String routerSubscriptionAdd = "/subscription/add";
|
||||||
|
|
||||||
|
static const String routerBackup = "/backup";
|
||||||
|
|
||||||
static Route<dynamic>? generateRoute(RouteSettings settings) {
|
static Route<dynamic>? generateRoute(RouteSettings settings) {
|
||||||
switch (settings.name) {
|
switch (settings.name) {
|
||||||
@@ -151,7 +163,35 @@ class Routes {
|
|||||||
(settings.arguments as Map)["content"],
|
(settings.arguments as Map)["content"],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
case routeScriptAdd:
|
||||||
|
return CupertinoPageRoute(
|
||||||
|
builder: (context) => const ScriptAddPage(),
|
||||||
|
);
|
||||||
|
case routerSubscription:
|
||||||
|
return CupertinoPageRoute(
|
||||||
|
builder: (context) => const SubscriptionPage(),
|
||||||
|
);
|
||||||
|
case routerSubscriptionDetail:
|
||||||
|
return CupertinoPageRoute(
|
||||||
|
builder: (context) => SubscriptionDetailPage(
|
||||||
|
settings.arguments as Subscription,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case routerSubscriptionAdd:
|
||||||
|
if (settings.arguments != null) {
|
||||||
|
return MaterialPageRoute(
|
||||||
|
builder: (context) => SubscriptionAddPage(
|
||||||
|
settings.arguments as Subscription,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return MaterialPageRoute(builder: (context) => const SubscriptionAddPage(null));
|
||||||
}
|
}
|
||||||
|
case routerBackup:
|
||||||
|
return CupertinoPageRoute(
|
||||||
|
builder: (context) => const BackUpPage(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ enum _ContextMenuLocation {
|
|||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [Apple's HIG for Context Menus](https://developer.apple.com/design/human-interface-guidelines/ios/controls/context-menus/)
|
/// * [Apple's HIG for Context Menus](https://developer.apple.com/design/human-interface-guidelines/ios/controls/context-menus/)
|
||||||
class QlCupertinoContextMenu extends StatefulWidget {
|
class QlCupertinoContextMenu<T> extends StatefulWidget {
|
||||||
/// Create a context menu.
|
/// Create a context menu.
|
||||||
///
|
///
|
||||||
/// [actions] is required and cannot be null or empty.
|
/// [actions] is required and cannot be null or empty.
|
||||||
@@ -105,7 +105,7 @@ class QlCupertinoContextMenu extends StatefulWidget {
|
|||||||
assert(child != null),
|
assert(child != null),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
final TaskBean bean;
|
final T bean;
|
||||||
|
|
||||||
/// The widget that can be "opened" with the [QlCupertinoContextMenu].
|
/// The widget that can be "opened" with the [QlCupertinoContextMenu].
|
||||||
///
|
///
|
||||||
@@ -322,8 +322,14 @@ class _QlCupertinoContextMenuState extends State<QlCupertinoContextMenu>
|
|||||||
_openController.reverse();
|
_openController.reverse();
|
||||||
} else {
|
} else {
|
||||||
if (_openController.isDismissed) {
|
if (_openController.isDismissed) {
|
||||||
|
if (widget.bean is TaskBean){
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.pushNamed(Routes.routeTaskDetail, arguments: widget.bean);
|
.pushNamed(Routes.routeTaskDetail, arguments: widget.bean);
|
||||||
|
}else{
|
||||||
|
Navigator.of(context)
|
||||||
|
.pushNamed(Routes.routerSubscriptionDetail, arguments: widget.bean);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ import 'package:qinglong_app/module/login/user_bean.dart';
|
|||||||
import 'package:qinglong_app/module/others/dependencies/dependency_bean.dart';
|
import 'package:qinglong_app/module/others/dependencies/dependency_bean.dart';
|
||||||
import 'package:qinglong_app/module/others/login_log/login_log_bean.dart';
|
import 'package:qinglong_app/module/others/login_log/login_log_bean.dart';
|
||||||
import 'package:qinglong_app/module/others/scripts/script_bean.dart';
|
import 'package:qinglong_app/module/others/scripts/script_bean.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_bean.dart';
|
||||||
import 'package:qinglong_app/module/others/task_log/task_log_bean.dart';
|
import 'package:qinglong_app/module/others/task_log/task_log_bean.dart';
|
||||||
import 'package:qinglong_app/module/task/task_bean.dart';
|
import 'package:qinglong_app/module/task/task_bean.dart';
|
||||||
|
|
||||||
|
|
||||||
class JsonConversion$Json {
|
class JsonConversion$Json {
|
||||||
|
|
||||||
static M fromJson<M>(dynamic json) {
|
static M fromJson<M>(dynamic json) {
|
||||||
if (json is List) {
|
if (json is List) {
|
||||||
return _getListChildType<M>(json);
|
return _getListChildType<M>(json);
|
||||||
@@ -21,6 +24,7 @@ class JsonConversion$Json {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static M _fromJsonSingle<M>(dynamic json) {
|
static M _fromJsonSingle<M>(dynamic json) {
|
||||||
|
|
||||||
String type = M.toString();
|
String type = M.toString();
|
||||||
|
|
||||||
if(type == (ConfigBean).toString()){
|
if(type == (ConfigBean).toString()){
|
||||||
@@ -63,13 +67,16 @@ class JsonConversion$Json {
|
|||||||
return TaskBean.jsonConversion(json) as M;
|
return TaskBean.jsonConversion(json) as M;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == (Subscription).toString()){
|
||||||
|
return Subscription.fromJson(json) as M;
|
||||||
|
}
|
||||||
|
|
||||||
throw Exception("not found");
|
throw Exception("not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
static M _getListChildType<M>(List<dynamic> data) {
|
static M _getListChildType<M>(List<dynamic> data) {
|
||||||
if(<ConfigBean>[] is M){
|
if(<ConfigBean>[] is M){
|
||||||
return data.map<ConfigBean>((e) => ConfigBean.jsonConversion(e)).toList()
|
return data.map<ConfigBean>((e) => ConfigBean.jsonConversion(e)).toList() as M;
|
||||||
as M;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(<EnvBean>[] is M){
|
if(<EnvBean>[] is M){
|
||||||
@@ -77,46 +84,39 @@ class JsonConversion$Json {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(<SystemBean>[] is M){
|
if(<SystemBean>[] is M){
|
||||||
return data.map<SystemBean>((e) => SystemBean.jsonConversion(e)).toList()
|
return data.map<SystemBean>((e) => SystemBean.jsonConversion(e)).toList() as M;
|
||||||
as M;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(<LoginBean>[] is M){
|
if(<LoginBean>[] is M){
|
||||||
return data.map<LoginBean>((e) => LoginBean.jsonConversion(e)).toList()
|
return data.map<LoginBean>((e) => LoginBean.jsonConversion(e)).toList() as M;
|
||||||
as M;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(<UserBean>[] is M){
|
if(<UserBean>[] is M){
|
||||||
return data.map<UserBean>((e) => UserBean.jsonConversion(e)).toList()
|
return data.map<UserBean>((e) => UserBean.jsonConversion(e)).toList() as M;
|
||||||
as M;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(<DependencyBean>[] is M){
|
if(<DependencyBean>[] is M){
|
||||||
return data
|
return data.map<DependencyBean>((e) => DependencyBean.jsonConversion(e)).toList() as M;
|
||||||
.map<DependencyBean>((e) => DependencyBean.jsonConversion(e))
|
|
||||||
.toList() as M;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(<LoginLogBean>[] is M){
|
if(<LoginLogBean>[] is M){
|
||||||
return data
|
return data.map<LoginLogBean>((e) => LoginLogBean.jsonConversion(e)).toList() as M;
|
||||||
.map<LoginLogBean>((e) => LoginLogBean.jsonConversion(e))
|
|
||||||
.toList() as M;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(<ScriptBean>[] is M){
|
if(<ScriptBean>[] is M){
|
||||||
return data.map<ScriptBean>((e) => ScriptBean.jsonConversion(e)).toList()
|
return data.map<ScriptBean>((e) => ScriptBean.jsonConversion(e)).toList() as M;
|
||||||
as M;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(<TaskLogBean>[] is M){
|
if(<TaskLogBean>[] is M){
|
||||||
return data
|
return data.map<TaskLogBean>((e) => TaskLogBean.jsonConversion(e)).toList() as M;
|
||||||
.map<TaskLogBean>((e) => TaskLogBean.jsonConversion(e))
|
|
||||||
.toList() as M;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(<TaskBean>[] is M){
|
if(<TaskBean>[] is M){
|
||||||
return data.map<TaskBean>((e) => TaskBean.jsonConversion(e)).toList()
|
return data.map<TaskBean>((e) => TaskBean.jsonConversion(e)).toList() as M;
|
||||||
as M;
|
}
|
||||||
|
|
||||||
|
if (<Subscription>[] is M){
|
||||||
|
return data.map<Subscription>((e) => Subscription.jsonConversion(e)).toList() as M;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception("not found");
|
throw Exception("not found");
|
||||||
|
|||||||
278
lib/module/others/backup/backup_page.dart
Normal file
278
lib/module/others/backup/backup_page.dart
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:qinglong_app/base/http/api.dart';
|
||||||
|
import 'package:qinglong_app/base/ql_app_bar.dart';
|
||||||
|
import 'package:qinglong_app/json.jc.dart';
|
||||||
|
import 'package:qinglong_app/module/env/env_bean.dart';
|
||||||
|
import 'package:qinglong_app/utils/extension.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
|
import '../../../base/theme.dart';
|
||||||
|
import '../../../base/userinfo_viewmodel.dart';
|
||||||
|
import '../../../main.dart';
|
||||||
|
|
||||||
|
class BackUpPage extends ConsumerStatefulWidget {
|
||||||
|
const BackUpPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_BackUpPageState createState() => _BackUpPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BackUpPageState extends ConsumerState<BackUpPage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: QlAppBar(
|
||||||
|
canBack: true,
|
||||||
|
title: '数据备份',
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
vertical: 15,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ref.watch(themeProvider).themeColor.settingBordorColor(),
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(15),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
getEnv();
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 5,
|
||||||
|
left: 15,
|
||||||
|
right: 15,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"环境变量备份到本地",
|
||||||
|
style: TextStyle(
|
||||||
|
color: ref
|
||||||
|
.watch(themeProvider)
|
||||||
|
.themeColor
|
||||||
|
.titleColor(),
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Icon(
|
||||||
|
CupertinoIcons.right_chevron,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
indent: 15,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
uploadEnv(context);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 5,
|
||||||
|
left: 15,
|
||||||
|
right: 15,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"从本地导入环境变量",
|
||||||
|
style: TextStyle(
|
||||||
|
color: ref
|
||||||
|
.watch(themeProvider)
|
||||||
|
.themeColor
|
||||||
|
.titleColor(),
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Icon(
|
||||||
|
CupertinoIcons.right_chevron,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
indent: 15,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
uploadEnvFromFile();
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 5,
|
||||||
|
left: 15,
|
||||||
|
right: 15,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"从文件导入环境变量",
|
||||||
|
style: TextStyle(
|
||||||
|
color: ref
|
||||||
|
.watch(themeProvider)
|
||||||
|
.themeColor
|
||||||
|
.titleColor(),
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Icon(
|
||||||
|
CupertinoIcons.right_chevron,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
indent: 15,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
shareEnvs();
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 5,
|
||||||
|
left: 15,
|
||||||
|
right: 15,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"导出环境变量",
|
||||||
|
style: TextStyle(
|
||||||
|
color: ref
|
||||||
|
.watch(themeProvider)
|
||||||
|
.themeColor
|
||||||
|
.titleColor(),
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Icon(
|
||||||
|
CupertinoIcons.right_chevron,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
indent: 15,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> shareEnvs() async {
|
||||||
|
Directory appDocDir = await getApplicationDocumentsDirectory();
|
||||||
|
var dir = Directory("${appDocDir.path}/backup/");
|
||||||
|
var fileList = await dir.list().toList();
|
||||||
|
List<String> paths = [];
|
||||||
|
for(var i=0;i<fileList.length;i++){
|
||||||
|
paths.add(fileList[i].path);
|
||||||
|
}
|
||||||
|
await Share.shareFiles(paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getEnv() async {
|
||||||
|
var envs = await Api.envs("");
|
||||||
|
var data = json.encode(envs.bean);
|
||||||
|
Directory appDocDir = await getApplicationDocumentsDirectory();
|
||||||
|
// String? path = await FilePicker.platform.getDirectoryPath(initialDirectory:appDocDir.path,dialogTitle: "请选择保存的文件夹");
|
||||||
|
Directory("${appDocDir.path}/backup/").create();
|
||||||
|
Uri uri = Uri.parse(getIt<UserInfoViewModel>().host!);
|
||||||
|
File file = File("${appDocDir.path}/backup/${uri.host}-${uri.port}.env");
|
||||||
|
file = await file.writeAsString(data, flush: true);
|
||||||
|
"备份成功".toast();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uploadEnvFromFile()async{
|
||||||
|
var result = await FilePicker.platform.pickFiles(dialogTitle: "请选择env文件");
|
||||||
|
if (result == null)return;
|
||||||
|
File f = File(result.paths[0]!);
|
||||||
|
var data = await f.readAsString();
|
||||||
|
var content = jsonDecode(data) as List<dynamic>;
|
||||||
|
for (var j = 0; j < content.length; j++) {
|
||||||
|
var bean = JsonConversion$Json.fromJson<EnvBean>(content[j]);
|
||||||
|
var resp = await Api.addEnv(bean.name!, bean.value!, bean.remarks!);
|
||||||
|
if (!resp.success){
|
||||||
|
resp.message.toast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"备份完成".toast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uploadEnv(BuildContext context) async {
|
||||||
|
Directory appDocDir = await getApplicationDocumentsDirectory();
|
||||||
|
List<Widget> widgets = [];
|
||||||
|
var dir = Directory("${appDocDir.path}/backup/");
|
||||||
|
var fileList = await dir.list().toList();
|
||||||
|
for (var i = 0; i < fileList.length; i++) {
|
||||||
|
var paths = fileList[i].path.split("/");
|
||||||
|
widgets.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(paths[paths.length - 1]),
|
||||||
|
onTap: () async {
|
||||||
|
File f = File(fileList[i].path);
|
||||||
|
var data = await f.readAsString();
|
||||||
|
var content = jsonDecode(data) as List<dynamic>;
|
||||||
|
for (var j = 0; j < content.length; j++) {
|
||||||
|
var bean = JsonConversion$Json.fromJson<EnvBean>(content[j]);
|
||||||
|
var resp = await Api.addEnv(bean.name!, bean.value!, bean.remarks!);
|
||||||
|
if (!resp.success){
|
||||||
|
resp.message.toast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"备份完成".toast();
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
var dialog = SimpleDialog(title: const Text("请选择备份文件"), children: widgets);
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return dialog;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:qinglong_app/base/routes.dart';
|
import 'package:qinglong_app/base/routes.dart';
|
||||||
import 'package:qinglong_app/base/sp_const.dart';
|
|
||||||
import 'package:qinglong_app/base/theme.dart';
|
import 'package:qinglong_app/base/theme.dart';
|
||||||
import 'package:qinglong_app/base/userinfo_viewmodel.dart';
|
import 'package:qinglong_app/base/userinfo_viewmodel.dart';
|
||||||
import 'package:qinglong_app/main.dart';
|
import 'package:qinglong_app/main.dart';
|
||||||
import 'package:qinglong_app/utils/extension.dart';
|
import 'package:qinglong_app/utils/extension.dart';
|
||||||
import 'package:qinglong_app/utils/sp_utils.dart';
|
|
||||||
|
|
||||||
class OtherPage extends ConsumerStatefulWidget {
|
class OtherPage extends ConsumerStatefulWidget {
|
||||||
const OtherPage({Key? key}) : super(key: key);
|
const OtherPage({Key? key}) : super(key: key);
|
||||||
@@ -28,8 +26,15 @@ class _OtherPageState extends ConsumerState<OtherPage> {
|
|||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.symmetric(
|
margin: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
vertical: 15,
|
vertical: 15,
|
||||||
),
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ref.watch(themeProvider).themeColor.settingBordorColor(),
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(15),
|
||||||
|
),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -230,13 +235,103 @@ class _OtherPageState extends ConsumerState<OtherPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Divider(
|
||||||
|
indent: 15,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pushNamed(
|
||||||
|
Routes.routerSubscription,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 5,
|
||||||
|
bottom: 10,
|
||||||
|
left: 15,
|
||||||
|
right: 15,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"订阅管理",
|
||||||
|
style: TextStyle(
|
||||||
|
color: ref
|
||||||
|
.watch(themeProvider)
|
||||||
|
.themeColor
|
||||||
|
.titleColor(),
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Icon(
|
||||||
|
CupertinoIcons.right_chevron,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
indent: 15,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pushNamed(
|
||||||
|
Routes.routerBackup,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 5,
|
||||||
|
bottom: 10,
|
||||||
|
left: 15,
|
||||||
|
right: 15,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"数据备份",
|
||||||
|
style: TextStyle(
|
||||||
|
color: ref
|
||||||
|
.watch(themeProvider)
|
||||||
|
.themeColor
|
||||||
|
.titleColor(),
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Icon(
|
||||||
|
CupertinoIcons.right_chevron,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
indent: 15,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.symmetric(
|
margin: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
vertical: 15,
|
vertical: 15,
|
||||||
),
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ref.watch(themeProvider).themeColor.settingBordorColor(),
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(15),
|
||||||
|
),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -272,40 +367,6 @@ class _OtherPageState extends ConsumerState<OtherPage> {
|
|||||||
const Divider(
|
const Divider(
|
||||||
indent: 15,
|
indent: 15,
|
||||||
),
|
),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 15,
|
|
||||||
right: 15,
|
|
||||||
bottom: 5,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"查看代码是否显示行号",
|
|
||||||
style: TextStyle(
|
|
||||||
color:
|
|
||||||
ref.watch(themeProvider).themeColor.titleColor(),
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
CupertinoSwitch(
|
|
||||||
activeColor: ref.watch(themeProvider).primaryColor,
|
|
||||||
value: SpUtil.getBool(spShowLine, defValue: false),
|
|
||||||
onChanged: (open) async {
|
|
||||||
await SpUtil.putBool(spShowLine, open);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Divider(
|
|
||||||
indent: 15,
|
|
||||||
height: 1,
|
|
||||||
),
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -316,7 +377,7 @@ class _OtherPageState extends ConsumerState<OtherPage> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 15,
|
horizontal: 15,
|
||||||
vertical: 10,
|
vertical: 5,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
|||||||
596
lib/module/others/subscription/subscription_add_page.dart
Normal file
596
lib/module/others/subscription/subscription_add_page.dart
Normal file
@@ -0,0 +1,596 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qinglong_app/base/http/api.dart';
|
||||||
|
import 'package:qinglong_app/base/ql_app_bar.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_bean.dart';
|
||||||
|
import 'package:qinglong_app/utils/extension.dart';
|
||||||
|
|
||||||
|
class SubscriptionAddPage extends StatefulWidget {
|
||||||
|
final Subscription? bean;
|
||||||
|
|
||||||
|
const SubscriptionAddPage(this.bean, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SubscriptionAddPage> createState() => _SubscriptionAddPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SubscriptionAddPageState extends State<SubscriptionAddPage> {
|
||||||
|
late Subscription _bean;
|
||||||
|
|
||||||
|
late String? _scheduleType;
|
||||||
|
late String? _type;
|
||||||
|
|
||||||
|
late String? _intervalScheduleType;
|
||||||
|
late String? _intervalScheduleValue;
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (widget.bean == null) {
|
||||||
|
_bean = Subscription();
|
||||||
|
_bean.scheduleType = "";
|
||||||
|
_type = "public-repo";
|
||||||
|
_scheduleType = "crontab";
|
||||||
|
|
||||||
|
_intervalScheduleType = "days";
|
||||||
|
} else {
|
||||||
|
_bean = widget.bean!;
|
||||||
|
_type = widget.bean?.type;
|
||||||
|
_scheduleType = widget.bean?.scheduleType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final TextEditingController _nameController = TextEditingController();
|
||||||
|
final TextEditingController _urlController = TextEditingController();
|
||||||
|
final TextEditingController _branchController = TextEditingController();
|
||||||
|
final TextEditingController _intervalScheduleValueController = TextEditingController();
|
||||||
|
final TextEditingController _scheduleController = TextEditingController();
|
||||||
|
final TextEditingController _whiteListController = TextEditingController();
|
||||||
|
final TextEditingController _blackListController = TextEditingController();
|
||||||
|
final TextEditingController _dependenceController = TextEditingController();
|
||||||
|
final TextEditingController _extensionsController = TextEditingController();
|
||||||
|
final TextEditingController _taskBeforeController = TextEditingController();
|
||||||
|
final TextEditingController _taskAfterController = TextEditingController();
|
||||||
|
FocusNode focusNode = FocusNode();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: QlAppBar(
|
||||||
|
canBack: true,
|
||||||
|
backCall: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
title: _bean.name == null ? "新增订阅" : "编辑订阅",
|
||||||
|
actions: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
submit(context);
|
||||||
|
},
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
"提交",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
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: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"名称:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
focusNode: focusNode,
|
||||||
|
controller: _nameController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "请输入名称",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"类型:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Radio(
|
||||||
|
value: "public-repo",
|
||||||
|
groupValue: _type,
|
||||||
|
onChanged: (data) {
|
||||||
|
setState(() {
|
||||||
|
_type = data as String?;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
const Text("公开仓库"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Radio(
|
||||||
|
value: "private-repo",
|
||||||
|
groupValue: _type,
|
||||||
|
onChanged: (data) {
|
||||||
|
setState(() {
|
||||||
|
_type = data as String?;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
const Text("私有仓库"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Radio(
|
||||||
|
value: "file",
|
||||||
|
groupValue: _type,
|
||||||
|
onChanged: (data) {
|
||||||
|
setState(() {
|
||||||
|
_type = data as String?;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
const Text("单文件"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 10,
|
||||||
|
thickness: 2,
|
||||||
|
indent: 15,
|
||||||
|
endIndent: 15,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"链接:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _urlController,
|
||||||
|
maxLines: 8,
|
||||||
|
minLines: 1,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "请输入仓库链接",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"分支:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _branchController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "请输入分支",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"定时类型:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Radio(
|
||||||
|
value: "crontab",
|
||||||
|
groupValue: _scheduleType,
|
||||||
|
onChanged: (data) {
|
||||||
|
setState(() {
|
||||||
|
_scheduleType = data as String?;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
const Text("crontab"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Radio(
|
||||||
|
value: "interval",
|
||||||
|
groupValue: _scheduleType,
|
||||||
|
onChanged: (data) {
|
||||||
|
setState(() {
|
||||||
|
_scheduleType = data as String?;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
const Text("interval"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 10,
|
||||||
|
thickness: 2,
|
||||||
|
indent: 15,
|
||||||
|
endIndent: 15,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"定时规则:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
getScheduler(context)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"白名单:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _whiteListController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "请输入白名单",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"黑名单:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _blackListController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "请输入黑名单",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"依赖文件:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _dependenceController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "请输入依赖文件",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"文件后缀:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _extensionsController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "请输入文件后缀",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"执行前:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _taskBeforeController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "执行前",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"执行后:",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _taskAfterController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "执行后",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getScheduler(BuildContext context) {
|
||||||
|
if (_scheduleType == "crontab") {
|
||||||
|
return TextField(
|
||||||
|
controller: _scheduleController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "秒(可选) 分 时 天 月 周",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
const Text("每"),
|
||||||
|
SizedBox(
|
||||||
|
width: 350,
|
||||||
|
child: TextField(
|
||||||
|
controller: _intervalScheduleValueController,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "",
|
||||||
|
),
|
||||||
|
autofocus: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
DropdownButton<String>(
|
||||||
|
underline: Container(height: 0),
|
||||||
|
value: _intervalScheduleType,
|
||||||
|
items: const [
|
||||||
|
DropdownMenuItem(child: Text("时"),value: "hours",),
|
||||||
|
DropdownMenuItem(child: Text("天"),value: "days",),
|
||||||
|
DropdownMenuItem(child: Text("周"),value: "weeks",),
|
||||||
|
],
|
||||||
|
onChanged: (data) {setState(() {
|
||||||
|
_intervalScheduleType = data;
|
||||||
|
});})
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void submit(BuildContext context) async {
|
||||||
|
if (_nameController.text.isEmpty){
|
||||||
|
"名称不能为空".toast();
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
_bean.name = _nameController.value.text;
|
||||||
|
}
|
||||||
|
if (_urlController.text.isEmpty){
|
||||||
|
"链接不能为空".toast();
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
_bean.url = _urlController.value.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bean.type = _type;
|
||||||
|
_bean.branch = _branchController.value.text;
|
||||||
|
if (_scheduleType != null && _scheduleType == "crontab"){
|
||||||
|
if (_scheduleController.text.isEmpty){
|
||||||
|
"定时规则不能为空".toast();
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
_bean.schedule = _scheduleController.value.text;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if (_intervalScheduleValueController.text.isEmpty){
|
||||||
|
"定时规则不能为空".toast();
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
var inter = IntervalSchedule();
|
||||||
|
inter.type = _intervalScheduleType;
|
||||||
|
inter.value = int.tryParse(_intervalScheduleValueController.value.text);
|
||||||
|
_bean.intervalSchedule = inter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_bean.alias = _bean.name;
|
||||||
|
_bean.scheduleType = _scheduleType;
|
||||||
|
_bean.whitelist = _whiteListController.value.text;
|
||||||
|
_bean.blacklist = _blackListController.value.text;
|
||||||
|
_bean.extensions = _extensionsController.value.text;
|
||||||
|
_bean.dependences = _dependenceController.value.text;
|
||||||
|
_bean.subBefore = _taskBeforeController.value.text;
|
||||||
|
_bean.subAfter = _taskAfterController.value.text;
|
||||||
|
var data = await Api.postSubscription(_bean);
|
||||||
|
if (!data.success){
|
||||||
|
data.message.toast();
|
||||||
|
}else{
|
||||||
|
"添加成功".toast();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
310
lib/module/others/subscription/subscription_bean.dart
Normal file
310
lib/module/others/subscription/subscription_bean.dart
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
class Subscription {
|
||||||
|
int? _id;
|
||||||
|
String? _name;
|
||||||
|
String? _url;
|
||||||
|
dynamic _schedule;
|
||||||
|
IntervalSchedule? _intervalSchedule;
|
||||||
|
String? _type;
|
||||||
|
String? _whitelist;
|
||||||
|
String? _blacklist;
|
||||||
|
int? _status;
|
||||||
|
String? _dependences;
|
||||||
|
dynamic _extensions;
|
||||||
|
dynamic _subBefore;
|
||||||
|
dynamic _subAfter;
|
||||||
|
String? _branch;
|
||||||
|
dynamic _pullType;
|
||||||
|
dynamic _pullOption;
|
||||||
|
int? _pid;
|
||||||
|
int? _isDisabled;
|
||||||
|
String? _logPath;
|
||||||
|
String? _scheduleType;
|
||||||
|
String? _alias;
|
||||||
|
String? _createdAt;
|
||||||
|
String? _updatedAt;
|
||||||
|
|
||||||
|
Subscription(
|
||||||
|
{int? id,
|
||||||
|
String? name,
|
||||||
|
String? url,
|
||||||
|
dynamic schedule,
|
||||||
|
IntervalSchedule? intervalSchedule,
|
||||||
|
String? type,
|
||||||
|
String? whitelist,
|
||||||
|
String? blacklist,
|
||||||
|
int? status,
|
||||||
|
String? dependences,
|
||||||
|
dynamic extensions,
|
||||||
|
dynamic subBefore,
|
||||||
|
dynamic subAfter,
|
||||||
|
String? branch,
|
||||||
|
dynamic pullType,
|
||||||
|
dynamic pullOption,
|
||||||
|
int? pid,
|
||||||
|
int? isDisabled,
|
||||||
|
String? logPath,
|
||||||
|
String? scheduleType,
|
||||||
|
String? alias,
|
||||||
|
String? createdAt,
|
||||||
|
String? updatedAt}) {
|
||||||
|
if (id != null) {
|
||||||
|
this._id = id;
|
||||||
|
}
|
||||||
|
if (name != null) {
|
||||||
|
this._name = name;
|
||||||
|
}
|
||||||
|
if (url != null) {
|
||||||
|
this._url = url;
|
||||||
|
}
|
||||||
|
if (schedule != null) {
|
||||||
|
this._schedule = schedule;
|
||||||
|
}
|
||||||
|
if (intervalSchedule != null) {
|
||||||
|
this._intervalSchedule = intervalSchedule;
|
||||||
|
}
|
||||||
|
if (type != null) {
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
if (whitelist != null) {
|
||||||
|
this._whitelist = whitelist;
|
||||||
|
}
|
||||||
|
if (blacklist != null) {
|
||||||
|
this._blacklist = blacklist;
|
||||||
|
}
|
||||||
|
if (status != null) {
|
||||||
|
this._status = status;
|
||||||
|
}
|
||||||
|
if (dependences != null) {
|
||||||
|
this._dependences = dependences;
|
||||||
|
}
|
||||||
|
if (extensions != null) {
|
||||||
|
this._extensions = extensions;
|
||||||
|
}
|
||||||
|
if (subBefore != null) {
|
||||||
|
this._subBefore = subBefore;
|
||||||
|
}
|
||||||
|
if (subAfter != null) {
|
||||||
|
this._subAfter = subAfter;
|
||||||
|
}
|
||||||
|
if (branch != null) {
|
||||||
|
this._branch = branch;
|
||||||
|
}
|
||||||
|
if (pullType != null) {
|
||||||
|
this._pullType = pullType;
|
||||||
|
}
|
||||||
|
if (pullOption != null) {
|
||||||
|
this._pullOption = pullOption;
|
||||||
|
}
|
||||||
|
if (pid != null) {
|
||||||
|
this._pid = pid;
|
||||||
|
}
|
||||||
|
if (isDisabled != null) {
|
||||||
|
this._isDisabled = isDisabled;
|
||||||
|
}
|
||||||
|
if (logPath != null) {
|
||||||
|
this._logPath = logPath;
|
||||||
|
}
|
||||||
|
if (scheduleType != null) {
|
||||||
|
this._scheduleType = scheduleType;
|
||||||
|
}
|
||||||
|
if (alias != null) {
|
||||||
|
this._alias = alias;
|
||||||
|
}
|
||||||
|
if (createdAt != null) {
|
||||||
|
this._createdAt = createdAt;
|
||||||
|
}
|
||||||
|
if (updatedAt != null) {
|
||||||
|
this._updatedAt = updatedAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int? get id => _id;
|
||||||
|
|
||||||
|
set id(int? id) => _id = id;
|
||||||
|
|
||||||
|
String? get name => _name;
|
||||||
|
|
||||||
|
set name(String? name) => _name = name;
|
||||||
|
|
||||||
|
String? get url => _url;
|
||||||
|
|
||||||
|
set url(String? url) => _url = url;
|
||||||
|
|
||||||
|
dynamic get schedule => _schedule;
|
||||||
|
|
||||||
|
set schedule(dynamic schedule) => _schedule = schedule;
|
||||||
|
|
||||||
|
IntervalSchedule? get intervalSchedule => _intervalSchedule;
|
||||||
|
|
||||||
|
set intervalSchedule(IntervalSchedule? intervalSchedule) =>
|
||||||
|
_intervalSchedule = intervalSchedule;
|
||||||
|
|
||||||
|
String? get type => _type;
|
||||||
|
|
||||||
|
set type(String? type) => _type = type;
|
||||||
|
|
||||||
|
String? get whitelist => _whitelist;
|
||||||
|
|
||||||
|
set whitelist(String? whitelist) => _whitelist = whitelist;
|
||||||
|
|
||||||
|
String? get blacklist => _blacklist;
|
||||||
|
|
||||||
|
set blacklist(String? blacklist) => _blacklist = blacklist;
|
||||||
|
|
||||||
|
int? get status => _status;
|
||||||
|
|
||||||
|
set status(int? status) => _status = status;
|
||||||
|
|
||||||
|
String? get dependences => _dependences;
|
||||||
|
|
||||||
|
set dependences(String? dependences) => _dependences = dependences;
|
||||||
|
|
||||||
|
dynamic get extensions => _extensions;
|
||||||
|
|
||||||
|
set extensions(dynamic extensions) => _extensions = extensions;
|
||||||
|
|
||||||
|
dynamic get subBefore => _subBefore;
|
||||||
|
|
||||||
|
set subBefore(dynamic subBefore) => _subBefore = subBefore;
|
||||||
|
|
||||||
|
dynamic get subAfter => _subAfter;
|
||||||
|
|
||||||
|
set subAfter(dynamic subAfter) => _subAfter = subAfter;
|
||||||
|
|
||||||
|
String? get branch => _branch;
|
||||||
|
|
||||||
|
set branch(String? branch) => _branch = branch;
|
||||||
|
|
||||||
|
dynamic get pullType => _pullType;
|
||||||
|
|
||||||
|
set pullType(dynamic pullType) => _pullType = pullType;
|
||||||
|
|
||||||
|
dynamic get pullOption => _pullOption;
|
||||||
|
|
||||||
|
set pullOption(dynamic pullOption) => _pullOption = pullOption;
|
||||||
|
|
||||||
|
int? get pid => _pid;
|
||||||
|
|
||||||
|
set pid(int? pid) => _pid = pid;
|
||||||
|
|
||||||
|
int? get isDisabled => _isDisabled;
|
||||||
|
|
||||||
|
set isDisabled(dynamic isDisabled) => _isDisabled = isDisabled;
|
||||||
|
|
||||||
|
String? get logPath => _logPath;
|
||||||
|
|
||||||
|
set logPath(String? logPath) => _logPath = logPath;
|
||||||
|
|
||||||
|
String? get scheduleType => _scheduleType;
|
||||||
|
|
||||||
|
set scheduleType(String? scheduleType) => _scheduleType = scheduleType;
|
||||||
|
|
||||||
|
String? get alias => _alias;
|
||||||
|
|
||||||
|
set alias(String? alias) => _alias = alias;
|
||||||
|
|
||||||
|
String? get createdAt => _createdAt;
|
||||||
|
|
||||||
|
set createdAt(String? createdAt) => _createdAt = createdAt;
|
||||||
|
|
||||||
|
String? get updatedAt => _updatedAt;
|
||||||
|
|
||||||
|
set updatedAt(String? updatedAt) => _updatedAt = updatedAt;
|
||||||
|
|
||||||
|
Subscription.fromJson(Map<String, dynamic> json) {
|
||||||
|
_id = json['id'];
|
||||||
|
_name = json['name'];
|
||||||
|
_url = json['url'];
|
||||||
|
_schedule = json['schedule'];
|
||||||
|
_intervalSchedule = json['interval_schedule'] != null
|
||||||
|
? new IntervalSchedule.fromJson(json['interval_schedule'])
|
||||||
|
: null;
|
||||||
|
_type = json['type'];
|
||||||
|
_whitelist = json['whitelist'];
|
||||||
|
_blacklist = json['blacklist'];
|
||||||
|
_status = json['status'];
|
||||||
|
_dependences = json['dependences'];
|
||||||
|
_extensions = json['extensions'];
|
||||||
|
_subBefore = json['sub_before'];
|
||||||
|
_subAfter = json['sub_after'];
|
||||||
|
_branch = json['branch'];
|
||||||
|
_pullType = json['pull_type'];
|
||||||
|
_pullOption = json['pull_option'];
|
||||||
|
_pid = json['pid'];
|
||||||
|
_isDisabled = json['is_disabled'];
|
||||||
|
_logPath = json['log_path'];
|
||||||
|
_scheduleType = json['schedule_type'];
|
||||||
|
_alias = json['alias'];
|
||||||
|
_createdAt = json['createdAt'];
|
||||||
|
_updatedAt = json['updatedAt'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['id'] = this._id;
|
||||||
|
data['name'] = this._name;
|
||||||
|
data['url'] = this._url;
|
||||||
|
data['schedule'] = this._schedule;
|
||||||
|
if (this._intervalSchedule != null) {
|
||||||
|
data['interval_schedule'] = this._intervalSchedule!.toJson();
|
||||||
|
}
|
||||||
|
data['type'] = this._type;
|
||||||
|
data['whitelist'] = this._whitelist;
|
||||||
|
data['blacklist'] = this._blacklist;
|
||||||
|
data['status'] = this._status;
|
||||||
|
data['dependences'] = this._dependences;
|
||||||
|
data['extensions'] = this._extensions;
|
||||||
|
data['sub_before'] = this._subBefore;
|
||||||
|
data['sub_after'] = this._subAfter;
|
||||||
|
data['branch'] = this._branch;
|
||||||
|
data['pull_type'] = this._pullType;
|
||||||
|
data['pull_option'] = this._pullOption;
|
||||||
|
data['pid'] = this._pid;
|
||||||
|
data['is_disabled'] = this._isDisabled;
|
||||||
|
data['log_path'] = this._logPath;
|
||||||
|
data['schedule_type'] = this._scheduleType;
|
||||||
|
data['alias'] = this._alias;
|
||||||
|
data['createdAt'] = this._createdAt;
|
||||||
|
data['updatedAt'] = this._updatedAt;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
static Subscription jsonConversion(Map<String, dynamic> json) {
|
||||||
|
return Subscription.fromJson(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntervalSchedule {
|
||||||
|
String? _type;
|
||||||
|
int? _value;
|
||||||
|
|
||||||
|
IntervalSchedule({String? type, int? value}) {
|
||||||
|
if (type != null) {
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
if (value != null) {
|
||||||
|
this._value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get type => _type;
|
||||||
|
|
||||||
|
set type(String? type) => _type = type;
|
||||||
|
|
||||||
|
int? get value => _value;
|
||||||
|
|
||||||
|
set value(int? value) => _value = value;
|
||||||
|
|
||||||
|
IntervalSchedule.fromJson(Map<String, dynamic> json) {
|
||||||
|
_type = json['type'];
|
||||||
|
_value = json['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['type'] = this._type;
|
||||||
|
data['value'] = this._value;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
338
lib/module/others/subscription/subscription_detail_page.dart
Normal file
338
lib/module/others/subscription/subscription_detail_page.dart
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||||
|
import 'package:qinglong_app/base/ql_app_bar.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_bean.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_time_log_page.dart';
|
||||||
|
|
||||||
|
import '../../../base/http/api.dart';
|
||||||
|
import '../../../base/routes.dart';
|
||||||
|
import '../../../base/theme.dart';
|
||||||
|
import '../../task/task_viewmodel.dart';
|
||||||
|
|
||||||
|
class SubscriptionDetailPage extends StatefulWidget {
|
||||||
|
final Subscription bean;
|
||||||
|
final bool hideAppbar;
|
||||||
|
|
||||||
|
const SubscriptionDetailPage(this.bean, {Key? key, this.hideAppbar = false})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SubscriptionDetailPage> createState() => _SubscriptionDetailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SubscriptionDetailPageState extends State<SubscriptionDetailPage> {
|
||||||
|
|
||||||
|
List<Widget> actions = [];
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
//actions.clear();
|
||||||
|
actions.addAll(
|
||||||
|
[
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () async {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
if (widget.bean.status! == 1) {
|
||||||
|
await startCron(context);
|
||||||
|
} else {
|
||||||
|
await stopCron(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 15,
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Text(
|
||||||
|
widget.bean.status! == 1 ? "运行" : "停止运行",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
showLog();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 15,
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: const Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Text(
|
||||||
|
"查看日志",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: QlAppBar(
|
||||||
|
title: "订阅管理",
|
||||||
|
canBack: true,
|
||||||
|
actions: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
showCupertinoModalPopup(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return CupertinoActionSheet(
|
||||||
|
title: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: const Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Text(
|
||||||
|
"更多操作",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: actions,
|
||||||
|
cancelButton: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
child: const Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Text(
|
||||||
|
"取消",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.red,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.more_horiz,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 26,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "名称",
|
||||||
|
desc: widget.bean.name,
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "仓库类型",
|
||||||
|
desc: widget.bean.type == "public-repo"
|
||||||
|
? "公开仓库"
|
||||||
|
: widget.bean.type == "private-repo"
|
||||||
|
? "私有仓库"
|
||||||
|
: "单文件",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "链接",
|
||||||
|
desc: widget.bean.url ?? "",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "分支",
|
||||||
|
desc: widget.bean.branch ?? "",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "定时类型",
|
||||||
|
desc: widget.bean.scheduleType ?? "",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "定时规则",
|
||||||
|
desc: widget.bean.scheduleType == "crontab"
|
||||||
|
? widget.bean.schedule ?? ""
|
||||||
|
: widget.bean.intervalSchedule!.type == "days"
|
||||||
|
? "每" + widget.bean.intervalSchedule!.value.toString() + "天"
|
||||||
|
: widget.bean.intervalSchedule!.type == "hours"
|
||||||
|
? "每" +
|
||||||
|
widget.bean.intervalSchedule!.value.toString() +
|
||||||
|
"小时"
|
||||||
|
: widget.bean.intervalSchedule!.type == "minutes"
|
||||||
|
? "每" +
|
||||||
|
widget.bean.intervalSchedule!.value.toString() +
|
||||||
|
"分钟"
|
||||||
|
: "每" +
|
||||||
|
widget.bean.intervalSchedule!.value.toString() +
|
||||||
|
"秒",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "白名单",
|
||||||
|
desc: widget.bean.whitelist ?? "",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "黑名单",
|
||||||
|
desc: widget.bean.blacklist ?? "",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "依赖文件",
|
||||||
|
desc: widget.bean.dependences ?? "",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "文件后缀",
|
||||||
|
desc: widget.bean.extensions ?? "",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "执行前",
|
||||||
|
desc: widget.bean.subAfter ?? "",
|
||||||
|
),
|
||||||
|
TaskDetailCell(
|
||||||
|
title: "执行后",
|
||||||
|
desc: widget.bean.subBefore ?? "",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
startCron(BuildContext context) async {
|
||||||
|
await Api.startSubscription([widget.bean.id!]);
|
||||||
|
setState(() {});
|
||||||
|
showLog();
|
||||||
|
}
|
||||||
|
|
||||||
|
stopCron(BuildContext context) async {
|
||||||
|
await Api.stopSubscription([widget.bean.id!]);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void showLog() {
|
||||||
|
showCupertinoModalBottomSheet(
|
||||||
|
expand: true,
|
||||||
|
context: context,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (context) =>
|
||||||
|
SubscriptionTimeLogPage(
|
||||||
|
widget.bean.id!,
|
||||||
|
true,
|
||||||
|
widget.bean.name ?? "",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TaskDetailCell extends ConsumerWidget {
|
||||||
|
final String title;
|
||||||
|
final String? desc;
|
||||||
|
final Widget? icon;
|
||||||
|
final bool hideDivide;
|
||||||
|
final Function? taped;
|
||||||
|
|
||||||
|
const TaskDetailCell({
|
||||||
|
Key? key,
|
||||||
|
required this.title,
|
||||||
|
this.desc,
|
||||||
|
this.icon,
|
||||||
|
this.hideDivide = false,
|
||||||
|
this.taped,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
left: 15,
|
||||||
|
right: 10,
|
||||||
|
bottom: 10,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
color: ref.watch(themeProvider).themeColor.titleColor(),
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 30,
|
||||||
|
),
|
||||||
|
desc != null
|
||||||
|
? Expanded(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: SelectableText(
|
||||||
|
desc!,
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
selectionHeightStyle: BoxHeightStyle.max,
|
||||||
|
selectionWidthStyle: BoxWidthStyle.max,
|
||||||
|
onTap: () {
|
||||||
|
if (taped != null) {
|
||||||
|
taped!();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
ref.watch(themeProvider).themeColor.descColor(),
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Expanded(
|
||||||
|
child:
|
||||||
|
Align(alignment: Alignment.centerRight, child: icon!),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
hideDivide
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: const Divider(
|
||||||
|
indent: 15,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
351
lib/module/others/subscription/subscription_page.dart
Normal file
351
lib/module/others/subscription/subscription_page.dart
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
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:highlight/languages/routeros.dart';
|
||||||
|
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||||
|
import 'package:qinglong_app/base/http/http.dart';
|
||||||
|
import 'package:qinglong_app/base/ql_app_bar.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_bean.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_time_log_page.dart';
|
||||||
|
import 'package:qinglong_app/utils/extension.dart';
|
||||||
|
|
||||||
|
import '../../../base/http/api.dart';
|
||||||
|
import '../../../base/routes.dart';
|
||||||
|
import '../../../base/theme.dart';
|
||||||
|
import '../../../base/ui/empty_widget.dart';
|
||||||
|
import '../../../base/ui/lazy_load_state.dart';
|
||||||
|
|
||||||
|
class SubscriptionPage extends ConsumerStatefulWidget {
|
||||||
|
const SubscriptionPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ScriptPageState createState() => _ScriptPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ScriptPageState extends ConsumerState<SubscriptionPage>
|
||||||
|
with LazyLoadState<SubscriptionPage> {
|
||||||
|
List<Subscription> list = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: QlAppBar(
|
||||||
|
title: "订阅管理",
|
||||||
|
canBack: true,
|
||||||
|
actions: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pushNamed(
|
||||||
|
Routes.routerSubscriptionAdd,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 15,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Icon(
|
||||||
|
CupertinoIcons.add,
|
||||||
|
size: 24,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: RefreshIndicator(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
onRefresh: () async {
|
||||||
|
return loadData();
|
||||||
|
},
|
||||||
|
child: list.isEmpty
|
||||||
|
? const EmptyWidget()
|
||||||
|
: ListView.builder(
|
||||||
|
keyboardDismissBehavior:
|
||||||
|
ScrollViewKeyboardDismissBehavior.onDrag,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
Subscription item = list[index];
|
||||||
|
return SubscriptionCell(list[index], index, ref);
|
||||||
|
},
|
||||||
|
itemCount: list.length,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadData() async {
|
||||||
|
HttpResponse<List<Subscription>> response = await Api.getSubscription();
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
if (response.bean == null || response.bean!.isEmpty) {
|
||||||
|
"暂无数据".toast();
|
||||||
|
}
|
||||||
|
list.clear();
|
||||||
|
list.addAll(response.bean ?? []);
|
||||||
|
setState(() {});
|
||||||
|
} else {
|
||||||
|
response.message?.toast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onLazyLoad() {
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubscriptionCell extends StatelessWidget {
|
||||||
|
final Subscription bean;
|
||||||
|
final int index;
|
||||||
|
final WidgetRef ref;
|
||||||
|
|
||||||
|
const SubscriptionCell(this.bean, this.index, this.ref, {Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ColoredBox(
|
||||||
|
color: ref.watch(themeProvider).themeColor.settingBgColor(),
|
||||||
|
child: Slidable(
|
||||||
|
key: ValueKey(bean.id),
|
||||||
|
endActionPane: ActionPane(
|
||||||
|
motion: const StretchMotion(),
|
||||||
|
extentRatio: 0.7,
|
||||||
|
children: [
|
||||||
|
SlidableAction(
|
||||||
|
backgroundColor: const Color(0xff5D5E70),
|
||||||
|
onPressed: (_) {
|
||||||
|
WidgetsBinding.instance.endOfFrame.then((timeStamp) {
|
||||||
|
Navigator.of(context).pushNamed(
|
||||||
|
Routes.routerSubscriptionAdd,
|
||||||
|
arguments: bean);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
icon: CupertinoIcons.pencil_outline,
|
||||||
|
),
|
||||||
|
SlidableAction(
|
||||||
|
backgroundColor: const Color(0xffA356D6),
|
||||||
|
onPressed: (_) {
|
||||||
|
changeSubscriptionEnable();
|
||||||
|
},
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
icon: bean.isDisabled == null || bean.isDisabled == 0
|
||||||
|
? Icons.dnd_forwardslash
|
||||||
|
: Icons.check_circle_outline_sharp,
|
||||||
|
),
|
||||||
|
SlidableAction(
|
||||||
|
backgroundColor: const Color(0xffEA4D3E),
|
||||||
|
onPressed: (_) {
|
||||||
|
Api.deleteSubscription([bean.id!]);
|
||||||
|
},
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
icon: CupertinoIcons.delete,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
startActionPane: ActionPane(
|
||||||
|
motion: const StretchMotion(),
|
||||||
|
extentRatio: 0.4,
|
||||||
|
children: [
|
||||||
|
SlidableAction(
|
||||||
|
backgroundColor: const Color(0xffD25535),
|
||||||
|
onPressed: (_) {
|
||||||
|
changeSubscriptionStatus();
|
||||||
|
},
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
icon: bean.status! == 1
|
||||||
|
? CupertinoIcons.memories
|
||||||
|
: CupertinoIcons.stop_circle,
|
||||||
|
),
|
||||||
|
SlidableAction(
|
||||||
|
backgroundColor: const Color(0xff606467),
|
||||||
|
onPressed: (_) {
|
||||||
|
Future.delayed(
|
||||||
|
const Duration(
|
||||||
|
milliseconds: 250,
|
||||||
|
), () {
|
||||||
|
showLog(context);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
icon: CupertinoIcons.text_justifyleft,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: (){
|
||||||
|
Navigator.of(context).pushNamed(
|
||||||
|
Routes.routerSubscriptionDetail,
|
||||||
|
arguments: bean);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
color: Colors.transparent,
|
||||||
|
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: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
bean.status == 1
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: SizedBox(
|
||||||
|
width: 15,
|
||||||
|
height: 15,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: ref
|
||||||
|
.watch(themeProvider)
|
||||||
|
.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: bean.status == 1 ? 0 : 5,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Text(
|
||||||
|
bean.name ?? "",
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
color: ref
|
||||||
|
.watch(themeProvider)
|
||||||
|
.themeColor
|
||||||
|
.titleColor(),
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Text(
|
||||||
|
bean.branch!,
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
color: ref
|
||||||
|
.watch(themeProvider)
|
||||||
|
.themeColor
|
||||||
|
.descColor(),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
bean.isDisabled == 1
|
||||||
|
? const Icon(
|
||||||
|
Icons.dnd_forwardslash,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.red,
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
const SizedBox(
|
||||||
|
width: 5,
|
||||||
|
),
|
||||||
|
Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Text(
|
||||||
|
bean.schedule ?? "",
|
||||||
|
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.url ?? "",
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
color:
|
||||||
|
ref.watch(themeProvider).themeColor.descColor(),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 1,
|
||||||
|
indent: 15,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showLog(BuildContext context) {
|
||||||
|
showCupertinoModalBottomSheet(
|
||||||
|
expand: true,
|
||||||
|
context: context,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (context) => SubscriptionTimeLogPage(
|
||||||
|
bean.id!,
|
||||||
|
true,
|
||||||
|
bean.name ?? "",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> changeSubscriptionStatus() async {
|
||||||
|
if (bean.status == null || bean.status == 0) {
|
||||||
|
await Api.stopSubscription([bean.id!]);
|
||||||
|
} else {
|
||||||
|
await Api.startSubscription([bean.id!]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> changeSubscriptionEnable() async {
|
||||||
|
if (bean.isDisabled == null || bean.isDisabled == 0) {
|
||||||
|
await Api.disableSubscription([bean.id!]);
|
||||||
|
} else {
|
||||||
|
await Api.enableSubscription([bean.id!]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
181
lib/module/others/subscription/subscription_time_log_page.dart
Normal file
181
lib/module/others/subscription/subscription_time_log_page.dart
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qinglong_app/base/http/api.dart';
|
||||||
|
import 'package:qinglong_app/base/http/http.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_page.dart';
|
||||||
|
import 'package:qinglong_app/module/others/subscription/subscription_page.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
|
import '../../../base/ui/lazy_load_state.dart';
|
||||||
|
|
||||||
|
class SubscriptionTimeLogPage extends StatefulWidget {
|
||||||
|
final int subId;
|
||||||
|
final bool needTimer;
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
const SubscriptionTimeLogPage(this.subId, this.needTimer, this.title, {Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SubscriptionTimeLogPageState createState() => _SubscriptionTimeLogPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SubscriptionTimeLogPageState extends State<SubscriptionTimeLogPage>
|
||||||
|
with LazyLoadState<SubscriptionTimeLogPage> {
|
||||||
|
Timer? _timer;
|
||||||
|
|
||||||
|
String? content;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRequest = false;
|
||||||
|
bool canRequest = true;
|
||||||
|
|
||||||
|
getLogData() async {
|
||||||
|
if (!canRequest) return;
|
||||||
|
if (isRequest) return;
|
||||||
|
isRequest = true;
|
||||||
|
HttpResponse<String> response = await Api.subTimeLog(widget.subId);
|
||||||
|
if (response.success) {
|
||||||
|
content = response.bean;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
isRequest = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_timer?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
child: SafeArea(
|
||||||
|
child: Container(
|
||||||
|
color: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
CupertinoIcons.chevron_down,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
widget.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Share.share(content ?? "");
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
CupertinoIcons.share,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
if (content == null)
|
||||||
|
const Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: CupertinoActivityIndicator(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
primary: true,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SelectableText(
|
||||||
|
content!,
|
||||||
|
scrollPhysics: const NeverScrollableScrollPhysics(),
|
||||||
|
onSelectionChanged: (TextSelection selection,
|
||||||
|
SelectionChangedCause? cause) {
|
||||||
|
final int newStart = min(
|
||||||
|
selection.baseOffset, selection.extentOffset);
|
||||||
|
final int newEnd = max(
|
||||||
|
selection.baseOffset, selection.extentOffset);
|
||||||
|
if (newEnd == newStart) {
|
||||||
|
canRequest = true;
|
||||||
|
} else {
|
||||||
|
canRequest = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectionHeightStyle: BoxHeightStyle.max,
|
||||||
|
selectionWidthStyle: BoxWidthStyle.max,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 400,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onLazyLoad() {
|
||||||
|
if (widget.needTimer) {
|
||||||
|
_timer = Timer.periodic(
|
||||||
|
const Duration(seconds: 2),
|
||||||
|
(timer) {
|
||||||
|
getLogData();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||||
|
getLogData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
340
pubspec.lock
340
pubspec.lock
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,7 @@ dependencies:
|
|||||||
file_picker: ^4.6.1
|
file_picker: ^4.6.1
|
||||||
code_text_field:
|
code_text_field:
|
||||||
path: ./pub/code_field-master
|
path: ./pub/code_field-master
|
||||||
|
path_provider: ^2.0.11
|
||||||
|
|
||||||
# flutter pub run build_runner build --delete-conflicting-outputs
|
# flutter pub run build_runner build --delete-conflicting-outputs
|
||||||
# flutter pub run change_app_package_name:main work.master.qinglongapp
|
# flutter pub run change_app_package_name:main work.master.qinglongapp
|
||||||
|
|||||||
Reference in New Issue
Block a user