admin_permissions_page.dart 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import 'package:flutter/material.dart';
  2. import 'package:go_router/go_router.dart';
  3. import 'package:flutter_riverpod/flutter_riverpod.dart';
  4. import '../../core/theme/app_colors.dart';
  5. import '../shell/nav_bar_config.dart';
  6. import '../../core/i18n/app_localizations.dart';
  7. import '../../shared/widgets/status_tag.dart';
  8. class AdminPermissionsPage extends ConsumerStatefulWidget {
  9. const AdminPermissionsPage({super.key});
  10. @override
  11. ConsumerState<AdminPermissionsPage> createState() =>
  12. _AdminPermissionsPageState();
  13. }
  14. class _AdminPermissionsPageState extends ConsumerState<AdminPermissionsPage> {
  15. final _employees = <Employee>[
  16. Employee(
  17. name: '张三',
  18. avatarText: '张',
  19. employeeId: '工号:0048',
  20. department: '销售部',
  21. roles: ['普通员工', '审批人'],
  22. isActive: true,
  23. ),
  24. Employee(
  25. name: '王经理',
  26. avatarText: '王',
  27. employeeId: '工号:0012',
  28. department: '销售部',
  29. roles: ['审批人', '系统管理员'],
  30. isActive: true,
  31. ),
  32. Employee(
  33. name: '李会计',
  34. avatarText: '李',
  35. employeeId: '工号:0025',
  36. department: '财务部',
  37. roles: ['财务人员'],
  38. isActive: true,
  39. ),
  40. Employee(
  41. name: '赵管理员',
  42. avatarText: '赵',
  43. employeeId: '工号:0001',
  44. department: '信息技术部',
  45. roles: ['系统管理员'],
  46. isActive: true,
  47. ),
  48. Employee(
  49. name: '钱六',
  50. avatarText: '钱',
  51. employeeId: '工号:0052',
  52. department: '财务部',
  53. roles: ['财务人员'],
  54. isActive: false,
  55. ),
  56. Employee(
  57. name: '孙七',
  58. avatarText: '孙',
  59. employeeId: '工号:0078',
  60. department: '行政部',
  61. roles: ['普通员工'],
  62. isActive: true,
  63. ),
  64. ];
  65. String _searchQuery = '';
  66. List<Employee> get _filteredEmployees {
  67. if (_searchQuery.isEmpty) return _employees;
  68. final q = _searchQuery.toLowerCase();
  69. return _employees.where((e) {
  70. return e.name.toLowerCase().contains(q) ||
  71. e.employeeId.toLowerCase().contains(q);
  72. }).toList();
  73. }
  74. @override
  75. Widget build(BuildContext context) {
  76. final l10n = AppLocalizations.of(context);
  77. ref
  78. .read(navBarConfigProvider.notifier)
  79. .update(
  80. NavBarConfig(
  81. title: l10n.get('adminPermissions'),
  82. showBack: true,
  83. onBack: () => context.pop(),
  84. ),
  85. );
  86. return Column(
  87. children: [
  88. // 搜索栏
  89. Container(
  90. width: double.infinity,
  91. padding: const EdgeInsets.fromLTRB(16, 10, 16, 10),
  92. color: AppColors.bgCard,
  93. child: Container(
  94. padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
  95. decoration: BoxDecoration(
  96. color: AppColors.bgPage,
  97. borderRadius: BorderRadius.circular(18),
  98. ),
  99. child: Row(
  100. children: [
  101. const Icon(
  102. Icons.search,
  103. size: 16,
  104. color: AppColors.textPlaceholder,
  105. ),
  106. const SizedBox(width: 6),
  107. Expanded(
  108. child: TextField(
  109. onChanged: (v) => setState(() => _searchQuery = v),
  110. decoration: const InputDecoration(
  111. hintText: '输入姓名或工号进行检索...',
  112. hintStyle: TextStyle(
  113. fontSize: 14,
  114. color: AppColors.textPlaceholder,
  115. ),
  116. border: InputBorder.none,
  117. contentPadding: EdgeInsets.zero,
  118. isDense: true,
  119. ),
  120. style: const TextStyle(
  121. fontSize: 14,
  122. color: AppColors.textPrimary,
  123. ),
  124. ),
  125. ),
  126. ],
  127. ),
  128. ),
  129. ),
  130. // 员工列表
  131. Expanded(
  132. child: ListView.builder(
  133. padding: const EdgeInsets.all(16),
  134. itemCount: _filteredEmployees.length,
  135. itemBuilder: (_, i) => _buildEmpCard(_filteredEmployees[i]),
  136. ),
  137. ),
  138. ],
  139. );
  140. }
  141. Widget _buildEmpCard(Employee emp) {
  142. return Padding(
  143. padding: const EdgeInsets.only(bottom: 16),
  144. child: Container(
  145. padding: const EdgeInsets.all(12),
  146. decoration: BoxDecoration(
  147. color: emp.isActive ? AppColors.bgCard : const Color(0xFFF9F9F9),
  148. borderRadius: BorderRadius.circular(8),
  149. ),
  150. child: Row(
  151. crossAxisAlignment: CrossAxisAlignment.start,
  152. children: [
  153. // 头像
  154. Container(
  155. width: 40,
  156. height: 40,
  157. decoration: BoxDecoration(
  158. color: AppColors.primary,
  159. borderRadius: BorderRadius.circular(20),
  160. ),
  161. child: Center(
  162. child: Text(
  163. emp.avatarText,
  164. style: const TextStyle(
  165. fontSize: 16,
  166. fontWeight: FontWeight.w600,
  167. color: Colors.white,
  168. ),
  169. ),
  170. ),
  171. ),
  172. const SizedBox(width: 10),
  173. // 信息区
  174. Expanded(
  175. child: Column(
  176. crossAxisAlignment: CrossAxisAlignment.start,
  177. children: [
  178. // 姓名 + 工号
  179. Row(
  180. children: [
  181. Text(
  182. emp.name,
  183. style: const TextStyle(
  184. fontSize: 15,
  185. fontWeight: FontWeight.w600,
  186. color: AppColors.textPrimary,
  187. ),
  188. ),
  189. const SizedBox(width: 6),
  190. Text(
  191. emp.employeeId,
  192. style: const TextStyle(
  193. fontSize: 12,
  194. color: AppColors.textPlaceholder,
  195. ),
  196. ),
  197. ],
  198. ),
  199. const SizedBox(height: 2),
  200. // 部门
  201. Text(
  202. emp.department,
  203. style: const TextStyle(
  204. fontSize: 12,
  205. color: AppColors.textSecondary,
  206. ),
  207. ),
  208. ],
  209. ),
  210. ),
  211. // 角色标签区
  212. Column(
  213. crossAxisAlignment: CrossAxisAlignment.end,
  214. children: [
  215. // 角色标签行
  216. ...emp.roles.map(
  217. (role) => Padding(
  218. padding: const EdgeInsets.only(bottom: 4),
  219. child: _buildRoleTag(role),
  220. ),
  221. ),
  222. // 状态标签
  223. if (!emp.isActive)
  224. StatusTag(
  225. text: '已禁用',
  226. textColor: AppColors.danger,
  227. backgroundColor: AppColors.dangerBg,
  228. ),
  229. ],
  230. ),
  231. ],
  232. ),
  233. ),
  234. );
  235. }
  236. Widget _buildRoleTag(String role) {
  237. Color bgColor;
  238. Color textColor;
  239. switch (role) {
  240. case '审批人':
  241. bgColor = AppColors.warningBg;
  242. textColor = AppColors.warning;
  243. break;
  244. case '财务人员':
  245. bgColor = AppColors.successBg;
  246. textColor = AppColors.success;
  247. break;
  248. case '系统管理员':
  249. bgColor = AppColors.dangerBg;
  250. textColor = AppColors.danger;
  251. break;
  252. default:
  253. bgColor = AppColors.primaryLight;
  254. textColor = AppColors.primary;
  255. }
  256. return Container(
  257. padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
  258. decoration: BoxDecoration(
  259. color: bgColor,
  260. borderRadius: BorderRadius.circular(3),
  261. ),
  262. child: Text(role, style: TextStyle(fontSize: 10, color: textColor)),
  263. );
  264. }
  265. }
  266. class Employee {
  267. final String name;
  268. final String avatarText;
  269. final String employeeId;
  270. final String department;
  271. final List<String> roles;
  272. final bool isActive;
  273. const Employee({
  274. required this.name,
  275. required this.avatarText,
  276. required this.employeeId,
  277. required this.department,
  278. required this.roles,
  279. this.isActive = true,
  280. });
  281. }