268 lines
8.1 KiB
Dart
268 lines
8.1 KiB
Dart
import 'package:aioa_mobile/features/admin/application/admin_controller.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
class AdminPage extends ConsumerWidget {
|
|
const AdminPage({super.key});
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final data = ref.watch(adminDashboardProvider);
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text('OA 管理中心')),
|
|
body: data.when(
|
|
loading: () => const Center(child: CircularProgressIndicator()),
|
|
error: (error, _) => Center(
|
|
child: FilledButton(
|
|
onPressed: () => ref.invalidate(adminDashboardProvider),
|
|
child: Text('加载失败,点击重试\n$error'),
|
|
),
|
|
),
|
|
data: (value) => DefaultTabController(
|
|
length: 4,
|
|
child: Column(
|
|
children: [
|
|
const TabBar(
|
|
isScrollable: true,
|
|
tabs: [
|
|
Tab(text: '指标'),
|
|
Tab(text: '组织'),
|
|
Tab(text: '流程'),
|
|
Tab(text: '审计'),
|
|
],
|
|
),
|
|
Expanded(
|
|
child: TabBarView(
|
|
children: [
|
|
_Metrics(value['metrics']! as Map<String, Object?>),
|
|
_Organization(value),
|
|
_Workflow(value),
|
|
_Audit(value['audits']! as List<Map<String, Object?>>),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _Metrics extends StatelessWidget {
|
|
const _Metrics(this.data);
|
|
final Map<String, Object?> data;
|
|
@override
|
|
Widget build(BuildContext context) => GridView.count(
|
|
padding: const EdgeInsets.all(12),
|
|
crossAxisCount: 2,
|
|
childAspectRatio: 1.5,
|
|
children: data.entries
|
|
.where((entry) => entry.key != 'leaveByStatus')
|
|
.map(
|
|
(entry) => Card(
|
|
child: Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
'${entry.value}',
|
|
style: Theme.of(context).textTheme.headlineSmall,
|
|
),
|
|
Text(entry.key, textAlign: TextAlign.center),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
)
|
|
.toList(),
|
|
);
|
|
}
|
|
|
|
class _Organization extends ConsumerWidget {
|
|
const _Organization(this.data);
|
|
final Map<String, Object?> data;
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final users = data['users']! as List<Map<String, Object?>>;
|
|
final roles = data['roles']! as List<Map<String, Object?>>;
|
|
return ListView(
|
|
padding: const EdgeInsets.all(12),
|
|
children: [
|
|
FilledButton.icon(
|
|
onPressed: () => _add(context, ref),
|
|
icon: const Icon(Icons.add),
|
|
label: const Text('新增部门'),
|
|
),
|
|
...users.map(
|
|
(user) => Card(
|
|
child: ListTile(
|
|
title: Text('${user['displayName']}'),
|
|
subtitle: Text(
|
|
'${user['username']} · ${(user['roles'] as List).join('、')}',
|
|
),
|
|
trailing: IconButton(
|
|
icon: const Icon(Icons.manage_accounts),
|
|
onPressed: () => _roles(context, ref, user, roles),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Future<void> _add(BuildContext context, WidgetRef ref) async {
|
|
final code = TextEditingController();
|
|
final name = TextEditingController();
|
|
final confirmed = await showDialog<bool>(
|
|
context: context,
|
|
builder: (dialogContext) => AlertDialog(
|
|
title: const Text('新增部门'),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
TextField(
|
|
controller: code,
|
|
decoration: const InputDecoration(labelText: '编码'),
|
|
),
|
|
TextField(
|
|
controller: name,
|
|
decoration: const InputDecoration(labelText: '名称'),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(dialogContext, false),
|
|
child: const Text('取消'),
|
|
),
|
|
FilledButton(
|
|
onPressed: () => Navigator.pop(dialogContext, true),
|
|
child: const Text('创建'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
if (confirmed == true) {
|
|
await ref
|
|
.read(adminDashboardProvider.notifier)
|
|
.createDepartment(code.text, name.text);
|
|
}
|
|
code.dispose();
|
|
name.dispose();
|
|
}
|
|
|
|
Future<void> _roles(
|
|
BuildContext context,
|
|
WidgetRef ref,
|
|
Map<String, Object?> user,
|
|
List<Map<String, Object?>> available,
|
|
) async {
|
|
final selected = (user['roles'] as List).cast<String>().toSet();
|
|
final result = await showDialog<Set<String>>(
|
|
context: context,
|
|
builder: (dialogContext) => StatefulBuilder(
|
|
builder: (context, setState) => AlertDialog(
|
|
title: Text('${user['displayName']} 的角色'),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: available.map((role) {
|
|
final code = '${role['code']}';
|
|
return CheckboxListTile(
|
|
value: selected.contains(code),
|
|
title: Text('${role['name']}'),
|
|
onChanged: (checked) => setState(
|
|
() => checked == true
|
|
? selected.add(code)
|
|
: selected.remove(code),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
actions: [
|
|
FilledButton(
|
|
onPressed: () => Navigator.pop(dialogContext, selected),
|
|
child: const Text('保存'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
if (result != null) {
|
|
await ref
|
|
.read(adminDashboardProvider.notifier)
|
|
.replaceRoles('${user['id']}', result);
|
|
}
|
|
}
|
|
}
|
|
|
|
class _Workflow extends StatelessWidget {
|
|
const _Workflow(this.data);
|
|
final Map<String, Object?> data;
|
|
@override
|
|
Widget build(BuildContext context) => ListView(
|
|
padding: const EdgeInsets.all(12),
|
|
children: [
|
|
const Text('业务流程绑定'),
|
|
...(data['bindings']! as List<Map<String, Object?>>).map(
|
|
(item) => Card(
|
|
child: ListTile(
|
|
leading: Icon(
|
|
item['status'] == 'ACTIVE' ? Icons.link : Icons.link_off,
|
|
),
|
|
title: Text(
|
|
'${item['business_type']} → ${item['process_definition_key']}',
|
|
),
|
|
subtitle: Text(
|
|
'表单 ${item['form_key']} v${item['form_version']} · 优先级 ${item['priority']}',
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const Divider(),
|
|
const Text('表单版本'),
|
|
...(data['formVersions']! as List<Map<String, Object?>>).map(
|
|
(item) => ListTile(
|
|
title: Text('${item['form_key']} v${item['version']}'),
|
|
subtitle: Text('${item['status']}'),
|
|
),
|
|
),
|
|
const Divider(),
|
|
const Text('流程定义'),
|
|
...(data['definitions']! as List<Map<String, Object?>>).map(
|
|
(item) => ListTile(
|
|
title: Text('${item['name'] ?? item['key']} v${item['version']}'),
|
|
subtitle: Text('${item['id']}'),
|
|
),
|
|
),
|
|
const Divider(),
|
|
const Text('最近实例'),
|
|
...(data['instances']! as List<Map<String, Object?>>).map(
|
|
(item) => ListTile(
|
|
title: Text('${item['businessKey'] ?? item['id']}'),
|
|
subtitle: Text(item['active'] == true ? '运行中' : '已结束'),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
class _Audit extends StatelessWidget {
|
|
const _Audit(this.items);
|
|
final List<Map<String, Object?>> items;
|
|
@override
|
|
Widget build(BuildContext context) => ListView.builder(
|
|
padding: const EdgeInsets.all(12),
|
|
itemCount: items.length,
|
|
itemBuilder: (context, index) {
|
|
final item = items[index];
|
|
return Card(
|
|
child: ListTile(
|
|
title: Text('${item['action']}'),
|
|
subtitle: Text('${item['resourceType']} · ${item['traceId']}'),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|