diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index a8e2d3a..0657f0e 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -28,6 +28,7 @@ class MessageLookup extends MessageLookupByLibrary { "byte": MessageLookupByLibrary.simpleMessage("byte"), "cancel": MessageLookupByLibrary.simpleMessage("Cancel"), "clearBacklog": MessageLookupByLibrary.simpleMessage("Clear Backlog"), + "codeQuery": MessageLookupByLibrary.simpleMessage("code query"), "confirm": MessageLookupByLibrary.simpleMessage("Confirm"), "confirmClearBacklog": MessageLookupByLibrary.simpleMessage("ConfirmClear?"), "confirmDeleteQuestion": MessageLookupByLibrary.simpleMessage("ConfirmDelete?"), @@ -40,6 +41,7 @@ class MessageLookup extends MessageLookupByLibrary { "deleteTopic": MessageLookupByLibrary.simpleMessage("Delete Topic"), "detail": MessageLookupByLibrary.simpleMessage("detail"), "email": MessageLookupByLibrary.simpleMessage("email"), + "execute": MessageLookupByLibrary.simpleMessage("execute"), "isLeader": MessageLookupByLibrary.simpleMessage("Is Leader"), "languageSettings": MessageLookupByLibrary.simpleMessage("Language Settings"), "name": MessageLookupByLibrary.simpleMessage("name"), diff --git a/lib/generated/intl/messages_zh.dart b/lib/generated/intl/messages_zh.dart index e9bdad7..99e1fdc 100644 --- a/lib/generated/intl/messages_zh.dart +++ b/lib/generated/intl/messages_zh.dart @@ -28,6 +28,7 @@ class MessageLookup extends MessageLookupByLibrary { "byte": MessageLookupByLibrary.simpleMessage("比特"), "cancel": MessageLookupByLibrary.simpleMessage("取消"), "clearBacklog": MessageLookupByLibrary.simpleMessage("清理积压"), + "codeQuery": MessageLookupByLibrary.simpleMessage("code 查询"), "confirm": MessageLookupByLibrary.simpleMessage("确认"), "confirmClearBacklog": MessageLookupByLibrary.simpleMessage("确认清理积压吗?"), "confirmDeleteQuestion": MessageLookupByLibrary.simpleMessage("确认删除吗?"), @@ -40,6 +41,7 @@ class MessageLookup extends MessageLookupByLibrary { "deleteTopic": MessageLookupByLibrary.simpleMessage("删除 Topic"), "detail": MessageLookupByLibrary.simpleMessage("详细信息"), "email": MessageLookupByLibrary.simpleMessage("邮箱"), + "execute": MessageLookupByLibrary.simpleMessage("执行"), "isLeader": MessageLookupByLibrary.simpleMessage("是否是主节点"), "languageSettings": MessageLookupByLibrary.simpleMessage("语言设置"), "name": MessageLookupByLibrary.simpleMessage("名称"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 865b143..03a9764 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -118,6 +118,16 @@ class S { ); } + /// `code query` + String get codeQuery { + return Intl.message( + 'code query', + name: 'codeQuery', + desc: '', + args: [], + ); + } + /// `Confirm` String get confirm { return Intl.message( @@ -238,6 +248,16 @@ class S { ); } + /// `execute` + String get execute { + return Intl.message( + 'execute', + name: 'execute', + desc: '', + args: [], + ); + } + /// `Is Leader` String get isLeader { return Intl.message( diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index ceb17d5..41a62fc 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -7,6 +7,7 @@ "byte": "byte", "cancel": "Cancel", "clearBacklog": "Clear Backlog", + "codeQuery": "code query", "confirm": "Confirm", "confirmClearBacklog": "ConfirmClear?", "confirmDeleteQuestion": "ConfirmDelete?", @@ -19,6 +20,7 @@ "deleteTopic": "Delete Topic", "detail": "detail", "email": "email", + "execute": "execute", "isLeader": "Is Leader", "languageSettings": "Language Settings", "name": "name", diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 2cbd11b..20eac49 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -7,6 +7,7 @@ "byte": "比特", "cancel": "取消", "clearBacklog": "清理积压", + "codeQuery": "code 查询", "confirm": "确认", "confirmClearBacklog": "确认清理积压吗?", "confirmDeleteQuestion": "确认删除吗?", @@ -19,6 +20,7 @@ "deleteTopic": "删除 Topic", "detail": "详细信息", "email": "邮箱", + "execute": "执行", "isLeader": "是否是主节点", "languageSettings": "语言设置", "name": "名称", diff --git a/lib/main.dart b/lib/main.dart index 382883e..5db2ab8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:paas_dashboard_flutter/generated/l10n.dart'; import 'package:paas_dashboard_flutter/route/page_route_const.dart'; import 'package:paas_dashboard_flutter/route/route_gen.dart'; import 'package:paas_dashboard_flutter/ui/bk/bk_page.dart'; +import 'package:paas_dashboard_flutter/ui/code/code_list_page.dart'; import 'package:paas_dashboard_flutter/ui/general/author_screen.dart'; import 'package:paas_dashboard_flutter/ui/general/settings_screen.dart'; import 'package:paas_dashboard_flutter/ui/home/home_page.dart'; @@ -13,6 +14,8 @@ import 'package:paas_dashboard_flutter/ui/mysql/mysql_page.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/pulsar_page.dart'; import 'package:paas_dashboard_flutter/ui/sql/sql_list_page.dart'; import 'package:paas_dashboard_flutter/vm/bk/bk_instance_list_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/code/code_list_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/code/code_view_model.dart'; import 'package:paas_dashboard_flutter/vm/general/settings_view_model.dart'; import 'package:paas_dashboard_flutter/vm/kubernetes/k8s_instance_list_view_model.dart'; import 'package:paas_dashboard_flutter/vm/mongo/mongo_database_view_model.dart'; @@ -68,6 +71,10 @@ class MyApp extends StatelessWidget { create: (context) => BkInstanceListViewModel(), child: BkPage(), ), + PageRouteConst.Code: (context) => ChangeNotifierProvider( + create: (context) => CodeListViewModel(), + child: CodeListPage(), + ), PageRouteConst.Kubernetes: (context) => ChangeNotifierProvider( create: (context) => K8sInstanceListViewModel(), child: K8sPage(), @@ -94,6 +101,10 @@ class MyApp extends StatelessWidget { ), }, onGenerateRoute: (settings) { + if (settings.name == PageRouteConst.CodeExecute) { + final args = settings.arguments as CodeViewModel; + return RouteGen.codeExecute(args); + } if (settings.name == PageRouteConst.MongoInstance) { final args = settings.arguments as MongoInstanceViewModel; return RouteGen.mongoInstance(args); diff --git a/lib/persistent/persistent.dart b/lib/persistent/persistent.dart index 63cd2b4..a9ea3e9 100644 --- a/lib/persistent/persistent.dart +++ b/lib/persistent/persistent.dart @@ -4,6 +4,7 @@ import 'package:paas_dashboard_flutter/persistent/persistent_api.dart'; import 'package:paas_dashboard_flutter/persistent/persistent_db.dart'; import 'package:paas_dashboard_flutter/persistent/persistent_memory.dart'; import 'package:paas_dashboard_flutter/persistent/po/bk_instance_po.dart'; +import 'package:paas_dashboard_flutter/persistent/po/code_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/k8s_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/mongo_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/mysql_instance_po.dart'; @@ -121,6 +122,18 @@ class Persistent { return (await getApi()).sqlList(); } + static Future saveCode(String name, String sql) async { + return (await getApi()).saveCode(name, sql); + } + + static Future deleteCode(int id) async { + return (await getApi()).deleteCode(id); + } + + static Future> codeList() async { + return (await getApi()).codeList(); + } + static bool supportDb() { return !kIsWeb; } diff --git a/lib/persistent/persistent_api.dart b/lib/persistent/persistent_api.dart index fb6bac0..f5d4daf 100644 --- a/lib/persistent/persistent_api.dart +++ b/lib/persistent/persistent_api.dart @@ -1,5 +1,6 @@ import 'package:paas_dashboard_flutter/module/ssh/ssh_step.dart'; import 'package:paas_dashboard_flutter/persistent/po/bk_instance_po.dart'; +import 'package:paas_dashboard_flutter/persistent/po/code_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/k8s_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/mongo_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/mysql_instance_po.dart'; @@ -63,4 +64,12 @@ abstract class PersistentApi { Future> sqlList(); Future sqlInstance(String name); + + Future saveCode(String name, String code); + + Future deleteCode(int id); + + Future> codeList(); + + Future codeInstance(String name); } diff --git a/lib/persistent/persistent_db.dart b/lib/persistent/persistent_db.dart index 5590dd6..af826a0 100644 --- a/lib/persistent/persistent_db.dart +++ b/lib/persistent/persistent_db.dart @@ -9,6 +9,7 @@ import 'package:paas_dashboard_flutter/module/ssh/ssh_step.dart'; import 'package:paas_dashboard_flutter/module/zk/const.dart'; import 'package:paas_dashboard_flutter/persistent/persistent_api.dart'; import 'package:paas_dashboard_flutter/persistent/po/bk_instance_po.dart'; +import 'package:paas_dashboard_flutter/persistent/po/code_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/k8s_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/mongo_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/mysql_instance_po.dart'; @@ -98,6 +99,9 @@ class PersistentDb implements PersistentApi { await db.execute( 'CREATE TABLE sql_list(id INTEGER PRIMARY KEY, name TEXT, sql TEXT)', ); + await db.execute( + 'CREATE TABLE code_list(id INTEGER PRIMARY KEY, name TEXT, code TEXT)', + ); } @override @@ -337,4 +341,38 @@ class PersistentDb implements PersistentApi { var current = maps[0]; return SqlPo(current['id'], current['name'], current['sql']); } + + @override + Future> codeList() async { + var aux = await getInstance(); + final List> maps = await aux.database.query('code_list'); + return List.generate(maps.length, (i) { + var aux = maps[i]; + return CodePo(aux['id'], aux['name'], aux['code']); + }); + } + + @override + Future deleteCode(int id) async { + var aux = await getInstance(); + aux.database.delete('code_list', where: 'id = ?', whereArgs: [id]); + } + + @override + Future saveCode(String name, String code) async { + var aux = await getInstance(); + var list = [name, code]; + aux.database.execute('INSERT INTO code_list(name, code) VALUES (?, ?)', list); + } + + @override + Future codeInstance(String name) async { + var aux = await getInstance(); + final List> maps = await aux.database.query('code_list', where: "name = ?", whereArgs: [name]); + if (maps.length == 0) { + return null; + } + var current = maps[0]; + return CodePo(current['id'], current['name'], current['code']); + } } diff --git a/lib/persistent/persistent_memory.dart b/lib/persistent/persistent_memory.dart index 7ea48d4..2b74b3b 100644 --- a/lib/persistent/persistent_memory.dart +++ b/lib/persistent/persistent_memory.dart @@ -6,6 +6,7 @@ import 'package:paas_dashboard_flutter/module/ssh/ssh_step.dart'; import 'package:paas_dashboard_flutter/module/zk/const.dart'; import 'package:paas_dashboard_flutter/persistent/persistent_api.dart'; import 'package:paas_dashboard_flutter/persistent/po/bk_instance_po.dart'; +import 'package:paas_dashboard_flutter/persistent/po/code_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/k8s_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/mongo_instance_po.dart'; import 'package:paas_dashboard_flutter/persistent/po/mysql_instance_po.dart'; @@ -193,4 +194,28 @@ class PersistentMemory implements PersistentApi { // TODO: implement sqlInstance throw UnimplementedError(); } + + @override + Future> codeList() { + // TODO: implement codeList + throw UnimplementedError(); + } + + @override + Future deleteCode(int id) { + // TODO: implement deleteCode + throw UnimplementedError(); + } + + @override + Future saveCode(String name, String code) { + // TODO: implement saveCode + throw UnimplementedError(); + } + + @override + Future codeInstance(String name) { + // TODO: implement codeInstance + throw UnimplementedError(); + } } diff --git a/lib/persistent/po/code_instance_po.dart b/lib/persistent/po/code_instance_po.dart new file mode 100644 index 0000000..9fcfead --- /dev/null +++ b/lib/persistent/po/code_instance_po.dart @@ -0,0 +1,19 @@ +class CodePo { + final int id; + final String name; + final String code; + + CodePo(this.id, this.name, this.code); + + CodePo deepCopy() { + return new CodePo(id, name, code); + } + + Map toMap() { + return { + 'id': id, + 'name': name, + 'code': code, + }; + } +} diff --git a/lib/route/page_route_const.dart b/lib/route/page_route_const.dart index 4931672..5e386ec 100644 --- a/lib/route/page_route_const.dart +++ b/lib/route/page_route_const.dart @@ -2,6 +2,8 @@ class PageRouteConst { static const String Root = '/'; static const String Author = '/author'; static const String Bookkeeper = '/bookkeeper'; + static const String Code = '/code'; + static const String CodeExecute = '/code/execute'; static const String Kubernetes = '/kubernetes'; static const String Mongo = '/mongo'; static const String MongoInstance = '/mongo/instance'; diff --git a/lib/route/route_gen.dart b/lib/route/route_gen.dart index eb0e412..0d3623a 100644 --- a/lib/route/route_gen.dart +++ b/lib/route/route_gen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/ui/code/screen/code_execute_screen.dart'; import 'package:paas_dashboard_flutter/ui/mongo/mongo_instance.dart'; import 'package:paas_dashboard_flutter/ui/mongo/screen/mongo_database.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/pulsar_instance.dart'; @@ -9,6 +10,7 @@ import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_source.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_tenant.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_topic.dart'; import 'package:paas_dashboard_flutter/ui/sql/screen/sql_execute_screen.dart'; +import 'package:paas_dashboard_flutter/vm/code/code_view_model.dart'; import 'package:paas_dashboard_flutter/vm/mongo/mongo_database_view_model.dart'; import 'package:paas_dashboard_flutter/vm/mongo/mongo_instance_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_instance_view_model.dart'; @@ -22,6 +24,15 @@ import 'package:paas_dashboard_flutter/vm/sql/sql_view_model.dart'; import 'package:provider/provider.dart'; class RouteGen { + static Route codeExecute(CodeViewModel viewModel) { + // deep copy view model + return MaterialPageRoute( + builder: (context) => ChangeNotifierProvider( + create: (context) => viewModel, + child: CodeExecuteScreen(), + )); + } + static Route mongoInstance(MongoInstanceViewModel viewModel) { // deep copy view model return MaterialPageRoute( diff --git a/lib/ui/code/code_list_page.dart b/lib/ui/code/code_list_page.dart new file mode 100644 index 0000000..859d834 --- /dev/null +++ b/lib/ui/code/code_list_page.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/generated/l10n.dart'; +import 'package:paas_dashboard_flutter/route/page_route_const.dart'; +import 'package:paas_dashboard_flutter/ui/util/data_cell_util.dart'; +import 'package:paas_dashboard_flutter/ui/util/form_util.dart'; +import 'package:paas_dashboard_flutter/vm/code/code_list_view_model.dart'; +import 'package:provider/provider.dart'; + +class CodeListPage extends StatefulWidget { + @override + State createState() { + return new _CodeListPageState(); + } +} + +class _CodeListPageState extends State { + @override + void initState() { + super.initState(); + Provider.of(context, listen: false).fetchCodeList(); + } + + @override + Widget build(BuildContext context) { + final vm = Provider.of(context); + var formButton = createInstanceButton(context); + var refreshButton = TextButton( + onPressed: () { + setState(() { + vm.fetchCodeList(); + }); + }, + child: Text(S.of(context).refresh)); + var body = ListView( + children: [ + Container( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: [formButton, refreshButton], + ), + ), + Center( + child: Text('Code List'), + ), + SingleChildScrollView( + child: DataTable( + showCheckboxColumn: false, + columns: [ + DataColumn(label: Text('Id')), + DataColumn(label: Text('Name')), + DataColumn(label: Text('Code')), + DataColumn(label: Text('Delete code')), + ], + rows: vm.instances + .map((itemRow) => DataRow( + onSelectChanged: (bool? selected) { + Navigator.pushNamed(context, PageRouteConst.CodeExecute, arguments: itemRow.deepCopy()); + }, + cells: [ + DataCell(Text(itemRow.id.toString())), + DataCell(Text(itemRow.name)), + DataCell(Text(itemRow.code)), + DataCellUtil.newDellDataCell(() { + vm.deleteCode(itemRow.id); + }), + ])) + .toList(), + ), + ), + ], + ); + return Scaffold( + appBar: AppBar( + title: Text('Code Dashboard'), + ), + body: body); + } + + ButtonStyleButton createInstanceButton(BuildContext context) { + final vm = Provider.of(context, listen: false); + var list = [ + FormFieldDef('Code Name'), + FormFieldDef('Code'), + ]; + return FormUtil.createButton2("Code", list, context, (name, code) { + vm.createCode(name, code); + }); + } +} diff --git a/lib/ui/code/screen/code_execute_screen.dart b/lib/ui/code/screen/code_execute_screen.dart new file mode 100644 index 0000000..84977b6 --- /dev/null +++ b/lib/ui/code/screen/code_execute_screen.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/ui/code/widget/code_execute_widget.dart'; +import 'package:paas_dashboard_flutter/vm/code/code_view_model.dart'; +import 'package:provider/provider.dart'; + +class CodeExecuteScreen extends StatefulWidget { + CodeExecuteScreen(); + + @override + State createState() { + return new CodeExecuteScreenState(); + } +} + +class CodeExecuteScreenState extends State { + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final vm = Provider.of(context); + return DefaultTabController( + length: 1, + child: Scaffold( + appBar: AppBar( + title: Text('Code Execute'), + ), + body: ChangeNotifierProvider( + create: (context) => vm.deepCopy(), + child: CodeExecuteWidget(), + ).build(context), + ), + ); + } +} diff --git a/lib/ui/code/widget/code_execute_widget.dart b/lib/ui/code/widget/code_execute_widget.dart new file mode 100644 index 0000000..27af327 --- /dev/null +++ b/lib/ui/code/widget/code_execute_widget.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/generated/l10n.dart'; +import 'package:paas_dashboard_flutter/vm/code/code_view_model.dart'; +import 'package:provider/provider.dart'; + +class CodeExecuteWidget extends StatefulWidget { + CodeExecuteWidget(); + + @override + State createState() { + return new CodeExecuteWidgetState(); + } +} + +class CodeExecuteWidgetState extends State { + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final vm = Provider.of(context); + var body = ListView( + children: [ + Container( + height: 50, + child: Text(vm.code), + ), + TextButton( + onPressed: () { + setState(() {}); + }, + child: Text(S.of(context).submit)), + ], + ); + return body; + } +} diff --git a/lib/ui/home/home_drawer.dart b/lib/ui/home/home_drawer.dart index e474d36..4feaf07 100644 --- a/lib/ui/home/home_drawer.dart +++ b/lib/ui/home/home_drawer.dart @@ -19,6 +19,10 @@ class NavDrawer extends StatelessWidget { image: DecorationImage( fit: BoxFit.fill, image: AssetImage('assets/images/background/joy_valley_slide.png'))), ), + ListTile( + title: Text(S.of(context).codeQuery), + onTap: () => {Navigator.of(context).pushNamed(PageRouteConst.Code)}, + ), ListTile( title: Text(S.of(context).sqlQuery), onTap: () => {Navigator.of(context).pushNamed(PageRouteConst.Sql)}, diff --git a/lib/ui/sql/widget/sql_execute_widget.dart b/lib/ui/sql/widget/sql_execute_widget.dart index 6ddeb42..3daae25 100644 --- a/lib/ui/sql/widget/sql_execute_widget.dart +++ b/lib/ui/sql/widget/sql_execute_widget.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/generated/l10n.dart'; +import 'package:paas_dashboard_flutter/vm/sql/sql_view_model.dart'; +import 'package:provider/provider.dart'; class SqlExecuteWidget extends StatefulWidget { SqlExecuteWidget(); @@ -22,7 +25,20 @@ class SqlExecuteWidgetState extends State { @override Widget build(BuildContext context) { - var body = ListView(); + final vm = Provider.of(context); + var body = ListView( + children: [ + Container( + height: 50, + child: Text(vm.sql), + ), + TextButton( + onPressed: () { + setState(() {}); + }, + child: Text(S.of(context).submit)), + ], + ); return body; } } diff --git a/lib/vm/code/code_list_view_model.dart b/lib/vm/code/code_list_view_model.dart new file mode 100644 index 0000000..444d615 --- /dev/null +++ b/lib/vm/code/code_list_view_model.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/persistent/persistent.dart'; + +import 'code_view_model.dart'; + +class CodeListViewModel extends ChangeNotifier { + List instances = []; + + Future fetchCodeList() async { + final results = await Persistent.codeList(); + this.instances = results.map((e) => CodeViewModel(e)).toList(); + notifyListeners(); + } + + Future createCode(String name, String code) async { + Persistent.saveCode(name, code); + fetchCodeList(); + } + + Future deleteCode(int id) async { + Persistent.deleteCode(id); + fetchCodeList(); + } +} diff --git a/lib/vm/code/code_view_model.dart b/lib/vm/code/code_view_model.dart new file mode 100644 index 0000000..e160544 --- /dev/null +++ b/lib/vm/code/code_view_model.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/persistent/po/code_instance_po.dart'; + +class CodeViewModel extends ChangeNotifier { + final CodePo codePo; + + CodeViewModel(this.codePo); + + CodeViewModel deepCopy() { + return new CodeViewModel(codePo.deepCopy()); + } + + int get id { + return this.codePo.id; + } + + String get name { + return this.codePo.name; + } + + String get code { + return this.codePo.code; + } +}