Skip to content

实战项目:待办事项小程序(上)- 需求分析和页面设计

🚀 理论学了这么多,是时候做个真正的项目了!我们来做一个待办事项小程序

项目介绍

我们要做一个简单而实用的待办事项小程序,用户可以:

  • 添加待办事项
  • 标记完成状态
  • 编辑和删除事项
  • 按分类管理
  • 数据本地存储

这个项目会用到我们前面学过的所有知识点!

需求分析

功能需求

1. 核心功能

  • ✅ 添加新的待办事项
  • ✅ 查看待办事项列表
  • ✅ 标记事项为完成/未完成
  • ✅ 编辑事项内容
  • ✅ 删除事项

2. 扩展功能

  • ✅ 事项分类(工作、生活、学习等)
  • ✅ 优先级设置(高、中、低)
  • ✅ 截止日期设置
  • ✅ 搜索功能
  • ✅ 统计功能

3. 数据管理

  • ✅ 本地存储(使用 uni.storage)
  • ✅ 数据导入导出
  • ✅ 数据备份

页面结构

待办事项小程序
├── 首页(待办列表)
├── 添加/编辑页面
├── 分类管理页面
├── 统计页面
└── 设置页面

技术选型

前端技术

  • 框架:UniApp + Vue.js
  • 样式:SCSS + Flex布局
  • 图标:Unicode图标 + 自定义图标
  • 动画:CSS3动画 + UniApp动画API

数据存储

  • 本地存储:uni.setStorageSync / uni.getStorageSync
  • 数据结构:JSON格式

开发工具

  • IDE:HBuilderX
  • 调试:浏览器 + 微信开发者工具

数据结构设计

待办事项数据结构

javascript
const todoItem = {
  id: 'todo_1640995200000',        // 唯一ID(时间戳)
  title: '完成UniApp教程',          // 事项标题
  description: '学习UniApp开发',    // 详细描述
  category: 'study',               // 分类
  priority: 'high',                // 优先级:high/medium/low
  dueDate: '2024-12-20',          // 截止日期
  completed: false,                // 是否完成
  createdAt: '2024-12-15 10:30',  // 创建时间
  updatedAt: '2024-12-15 10:30',  // 更新时间
  completedAt: null                // 完成时间
}

分类数据结构

javascript
const categories = [
  { id: 'work', name: '工作', color: '#ff6b6b', icon: '💼' },
  { id: 'life', name: '生活', color: '#4ecdc4', icon: '🏠' },
  { id: 'study', name: '学习', color: '#45b7d1', icon: '📚' },
  { id: 'health', name: '健康', color: '#96ceb4', icon: '💪' },
  { id: 'other', name: '其他', color: '#feca57', icon: '📝' }
]

页面设计

1. 首页(待办列表)

页面功能

  • 显示待办事项列表
  • 快速标记完成状态
  • 搜索和筛选
  • 添加新事项

页面布局

vue
<template>
  <view class="todo-home">
    <!-- 头部统计 -->
    <view class="header-stats">
      <view class="stat-item">
        <text class="stat-number">\{\{ totalTodos \}\}</text>
        <text class="stat-label">总计</text>
      </view>
      <view class="stat-item">
        <text class="stat-number">\{\{ pendingTodos \}\}</text>
        <text class="stat-label">待完成</text>
      </view>
      <view class="stat-item">
        <text class="stat-number">\{\{ completedTodos \}\}</text>
        <text class="stat-label">已完成</text>
      </view>
    </view>
    
    <!-- 搜索栏 -->
    <view class="search-bar">
      <input 
        v-model="searchKeyword"
        placeholder="搜索待办事项..."
        class="search-input"
      />
      <button class="filter-btn" @click="showFilter">筛选</button>
    </view>
    
    <!-- 分类标签 -->
    <scroll-view scroll-x class="category-tabs">
      <view 
        class="category-tab"
        :class="{ active: selectedCategory === 'all' }"
        @click="selectCategory('all')"
      >
        全部
      </view>
      <view 
        v-for="category in categories"
        :key="category.id"
        class="category-tab"
        :class="{ active: selectedCategory === category.id }"
        @click="selectCategory(category.id)"
      >
        \{\{ category.icon \}\} \{\{ category.name \}\}
      </view>
    </scroll-view>
    
    <!-- 待办列表 -->
    <scroll-view scroll-y class="todo-list">
      <view 
        v-for="todo in filteredTodos"
        :key="todo.id"
        class="todo-item"
        :class="{ completed: todo.completed }"
      >
        <!-- 完成状态 -->
        <view 
          class="todo-checkbox"
          :class="{ checked: todo.completed }"
          @click="toggleTodo(todo.id)"
        >
          <text v-if="todo.completed" class="check-icon">✓</text>
        </view>
        
        <!-- 事项内容 -->
        <view class="todo-content" @click="editTodo(todo)">
          <text class="todo-title">\{\{ todo.title \}\}</text>
          <text v-if="todo.description" class="todo-desc">\{\{ todo.description \}\}</text>
          <view class="todo-meta">
            <text class="todo-category">\{\{ getCategoryName(todo.category) \}\}</text>
            <text v-if="todo.dueDate" class="todo-date">\{\{ todo.dueDate \}\}</text>
            <text class="todo-priority" :class="todo.priority">\{\{ getPriorityText(todo.priority) \}\}</text>
          </view>
        </view>
        
        <!-- 操作按钮 -->
        <view class="todo-actions">
          <button class="action-btn edit" @click="editTodo(todo)">编辑</button>
          <button class="action-btn delete" @click="deleteTodo(todo.id)">删除</button>
        </view>
      </view>
      
      <!-- 空状态 -->
      <view v-if="filteredTodos.length === 0" class="empty-state">
        <text class="empty-icon">📝</text>
        <text class="empty-text">暂无待办事项</text>
        <button class="add-first-btn" @click="addTodo">添加第一个待办</button>
      </view>
    </scroll-view>
    
    <!-- 添加按钮 -->
    <view class="fab" @click="addTodo">
      <text class="fab-icon">+</text>
    </view>
  </view>
</template>

2. 添加/编辑页面

页面功能

  • 输入事项标题和描述
  • 选择分类和优先级
  • 设置截止日期
  • 保存或更新事项

表单设计

vue
<template>
  <view class="todo-form">
    <view class="form-header">
      <button class="cancel-btn" @click="goBack">取消</button>
      <text class="form-title">\{\{ isEdit ? '编辑待办' : '新增待办' \}\}</text>
      <button class="save-btn" @click="saveTodo" :disabled="!canSave">保存</button>
    </view>
    
    <view class="form-content">
      <!-- 标题输入 -->
      <view class="form-group">
        <text class="form-label">标题 *</text>
        <input 
          v-model="formData.title"
          placeholder="请输入待办事项标题"
          class="form-input"
          maxlength="50"
        />
      </view>
      
      <!-- 描述输入 -->
      <view class="form-group">
        <text class="form-label">描述</text>
        <textarea 
          v-model="formData.description"
          placeholder="请输入详细描述(可选)"
          class="form-textarea"
          maxlength="200"
        />
      </view>
      
      <!-- 分类选择 -->
      <view class="form-group">
        <text class="form-label">分类</text>
        <picker 
          :range="categoryOptions" 
          range-key="name"
          :value="categoryIndex"
          @change="onCategoryChange"
        >
          <view class="picker-input">
            <text>\{\{ selectedCategory.icon \}\} \{\{ selectedCategory.name \}\}</text>
            <text class="arrow">></text>
          </view>
        </picker>
      </view>
      
      <!-- 优先级选择 -->
      <view class="form-group">
        <text class="form-label">优先级</text>
        <view class="priority-options">
          <view 
            v-for="priority in priorityOptions"
            :key="priority.value"
            class="priority-option"
            :class="{ active: formData.priority === priority.value }"
            @click="selectPriority(priority.value)"
          >
            <text class="priority-dot" :class="priority.value"></text>
            <text>\{\{ priority.label \}\}</text>
          </view>
        </view>
      </view>
      
      <!-- 截止日期 -->
      <view class="form-group">
        <text class="form-label">截止日期</text>
        <picker 
          mode="date"
          :value="formData.dueDate"
          :start="today"
          @change="onDateChange"
        >
          <view class="picker-input">
            <text>\{\{ formData.dueDate || '请选择截止日期' \}\}</text>
            <text class="arrow">></text>
          </view>
        </picker>
      </view>
    </view>
  </view>
</template>

样式设计

设计原则

  1. 简洁明了:界面简洁,操作直观
  2. 色彩搭配:使用温和的色彩,不同优先级用不同颜色
  3. 响应式:适配不同屏幕尺寸
  4. 动画效果:适当的动画提升用户体验

色彩方案

scss
// 主色调
$primary-color: #007aff;
$success-color: #34c759;
$warning-color: #ff9500;
$danger-color: #ff3b30;

// 优先级颜色
$high-priority: #ff3b30;
$medium-priority: #ff9500;
$low-priority: #34c759;

// 分类颜色
$work-color: #ff6b6b;
$life-color: #4ecdc4;
$study-color: #45b7d1;
$health-color: #96ceb4;
$other-color: #feca57;

// 背景色
$bg-color: #f8f9fa;
$card-bg: #ffffff;
$border-color: #e9ecef;

组件样式

scss
// 待办事项卡片
.todo-item {
  background: $card-bg;
  border-radius: 12px;
  padding: 16px;
  margin-bottom: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  display: flex;
  align-items: center;
  transition: all 0.3s ease;
  
  &.completed {
    opacity: 0.6;
    
    .todo-title {
      text-decoration: line-through;
      color: #999;
    }
  }
  
  &:active {
    transform: scale(0.98);
  }
}

// 复选框
.todo-checkbox {
  width: 24px;
  height: 24px;
  border: 2px solid #ddd;
  border-radius: 50%;
  margin-right: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
  
  &.checked {
    background: $success-color;
    border-color: $success-color;
    
    .check-icon {
      color: white;
      font-size: 14px;
      font-weight: bold;
    }
  }
}

// 浮动按钮
.fab {
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 56px;
  height: 56px;
  background: $primary-color;
  border-radius: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
  
  .fab-icon {
    color: white;
    font-size: 24px;
    font-weight: bold;
  }
  
  &:active {
    transform: scale(0.95);
  }
}

项目文件结构

todo-app/
├── pages/
│   ├── index/              # 首页
│   │   └── index.vue
│   ├── add-todo/           # 添加/编辑页面
│   │   └── add-todo.vue
│   ├── categories/         # 分类管理
│   │   └── categories.vue
│   ├── statistics/         # 统计页面
│   │   └── statistics.vue
│   └── settings/           # 设置页面
│       └── settings.vue
├── components/             # 自定义组件
│   ├── TodoItem.vue       # 待办事项组件
│   ├── CategoryPicker.vue # 分类选择器
│   └── PriorityPicker.vue # 优先级选择器
├── utils/                  # 工具函数
│   ├── storage.js         # 存储工具
│   ├── date.js            # 日期工具
│   └── constants.js       # 常量定义
├── static/                 # 静态资源
│   └── images/
└── styles/                 # 全局样式
    ├── variables.scss     # 变量定义
    ├── mixins.scss        # 混合器
    └── common.scss        # 通用样式

下一步计划

在下一篇文章中,我们将:

  1. 实现数据管理逻辑
  2. 完成核心功能开发
  3. 添加本地存储
  4. 实现搜索和筛选
  5. 优化用户体验

小结

今天我们完成了:

  • ✅ 项目需求分析
  • ✅ 技术选型
  • ✅ 数据结构设计
  • ✅ 页面结构规划
  • ✅ UI设计方案
  • ✅ 项目文件结构

设计要点

  • 功能要简单实用
  • 界面要简洁美观
  • 数据结构要合理
  • 代码结构要清晰

下一篇预告

下一篇《实战项目:待办事项小程序(中)- 功能实现和数据处理》,我们将开始编写具体的代码,实现所有核心功能。


好的设计是成功的一半,接下来让我们把设计变成现实!