Penny Lens PC 设计系统

2025年1月27日
7 分钟阅读
作者:Penny Lens Team

设计系统

🎨 设计理念

现代简约的设计理念,注重用户体验和数据可视化,为家庭财务管理提供直观、高效的界面设计。

🌈 色彩系统

主色系 - 金黄色 #ffda44

/* 主品牌色 - 金黄色 */
--primary-color: #ffda44;
--primary-50: #fffdf0;
--primary-100: #fff8d6;
--primary-200: #fff0ad;
--primary-300: #ffe584;
--primary-400: #ffda44; /* 主色 */
--primary-500: #ffd700;
--primary-600: #e6c200;
--primary-700: #ccad00;
--primary-800: #b39900;
--primary-900: #998000;
 
/* 渐变色 */
--primary-gradient: linear-gradient(135deg, #ffda44 0%, #ffd700 100%);
--primary-gradient-dark: linear-gradient(135deg, #e6c200 0%, #ccad00 100%);

功能色系

/* 页面主题色 */
--dashboard-blue: #3b82f6;     /* 首页 - 仪表盘 */
--transaction-green: #10b981;  /* 交易记录 - 交易 */
--wallet-purple: #8b5cf6;      /* 资产管理 - 钱包 */
--budget-blue: #3b82f6;        /* 预算管理 - 资金 */
--subscription-orange: #f59e0b; /* 订阅管理 - 日历 */
--category-cyan: #06b6d4;      /* 分类管理 - 标签 */
 
/* 语义色 */
--success: #10b981;   /* 成功/收入 */
--warning: #f59e0b;   /* 警告 */
--error: #ef4444;     /* 错误/支出 */
--info: #3b82f6;      /* 信息 */
--neutral: #6b7280;   /* 中性 */

中性色系

/* 灰色调 */
--gray-50: #f9fafb;
--gray-100: #f3f4f6;
--gray-200: #e5e7eb;
--gray-300: #d1d5db;
--gray-400: #9ca3af;
--gray-500: #6b7280;
--gray-600: #4b5563;
--gray-700: #374151;
--gray-800: #1f2937;
--gray-900: #111827;

深色模式

/* 深色背景 */
--dark-bg-primary: #0f172a;
--dark-bg-secondary: #1e293b;
--dark-bg-tertiary: #334155;
 
/* 深色文本 */
--dark-text-primary: #f8fafc;
--dark-text-secondary: #cbd5e1;
--dark-text-tertiary: #94a3b8;

📐 布局规范

页面结构模板

<template>
  <div class="page-container min-h-screen bg-gray-50/50 dark:bg-gray-900/50">
    <!-- 1. 粘性页头 -->
    <div class="page-header sticky top-0 z-10 backdrop-blur-sm bg-white/90 dark:bg-gray-800/90 border-b border-gray-200/50 dark:border-gray-700/50 px-6 py-4 mb-6 shadow-sm">
      <div class="flex items-center justify-between">
        <div class="flex items-center gap-3">
          <div class="w-10 h-10 rounded-lg bg-gradient-to-br from-{color}-500 to-{color}-600 dark:from-{color}-600 dark:to-{color}-700 shadow-md">
            <span class="i-{icon} text-white text-xl" />
          </div>
          <div>
            <h2 class="text-2xl font-bold text-gray-800 dark:text-gray-100">页面标题</h2>
            <p class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">页面副标题</p>
          </div>
        </div>
        <a-button type="primary" size="small" class="!rounded-lg !px-4">
          <span class="i-ant-design-plus-outlined mr-1" />
          主要操作
        </a-button>
      </div>
    </div>
 
    <!-- 2. 页面内容 -->
    <div class="page-content px-4 md:px-6 pb-6 space-y-6">
      <!-- 内容区域 -->
    </div>
  </div>
</template>

网格系统

/* 统计卡片布局 */
.stats-grid {
  @apply grid gap-3;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
 
/* 响应式列数 */
.responsive-grid {
  @apply grid gap-3;
  grid-template-columns: repeat(1, 1fr); /* 默认 1 列 */
 
  @media (min-width: 640px) {
    grid-template-columns: repeat(2, 1fr); /* sm: 2 列 */
  }
 
  @media (min-width: 768px) {
    grid-template-columns: repeat(3, 1fr); /* md: 3 列 */
  }
 
  @media (min-width: 1024px) {
    grid-template-columns: repeat(4, 1fr); /* lg: 4 列 */
  }
}

🧩 组件规范

统计卡片 (StatCard)

<template>
  <div class="stat-card bg-white dark:bg-gray-800 rounded-xl p-3 shadow-sm border border-gray-100 dark:border-gray-700/50 transition-all duration-300 hover:shadow-md">
    <div class="flex items-center justify-between mb-2">
      <div class="flex items-center gap-2">
        <div class="w-8 h-8 rounded-lg bg-gradient-to-br from-{color}-500 to-{color}-600 flex items-center justify-center">
          <span class="i-{icon} text-white text-base" />
        </div>
        <div class="min-w-0 flex-1">
          <h3 class="text-sm font-semibold text-gray-800 dark:text-gray-200 truncate">{{ title }}</h3>
          <p class="text-[10px] text-gray-500 dark:text-gray-400 truncate leading-tight">{{ subtitle }}</p>
        </div>
      </div>
    </div>
    <div class="text-right">
      <div class="text-lg font-bold text-gray-900 dark:text-gray-100 whitespace-nowrap">
        {{ prefix }}{{ formatValue(value) }}
      </div>
      <div class="text-[10px] text-gray-500 dark:text-gray-400 whitespace-nowrap">
        {{ footnote }}
      </div>
    </div>
  </div>
</template>

表格组件

<template>
  <div class="table-container bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700/50 overflow-hidden">
    <div class="p-5">
      <a-table
        :columns="columns"
        :data-source="data"
        size="small"
        class="custom-table"
        :pagination="{ pageSize: 10 }"
      >
        <!-- 表格内容 -->
      </a-table>
    </div>
  </div>
</template>
 
<style scoped>
.custom-table {
  :deep(.ant-table) {
    @apply rounded-xl overflow-hidden;
  }
 
  :deep(.ant-table-thead > tr > th) {
    @apply bg-gradient-to-b from-gray-50 to-gray-100/50 dark:from-gray-700 dark:to-gray-700/50 text-gray-700 dark:text-gray-300 font-semibold border-b-2 border-gray-200 dark:border-gray-600 !text-xs !py-2;
  }
 
  :deep(.ant-table-tbody > tr) {
    @apply transition-colors duration-200;
  }
 
  :deep(.ant-table-tbody > tr:hover) {
    @apply bg-blue-50/50 dark:bg-gray-700/30;
  }
 
  :deep(.ant-table-tbody > tr > td) {
    @apply border-b border-gray-100 dark:border-gray-700/50 !py-2 !text-xs;
  }
}
</style>

筛选表单

<template>
  <div class="filter-container bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700/50 overflow-hidden">
    <div class="p-4">
      <a-form :model="filters" class="filter-form">
        <a-row :gutter="[12, 12]">
          <a-col :xs="12" :sm="6" :md="6" :lg="6">
            <a-form-item label="筛选条件" class="!mb-3">
              <a-select
                v-model:value="filters.field"
                placeholder="请选择"
                size="small"
                class="w-full !rounded-lg"
                allow-clear
              >
                <a-select-option value="option1">选项1</a-select-option>
                <a-select-option value="option2">选项2</a-select-option>
              </a-select>
            </a-form-item>
          </a-col>
        </a-row>
        <a-row>
          <a-col :span="24" class="text-right">
            <a-space>
              <a-button type="primary" size="small" class="!rounded-lg !px-4">
                <span class="i-ant-design-search-outlined mr-1" />
                搜索
              </a-button>
              <a-button size="small" class="!rounded-lg !px-4">
                <span class="i-ant-design-reload-outlined mr-1" />
                重置
              </a-button>
            </a-space>
          </a-col>
        </a-row>
      </a-form>
    </div>
  </div>
</template>

📝 字体规范

字体大小层级

/* 页面标题 */
.text-page-title {
  @apply text-2xl font-bold text-gray-800 dark:text-gray-100;
}
 
/* 卡片标题 */
.text-card-title {
  @apply text-sm font-semibold text-gray-800 dark:text-gray-200;
}
 
/* 表格标题 */
.text-table-title {
  @apply text-xs font-semibold text-gray-700 dark:text-gray-300;
}
 
/* 正文内容 */
.text-body {
  @apply text-xs text-gray-600 dark:text-gray-300;
}
 
/* 辅助文字 */
.text-caption {
  @apply text-[10px] text-gray-500 dark:text-gray-400;
}
 
/* 数值显示 */
.text-value {
  @apply text-sm font-bold text-gray-900 dark:text-gray-100;
}
 
/* 小数值 */
.text-value-small {
  @apply text-xs font-semibold text-gray-800 dark:text-gray-200;
}

文本处理

/* 不换行 */
.text-nowrap {
  @apply whitespace-nowrap;
}
 
/* 截断 */
.text-truncate {
  @apply truncate;
}
 
/* 多行截断 */
.text-clamp-2 {
  @apply overflow-hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

📏 间距系统

基础间距

/* 内边距 */
.p-xs { @apply p-1; }   /* 4px */
.p-sm { @apply p-2; }   /* 8px */
.p-md { @apply p-3; }   /* 12px */
.p-lg { @apply p-4; }   /* 16px */
.p-xl { @apply p-5; }   /* 20px */
.p-2xl { @apply p-6; }  /* 24px */
 
/* 外边距 */
.m-xs { @apply m-1; }   /* 4px */
.m-sm { @apply m-2; }   /* 8px */
.m-md { @apply m-3; }   /* 12px */
.m-lg { @apply m-4; }   /* 16px */
.m-xl { @apply m-5; }   /* 20px */
.m-2xl { @apply m-6; }  /* 24px */
 
/* 间距 */
.gap-xs { @apply gap-1; }  /* 4px */
.gap-sm { @apply gap-2; }  /* 8px */
.gap-md { @apply gap-3; }  /* 12px */
.gap-lg { @apply gap-4; }  /* 16px */
.gap-xl { @apply gap-5; }  /* 20px */

组件间距

/* 页面级间距 */
.page-spacing {
  @apply px-4 md:px-6 pb-6 space-y-6;
}
 
/* 卡片间距 */
.card-spacing {
  @apply p-3 md:p-4 lg:p-5;
}
 
/* 表格间距 */
.table-spacing {
  @apply p-4 md:p-5;
}
 
/* 表单间距 */
.form-spacing {
  @apply p-4;
}
 
/* 网格间距 */
.grid-spacing {
  @apply gap-3;
}

✨ 动画效果

页面动画

/* 页头下滑 */
.page-header {
  animation: slideDown 0.3s ease-out;
}
 
@keyframes slideDown {
  from {
    transform: translateY(-10px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}
 
/* 内容淡入 */
.page-content {
  animation: fadeInUp 0.4s ease-out;
}
 
@keyframes fadeInUp {
  from {
    transform: translateY(20px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

交互动画

/* 按钮悬停 */
.btn-hover {
  @apply transition-all duration-200;
}
 
.btn-hover:hover {
  @apply transform -translate-y-0.5;
}
 
/* 卡片悬停 */
.card-hover {
  @apply transition-all duration-300;
}
 
.card-hover:hover {
  @apply transform -translate-y-0.5 shadow-md;
}
 
/* 标签悬停 */
.tag-hover {
  @apply transition-all duration-300;
}
 
.tag-hover:hover {
  @apply opacity-80 transform scale-105;
}

📱 响应式设计

断点系统

/* 移动端 */
@media (max-width: 639px) {
  .mobile-only { display: block; }
  .desktop-only { display: none; }
}
 
/* 平板端 */
@media (min-width: 640px) and (max-width: 1023px) {
  .tablet-only { display: block; }
}
 
/* 桌面端 */
@media (min-width: 1024px) {
  .desktop-only { display: block; }
  .mobile-only { display: none; }
}

网格响应式

/* 统计卡片 */
.stats-responsive {
  @apply grid gap-3;
  grid-template-columns: repeat(1, 1fr); /* 默认 1 列 */
 
  @media (min-width: 640px) {
    grid-template-columns: repeat(2, 1fr); /* sm: 2 列 */
  }
 
  @media (min-width: 768px) {
    grid-template-columns: repeat(3, 1fr); /* md: 3 列 */
  }
 
  @media (min-width: 1024px) {
    grid-template-columns: repeat(4, 1fr); /* lg: 4 列 */
  }
}

🎯 主色系使用规范

1. 主色 #ffda44 使用场景

/* 主要按钮 */
.btn-primary {
  @apply bg-[#ffda44] hover:bg-[#e6c200] text-gray-900 font-semibold;
}
 
/* 重要提示 */
.alert-primary {
  @apply bg-[#fffdf0] border-[#ffda44] text-gray-800;
}
 
/* 高亮文本 */
.text-highlight {
  @apply text-[#ffda44] font-semibold;
}
 
/* 进度条 */
.progress-primary {
  @apply bg-[#ffda44];
}
 
/* 徽章 */
.badge-primary {
  @apply bg-[#ffda44] text-gray-900;
}

2. 渐变色使用

/* 卡片头部 */
.card-header-primary {
  background: linear-gradient(135deg, #ffda44 0%, #ffd700 100%);
}
 
/* 按钮渐变 */
.btn-gradient-primary {
  background: linear-gradient(135deg, #ffda44 0%, #ffd700 100%);
}
 
/* 图标背景 */
.icon-bg-primary {
  background: linear-gradient(135deg, #ffda44 0%, #ffd700 100%);
}

3. 深色模式适配

/* 深色模式下的主色 */
.dark .btn-primary {
  @apply bg-[#e6c200] hover:bg-[#ccad00] text-gray-900;
}
 
.dark .alert-primary {
  @apply bg-[#332a00] border-[#e6c200] text-[#ffda44];
}
 
.dark .text-highlight {
  @apply text-[#e6c200];
}

📋 设计原则

1. 统一性原则

  • 所有页面使用统一的设计语言
  • 保持视觉风格的一致性
  • 组件复用最大化

2. 紧凑性原则

  • 信息密度优化,减少空白浪费
  • 合理利用屏幕空间
  • 提升用户操作效率

3. 可读性原则

  • 确保文本清晰易读
  • 合理的对比度
  • 避免文本换行

4. 响应式原则

  • 适配各种屏幕尺寸
  • 移动端优先设计
  • 渐进式增强

🚀 最佳实践

1. 性能优化

  • 使用 v-memo 缓存复杂计算
  • 合理使用 v-showv-if
  • 图片懒加载
  • 组件按需加载

2. 用户体验

  • 加载状态提示
  • 错误边界处理
  • 操作反馈及时
  • 键盘导航支持

3. 可维护性

  • 组件职责单一
  • 样式模块化
  • 类型定义完整
  • 文档注释清晰

4. 可扩展性

  • 主题色可配置
  • 组件参数灵活
  • 样式可覆盖
  • 功能可扩展

重要提醒:

  1. 始终遵循设计系统规范
  2. 确保深色模式兼容性
  3. 保持视觉一致性
  4. 优化用户体验
  5. 遵循响应式设计原则