feat: 进行功能分区方便后期维护
- 添加导航条目和分类数据模型 - 实现导航卡片网格展示 - 支持点击卡片打开链接 - 添加主题样式(渐变色、卡片阴影) - 区分系统默认和自定义条目 - 添加分类统计信息"
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 全局配色。
|
||||
///
|
||||
/// 取值逐条对应 `index.html` 里 `:root` 的 CSS 变量,
|
||||
/// 组件只引用这里的语义名,不再出现十六进制字面量。
|
||||
/// 将来调色只需要改这个文件。
|
||||
abstract final class AppColors {
|
||||
// ---- 基础色 ----
|
||||
|
||||
/// 页面底色(`--bg-color`)。
|
||||
static const Color background = Color(0xFFF7F9FB);
|
||||
|
||||
/// 卡片 / 侧栏底色(`--card-bg`)。
|
||||
static const Color surface = Color(0xFFFFFFFF);
|
||||
|
||||
/// 主色(`--primary-color`):logo、导航选中态、图标文字、焦点边框。
|
||||
static const Color primary = Color(0xFF3B82F6);
|
||||
|
||||
/// 主要文字(`--text-main`):标题与卡片标题。
|
||||
static const Color textPrimary = Color(0xFF1E293B);
|
||||
|
||||
/// 次要文字(`--text-sub`):描述、未选中的导航项。
|
||||
static const Color textSecondary = Color(0xFF64748B);
|
||||
|
||||
/// 描边(`--border-color`):侧栏右边界、卡片边框、搜索框边框。
|
||||
static const Color border = Color(0xFFE2E8F0);
|
||||
|
||||
/// 导航项悬停 / 选中底色(`.nav-item:hover`)。
|
||||
static const Color primaryFaint = Color(0xFFEFF6FF);
|
||||
|
||||
/// 导航项悬停底色(`.nav-link:hover`,比选中态更浅)。
|
||||
static const Color navItemHover = Color(0xFFF8FAFC);
|
||||
|
||||
/// 提交按钮悬停底色(`.submit-btn:hover`),与卡片图标底色同值。
|
||||
static const Color navItemHoverDeep = Color(0xFFF1F5F9);
|
||||
|
||||
/// 折叠箭头颜色(`.arrow-icon`)。
|
||||
static const Color navArrow = Color(0xFF94A3B8);
|
||||
|
||||
/// 卡片图标底色(`.card-icon`)。
|
||||
static const Color iconSurface = Color(0xFFF1F5F9);
|
||||
|
||||
/// 搜索框聚焦时的外发光(`0 0 0 3px rgba(59,130,246,.15)`)。
|
||||
static const Color focusRing = Color(0x263B82F6);
|
||||
|
||||
// ---- 阴影 ----
|
||||
|
||||
/// 卡片默认阴影(`--shadow`)。
|
||||
static const List<BoxShadow> cardShadow = <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: Color(0x0D000000),
|
||||
blurRadius: 6,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color(0x08000000),
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
];
|
||||
|
||||
/// 卡片悬停阴影(`--shadow-hover`)。
|
||||
static const List<BoxShadow> cardShadowHover = <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: Color(0x14000000),
|
||||
blurRadius: 15,
|
||||
offset: Offset(0, 10),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color(0x0A000000),
|
||||
blurRadius: 6,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'app_colors.dart';
|
||||
|
||||
/// 语义化文字样式。
|
||||
///
|
||||
/// 字号与字重对应 `index.html` 的 CSS;字体族不显式指定,
|
||||
/// 沿用 HTML 里的系统字体栈(`-apple-system, Segoe UI, ...`)。
|
||||
/// 颜色留空的样式由调用方按状态着色(例如导航项的选中 / 未选中)。
|
||||
abstract final class AppTextStyles {
|
||||
/// 侧栏 logo 文字(`.logo`,18px / 700)。
|
||||
static const TextStyle logo = TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primary,
|
||||
);
|
||||
|
||||
/// 侧栏折叠后的 logo 图标(`.logo-icon`,20px / 800)。
|
||||
static const TextStyle logoIcon = TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: AppColors.primary,
|
||||
);
|
||||
|
||||
/// 侧栏一级导航项(`.nav-link`,14px / 500)。
|
||||
static const TextStyle navItem = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
|
||||
/// 侧栏二级导航项(`.sub-menu .nav-link`,13px / 500)。
|
||||
static const TextStyle subNavItem = TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
|
||||
/// 折叠箭头(`.arrow-icon`,14px)。
|
||||
static const TextStyle navArrow = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.navArrow,
|
||||
fontFamily: 'monospace',
|
||||
height: 1,
|
||||
);
|
||||
|
||||
/// 提交按钮文字(`.submit-btn`,14px / 500)。
|
||||
static const TextStyle submitButton = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
|
||||
/// 分组标题(`.section-title`,16px / 600)。
|
||||
static const TextStyle sectionTitle = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
);
|
||||
|
||||
/// 搜索框正文(`.search-box`,15px)。
|
||||
static const TextStyle searchInput = TextStyle(
|
||||
fontSize: 15,
|
||||
color: AppColors.textPrimary,
|
||||
);
|
||||
|
||||
/// 搜索框占位文字。
|
||||
static const TextStyle searchHint = TextStyle(
|
||||
fontSize: 15,
|
||||
color: AppColors.textSecondary,
|
||||
);
|
||||
|
||||
/// 卡片标题(`.card-title`,14px / 600)。
|
||||
static const TextStyle cardTitle = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
);
|
||||
|
||||
/// 卡片描述(`.card-desc`,12px)。
|
||||
static const TextStyle cardDesc = TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.textSecondary,
|
||||
);
|
||||
|
||||
/// 卡片图标里的首字母(`.card-icon`)。
|
||||
///
|
||||
/// 原型没有给 .card-icon 设 font-size,它继承 body 的浏览器默认字号 16px。
|
||||
static const TextStyle cardIcon = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.primary,
|
||||
);
|
||||
|
||||
/// 空状态主文案。
|
||||
static const TextStyle emptyTitle = TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
);
|
||||
|
||||
/// 空状态补充说明。
|
||||
static const TextStyle emptyMessage = TextStyle(
|
||||
fontSize: 13,
|
||||
color: AppColors.textSecondary,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'app_colors.dart';
|
||||
|
||||
/// 全局主题。
|
||||
abstract final class AppTheme {
|
||||
/// 浅色主题(当前唯一主题,取值来自 index.html 的设计变量)。
|
||||
static ThemeData get light {
|
||||
return ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: AppColors.primary,
|
||||
primary: AppColors.primary,
|
||||
surface: AppColors.surface,
|
||||
),
|
||||
scaffoldBackgroundColor: AppColors.background,
|
||||
dividerColor: AppColors.border,
|
||||
iconTheme: const IconThemeData(color: AppColors.textSecondary, size: 20),
|
||||
// 搜索框自己绘制边框与聚焦光晕,这里只负责光标颜色。
|
||||
textSelectionTheme: const TextSelectionThemeData(
|
||||
cursorColor: AppColors.primary,
|
||||
selectionColor: AppColors.focusRing,
|
||||
selectionHandleColor: AppColors.primary,
|
||||
),
|
||||
snackBarTheme: const SnackBarThemeData(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
backgroundColor: AppColors.textPrimary,
|
||||
contentTextStyle: TextStyle(color: AppColors.surface, fontSize: 14),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user