Browse Source

Merge pull request #1 from colinin/master

merge
pull/855/head
leehom0123 2 years ago
committed by GitHub
parent
commit
2faaf6cf51
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      apps/flutter/components/lib/widgets/index.dart
  2. 87
      apps/flutter/components/lib/widgets/menu/index.dart
  3. 3
      apps/flutter/components/lib/widgets/menu/state.dart
  4. 23
      apps/flutter/core/lib/models/common.dart
  5. 2
      apps/flutter/core/lib/models/environment.dart
  6. 6
      apps/flutter/core/lib/models/environment.g.dart
  7. 16
      apps/flutter/core/lib/utils/color.utils.dart
  8. 1
      apps/flutter/core/lib/utils/index.dart
  9. 4
      apps/flutter/dev_app/.gitignore
  10. 7
      apps/flutter/dev_app/lib/main.dart
  11. 3
      apps/flutter/dev_app/lib/main.module.dart
  12. 13
      apps/flutter/dev_app/lib/pages/main/controller.dart
  13. 6
      apps/flutter/dev_app/lib/pages/public/center/controller.dart
  14. 1
      apps/flutter/dev_app/lib/pages/public/error/index.dart
  15. 1
      apps/flutter/dev_app/lib/pages/public/error/not_found/index.dart
  16. 23
      apps/flutter/dev_app/lib/pages/public/error/not_found/view.dart
  17. 49
      apps/flutter/dev_app/lib/pages/public/home/controller.dart
  18. 28
      apps/flutter/dev_app/lib/pages/public/home/state.dart
  19. 99
      apps/flutter/dev_app/lib/pages/public/home/view.dart
  20. 5
      apps/flutter/dev_app/lib/pages/public/home/widget/index.dart
  21. 76
      apps/flutter/dev_app/lib/pages/public/home/widget/menu_drawer.dart
  22. 42
      apps/flutter/dev_app/lib/pages/public/home/widget/my_favorite.dart
  23. 55
      apps/flutter/dev_app/lib/pages/public/home/widget/notification_bar.dart
  24. 33
      apps/flutter/dev_app/lib/pages/public/home/widget/quick_navigation.dart
  25. 23
      apps/flutter/dev_app/lib/pages/public/home/widget/search.dart
  26. 1
      apps/flutter/dev_app/lib/pages/public/index.dart
  27. 2
      apps/flutter/dev_app/lib/pages/public/route.name.dart
  28. 6
      apps/flutter/dev_app/lib/pages/public/route.public.dart
  29. 17
      apps/flutter/dev_app/lib/services/notification.send.local.service.dart
  30. 20
      apps/flutter/dev_app/lib/services/translation.service.res.service.dart
  31. 56
      apps/flutter/dev_app/pubspec.lock
  32. 1
      apps/flutter/dev_app/pubspec.yaml
  33. 14
      apps/flutter/dev_app/res/config/demo.json
  34. BIN
      apps/flutter/dev_app/res/images/no_data.png
  35. BIN
      apps/flutter/dev_app/res/images/notification.png
  36. BIN
      apps/flutter/dev_app/res/images/profile.png
  37. BIN
      apps/flutter/dev_app/res/images/setting.png
  38. 7
      apps/flutter/dev_app/res/translations/en.json
  39. 9
      apps/flutter/dev_app/res/translations/zh-Hans.json
  40. 1
      apps/flutter/notifications/lib/models/notification.state.dart
  41. 11
      apps/flutter/notifications/lib/services/notification.state.service.dart
  42. 14
      apps/flutter/platform/lib/services/favorite.menu.state.service.dart
  43. 14
      apps/flutter/platform/lib/services/menu.state.service.dart

1
apps/flutter/components/lib/widgets/index.dart

@ -3,3 +3,4 @@ export 'action-button/index.dart';
export 'back-to-top/index.dart';
export 'bottom-button/index.dart';
export 'empty/index.dart';
export 'menu/index.dart';

87
apps/flutter/components/lib/widgets/menu/index.dart

@ -0,0 +1,87 @@
import 'package:core/models/common.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class Navigation extends StatefulWidget {
const Navigation({
super.key,
this.activedMenu,
this.menus = const [],
this.onMenuExpanded,
});
final List<Menu> menus;
final String? activedMenu;
final void Function(Menu menu)? onMenuExpanded;
@override
State<Navigation> createState() => _NavigationState();
}
class _NavigationState extends State<Navigation> {
@override
Widget build(BuildContext context) {
return _renderNavigations(widget.menus);
}
Widget _renderNavigations(List<Menu> menus) {
var mainMenus = menus.where((menu) => menu.children?.isNotEmpty == false);
var subMenus = menus.where((menu) => menu.children?.isNotEmpty == true);
return Column(
children: [
_renderMenus(subMenus.toList()),
...mainMenus.map((menu) => _buildMenuItem(menu)),
],
);
}
Widget _renderMenus(List<Menu> menus) {
return ExpansionPanelList.radio(
initialOpenPanelValue: widget.activedMenu,
expandedHeaderPadding: const EdgeInsets.all(0),
expansionCallback: (panelIndex, isExpanded) {
if (widget.onMenuExpanded != null) {
widget.onMenuExpanded!(menus[panelIndex]);
}
},
children: menus.map<ExpansionPanelRadio>((Menu menu) {
var body = menu.children?.isNotEmpty == true
? _renderNavigations(menu.children!)
: _buildMenuItem(menu);
return ExpansionPanelRadio(
canTapOnHeader: true,
headerBuilder: (BuildContext context, bool isExpanded) {
return SizedBox(
height: 30,
child: ListTile(
title: Text(
(menu.meta?['displayName']?.toString().tr ?? menu.displayName).padLeft(menu.level * 4)),
),
);
},
body: body,
value: menu.name,
);
}).toList(),
);
}
Widget _buildMenuItem(Menu menu) {
return InkWell(
onTap: () {
Get.toNamed(menu.path);
},
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
height: 30,
margin: const EdgeInsets.only(top: 10),
child: Text(
(menu.meta?['displayName']?.toString().tr ?? menu.displayName).padLeft(menu.level * 8),
),
),
),
);
}
}

3
apps/flutter/components/lib/widgets/menu/state.dart

@ -0,0 +1,3 @@
class MenuState {
}

23
apps/flutter/core/lib/models/common.dart

@ -42,3 +42,26 @@ class SignalrMessage {
String method;
List<Object?> data;
}
class Menu {
Menu({
required this.path,
required this.name,
required this.displayName,
this.id = 0,
this.level = 0,
this.description,
this.redirect,
this.meta,
this.children,
});
String path;
String name;
String displayName;
String? description;
String? redirect;
int level;
int id;
Map<String, dynamic>? meta;
List<Menu>? children;
}

2
apps/flutter/core/lib/models/environment.dart

@ -177,10 +177,12 @@ class LocalizationConfig {
this.defaultLanguage,
this.useLocalResources = true,
this.supportedLocales = const [],
this.translationFiles = const {},
});
String? defaultLanguage;
bool? useLocalResources;
List<LanguageInfo>? supportedLocales;
Map<String, List<String>>? translationFiles;
factory LocalizationConfig.fromJson(Map<String, dynamic> json) => _$LocalizationConfigFromJson(json);
Map<String, dynamic> toJson() => _$LocalizationConfigToJson(this);

6
apps/flutter/core/lib/models/environment.g.dart

@ -102,6 +102,11 @@ LocalizationConfig _$LocalizationConfigFromJson(Map<String, dynamic> json) =>
supportedLocales: json['supportedLocales'] != null
? (json['supportedLocales'] as List<dynamic>).map((e) => LanguageInfo.fromJson(e)).toList()
: null,
translationFiles: json['translationFiles'] != null
? (json['translationFiles'] as Map<String, dynamic>)
.map((key, value) => MapEntry(key, (value as List<dynamic>)
.map((e) => e as String).toList()))
: null,
);
Map<String, dynamic> _$LocalizationConfigToJson(LocalizationConfig instance) =>
@ -109,6 +114,7 @@ Map<String, dynamic> _$LocalizationConfigToJson(LocalizationConfig instance) =>
'defaultLanguage': instance.defaultLanguage,
'useLocalResources': instance.useLocalResources,
'supportedLocales': instance.supportedLocales,
'translationFiles': instance.translationFiles,
};
RemoteService _$RemoteServiceFromJson(Map<String, dynamic> json) =>

16
apps/flutter/core/lib/utils/color.utils.dart

@ -0,0 +1,16 @@
import 'dart:ui';
class ColorUtils {
static Color fromHex(String hexString) {
final buffer = StringBuffer();
if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
buffer.write(hexString.replaceFirst('#', ''));
return Color(int.parse(buffer.toString(), radix: 16));
}
}
extension HexStringToColor on String {
Color toColor() {
return ColorUtils.fromHex(this);
}
}

1
apps/flutter/core/lib/utils/index.dart

@ -1,3 +1,4 @@
export 'color.utils.dart';
export 'environment.utils.dart';
export 'internal.store.dart';
export 'localization.utils.dart';

4
apps/flutter/dev_app/.gitignore

@ -45,3 +45,7 @@ app.*.map.json
# Environment config
/res/config/development.json
# Ignored translations
/res/translations/merge-en.json
/res/translations/merge-zh-Hans.json

7
apps/flutter/dev_app/lib/main.dart

@ -2,10 +2,12 @@ import 'package:core/dependency/index.dart';
import 'package:core/utils/theme.utils.dart';
import 'package:core/utils/logging.dart';
import 'package:dev_app/main.module.dart';
import 'package:dev_app/pages/index.dart';
import 'package:dev_app/pages/public/route.name.dart';
import 'package:dev_app/utils/localization.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
Future main() async {
@ -25,8 +27,9 @@ class MyApp extends StatelessWidget {
theme: ThemeUtils.lightTheme,
darkTheme: ThemeUtils.darkTheme,
themeMode: ThemeMode.system,
initialRoute: '/',
initialRoute: PublicRoutes.main,
getPages: module.getRoutes(),
unknownRoute: PublicRoute.notFound,
debugShowMaterialGrid: false,
enableLog: true,
builder: EasyLoading.init(),

3
apps/flutter/dev_app/lib/main.module.dart

@ -1,5 +1,6 @@
import 'package:components/index.dart';
import 'package:dev_app/pages/index.dart';
import 'package:dev_app/pages/public/route.name.dart';
import 'package:dev_app/services/index.dart';
import 'package:dev_app/utils/initial.utils.dart';
import 'package:dev_app/utils/loading.dart';
@ -30,7 +31,7 @@ class MainModule extends Module {
@override
List<GetPage> get routes => [
GetPage(
name: '/',
name: PublicRoutes.main,
page: () => const MainPage(),
bindings: [
MainBinding(),

13
apps/flutter/dev_app/lib/pages/main/controller.dart

@ -1,13 +1,13 @@
import 'package:core/dependency/index.dart';
import 'package:core/abstracts/signalr.service.dart';
import 'package:core/services/environment.service.dart';
import 'package:core/services/notification.send.service.dart';
import 'package:dev_app/handlers/index.dart';
import 'package:get/get.dart';
import 'package:core/services/session.service.dart';
import 'package:core/services/subscription.service.dart';
import 'package:core/utils/index.dart';
import 'package:notifications/models/index.dart';
import 'package:notifications/services/notification.state.service.dart';
import 'package:notifications/tokens/index.dart';
class MainController extends GetxController {
@ -17,7 +17,7 @@ class MainController extends GetxController {
SessionService get _sessionService => injector.get();
SubscriptionService get _subscriptionService => injector.get(tag: NotificationTokens.consumer);
SignalrService get _signalrService => injector.get(tag: NotificationTokens.producer);
NotificationSendService get _notificationSendService => injector.get();
NotificationStateService get _notificationStateService => injector.get();
EnvironmentService get _environmentService => injector.get();
ErrorHandler get _errorHandler => injector.get();
@ -37,14 +37,7 @@ class MainController extends GetxController {
if (data == null) continue;
//
var notification = NotificationInfo.fromJson(data as dynamic);
//
var payload = NotificationPaylod.fromNotification(notification);
//
await _notificationSendService.send(
payload.title,
payload.body,
payload.payload,
);
_notificationStateService.addNotification(notification);
}
},
);

6
apps/flutter/dev_app/lib/pages/public/center/controller.dart

@ -44,15 +44,15 @@ class CenterController extends GetxController {
}
void onClickFeedback() {
redirectToRoute('/feedback');
}
void onClickHelp() {
redirectToRoute('/help');
}
void onClickInfo() {
redirectToRoute('/info');
}
void onClickMessage() {

1
apps/flutter/dev_app/lib/pages/public/error/index.dart

@ -0,0 +1 @@
export './not_found/index.dart';

1
apps/flutter/dev_app/lib/pages/public/error/not_found/index.dart

@ -0,0 +1 @@
export 'view.dart';

23
apps/flutter/dev_app/lib/pages/public/error/not_found/view.dart

@ -0,0 +1,23 @@
import 'package:bruno/bruno.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class PageNotFound extends StatelessWidget {
const PageNotFound({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("404Message".tr)
),
body: BrnAbnormalStateWidget(
img: Image.asset(
'res/images/no_data.png',
scale: 3.0,
),
content: "404MessageDetail".tr,
),
);
}
}

49
apps/flutter/dev_app/lib/pages/public/home/controller.dart

@ -1,5 +1,7 @@
import 'package:core/models/common.dart';
import 'package:get/get.dart';
import 'package:core/dependency/index.dart';
import 'package:notifications/services/notification.state.service.dart';
import 'package:platforms/services/index.dart';
import 'state.dart';
@ -7,6 +9,7 @@ import 'state.dart';
class HomeController extends GetxController {
MenuStateService get _menuStateService => injector.get<MenuStateService>();
FavoriteMenuStateService get _favoriteMenuStateService => injector.get<FavoriteMenuStateService>();
NotificationStateService get _notificationStateService => injector.get<NotificationStateService>();
final Rx<HomeState> _state = Rx<HomeState>(HomeState());
HomeState get state => _state.value;
@ -14,17 +17,39 @@ class HomeController extends GetxController {
@override
void onInit() {
super.onInit();
// _menuStateService.getMyMenus$()
// .listen((menus) {
// _state.update((val) {
// val?.menus = menus;
// });
// });
// _favoriteMenuStateService.getFavoriteMenus$()
// .listen((menus) {
// _state.update((val) {
// val?.favoriteMenus = menus;
// });
// });
_menuStateService.getMyMenus$()
.listen((menus) {
_state.update((val) {
val?.menus = menus;
});
});
_favoriteMenuStateService.getFavoriteMenus$()
.listen((menus) {
_state.update((val) {
val?.favoriteMenus = menus;
});
});
_notificationStateService.getNotifications$()
.listen((payload) {
var notifications = state.notifications.reversed.take(5).toList();
notifications.add(payload);
_state.update((val) {
val?.notifications = notifications;
});
});
}
Future<void> refreshMenus() async {
await _menuStateService.refreshState();
}
void redirectToRoute(String route) {
Get.toNamed(route);
}
void onMenuExpanded(Menu menu) {
_state.update((val) {
val?.activedMenu = menu.name;
});
}
}

28
apps/flutter/dev_app/lib/pages/public/home/state.dart

@ -1,10 +1,38 @@
import 'package:core/models/common.dart';
import 'package:notifications/models/common.dart';
import 'package:platforms/modes/menu.dto.dart';
class HomeState {
HomeState({
this.activedMenu,
this.menus = const [],
this.favoriteMenus = const [],
this.notifications = const [],
});
String? activedMenu;
List<MenuDto> menus;
List<UserFavoriteMenuDto> favoriteMenus;
List<NotificationPaylod> notifications;
List<Menu> getMenus() => _buildTreeRecursive(menus, null, 0);
List<Menu> _buildTreeRecursive(List<MenuDto> treeMenus, String? parentId, int level) {
List<Menu> results = [];
var tempList = treeMenus.where((menu) => menu.parentId == parentId).toList();
for (int i = 0; i < tempList.length; i++) {
var menu = Menu(
id: tempList[i].id.hashCode,
path: tempList[i].path,
name: tempList[i].name,
displayName: tempList[i].displayName,
description: tempList[i].description,
redirect: tempList[i].redirect,
meta: tempList[i].meta,
level: level + 1
);
menu.children = _buildTreeRecursive(treeMenus, tempList[i].id, menu.level);
results.add(menu);
}
return results;
}
}

99
apps/flutter/dev_app/lib/pages/public/home/view.dart

@ -1,8 +1,13 @@
import 'package:account/pages/route.name.dart';
import 'package:components/index.dart';
import 'package:dev_app/pages/public/home/widget/search.dart';
import 'package:core/utils/index.dart';
import 'package:dev_app/pages/system/route.name.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'controller.dart';
import './widget/index.dart';
class HomePage extends BasePage<HomeController> {
const HomePage({super.key});
@ -20,27 +25,99 @@ class HomePage extends BasePage<HomeController> {
onPressed: () {
showSearch(context: context, delegate: SearchBarDelegate(menus: bloc.state.menus));
},
child: const Row(
child: Row(
children: <Widget>[
Icon(Icons.search),
Expanded(child: Text('搜索功能'))
const Icon(Icons.search),
Expanded(child: Text('Label:SearchFeatures'.tr))
],
),
),
),
body: Column(
body: ListView(
children: [
Expanded(
child: ListView.builder(
itemCount: bloc.state.favoriteMenus.length,
itemBuilder: (context, index) {
var favoriteMenu = bloc.state.favoriteMenus[index];
return Text(favoriteMenu.displayName ?? favoriteMenu.name);
Obx(() => NotificationBar(notifications: bloc.state.notifications)),
QuickNavigation(
menus: [
_buildMenu(
SystemRoutes.settings,
SystemRoutes.settings,
icon: 'res/images/setting.png',
displayName: "Label:SystemSettings".tr,
color: Colors.red.hex),
_buildMenu(
AccountRoutes.profile,
AccountRoutes.profile,
icon: 'res/images/profile.png',
displayName: "Page:UserProfile".tr,
color: const Color.fromARGB(255, 68, 160, 206).hex),
],
),
Obx(() => MyFavorite(
favoriteMenus: bloc.state.favoriteMenus,
favoriteMenuBuilder: (favoriteMenu) {
return _buildMenu(
favoriteMenu.name,
favoriteMenu.path,
aliasName: favoriteMenu.aliasName,
//icon: favoriteMenu.icon,
// TODO:
icon: 'res/images/setting.png',
color: favoriteMenu.color,
displayName: favoriteMenu.displayName,
);
},
)),
],
),
drawer: SafeArea(
child: Obx(() => MenuDrawer(
activedMenu: bloc.state.activedMenu,
menus: bloc.state.getMenus(),
onMenuExpanded: bloc.onMenuExpanded,
onMenuRefresh: bloc.refreshMenus,
)),
),
);
}
Widget _buildMenu(
String name,
String path,
{
String? aliasName,
String? icon,
String? color,
String? displayName,
}
) {
return InkWell(
onTap: () {
bloc.redirectToRoute(path);
},
child: SizedBox(
height: 20,
width: 30,
child: Column(
children: [
const SizedBox(height: 10),
icon != null
? Image.asset(
icon,
height: 40,
width: 40,
color: color.isNullOrWhiteSpace() ? null : ColorUtils.fromHex(color!),
)
: Empty.none,
Text(
displayName ?? aliasName ?? name,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 14
),
)
],
),
),
);
}
}

5
apps/flutter/dev_app/lib/pages/public/home/widget/index.dart

@ -0,0 +1,5 @@
export 'search.dart';
export 'menu_drawer.dart';
export 'my_favorite.dart';
export 'notification_bar.dart';
export 'quick_navigation.dart';

76
apps/flutter/dev_app/lib/pages/public/home/widget/menu_drawer.dart

@ -0,0 +1,76 @@
import 'package:components/widgets/menu/index.dart';
import 'package:core/models/common.dart';
import 'package:flutter/material.dart';
class MenuDrawer extends StatelessWidget {
const MenuDrawer({
super.key,
this.activedMenu,
this.menus = const [],
required this.onMenuRefresh,
this.onMenuExpanded,
});
final String? activedMenu;
final List<Menu> menus;
final void Function(Menu menu)? onMenuExpanded;
final Future<void> Function() onMenuRefresh;
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: onMenuRefresh,
child: Drawer(
width: 260,
child: Column(
children: [
_buildLogo(),
Expanded(
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children: [
Navigation(
activedMenu: activedMenu,
menus: menus,
onMenuExpanded: onMenuExpanded,
),
],
),
)
),
],
),
),
);
}
Widget _buildLogo() {
return Container(
height: 24,
margin: const EdgeInsets.all(10),
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 10),
child: Image.asset(
'res/images/logo.png',
height: 20,
width: 20,
),
),
const Padding(
padding: EdgeInsets.only(left: 10),
child: Text(
'abp flutter',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
),
],
),
);
}
}

42
apps/flutter/dev_app/lib/pages/public/home/widget/my_favorite.dart

@ -0,0 +1,42 @@
import 'package:components/widgets/empty/index.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:platforms/modes/menu.dto.dart';
class MyFavorite extends StatelessWidget {
const MyFavorite({
super.key,
required this.favoriteMenus,
required this.favoriteMenuBuilder,
});
final List<UserFavoriteMenuDto> favoriteMenus;
final Widget Function(UserFavoriteMenuDto favoriteMenu) favoriteMenuBuilder;
@override
Widget build(BuildContext context) {
return ExpansionTile(
initiallyExpanded: true,
title: Text('Label:MyFavorite'.tr,
style: Theme.of(context).textTheme.titleMedium,
),
children: [
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: favoriteMenus.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 5,
),
itemBuilder: (BuildContext context, int index) {
if (index >= favoriteMenus.length) {
return Empty.none;
}
return favoriteMenuBuilder(favoriteMenus[index]);
},
),
],
);
}
}

55
apps/flutter/dev_app/lib/pages/public/home/widget/notification_bar.dart

@ -0,0 +1,55 @@
import 'package:bruno/bruno.dart';
import 'package:components/widgets/empty/index.dart';
import 'package:flutter/material.dart';
import 'package:notifications/models/common.dart';
import 'package:notifications/models/notification.dart';
class NotificationBar extends StatelessWidget {
const NotificationBar({
super.key,
required this.notifications
});
final List<NotificationPaylod> notifications;
@override
Widget build(BuildContext context) {
if (notifications.isEmpty) {
return Empty.none;
}
return SizedBox(
height: 40,
child: SingleChildScrollView(
child: Column(
children: notifications.map<BrnNoticeBar>((payload) {
return BrnNoticeBar(
padding: const EdgeInsets.only(left: 5, right: 5, top: 3),
leftWidget: Image.asset(
'res/images/notification.png',
height: 30,
width: 30,
),
content: payload.title,
marquee: true,
noticeStyle: _mapNoticeStyles(payload.severity),
);
}).toList(),
),
),
);
}
NoticeStyle _mapNoticeStyles(NotificationSeverity? severity) {
if (severity == null) return NoticeStyles.normalNoticeWithArrow;
switch (severity) {
case NotificationSeverity.info:
case NotificationSeverity.success:
return NoticeStyles.succeedWithArrow;
case NotificationSeverity.fatal:
case NotificationSeverity.error:
return NoticeStyles.failWithArrow;
case NotificationSeverity.warn:
return NoticeStyles.warningWithArrow;
}
}
}

33
apps/flutter/dev_app/lib/pages/public/home/widget/quick_navigation.dart

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class QuickNavigation extends StatelessWidget {
const QuickNavigation({
super.key,
this.menus = const [],
});
final List<Widget> menus;
@override
Widget build(BuildContext context) {
return ExpansionTile(
initiallyExpanded: true,
title: Text('Label:QuickNavigation'.tr,
style: Theme.of(context).textTheme.titleMedium,
),
children: [
SizedBox(
height: 120,
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 4,
crossAxisSpacing: 5,
physics: const NeverScrollableScrollPhysics(),
children: menus,
),
),
],
);
}
}

23
apps/flutter/dev_app/lib/pages/public/home/widget/search.dart

@ -13,8 +13,7 @@ class SearchBarDelegate extends SearchDelegate<String> {
List<Widget>? buildActions(BuildContext context) {
Widget button = IconButton(
onPressed: () {
query = "";
showSuggestions(context);
close(context, "error");
},
icon: const Icon(Icons.clear),
);
@ -24,15 +23,17 @@ class SearchBarDelegate extends SearchDelegate<String> {
@override
Widget? buildLeading(BuildContext context) {
return IconButton(
onPressed: () {
close(context, "error");
},
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
);
// return IconButton(
// onPressed: () {
// query = "";
// showSuggestions(context);
// },
// icon: AnimatedIcon(
// icon: AnimatedIcons.menu_arrow,
// progress: transitionAnimation,
// ),
// );
return null;
}
@override

1
apps/flutter/dev_app/lib/pages/public/index.dart

@ -1,3 +1,4 @@
export './center/index.dart';
export './home/index.dart';
export './work/index.dart';
export './error/index.dart';

2
apps/flutter/dev_app/lib/pages/public/route.name.dart

@ -1,5 +1,7 @@
class PublicRoutes {
static String main = '/main';
static String home = '/home';
static String work = '/work';
static String center = '/center';
static String notFound = '/error/not_found';
}

6
apps/flutter/dev_app/lib/pages/public/route.public.dart

@ -4,7 +4,13 @@ import 'index.dart';
import 'route.name.dart';
class PublicRoute {
static GetPage notFound = GetPage(
name: PublicRoutes.notFound,
page: () => const PageNotFound(),
);
static List<GetPage> routes = [
notFound,
GetPage(
name: PublicRoutes.home,
page: () => const HomePage(),

17
apps/flutter/dev_app/lib/services/notification.send.local.service.dart

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:core/index.dart';
import 'package:get/get.dart';
import 'package:notifications/services/notification.state.service.dart';
import 'package:rxdart/rxdart.dart' hide Notification;
import 'package:core/models/notifications.dart';
@ -14,6 +15,22 @@ class FlutterLocalNotificationsSendService extends NotificationSendService {
final Subject<String?> _selectedNotifications$ = BehaviorSubject<String?>();
EnvironmentService get _environmentService => resolve<EnvironmentService>();
NotificationStateService get _notificationStateService => resolve<NotificationStateService>();
@override
void onInit() {
super.onInit();
_notificationStateService
.getNotifications$()
.listen((payload) async {
//
await send(
payload.title,
payload.body,
payload.payload,
);
});
}
Future<void> initAsync() async {
var environment = _environmentService.getEnvironment();

20
apps/flutter/dev_app/lib/services/translation.service.res.service.dart

@ -1,5 +1,6 @@
import 'dart:ui';
import 'package:core/services/environment.service.dart';
import 'package:core/services/localization.service.dart';
import 'package:core/services/service.base.dart';
import 'package:core/services/session.service.dart';
@ -17,6 +18,7 @@ class TranslationResService extends ServiceBase implements TranslationService {
final InternalStore<TranslationState> _store = InternalStore<TranslationState>(state: TranslationState());
SessionService get _sessionService => resolve<SessionService>();
EnvironmentService get _environmentService => resolve<EnvironmentService>();
LocalizationService get _localizationService => resolve<LocalizationService>();
@override
@ -44,13 +46,21 @@ class TranslationResService extends ServiceBase implements TranslationService {
Future<TranslationState> _mapTranslationsMap(String language) async {
Map<String, Map<String, String>> translationsMap = {};
var filePath = 'res/translations/$language.json';
var environment = _environmentService.getEnvironment();
var translationFiles = environment.localization.translationFiles?[language] ?? ['$language.json'];
for (var translationFile in translationFiles) {
try {
var filePath = 'res/translations/$translationFile';
var content = await rootBundle.loadString(filePath);
var translationsObject = jsonDecode(content) as Map<String, dynamic>;
translationsMap.putIfAbsent(
language,
() => translationsObject.map((key, value) => MapEntry(key, value))
);
var translations = translationsMap[language] ?? {};
translations.addAll(translationsObject.map((key, value) => MapEntry(key, value)));
translationsMap.putIfAbsent(language, () => translations);
} catch (e) {
logger.error(e);
}
}
return TranslationState(
language: language,
translations: translationsMap,

56
apps/flutter/dev_app/pubspec.lock

@ -40,6 +40,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.11.0"
bindings_compatible:
dependency: transitive
description:
name: bindings_compatible
sha256: "5dd5189f7512aff8ec180a8a11bd59230aa34a2d743e65e427192b7292a78d87"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
boolean_selector:
dependency: transitive
description:
@ -48,6 +56,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
bruno:
dependency: "direct main"
description:
name: bruno
sha256: "8bd461a658996000eab1111a93fb4826ade878103f5a9afa29a414046805448b"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.4.1"
build:
dependency: transitive
description:
@ -307,6 +323,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.5"
flutter_easyrefresh:
dependency: transitive
description:
name: flutter_easyrefresh
sha256: "5d161ee5dcac34da9065116568147d742dd25fb9bff3b10024d9054b195087ad"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.2"
flutter_highlight:
dependency: transitive
description:
@ -514,6 +538,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
lpinyin:
dependency: transitive
description:
name: lpinyin
sha256: "0bb843363f1f65170efd09fbdfc760c7ec34fc6354f9fcb2f89e74866a0d814a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.3"
markdown:
dependency: transitive
description:
@ -608,6 +640,22 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.3"
path_drawing:
dependency: transitive
description:
name: path_drawing
sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
path_provider:
dependency: transitive
description:
@ -664,6 +712,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.4.0"
photo_view:
dependency: transitive
description:
name: photo_view
sha256: "8036802a00bae2a78fc197af8a158e3e2f7b500561ed23b4c458107685e645bb"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.14.0"
platform:
dependency: transitive
description:

1
apps/flutter/dev_app/pubspec.yaml

@ -49,6 +49,7 @@ dependencies:
platforms:
path: '../platform'
bruno: ^3.3.0
dio: ^5.2.0+1
flutter_easyloading: ^3.0.5
flutter_picker: ^2.1.0

14
apps/flutter/dev_app/res/config/demo.json

@ -13,6 +13,20 @@
"localization": {
"useLocalResources": true,
"defaultLanguage": "zh-Hans",
"translationFiles": {
"zh-Hans": [
"zh-Hans.json"
],
"zh_CN": [
"zh-Hans.json"
],
"en": [
"en.json"
],
"en_US": [
"en.json"
]
},
"supportedLocales": [
{
"cultureName": "en",

BIN
apps/flutter/dev_app/res/images/no_data.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
apps/flutter/dev_app/res/images/notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
apps/flutter/dev_app/res/images/profile.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
apps/flutter/dev_app/res/images/setting.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

7
apps/flutter/dev_app/res/translations/en.json

@ -5,6 +5,7 @@
"404Message": "Page not found",
"404MessageDetail": "Sorry, there's nothing at this address.",
"500Message": "Internal Server Error",
"Avatar": "Avatar",
"Center:Feedback": "Feedback",
"Center:Help": "Help",
"Center:Info": "Info",
@ -12,6 +13,9 @@
"Center:Settings": "Settings",
"DisplayName:Abp.Localization.DefaultLanguage": "Default language",
"Description:Abp.Localization.DefaultLanguage": "The default language of the application.",
"DisplayName:UserName": "User Name",
"DisplayName:Email": "Email",
"DisplayName:PhoneNumber": "Phone Number",
"DefaultErrorMessage": "An error has occurred!",
"DefaultErrorMessageDetail": "Error detail not sent by server.",
"DefaultErrorMessage401": "You are not authenticated!",
@ -37,6 +41,7 @@
"Label:NotifierSettings": "Notifier Settings",
"Label:PasswordRequired": "Please enter password",
"Label:PhoneNumberNotBound": "PhoneNumber Not Bound",
"Label:SearchFeatures": "Search Features",
"Label:Submit": "Submit",
"Label:SwitchTheme": "Switch Theme",
"Label:SystemSettings": "System Settings",
@ -45,6 +50,8 @@
"Label:Cancel": "Cancel",
"Label:Confirm": "Confirm",
"Label:LoginToPortal": "Login To Portal",
"Label:QuickNavigation": "Quick navigation",
"Label:MyFavorite": "My favorites",
"Languages": "Languages",
"Page:Center": "Center",
"Page:Home": "Home",

9
apps/flutter/dev_app/res/translations/zh-Hans.json

@ -2,9 +2,10 @@
"401Message": "未授权",
"403Message": "禁止访问",
"403MessageDetail": "您没有权限执行此操作!",
"404Message": "页未找到",
"404Message": "页未找到",
"404MessageDetail": "抱歉, 这个地址是空的.",
"500Message": "内部服务器错误",
"Avatar": "头像",
"Center:Feedback": "意见反馈",
"Center:Help": "在线帮助",
"Center:Info": "关于",
@ -12,6 +13,9 @@
"Center:Settings": "设置",
"DisplayName:Abp.Localization.DefaultLanguage": "默认语言",
"Description:Abp.Localization.DefaultLanguage": "应用程序的默认语言.",
"DisplayName:UserName": "用户名",
"DisplayName:Email": "邮件地址",
"DisplayName:PhoneNumber": "手机号码",
"DefaultErrorMessage": "发生错误!",
"DefaultErrorMessageDetail": "服务器未发送错误的详细信息.",
"DefaultErrorMessage401": "未通过身份验证!",
@ -37,6 +41,7 @@
"Label:NotifierSettings": "通知设置",
"Label:PasswordRequired": "请输入密码",
"Label:PhoneNumberNotBound": "未绑定手机号",
"Label:SearchFeatures": "搜索功能",
"Label:Submit": "提交",
"Label:SwitchTheme": "切换主题",
"Label:SystemSettings": "系统设置",
@ -45,6 +50,8 @@
"Label:Cancel": "取消",
"Label:Confirm": "确认",
"Label:LoginToPortal": "登录到门户",
"Label:QuickNavigation": "快捷导航",
"Label:MyFavorite": "我的收藏",
"Languages": "语言",
"Page:Center": "个人中心",
"Page:Home": "首页",

1
apps/flutter/notifications/lib/models/notification.state.dart

@ -15,6 +15,7 @@ class NotificationState {
bool isEnabled;
List<NotificationGroup> groups;
NotificationGroup? findGroup(String name) {
return groups.firstWhereOrNull((item) => item.name == name);
}

11
apps/flutter/notifications/lib/services/notification.state.service.dart

@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:notifications/models/common.dart';
import 'package:rxdart/rxdart.dart' hide Notification;
import 'package:notifications/models/notification.dart';
import 'package:core/services/session.service.dart';
@ -20,6 +21,8 @@ class NotificationStateService extends ServiceBase {
NotificationService get _notificationService => resolve<NotificationService>();
SignalrService get _signalrService => resolve<SignalrService>(tag: NotificationTokens.producer);
final BehaviorSubject<NotificationPaylod> _notifications = BehaviorSubject<NotificationPaylod>();
final InternalStore<NotificationState> _store = InternalStore<NotificationState>(
state: _initState()
);
@ -41,6 +44,10 @@ class NotificationStateService extends ServiceBase {
return _store.sliceUpdate((state) => state);
}
Stream<NotificationPaylod> getNotifications$() {
return _notifications;
}
NotificationGroup? findGroup(String name) {
return _store.state.findGroup(name);
}
@ -75,6 +82,10 @@ class NotificationStateService extends ServiceBase {
return configState ?? NotificationState(isEnabled: true, groups: []);
}
void addNotification(NotificationInfo notification) {
_notifications.add(NotificationPaylod.fromNotification(notification));
}
Future<List<NotificationGroup>> getGroupAndCombineWithNotification(List<NotificationGroupDto> groupItems) {
return _notificationService.getMySubscribedListAsync()
.then((subscres) {

14
apps/flutter/platform/lib/services/favorite.menu.state.service.dart

@ -1,5 +1,6 @@
import 'package:core/services/environment.service.dart';
import 'package:core/services/service.base.dart';
import 'package:core/services/session.service.dart';
import 'package:core/utils/index.dart';
import 'package:platforms/modes/state.dart';
import 'package:platforms/modes/menu.dto.dart';
@ -10,18 +11,29 @@ class FavoriteMenuStateService extends ServiceBase {
final InternalStore<FavoriteMenuState> _state = InternalStore<FavoriteMenuState>(state: FavoriteMenuState());
SessionService get _sessionService => resolve<SessionService>();
EnvironmentService get _environmentService => resolve<EnvironmentService>();
FavoriteMenuService get _favoriteMenuService => resolve<FavoriteMenuService>();
@override
void onInit() {
super.onInit();
_initState();
}
void _initState() {
_sessionService.getToken$()
.listen((token) {
_state.patch((state) => state.menus = []);
if (token != null) {
refreshState();
}
});
}
Future<void> refreshState() async {
var environment = _environmentService.getEnvironment();
var framework = environment.application.framework ?? 'flutter';
var framework = environment.application.framework ?? 'abp-flutter';
var result = await _favoriteMenuService.getMyFavoriteMenuList(framework);
_state.patch((state) => state.menus = result.items);
}

14
apps/flutter/platform/lib/services/menu.state.service.dart

@ -1,5 +1,6 @@
import 'package:core/services/environment.service.dart';
import 'package:core/services/service.base.dart';
import 'package:core/services/session.service.dart';
import 'package:core/utils/index.dart';
import 'package:platforms/modes/state.dart';
import 'package:platforms/modes/menu.dto.dart';
@ -11,17 +12,28 @@ class MenuStateService extends ServiceBase {
final InternalStore<MenuState> _state = InternalStore<MenuState>(state: MenuState());
EnvironmentService get _environmentService => resolve<EnvironmentService>();
SessionService get _sessionService => resolve<SessionService>();
MenuService get _menuService => resolve<MenuService>();
@override
void onInit() {
super.onInit();
_initState();
}
void _initState() {
_sessionService.getToken$()
.listen((token) {
_state.patch((state) => state.menus = []);
if (token != null) {
refreshState();
}
});
}
Future<void> refreshState() async {
var environment = _environmentService.getEnvironment();
var framework = environment.application.framework ?? 'flutter';
var framework = environment.application.framework ?? 'abp-flutter';
var result = await _menuService.getCurrentUserMenuList(framework);
_state.patch((state) => state.menus = result.items);
}

Loading…
Cancel
Save