be9afcf68d
对齐移动原型体系(m-*.html)的 Stage 0 共享基建,桌面形态零改动: - 导航:router 新增 branch 9(/me 我的 + /me/license 授权屏);窄屏 tab 根 (库存/入库/出库/财务/我的)出底部 MTabBar,二级屏隐藏 tabbar + 顶栏返回箭头 (PopScope 拦系统返回键防退 app);窄屏 Drawer/汉堡删除;窄屏顶栏对齐 m-top (标题 + 主题/通知铃/头像→我的) - sheet:showMSheet(grip/标题/86vh/键盘避让)+ showAdaptiveSheet;六个共享 弹层呈现函数加窄屏 sheet 分支(订单/往来/财务往来/商品编辑抽屉 + 退单/登记收支 dialog 的 asSheet 形态) - 徽章:DsBadge/StatusPill/StatusBadge 加图标变体(默认圆点零漂移)+ status_icon_map 转录原型 BADGE_ICON 28 词 - 组件:MKpiGrid(2×2 可点 KPI)/ MSearchRow(搜索+状态钮+详搜钮)/ MHubGroup(hub 入口组)/ showThemeSheet(主题外观 sheet) - 授权:_LicensePanel/_SettingsCard 抽取为公共 LicensePanel/SettingsCard, 窄屏授权跳转 /me/license - golden:app_shell_mobile + me 各三主题(390×844@2x);全量 259 测试绿, check_ds_code / check-l1-sync 闸过 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
209 lines
7.0 KiB
Dart
209 lines
7.0 KiB
Dart
// screens/me/me_screen.dart — 「我的」hub(镜像原型 m-me.html,移动愿景导航中枢)。
|
|
// 结构:用户头卡(54 渐变圆头像 + 真名 + 店名·账号 + 角色徽章)
|
|
// + 经营管理组(往来单位/基础数据/库存盘点)
|
|
// + 系统组(用户管理/授权管理/设备管理/系统设置/主题外观/关于我们)
|
|
// + 退出登录(确认 sheet)。窄屏底部 tab「我的」入口;桌面侧栏不含此屏。
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
|
|
|
import '../../core/auth/auth_state.dart';
|
|
import '../../core/theme/app_dims.g.dart';
|
|
import '../../core/theme/context_tokens.dart';
|
|
import '../../providers/shop_provider.dart';
|
|
import '../../widgets/ds/ds_atoms.dart';
|
|
import '../../widgets/ds/m_hub.dart';
|
|
import '../../widgets/ds/m_sheet.dart';
|
|
import '../../widgets/ds/status_icon_map.dart';
|
|
import '../../widgets/theme_sheet.dart';
|
|
|
|
String _roleLabel(String role) {
|
|
switch (role) {
|
|
case 'superadmin':
|
|
return '超级管理员';
|
|
case 'admin':
|
|
return '管理员';
|
|
case 'readonly':
|
|
return '只读';
|
|
default:
|
|
return '操作员';
|
|
}
|
|
}
|
|
|
|
class MeScreen extends ConsumerWidget {
|
|
const MeScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final t = context.tokens;
|
|
final user = ref.watch(authStateProvider).user;
|
|
final shopName =
|
|
ref.watch(shopInfoProvider).valueOrNull?.name ?? user?.shopNo ?? '';
|
|
|
|
return Container(
|
|
color: t.bg,
|
|
child: ListView(
|
|
padding: const EdgeInsets.all(14),
|
|
children: [
|
|
if (user != null) _header(t, user, shopName),
|
|
MHubGroup(label: '经营管理', items: [
|
|
MHubItem(
|
|
icon: LucideIcons.users,
|
|
label: '往来单位',
|
|
onTap: () => context.go('/partners')),
|
|
MHubItem(
|
|
icon: LucideIcons.layoutGrid,
|
|
label: '基础数据',
|
|
onTap: () => context.go('/products')),
|
|
MHubItem(
|
|
icon: LucideIcons.clipboardCheck,
|
|
label: '库存盘点',
|
|
onTap: () => context.go('/inventory/check')),
|
|
]),
|
|
MHubGroup(label: '系统', items: [
|
|
MHubItem(
|
|
icon: LucideIcons.userPlus,
|
|
label: '用户管理',
|
|
onTap: () => context.go('/settings/users')),
|
|
MHubItem(
|
|
icon: LucideIcons.trendingUp,
|
|
label: '授权管理',
|
|
onTap: () => context.go('/me/license')),
|
|
MHubItem(
|
|
icon: LucideIcons.monitorSmartphone,
|
|
label: '设备管理',
|
|
onTap: () => context.go('/devices')),
|
|
MHubItem(
|
|
icon: LucideIcons.settings,
|
|
label: '系统设置',
|
|
onTap: () => context.go('/settings')),
|
|
MHubItem(
|
|
icon: LucideIcons.shirt,
|
|
label: '主题外观',
|
|
onTap: () => showThemeSheet(context)),
|
|
MHubItem(
|
|
icon: LucideIcons.info,
|
|
label: '关于我们',
|
|
onTap: () => context.go('/about')),
|
|
]),
|
|
const SizedBox(height: 18),
|
|
_LogoutButton(onConfirm: () {
|
|
ref.read(authStateProvider.notifier).logout();
|
|
context.go('/login');
|
|
}),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 用户头卡(原型 .me-hd):surface 卡 + 54 渐变圆头像 + 名/店·账号 + 角色徽章。
|
|
Widget _header(dynamic t, AuthUser user, String shopName) {
|
|
final name = user.realName.isNotEmpty ? user.realName : user.username;
|
|
final initial = name.isNotEmpty ? name.characters.first : '用';
|
|
final roleLabel = _roleLabel(user.role);
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: t.surface,
|
|
border: Border.all(color: t.border),
|
|
borderRadius: BorderRadius.circular(AppDims.rLg),
|
|
),
|
|
child: Row(children: [
|
|
Container(
|
|
width: 54,
|
|
height: 54,
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [t.primary, t.primaryDark],
|
|
),
|
|
shape: BoxShape.circle,
|
|
),
|
|
alignment: Alignment.center,
|
|
child: Text(initial,
|
|
style: TextStyle(
|
|
color: t.onPrimary,
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.w800)),
|
|
),
|
|
const SizedBox(width: 14),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(name,
|
|
style: TextStyle(
|
|
fontSize: AppDims.fsH2,
|
|
fontWeight: FontWeight.w700,
|
|
color: t.heading)),
|
|
const SizedBox(height: 3),
|
|
Text('$shopName · ${user.username}',
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: TextStyle(fontSize: AppDims.fsSm, color: t.muted)),
|
|
],
|
|
),
|
|
),
|
|
DsBadge(roleLabel,
|
|
tone: DsBadgeTone.info, icon: statusIcon(roleLabel)),
|
|
]),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _LogoutButton extends StatelessWidget {
|
|
final VoidCallback onConfirm;
|
|
const _LogoutButton({required this.onConfirm});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final t = context.tokens;
|
|
// Material 自带(InkWell 水波 + golden 直挂无 Scaffold 场景)
|
|
return Material(
|
|
color: t.surface,
|
|
borderRadius: BorderRadius.circular(AppDims.rMd),
|
|
child: InkWell(
|
|
onTap: () => _confirm(context),
|
|
borderRadius: BorderRadius.circular(AppDims.rMd),
|
|
child: Container(
|
|
height: 44,
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: t.border),
|
|
borderRadius: BorderRadius.circular(AppDims.rMd),
|
|
),
|
|
child: Text('退出登录',
|
|
style: TextStyle(
|
|
fontSize: AppDims.fsBody,
|
|
fontWeight: FontWeight.w600,
|
|
color: t.danger)),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _confirm(BuildContext context) {
|
|
showMSheet<void>(
|
|
context,
|
|
title: '退出登录',
|
|
builder: (ctx) => Text('确认退出当前账号?',
|
|
style: TextStyle(
|
|
fontSize: AppDims.fsBody, color: ctx.tokens.text)),
|
|
actions: [
|
|
Builder(
|
|
builder: (ctx) =>
|
|
DsButton('取消', onPressed: () => Navigator.of(ctx).pop())),
|
|
Builder(
|
|
builder: (ctx) => DsButton('退出登录',
|
|
variant: DsBtnVariant.danger, onPressed: () {
|
|
Navigator.of(ctx).pop();
|
|
onConfirm();
|
|
}),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|