diff --git a/assets/images/svg/bug.svg b/assets/images/svg/bug.svg
new file mode 100644
index 0000000..a54a5e1
--- /dev/null
+++ b/assets/images/svg/bug.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/categories.svg b/assets/images/svg/categories.svg
new file mode 100644
index 0000000..dd55441
--- /dev/null
+++ b/assets/images/svg/categories.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/images/svg/contragents.svg b/assets/images/svg/contragents.svg
new file mode 100644
index 0000000..653fa83
--- /dev/null
+++ b/assets/images/svg/contragents.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/images/svg/global.svg b/assets/images/svg/global.svg
new file mode 100644
index 0000000..6ae3271
--- /dev/null
+++ b/assets/images/svg/global.svg
@@ -0,0 +1,25 @@
+
diff --git a/assets/images/svg/goods.svg b/assets/images/svg/goods.svg
new file mode 100644
index 0000000..16d55e1
--- /dev/null
+++ b/assets/images/svg/goods.svg
@@ -0,0 +1,16 @@
+
diff --git a/assets/images/svg/inventarization.svg b/assets/images/svg/inventarization.svg
new file mode 100644
index 0000000..91e79e7
--- /dev/null
+++ b/assets/images/svg/inventarization.svg
@@ -0,0 +1,10 @@
+
diff --git a/assets/images/svg/logout.svg b/assets/images/svg/logout.svg
new file mode 100644
index 0000000..ebbb408
--- /dev/null
+++ b/assets/images/svg/logout.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/options.svg b/assets/images/svg/options.svg
new file mode 100644
index 0000000..0074666
--- /dev/null
+++ b/assets/images/svg/options.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/images/svg/question.svg b/assets/images/svg/question.svg
new file mode 100644
index 0000000..10637cc
--- /dev/null
+++ b/assets/images/svg/question.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/settings.svg b/assets/images/svg/settings.svg
new file mode 100644
index 0000000..e6c00f4
--- /dev/null
+++ b/assets/images/svg/settings.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/routes/route_names.dart b/lib/routes/route_names.dart
index d47608a..377d7fd 100644
--- a/lib/routes/route_names.dart
+++ b/lib/routes/route_names.dart
@@ -13,4 +13,8 @@ const String settingPrinterBluetoothViewRoute = 'SettingPrinterBluetoothView';
const String categoryEditRoute = 'categoryEditRoute';
-const String categorySelectViewRoute = 'categorySelectViewRoute';
\ No newline at end of file
+const String categorySelectViewRoute = 'categorySelectViewRoute';
+
+const String goodsEditRoute = 'goodsEditRoute';
+const String goodsDictionaryViewRoute = 'goodsDictionaryViewRoute';
+
diff --git a/lib/routes/router.dart b/lib/routes/router.dart
index f4632b1..8664945 100644
--- a/lib/routes/router.dart
+++ b/lib/routes/router.dart
@@ -2,6 +2,7 @@
import 'package:satu/views/dictionaries/category/category_edit.dart';
import 'package:satu/views/dictionaries/category/category_select_view.dart';
import 'package:satu/views/dictionaries/category/category_view.dart';
+import 'package:satu/views/dictionaries/goods/goods_view.dart';
import 'package:satu/views/work/views/add_by_barcode/add_by_barcode_view.dart';
import 'package:satu/views/work/views/add_product/add_product_view.dart';
import 'package:satu/views/login/login_view.dart';
@@ -68,6 +69,11 @@ Route generateRoute(RouteSettings settings) {
routeName: settings.name!,
viewToShow: CategorySelectView(),
);
+ case goodsDictionaryViewRoute:
+ return _getPageRoute(
+ routeName: settings.name!,
+ viewToShow: GoodsDictionaryView(),
+ );
// case ImageShowRoute:
// ImageShowModel data = settings.arguments as ImageShowModel;
diff --git a/lib/views/dictionaries/category/category_select_view.dart b/lib/views/dictionaries/category/category_select_view.dart
index 04bd12a..8109b9b 100644
--- a/lib/views/dictionaries/category/category_select_view.dart
+++ b/lib/views/dictionaries/category/category_select_view.dart
@@ -91,12 +91,23 @@ class _CategorySelectViewState extends State {
itemBuilder: (BuildContext context, int index) {
final CategoryRowDao category = items[index];
return DictionaryTile(
- title: category.name,
- subTitle: category.parentName.isEmpty
- ? 'Корневая категория'
- : 'Родитель: ${category.parentName}',
key: Key('category_${category.id}'),
onPress: () => handlerCategory(category.id!),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ category.name,
+ style: const TextStyle(fontSize: 12, color: textColor),
+ ),
+ Text(
+ category.parentName.isEmpty
+ ? 'Корневая категория'
+ : 'Родитель: ${category.parentName}',
+ style: const TextStyle(
+ fontSize: 10, color: placeholderColor)),
+ ],
+ ),
);
},
separatorBuilder: (BuildContext context, int index) {
diff --git a/lib/views/dictionaries/category/category_view.dart b/lib/views/dictionaries/category/category_view.dart
index 5d7f77c..57e497a 100644
--- a/lib/views/dictionaries/category/category_view.dart
+++ b/lib/views/dictionaries/category/category_view.dart
@@ -70,6 +70,7 @@ class _CategoryDictionaryViewState extends State {
controller: _searchTextController,
fieldFocusNode: _searchFocusNode,
),
+ const ProductsTitleBarBar(title: 'Список категории'),
Expanded(
child: ListView.separated(
physics: const BouncingScrollPhysics(),
@@ -77,13 +78,24 @@ class _CategoryDictionaryViewState extends State {
itemBuilder: (BuildContext context, int index) {
final CategoryRowDao category = items[index];
return DictionaryTile(
- title: category.name,
- subTitle: category.parentName.isEmpty
- ? 'Корневая категория'
- : 'Родитель: ${category.parentName}',
key: Key('category_${category.id}'),
onPress: () => _navigatorService.push(categoryEditRoute,
arguments: category),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ category.name,
+ style: const TextStyle(fontSize: 12, color: textColor),
+ ),
+ Text(
+ category.parentName.isEmpty
+ ? 'Корневая категория'
+ : 'Родитель: ${category.parentName}',
+ style: const TextStyle(
+ fontSize: 10, color: placeholderColor)),
+ ],
+ ),
);
},
separatorBuilder: (BuildContext context, int index) {
@@ -139,7 +151,7 @@ class _CategoryDictionaryViewState extends State {
}
class CategoryRowDao {
- CategoryRowDao(this.name, this.parentName, this.id, { this.parentId = 0});
+ CategoryRowDao(this.name, this.parentName, this.id, {this.parentId = 0});
final String name;
final String parentName;
diff --git a/lib/views/dictionaries/component/dictionary_list_tile.dart b/lib/views/dictionaries/component/dictionary_list_tile.dart
index 81eb705..7da16c5 100644
--- a/lib/views/dictionaries/component/dictionary_list_tile.dart
+++ b/lib/views/dictionaries/component/dictionary_list_tile.dart
@@ -3,13 +3,13 @@ import 'package:satu/shared/app_colors.dart';
class DictionaryTile extends StatelessWidget {
const DictionaryTile({
- required this.title,
+ required this.child,
Key? key,
this.subTitle,
this.onPress,
}) : super(key: key);
- final String title;
+ final Widget child;
final String? subTitle;
final Function()? onPress;
@@ -24,19 +24,7 @@ class DictionaryTile extends StatelessWidget {
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- title,
- style: const TextStyle(fontSize: 12, color: textColor),
- ),
- if (subTitle != null && subTitle!.isNotEmpty)
- Text(subTitle!,
- style: const TextStyle(
- fontSize: 10, color: placeholderColor)),
- ],
- ),
+ child: child,
),
),
),
diff --git a/lib/views/dictionaries/goods/goods_view.dart b/lib/views/dictionaries/goods/goods_view.dart
new file mode 100644
index 0000000..18428d5
--- /dev/null
+++ b/lib/views/dictionaries/goods/goods_view.dart
@@ -0,0 +1,168 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:satu/core/entity/category_entity.dart';
+import 'package:satu/core/entity/goods_entity.dart';
+import 'package:satu/core/redux/actions/sell_actions.dart';
+import 'package:satu/core/services/dictionary_service.dart';
+import 'package:satu/core/services/navigator_service.dart';
+import 'package:satu/core/utils/locator.dart';
+import 'package:satu/routes/route_names.dart';
+import 'package:satu/shared/app_colors.dart';
+import 'package:satu/views/dictionaries/component/dictionary_list_tile.dart';
+import 'package:satu/widgets/bar/products_app_bar.dart';
+import 'package:satu/widgets/bar/products_title_bar.dart';
+import 'package:satu/widgets/fields/input_field.dart';
+import 'package:satu/widgets/ui/product_title_widget.dart';
+
+class GoodsDictionaryView extends StatefulWidget {
+ @override
+ _GoodsDictionaryViewState createState() => _GoodsDictionaryViewState();
+}
+
+class _GoodsDictionaryViewState extends State {
+ final DictionaryService _dictionaryService = locator();
+ final NavigatorService _navigatorService = locator();
+ late TextEditingController _searchTextController;
+ final FocusNode _searchFocusNode = FocusNode();
+
+ late List _goods = [];
+ late List _categories = [];
+ late List items = [];
+
+ @override
+ void initState() {
+ _searchTextController = TextEditingController();
+ _searchTextController.addListener(() {
+ if (_searchTextController.text.isNotEmpty) {
+ searchByField(_searchTextController.text);
+ } else {
+ reset();
+ }
+ });
+ initQuery();
+ super.initState();
+ }
+
+ Future initQuery() async {
+ _goods = await _dictionaryService.getGoodsByNameOrEan('');
+ _categories = await _dictionaryService.getCategoriesAll();
+ searchByField('');
+ }
+
+ @override
+ void dispose() {
+ _searchTextController.dispose();
+ _searchFocusNode.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: ProductsAppBar(
+ title: 'Товары',
+ drawerShow: true,
+ actions: [
+ ClipRRect(
+ borderRadius: BorderRadius.circular(90),
+ child: Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: (){},
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: SvgPicture.asset(
+ 'assets/images/svg/options.svg',
+ height: 20,
+ width: 20,
+ color: textColor,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ body: Column(
+ children: [
+ InputField(
+ placeholder: 'Поиск по наименованию товара или штрих-код',
+ search: true,
+ controller: _searchTextController,
+ fieldFocusNode: _searchFocusNode,
+ ),
+ const ProductsTitleBarBar(title: 'Список товаров'),
+ Expanded(
+ child: ListView.separated(
+ physics: const BouncingScrollPhysics(),
+ itemCount: items.length,
+ itemBuilder: (BuildContext context, int index) {
+ final GoodRowDao good = items[index];
+ return DictionaryTile(
+ child: ProductTitleWidget(
+ name: good.name,
+ categoryName: good.category,
+ ean: good.ean,
+ ),
+ );
+ },
+ separatorBuilder: (BuildContext context, int index) {
+ return const Divider(
+ height: 1.0,
+ color: disableColor,
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+ floatingActionButton: FloatingActionButton(
+ elevation: 2,
+ onPressed: () => locator().push(categoryEditRoute),
+ child: const Icon(
+ Icons.add_rounded,
+ size: 34.0,
+ color: whiteColor,
+ ),
+ ),
+ );
+ }
+
+ void reset() {
+ _searchTextController.clear();
+ searchByField('');
+ }
+
+ Future searchByField(String query) async {
+ log.i(query);
+ final List list = [];
+ final Iterable filtered = query == ''
+ ? _goods
+ : _goods.where((element) =>
+ element.name.toLowerCase().contains(query.toLowerCase()) ||
+ (element.ean != null &&
+ element.ean!.contains(query.toLowerCase())));
+ filtered.forEach((element) {
+ final Category category = _categories
+ .firstWhere((parent) => parent.id == element.categoryId, orElse: () {
+ return Category();
+ });
+ final String parentName = category.name;
+ final GoodRowDao rowDao = GoodRowDao(element.name, parentName,
+ ean: element.ean, id: element.id);
+ list.add(rowDao);
+ });
+ setState(() {
+ items = list;
+ });
+ }
+}
+
+class GoodRowDao {
+ GoodRowDao(this.name, this.category, {this.ean, this.id});
+
+ final String name;
+ final String category;
+ final String? ean;
+ final int? id;
+}
diff --git a/lib/views/login/login_view.dart b/lib/views/login/login_view.dart
index 3c4f719..6d6852c 100644
--- a/lib/views/login/login_view.dart
+++ b/lib/views/login/login_view.dart
@@ -57,44 +57,47 @@ class _LoginViewState extends State {
converter: (store) => store.state.userState!,
builder: (context, vm) {
return Scaffold(
- body: Column(
- mainAxisSize: MainAxisSize.max,
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- LogoSatu(),
- InputField(
- placeholder: 'Введите почту',
- controller: emailController,
- textInputType: TextInputType.emailAddress,
- nextFocusNode: passwordNode,
- ),
- verticalSpaceSmall,
- InputField(
- placeholder: 'Введите пароль',
- password: true,
- controller: passwordController,
- fieldFocusNode: passwordNode,
- enterPressed: _pressBtnEnter,
- textInputAction: TextInputAction.done,
- ),
- verticalSpaceMedium,
- Padding(
- padding: EdgeInsets.only( left: 45.sp, right: 45.sp, top: 30.sp ),
- child: BusyButton(
- title: 'ВОЙТИ',
- busy: vm.isLoading!,
- onPressed: _pressBtnEnter,
+ body: SingleChildScrollView(
+ physics: BouncingScrollPhysics(),
+ child: Column(
+ mainAxisSize: MainAxisSize.max,
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ LogoSatu(),
+ InputField(
+ placeholder: 'Введите почту',
+ controller: emailController,
+ textInputType: TextInputType.emailAddress,
+ nextFocusNode: passwordNode,
),
- ),
- verticalSpaceLarge,
- IconButton(
- icon: Icon(MdiIcons.qrcodeScan),
- iconSize: ScreenUtil().setSp(40.0),
- tooltip: "Scan",
- onPressed: scan,
- )
- ],
+ verticalSpaceSmall,
+ InputField(
+ placeholder: 'Введите пароль',
+ password: true,
+ controller: passwordController,
+ fieldFocusNode: passwordNode,
+ enterPressed: _pressBtnEnter,
+ textInputAction: TextInputAction.done,
+ ),
+ verticalSpaceMedium,
+ Padding(
+ padding: EdgeInsets.only( left: 45.sp, right: 45.sp, top: 30.sp ),
+ child: BusyButton(
+ title: 'ВОЙТИ',
+ busy: vm.isLoading!,
+ onPressed: _pressBtnEnter,
+ ),
+ ),
+ verticalSpaceLarge,
+ IconButton(
+ icon: Icon(MdiIcons.qrcodeScan),
+ iconSize: ScreenUtil().setSp(40.0),
+ tooltip: "Scan",
+ onPressed: scan,
+ )
+ ],
+ ),
));
});
}
diff --git a/lib/views/main/main_view.dart b/lib/views/main/main_view.dart
index a3f46bb..3866dce 100644
--- a/lib/views/main/main_view.dart
+++ b/lib/views/main/main_view.dart
@@ -5,6 +5,7 @@ import 'package:satu/core/redux/store.dart';
import 'package:satu/core/services/navigator_service.dart';
import 'package:satu/core/utils/locator.dart';
import 'package:satu/views/dictionaries/category/category_view.dart';
+import 'package:satu/views/dictionaries/goods/goods_view.dart';
import 'package:satu/views/settings/printer_bluetooth/PrinterSelect.dart';
import 'package:satu/views/settings/setting_view.dart';
import 'package:satu/views/work/work_view.dart';
@@ -22,6 +23,7 @@ class _MainViewState extends State {
final _workView = const WorkView();
final _settingsView = SettingsView();
final _categoryDictView = CategoryDictionaryView();
+ final _goodDictView = GoodsDictionaryView();
Widget _body(Type viewClass) {
if(viewClass == WorkView) {
@@ -33,6 +35,9 @@ class _MainViewState extends State {
if(viewClass == CategoryDictionaryView) {
return _categoryDictView;
}
+ if(viewClass == GoodsDictionaryView) {
+ return _goodDictView;
+ }
return _workView;
}
diff --git a/lib/widgets/drawer/app_drawer.dart b/lib/widgets/drawer/app_drawer.dart
index 58b9673..25dad1b 100644
--- a/lib/widgets/drawer/app_drawer.dart
+++ b/lib/widgets/drawer/app_drawer.dart
@@ -1,6 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_svg/svg.dart';
import 'package:satu/core/redux/actions/nav_actions.dart';
import 'package:satu/core/redux/actions/user_actions.dart';
import 'package:satu/core/redux/store.dart';
@@ -8,7 +9,7 @@ import 'package:satu/core/redux/store.dart';
import 'package:satu/shared/app_colors.dart';
import 'package:satu/shared/ui_helpers.dart';
import 'package:satu/views/dictionaries/category/category_view.dart';
-import 'package:satu/views/settings/setting_view.dart';
+import 'package:satu/views/dictionaries/goods/goods_view.dart';
import 'package:satu/views/work/work_view.dart';
class AppDrawer extends StatelessWidget {
@@ -23,31 +24,38 @@ class AppDrawer extends StatelessWidget {
_createHeader(),
_createDrawerSectionTitle(text: 'ОСНОВНОЙ РАЗДЕЛ'),
_createDrawerItem(
- icon: Icons.campaign_sharp,
+ svgFile: 'sell',
text: 'Касса',
onTap: () {
Navigator.of(context).pop();
Redux.store!.dispatch(navigateDrawer(WorkView));
}),
- _createDrawerItem(icon: Icons.check, text: 'Инвентаризация'),
+ _createDrawerItem(
+ svgFile: 'inventarization', text: 'Инвентаризация'),
_createDrawerSectionTitle(text: 'СПРАВОЧНИКИ'),
_createDrawerItem(
- icon: Icons.list,
+ svgFile: 'categories',
text: 'Категории',
onTap: () {
Navigator.of(context).pop();
Redux.store!.dispatch(navigateDrawer(CategoryDictionaryView));
}),
_createDrawerItem(
- icon: Icons.production_quantity_limits, text: 'Товары'),
- _createDrawerItem(icon: Icons.people, text: 'Контрагенты'),
+ svgFile: 'goods',
+ text: 'Товары',
+ onTap: () {
+ Navigator.of(context).pop();
+ Redux.store!.dispatch(navigateDrawer(GoodsDictionaryView));
+ }),
+ _createDrawerItem(svgFile: 'contragents', text: 'Контрагенты'),
_createDrawerSectionTitle(text: 'ИНФОРМАЦИЯ'),
- _createDrawerItem(icon: Icons.question_answer, text: 'Справочник'),
+ _createDrawerItem(svgFile: 'question', text: 'Справочник'),
_createDrawerSectionTitle(text: 'ПРОЧЕЕ'),
- _createDrawerItem(icon: Icons.settings, text: 'Настройки'),
- _createDrawerItem(icon: Icons.next_plan, text: 'Перейти на сайт'),
+ _createDrawerItem(svgFile: 'settings', text: 'Настройки'),
+ _createDrawerItem(svgFile: 'global', text: 'Перейти на сайт'),
+ _createDrawerItem(svgFile: 'bug', text: 'Сообщить об ошибке'),
_createDrawerItem(
- icon: Icons.exit_to_app,
+ svgFile: 'logout',
text: 'Выйти из аккаунта',
isDanger: true,
onTap: () async {
@@ -102,8 +110,9 @@ class AppDrawer extends StatelessWidget {
}
Widget _createDrawerItem(
- {required IconData icon,
- required String text,
+ {required String text,
+ IconData? icon,
+ String? svgFile,
GestureTapCallback? onTap,
bool isDanger = false}) {
return Container(
@@ -117,13 +126,21 @@ class AppDrawer extends StatelessWidget {
const EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0),
child: Row(
children: [
- Icon(
- icon,
- size: 20.0,
- color: isDanger ? dangerColor : textColor,
- ),
+ if (svgFile != null)
+ SvgPicture.asset(
+ 'assets/images/svg/$svgFile.svg',
+ height: 20,
+ width: 20,
+ color: isDanger ? dangerColor : textColor,
+ ),
+ if (icon != null)
+ Icon(
+ icon,
+ size: 20.0,
+ color: isDanger ? dangerColor : textColor,
+ ),
Padding(
- padding: EdgeInsets.only(left: 8.0),
+ padding: const EdgeInsets.only(left: 8.0),
child: Text(
text,
style: TextStyle(