From 6e50e846942e04bce217f22a424d459cafd3c3e4 Mon Sep 17 00:00:00 2001 From: Simon Ding Date: Fri, 5 Jul 2024 14:45:42 +0800 Subject: [PATCH] add setting, alertbox, etc --- server/setting.go | 2 +- ui/lib/APIs.dart | 3 ++ ui/lib/main.dart | 11 +++-- ui/lib/navdrawer.dart | 9 +++- ui/lib/search.dart | 16 +++++-- ui/lib/server_response.dart | 11 +++++ ui/lib/system_settings.dart | 93 +++++++++++++++++++++++++++++++++++++ ui/lib/utils.dart | 31 +++++++++++++ ui/lib/weclome.dart | 1 + 9 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 ui/lib/system_settings.dart create mode 100644 ui/lib/utils.dart diff --git a/server/setting.go b/server/setting.go index f7aaa92..8c55781 100644 --- a/server/setting.go +++ b/server/setting.go @@ -29,5 +29,5 @@ func (s *Server) GetSetting(c *gin.Context) (interface{}, error) { } v := s.db.GetSetting(q) log.Infof("get value for key %v: %v", q, v) - return v, nil + return gin.H{q: v}, nil } diff --git a/ui/lib/APIs.dart b/ui/lib/APIs.dart index 1adf7fe..89cf206 100644 --- a/ui/lib/APIs.dart +++ b/ui/lib/APIs.dart @@ -1,6 +1,9 @@ class APIs { static const _baseUrl = "http://127.0.0.1:8080"; static const searchUrl = "$_baseUrl/api/v1/tv/search"; + static const settingsUrl = "$_baseUrl/api/v1/setting/do"; static const tmdbImgBaseUrl = "https://image.tmdb.org/t/p/w500/"; + + static const tmdbApiKey = "tmdb_api_key"; } diff --git a/ui/lib/main.dart b/ui/lib/main.dart index 60b1dec..d053cf4 100644 --- a/ui/lib/main.dart +++ b/ui/lib/main.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:ui/navdrawer.dart'; import 'package:ui/search.dart'; +import 'package:ui/system_settings.dart'; import 'package:ui/weclome.dart'; void main() { @@ -41,19 +42,23 @@ class MyApp extends StatelessWidget { }, routes: [ GoRoute( - path: '/', + path: WelcomePage.route, builder: (context, state) => WelcomePage(), ), GoRoute( - path: "/search", + path: SearchPage.route, builder: (context, state) => const SearchPage(), + ), + GoRoute( + path: SystemSettingsPage.route, + builder: (context, state) => SystemSettingsPage(), ) ], ); final _router = GoRouter( navigatorKey: _rootNavigatorKey, - initialLocation: '/', + initialLocation: WelcomePage.route, routes: [ _shellRoute, ], diff --git a/ui/lib/navdrawer.dart b/ui/lib/navdrawer.dart index 54a252d..762f8c3 100644 --- a/ui/lib/navdrawer.dart +++ b/ui/lib/navdrawer.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:ui/search.dart'; +import 'package:ui/system_settings.dart'; +import 'package:ui/weclome.dart'; class NavDrawer extends StatefulWidget { @override @@ -27,9 +30,11 @@ class _NavDrawerState extends State { _counter = value; }); if (value == 0) { - context.go('/'); + context.go(WelcomePage.route); } else if (value == 1) { - context.go("/search"); + context.go(SearchPage.route); + } else if (value == 2) { + context.go(SystemSettingsPage.route); } }, extended: MediaQuery.of(context).size.width >= 850, diff --git a/ui/lib/search.dart b/ui/lib/search.dart index 7849ac5..1f4deba 100644 --- a/ui/lib/search.dart +++ b/ui/lib/search.dart @@ -1,10 +1,14 @@ import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import 'package:ui/APIs.dart'; +import 'package:ui/server_response.dart'; +import 'package:ui/utils.dart'; class SearchPage extends StatefulWidget { const SearchPage({super.key}); + static const route = "/search"; + @override State createState() { return _SearchPageState(); @@ -14,15 +18,19 @@ class SearchPage extends StatefulWidget { class _SearchPageState extends State { List list = List.empty(); - void _queryResults(String q) async { + void _queryResults(BuildContext context, String q) async { final dio = Dio(); var resp = await dio.get(APIs.searchUrl, queryParameters: {"query": q}); //var dy = jsonDecode(resp.data.toString()); print("search page results: ${resp.data}"); + var rsp = ServerResponse.fromJson(resp.data as Map); + if (rsp.code != 0 && context.mounted) { + Utils.showAlertDialog(context, rsp.message); + return; + } - var rsp = resp.data as Map; - var data = rsp["data"] as Map; + var data = rsp.data as Map; var results = data["results"] as List; setState(() { @@ -80,7 +88,7 @@ class _SearchPageState extends State { children: [ TextField( autofocus: true, - onSubmitted: (value) => _queryResults(value), + onSubmitted: (value) => _queryResults(context,value), decoration: const InputDecoration( labelText: "搜索", hintText: "搜索剧集名称", diff --git a/ui/lib/server_response.dart b/ui/lib/server_response.dart index e69de29..eebdcb8 100644 --- a/ui/lib/server_response.dart +++ b/ui/lib/server_response.dart @@ -0,0 +1,11 @@ +class ServerResponse { + final int code; + final String message; + final dynamic data; + ServerResponse(this.code, this.message, this.data); + + ServerResponse.fromJson(Map json) + : code = json["code"] as int, + message = json["message"] as String, + data = json["data"]; +} diff --git a/ui/lib/system_settings.dart b/ui/lib/system_settings.dart new file mode 100644 index 0000000..4d7fd06 --- /dev/null +++ b/ui/lib/system_settings.dart @@ -0,0 +1,93 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:ui/APIs.dart'; +import 'package:ui/server_response.dart'; +import 'package:ui/utils.dart'; + +class SystemSettingsPage extends StatefulWidget { + static const route = "/systemsettings"; + @override + State createState() { + return _SystemSettingsPageState(); + } +} + +class _SystemSettingsPageState extends State { + final GlobalKey _formKey = GlobalKey(); + final TextEditingController _tmdbApiKeyController = TextEditingController(); + + @override + Widget build(BuildContext context) { + _handleRefresh(); + return Expanded( + child: RefreshIndicator( + onRefresh: _handleRefresh, + child: Form( + key: _formKey, //设置globalKey,用于后面获取FormState + autovalidateMode: AutovalidateMode.onUserInteraction, + child: Column( + children: [ + TextFormField( + autofocus: true, + controller: _tmdbApiKeyController, + decoration: const InputDecoration( + labelText: "TMDB Api Key", + icon: Icon(Icons.key), + ), + // 校验用户名 + validator: (v) { + return v!.trim().isNotEmpty ? null : "ApiKey 不能为空"; + }, + ), + Padding( + padding: const EdgeInsets.only(top: 28.0), + child: Row( + children: [ + Center( + child: ElevatedButton( + child: const Padding( + padding: EdgeInsets.all(16.0), + child: Text("保存"), + ), + onPressed: () { + // 通过_formKey.currentState 获取FormState后, + // 调用validate()方法校验用户名密码是否合法,校验 + // 通过后再提交数据。 + if ((_formKey.currentState as FormState) + .validate()) { + _submitSettings(context,_tmdbApiKeyController.text); + } + }, + ), + ), + ], + ), + ) + ], + ), + )), + ); + } + + Future _handleRefresh() async { + final dio = Dio(); + var resp = await dio + .get(APIs.settingsUrl, queryParameters: {"key": APIs.tmdbApiKey}); + var rrr = resp.data as Map; + var data = rrr["data"] as Map; + var key = data[APIs.tmdbApiKey] as String; + _tmdbApiKeyController.text = key; + + // Fetch new data and update the UI + } + + void _submitSettings(BuildContext context, String v) async { + var resp = await Dio().post(APIs.settingsUrl, data: {APIs.tmdbApiKey: v}); + var sp = ServerResponse.fromJson(resp.data as Map); + if (sp.code != 0) { + if (context.mounted) { + Utils.showAlertDialog(context, sp.message); + } + } + } +} diff --git a/ui/lib/utils.dart b/ui/lib/utils.dart new file mode 100644 index 0000000..41e8b75 --- /dev/null +++ b/ui/lib/utils.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +class Utils { + static Future showAlertDialog(BuildContext context, String msg) async { + + return showDialog( + context: context, + barrierDismissible: true, // user must tap button! + builder: (BuildContext context) { + return AlertDialog( + title: const Text('警告 ⚠️'), + content: SingleChildScrollView( + child: ListBody( + children: [ + Text(msg), + ], + ), + ), + actions: [ + TextButton( + child: const Text('确定'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } +} diff --git a/ui/lib/weclome.dart b/ui/lib/weclome.dart index 9326faf..bab1eec 100644 --- a/ui/lib/weclome.dart +++ b/ui/lib/weclome.dart @@ -3,6 +3,7 @@ import 'package:ui/APIs.dart'; class WelcomePage extends StatefulWidget { const WelcomePage({super.key}); + static const route = "/welcome"; @override State createState() {