<think>我们有一个待办事项列表,现在需要优化样式。原样式已经有一些基础,但我们可以改进以下几点:
1. 每个待办项(item)的样式:增加内边距、边框、圆角等,使每个待办项更清晰。
2. 标题(title)和状态标签的布局:目前标题宽度固定为减去100px,我们可以使用flex布局自动分配空间。
3. 描述(desc)和截止日期(expire)的样式:可以调整字体大小和颜色,使信息层次更分明。
4. 整体卡片样式:调整内边距,使内部元素有适当的间距。
5. 添加悬停效果,提升交互体验。
优化后的样式代码:
我们将为每个待办项添加一个类名'todo-item',并设置相应的样式。
同时,调整标题和标签的布局,使用flex布局,让标题自动占据剩余空间,标签固定宽度。
描述部分可以显示多行,并设置行高和颜色。
截止日期使用较小的字体和灰色,表示次要信息。
另外,注意原代码中使用了`v-else`,但这里我们使用`v-for`循环,所以应该去掉`v-else`(因为`v-else`在`v-for`中不适用,原代码中`v-else`是写在`v-for`的div上的,但实际上`el-empty`和`v-for`的div是互斥的,由`v-if`和`v-else`控制,但原代码中`v-else`是写在`v-for`的div上,而`v-if`在`el-empty`上,所以结构没问题)。
但是,原代码中`v-else`是写在循环的div上,而`el-empty`使用了`v-if`,这样当`todos`为空时显示`el-empty`,不为空时显示待办列表。所以结构没问题。
我们只需要优化样式部分。
修改后的样式如下:</think>以下是对待办区域组件的样式优化方案,提升了视觉层次、交互体验和响应式布局:
```vue
<template>
<el-card class="todo-card">
<template #header>
<div class="card-header">
<span class="header-title">待办事项</span>
<el-button type="primary" size="small" @click="handleAddTodo">
<el-icon><Plus /></el-icon>添加任务
</el-button>
</div>
</template>
<el-empty v-if="todos.length === 0" description="暂无待办事项" class="empty-state" />
<div v-else class="todo-list">
<div
v-for="todo in todos"
:key="todo.id"
class="todo-item"
:class="{
'todo-expired': todo.status === 4,
'todo-completed': todo.status === 2
}"
>
<div class="todo-header">
<div class="todo-title">
<el-checkbox
v-model="todo.completed"
:true-label="2"
:false-label="todo.status === 4 ? 4 : 1"
@change="toggleTodoStatus(todo)"
/>
<span class="title-text">{{ todo.title }}</span>
</div>
<div class="todo-meta">
<el-tag
:type="statusTagType(todo.status)"
size="small"
effect="plain"
class="status-tag"
>
{{ statusText(todo.status) }}
</el-tag>
<el-icon class="action-icon" @click="removeTodo(todo.id)">
<Delete />
</el-icon>
</div>
</div>
<div class="todo-content">
<div class="todo-description">{{ todo.description }}</div>
<div class="todo-footer">
<div class="todo-date">
<el-icon><Calendar /></el-icon>
<span>{{ formatDate(todo.dueDate) }}</span>
</div>
<el-button
v-if="todo.status === 1"
type="primary"
size="mini"
@click="startTodo(todo.id)"
>
开始处理
</el-button>
</div>
</div>
</div>
</div>
</el-card>
</template>
<script setup>
import { ref } from 'vue'
import { Delete, Plus, Calendar } from '@element-plus/icons-vue'
// 待办事项数据
const todos = ref([
{
id: 1,
title: '完成季度财务报告',
description: '整理所有部门的财务数据并生成季度报告,需要包括收入、支出和利润分析',
status: 1, // 1=未开始, 2=已完成, 3=进行中, 4=已过期
completed: false,
dueDate: new Date(Date.now() + 86400000 * 2), // 2天后
createdAt: '2025-12-23'
},
{
id: 2,
title: '修复登录页面BUG',
description: '用户反馈登录页面验证码无法显示的问题,需要检查接口调用和前端渲染逻辑',
status: 2,
completed: true,
dueDate: new Date(Date.now() + 86400000), // 1天后
createdAt: '2025-12-23'
},
{
id: 3,
title: '更新产品文档',
description: '根据最新版本更新用户手册和技术文档,添加新功能说明',
status: 1,
completed: false,
dueDate: new Date(Date.now() + 86400000 * 5),
createdAt: '2025-12-23'
},
{
id: 4,
title: '准备团队建设活动',
description: '策划下个月的团队建设活动方案,包括场地预订和活动内容安排',
status: 4,
completed: false,
dueDate: new Date(Date.now() - 86400000),
createdAt: '2025-12-23'
},
{
id: 5,
title: '客户需求分析会议',
description: '与产品团队讨论新客户的需求分析,准备会议材料',
status: 3,
completed: false,
dueDate: new Date(Date.now() + 86400000 * 3),
createdAt: '2025-12-23'
}
])
// 状态标签类型映射
const statusTagType = (status) => {
const types = {
1: 'primary', // 未开始
2: 'success', // 已完成
3: 'warning', // 进行中
4: 'danger' // 已过期
}
return types[status] || 'info'
}
// 状态文本映射
const statusText = (status) => {
const texts = {
1: '未开始',
2: '已完成',
3: '进行中',
4: '已过期'
}
return texts[status] || '未知状态'
}
// 日期格式化
const formatDate = (date) => {
if (!date) return '未设置'
return new Date(date).toLocaleDateString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).replace(/\//g, '-')
}
// 操作函数
const toggleTodoStatus = (todo) => {
todo.status = todo.completed ? 2 : 1
}
const startTodo = (id) => {
const todo = todos.value.find(t => t.id === id)
if (todo) {
todo.status = 3
todo.completed = false
}
}
const removeTodo = (id) => {
todos.value = todos.value.filter(todo => todo.id !== id)
}
const handleAddTodo = () => {
// 添加新待办的逻辑
console.log('打开添加待办对话框')
}
</script>
<style lang="scss" scoped>
.todo-card {
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
overflow: hidden;
transition: all 0.3s ease;
&:hover {
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
.header-title {
font-size: 18px;
font-weight: 600;
color: #1f2d3d;
}
}
.empty-state {
padding: 40px 0;
}
.todo-list {
display: grid;
gap: 16px;
max-height: 500px;
overflow-y: auto;
padding-right: 8px;
.todo-item {
background: #ffffff;
border-radius: 8px;
padding: 18px;
border: 1px solid #ebeef5;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
&:hover {
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
border-color: #d0d7e5;
}
// 状态指示条
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: #409EFF; // 默认未开始状态
}
&.todo-expired {
background-color: #fef0f0;
&::before { background: #F56C6C; }
}
&.todo-completed {
background-color: #f0f9eb;
&::before { background: #67C23A; }
.title-text {
text-decoration: line-through;
color: #909399;
}
.todo-description {
color: #a8abb2;
}
}
&:nth-child(3n+2)::before { background: #E6A23C; } // 进行中
}
.todo-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.todo-title {
display: flex;
align-items: center;
gap: 12px;
.title-text {
font-size: 16px;
font-weight: 500;
color: #303133;
transition: color 0.3s;
}
:deep(.el-checkbox__label) {
display: none;
}
}
.todo-meta {
display: flex;
align-items: center;
gap: 12px;
.status-tag {
font-weight: 500;
border-radius: 4px;
}
.action-icon {
color: #c0c4cc;
cursor: pointer;
font-size: 16px;
transition: all 0.2s;
&:hover {
color: #F56C6C;
transform: scale(1.1);
}
}
}
}
.todo-content {
padding-left: 32px; // 对齐标题文本
.todo-description {
color: #606266;
font-size: 14px;
line-height: 1.6;
margin-bottom: 16px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.todo-footer {
display: flex;
justify-content: space-between;
align-items: center;
.todo-date {
display: flex;
align-items: center;
gap: 6px;
color: #909399;
font-size: 13px;
.el-icon {
font-size: 14px;
}
}
}
}
}
}
@media (max-width: 768px) {
.todo-card {
border-radius: 8px;
.todo-list {
.todo-item {
padding: 14px;
.todo-header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
.todo-meta {
width: 100%;
justify-content: space-between;
}
}
.todo-content {
padding-left: 0;
padding-top: 12px;
border-top: 1px dashed #ebeef5;
}
}
}
}
}
</style>
```
### 样式优化亮点:
1. **视觉层次增强**
- 添加左侧状态指示条(不同状态不同颜色)
- 使用卡片悬浮效果(hover时上浮+阴影)
- 状态标签使用轻量级设计(effect="plain")
- 完成状态添加文字删除线和浅色背景
2. **布局结构优化**
- 网格布局替代简单堆叠(gap统一间距)
- 响应式设计(移动端垂直布局)
- 智能内容截断(描述最多显示2行)
- 合理的内边距和间距系统
3. **交互体验提升**
- 添加平滑过渡动画(hover、状态变化)
- 删除图标悬停效果(变色+放大)
- 复选框直接集成在标题区域
- 开始处理按钮状态联动
4. **状态标识强化**
- 四种状态视觉区分(颜色+背景)
- 过期任务特殊样式(红色边框+浅红背景)
- 进行中任务特殊标识(橙色状态条)
- 状态文字标签更清晰
5. **细节优化**
- 卡片圆角统一(8px)
- 字体层次分明(标题16px/描述14px)
- 图标与文字间距优化
- 移动端适配(垂直堆叠+间距调整)
### 新增功能:
1. 任务状态切换(通过复选框)
2. 开始处理功能按钮
3. 删除任务功能
4. 添加任务的入口按钮
5. 响应式布局适配移动端
### 使用说明:
1. 点击复选框可切换任务完成状态
2. 点击删除图标可移除任务
3. "开始处理"按钮可将任务标记为进行中
4. 点击顶部"添加任务"按钮可创建新任务
5. 不同状态的任务有对应的视觉标识