Files
pangolin/client/lib/widgets/nav_sidebar.dart
T
wangjia b1dd78fddd
ci-pangolin / Lint — shellcheck (push) Successful in 5s
ci-pangolin / OpenAPI Sync Check (push) Successful in 20s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 6s
ci-pangolin / Flutter — analyze + test (push) Successful in 23s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 6s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 17s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Successful in 4m16s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
feat(client/shell): 侧栏改版 — 我的移到顶部品牌按钮 + 联系放设置下面
- 顶部品牌区(logo+穿山甲)做成可点按钮 → 打开「我的」,选中态高亮;
  从导航列表移除「我的」项(desktop+tablet)
- desktop 导航顺序:…统计 → 设置 → 联系(联系移到设置下面)
桌面/平板页 golden 重生成(CI 闸 components/auth 未动)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 20:24:03 +08:00

120 lines
4.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// nav_sidebar.dart — desktop/tablet 左侧栏导航
//
// 对照 ui_kits/desktop/dapp.jsx:宽 204、bgSubtle 底 + 右边框;
// 品牌区(mark28 + 穿山甲 + PANGOLIN) → NavItem 列表 → Spacer → 套餐卡。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../pangolin_theme.dart';
import '../state/app_providers.dart';
import '../state/navigation_provider.dart';
import 'pangolin_logo.dart';
import 'plan_badge_card.dart';
typedef NavSidebarItem = ({IconData icon, String label, NavView view});
class NavSidebar extends ConsumerWidget {
const NavSidebar({super.key, required this.items, this.width = 204, this.dense = true});
final List<NavSidebarItem> items;
final double width;
/// dense=true 桌面紧凑(行高~36)dense=false 平板触控(行高≥48, 字号更大)。
final bool dense;
@override
Widget build(BuildContext context, WidgetRef ref) {
final c = context.pangolin;
final t = ref.watch(appTextProvider);
final current = ref.watch(navViewProvider);
return Container(
width: width,
decoration: BoxDecoration(
color: c.bgSubtle,
border: Border(right: BorderSide(color: c.border)),
),
padding: EdgeInsets.fromLTRB(dense ? 12 : 14, dense ? 14 : 18, dense ? 12 : 14, dense ? 14 : 16),
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
// 品牌区(可点 → 我的/账户;选中态高亮)。「我的」已从导航列表移到此处。
Material(
color: current == NavView.account ? c.accentSubtle : Colors.transparent,
borderRadius: BorderRadius.circular(PangolinRadius.md),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () => ref.read(navViewProvider.notifier).state = NavView.account,
child: Padding(
padding: EdgeInsets.fromLTRB(6, dense ? 7 : 9, 6, dense ? 7 : 9),
child: Row(children: [
PangolinMark(size: dense ? 28 : 30),
SizedBox(width: dense ? 9 : 10),
Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [
Text(t.brand,
style: PangolinText.h3.copyWith(
color: c.fg1, fontWeight: FontWeight.w700, fontSize: dense ? 16 : 17, height: 1)),
const SizedBox(height: 3),
Text('PANGOLIN',
style: PangolinText.caption.copyWith(
color: c.accent, fontWeight: FontWeight.w600, fontSize: 9, letterSpacing: 1.6)),
]),
]),
),
),
),
SizedBox(height: dense ? 12 : 16),
// Nav 列表
for (final item in items)
Padding(
padding: EdgeInsets.only(bottom: dense ? 3 : 4),
child: _NavItem(
icon: item.icon,
label: item.label,
active: current == item.view,
dense: dense,
onTap: () => ref.read(navViewProvider.notifier).state = item.view,
),
),
const Spacer(),
const PlanBadgeCard(),
]),
);
}
}
class _NavItem extends StatelessWidget {
const _NavItem({required this.icon, required this.label, required this.active, required this.onTap, this.dense = true});
final IconData icon;
final String label;
final bool active;
final VoidCallback onTap;
final bool dense;
@override
Widget build(BuildContext context) {
final c = context.pangolin;
return Material(
color: active ? c.accentSubtle : Colors.transparent,
borderRadius: BorderRadius.circular(PangolinRadius.md),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: onTap,
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: dense ? 0 : 48),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: dense ? 12 : 14, vertical: dense ? 9 : 13),
child: Row(children: [
Icon(icon, size: dense ? 18 : 20, color: active ? c.accent : c.fg3),
SizedBox(width: dense ? 11 : 12),
Text(label,
style: PangolinText.sm.copyWith(
color: active ? c.accent : c.fg2,
fontSize: dense ? 13.5 : 15,
fontWeight: active ? (dense ? FontWeight.w600 : FontWeight.w700) : FontWeight.w500)),
]),
),
),
),
);
}
}