# 工作清单开发文档

页面地址:工作清单 (opens new window)

# 目录

[toc]

# *.更新日志

版本 内容 时间
0.9 完成了大体内容与主要功能 2021-07-03
0.95 初步完成移动端适配、回车键的交互效果、同时调整了部分变量名,使其更加统一。调整了界面和按钮的布局 2021-07-04
bug修复 修复了移动端输入框中看不到所输入的文本的bug 2021-07-08
bug修复 添加子事项回车键无法使用的bug 2021-07-08
代码优化 使用数组方法 splice() 完成对数组的操作 2021-07-08
功能优化 str.trim() 2021-07-08
bug修复 删除分类、事项之后,对后续分类、事项、子事项进行操作时,出现目标错误的情况,由删除之后未重新定位序号导致的 2021-07-14

# 待完成事项:

  • 删除按钮的二次确认
  • 交互的提示
  • 使用解构赋值优化代码

# Issue

  • 移动端输入框看不到所输入的文本
    • 进一步测试,浅色主题下,可以看到输入框的文本正常
    • 初步判断是因为深色主题下,文本颜色为白色导致输入框看不到所输入的颜色
    • 已大体解决
  • 代码,通过使用 const xxx = this.$store.state.xxxx,然后再进行修改,让代码可读性提高的可行性?
  • 部分移动端的按钮图标、渐变文字会丢失
    • 框架官方bug……
  • 输入框输入空格便可以完成输入的问题(到底允不允许这种情况?)
    • str.trim()
  • 添加子事项使用回车键无法正常添加的异常:“this.createMatterSon is not a function”
    • 已解决
  • 关于数组方法的修改——原方法过于累赘,可以直接使用 splice()
  • 关于移动端至今为止的各种情况
    • 渐变字体无法正常展示渐变色(华为浏览器)
    • 按钮图标丢失(小米、safari)
    • 输入框背景色为白色(华为)
  • 事项修改优先级——Cannot read property 'matters' of undefined
    • 当删除了分类、事项的时候
      • 后续的分类、事项、子事项的ID需要进行修改
      • 已解决
  • 当同时打开两个及以上该页面时,在其中一个页面进行待办事项的修改之后,另外一个页面再进行修改是否会出现bug?
    • 预测:因为是中间经过了vuex之后再同步到本地上的,所以可能会出现互相覆盖的现象。
    • 测试结果:会互相覆盖。

# 0.前言

我原本想要写点什么东西……

# 1.系统规格说明

# 1.1:系统功能和目标

toDoList——一个能够记录更多细节和更清晰更详细内容的待办事项列表

# 1.2:主要功能

功能 描述 备注
登录/注册 用户可以登录/注册账号 暂不具备实际作用
新建分类 用户可以根据自己的习惯新建一个分类
新建事项 用户可以在分类下新建一个事项
新建子事项 用户可以在一个事项下新建子事项,用于对事项的详细描述、规划
修改事项 用户可以修改事项的内容(包括子事项)
完成事项 用户可以勾选事项表示事项已经完成(包括子事项)
删除事项 用户可以删除不需要的事项
云同步 用户可以根据账号信息同步事项列表 暂不具备实际作用

# 2.总体设计

# 2.1:系统输入

  1. 新建分类信息
  2. 新建事项信息
  3. 新建子事项信息
  4. 修改事项信息
  5. 完成事项请求
  6. 删除事项请求
  7. 用户注册信息
  8. 用户登录信息
  9. 云同步请求

# 2.2:系统输出

  1. 事项列表
  2. 用户信息

# 3.概要设计

# 3.1:操作流程

操作流程图

# 3.2:用例图

用例图

# 4.数据库设计

# 4.1 E-R图

E-R图

# 4.2 用户基础信息关系模式定义——userInfo

字段名称 字段代码 字段类型 数据约束
用户账号 account Varchar(6,16) 主键
用户密码 password Varchar(6,18) Not Null
用户ID userID Int 外键

# 4.3 代办事项列表关系模式定义——matterList

字段名称 字段代码 字段类型 数据约束
用户ID userID Int 主键
分类 classification Null

# 4.4 分类关系模式定义——classification

字段名称 字段代码 字段类型 数据约束
分类名 label Varchar(1,20) Not Null
代办事项 matters Null

# 4.5 代办事项关系模式定义——matters

字段名称 字段代码 字段类型 数据约束
事项名 label Varchar(1,20) Not Null
事项描述 describe Varchar(100) Null
创建时间 createTime Datetime Not Null
拟完成时间 completionTime Datetime Null
优先级 priority Int Null
完成情况 completion Bit Not Null
代办子事项 matter-sons

# 4.6 代办子事项关系模式定义——matter-sons

字段名称 字段代码 字段类型 数据约束
事项名 label Varchar(1,20) Not Null
事项描述 describle Varchar(100) Null
创建时间 createTime Datetime Not Null
拟完成时间 completionTime Datetime Null
优先级 priority Int Null
完成情况 completion Bit Not Null

# 4.7 数据结构设计

user = {
	account: '',
	password: '',
	userID: 0,
	mattersLists: {}
}

mattersList = {
    classifications: []
}

classification = {
    label: '',
    matters: []
}

matter = {
	label: '',
	describe: '',
	createTime: '',
	completionTime: '',
	priority: 0,
	completion: false,
	matter-sons: []
}

matter_son = {
	label: '',
	describe: '',
	createTime: '',
	completionTime: '',
	priority: 0,
	completion: false
}

# 5.页面设计

简单线框图(因为工具的问题,暂不进行细化)

用例图

# 6.编程实现

# 6.1 项目创建与依赖导入

使用vue.js技术,vue版本为 3.0.0, @Vue/cli 版本为 4.5.13

使用naive-ui框架——主要是对naive-ui的一次尝试

naive-ui 官网:https://www.naiveui.com/zh-CN/light (opens new window)

同时使用 vuex,以支持数据的存储。

提示

⚠️ 因为暂不打算开发后端,所以所有的数据都是以本地缓存的方式进行存储

提示

vuex的版本是4.0.2

且在vue2与vue3中,vuex的使用方式不同

// vue3 store/index.js
import { createStore } from 'vuex'

# 6.2 全局数据准备 (版本:0.9)

鉴于暂不搭建后端的缘故、所以登录相关的操作暂不考虑,而相关数据也以本地存储的方式(localStorage)进行。

为了让数据更加便于获取与更新,这里使用vuex进行数据的存储

export default createStore({
	state: {
		mattersList: {
			classifications: []
		}
	},
	mutations: {
		// 获取事项列表
		getMattersList(state, List) {
			state.mattersList = List
		},
		//============================================//
		// 分类相关——增删改
		// 添加新的分类
		pushClassification(state, classification) {
			state.mattersList.classifications.push(classification)
		},
		// 移除分类
		removeClassification(state, classIndex) {
			state.mattersList.classifications.splice(classIndex, 1)
		},
		// 修改分类名
		editClassification(state, editClassForm) {
			let label = editClassForm.label
			let index = editClassForm.index
			state.mattersList.classifications[index].label = label
		},
		// 分类相关操作到此截至

		//===============================================//
		// 待办事项相关——增删改
		// 增加新的待办事项
		pushMatter(state, pushMatterForm) {
			// classIndex, matter
			let classIndex = pushMatterForm.classIndex
			let matter = pushMatterForm
			state.mattersList.classifications[classIndex].matters.push(matter)
		},
		// 删除待办事项
		removeMatter(state, removeMatterForm) {
			// classIndex, matterIndex
			let classIndex = removeMatterForm.classIndex
			let matterIndex = removeMatterForm
			const matters = state.mattersList.classifications[classIndex].matters
			matters.splice(matterIndex, 1)
		},
		// 修改待办事例
		editMatter(state, editMatterForm) {
			// classIndex, matterIndex, matter
			let classIndex = editMatterForm.classIndex
			let matterIndex = editMatterForm.matterIndex
			// 获取所修改的待办事项
			const matter = state.mattersList.classifications[classIndex].matters[matterIndex]
			matter.label = editMatterForm.label
			matter.describe = editMatterForm.describe
			matter.completionTime = editMatterForm.completionTime
			matter.priority = editMatterForm.priority
		},
		// 待办事例完成情况修改
		complateMatter(state, form) {
			let classIndex = form.classIndex
			let matterIndex = form.matterIndex
			// 获取所修改的待办事项
			const matter = state.mattersList.classifications[classIndex].matters[matterIndex]
			if (matter.completion) {
				matter.completion = false
			} else {
				matter.completion = true
			}
		},

		// 待办事项相关操作到此截至

		//==================================================//
		// 待办子事项相关——增删改
		// 增加新的待办子事项
		pushMatterSon(state, pushMatterSonForm) {
			// classIndex, matterIndex, matterSon
			let classIndex = pushMatterSonForm.classIndex
			let matterIndex = pushMatterSonForm.matterIndex
			let matterSon = pushMatterSonForm
			state.mattersList.classifications[classIndex].matters[matterIndex].matterSons.push(matterSon)
		},
		// 删除待办子事项
		removeMatterSon(state, removeMatterSonForm) {
			// classIndex, matterIndex, matterSonIndex
			let classIndex = removeMatterSonForm.classIndex
			let matterIndex = removeMatterSonForm.matterIndex
			let matterSonIndex = removeMatterSonForm.matterSonIndex
			// 获取所删除的子事项所在的子事项组
			const matterSons = state.mattersList.classifications[classIndex].matters[matterIndex].matterSons
			matterSons.splice(matterSonIndex, 1)
		},
		// 修改待办子事项
		editMatterSon(state, editMatterSonForm) {
			// classIndex, matterIndex, matterSonIndex, matterSon
			let classIndex = editMatterSonForm.classIndex
			let matterIndex = editMatterSonForm.matterIndex
			let matterSonIndex = editMatterSonForm.matterSonIndex
			// 获取到当前修改的子事项
			const matterSon = state.mattersList.classifications[classIndex].matters[matterIndex].matterSons[
				matterSonIndex]
			matterSon.label = editMatterSonForm.label
			matterSon.describe = editMatterSonForm.describe
			matterSon.completionTime = editMatterSonForm.completionTime
			matterSon.priority = editMatterSonForm.priority
		},
		// 子事项完成情况修改
		complateMatterSon(state, form) {
			let classIndex = form.classIndex
			let matterIndex = form.matterIndex
			let matterSonIndex = form.matterSonIndex
			// 获取到当前修改的子事项
			const matterSon = state.mattersList.classifications[classIndex].matters[matterIndex].matterSons[
				matterSonIndex]
			if (matterSon.completion) {
				matterSon.completion = false
			} else {
				matterSon.completion = true
			}
		}
	}
})

同时,我们也可以准备以下类,以便于添加数据。

// 分类
export class Classification {
	constructor(label) {
		this.label = label
		this.matters = []
	}
}

// 待办事项
export class Matter {
	constructor(classIndex, label, describe, createTime, completionTime, priority) {
		this.classIndex = classIndex
		this.label = label
		this.describe = describe
		this.createTime = createTime
		this.completionTime = completionTime
		this.priority = priority
		this.completion = false
		this.matterSons = []
	}
}
// 代办子事项
export class matterSon {
	constructor(classIndex, matterIndex, label, describe, createTime, completionTime, priority) {
		this.classIndex = classIndex
		this.matterIndex = matterIndex
		this.label = label
		this.describe = describe
		this.createTime = createTime
		this.completionTime = completionTime
		this.priority = priority
		this.completion = false
	}
}

测试是否能够创建类的实例对象

classtest () {
	let classification = new Classfication('试试');
	console.log(classification);
}

# 6.3 主题 (版本:0.9)

为了使用 naive-ui提供的主题样式,所以需要配置naive-ui的主题

<!-- App.vue -->
<template>
	<n-config-provider :theme="themeData" :locale="zhCN" :date-locale="dateZhCN">
    	<n-space vertical>
    		<n-button>naive-ui</n-button>
             <n-button type="success" @click="themetest()">主题切换测试</n-button>
    	</n-space>
    </n-config-provider>
</template>

<script>
import { defineComponent } from 'vue'
import { NConfigProvider, darkTheme, zhCN, dateZhCN} from 'naive-ui'
import { NSpace, NButton } from 'naive-ui'
export default defineComponent({
    name: 'App',
    components: {
        NConfigProvider,
        NSpace,
        NButton
    },
    setup () {
        return {
            darkTheme,
            zhCN,
            dateZhCN
        }
    },
    data () {
        return {
            // 用于保存主题,以实现主题切换
            themeData: darkTheme
        }
    },
    methods: {
        // 用来测试主题切换的事件
        themetest () {
            // n-config-provider 的 theme属性,如果为undefined,则为naive-ui的默认主题——亮色,不过在这里我默认的主题是暗色
            // 如果为darkTheme 则为暗色
            this.themeData = this.themeData === undefined?this.darkTheme:undefined
        }
    }
})
</script>

反复点击按钮,看看按钮的样式是否会改变?

因为打算切换主题,所以我们需要给背景添加样式

body {
  background: black;
  transition: all 1s;
  color: white;
}

同时在主题切换的按钮点击触发事件中,也要添加对背景颜色的修改

themetest () {
	this.themeData = this.themeData === undefined ? this.darkTheme : undefined
document.querySelector('body').style.background = document.querySelector('body').style
	.background === 'rgb(255, 254, 240)' ? 'black' : 'rgb(255, 254, 240)'
document.querySelector('body').style.color = document.querySelector('body').style.color ===
	'black' ? 'white' : 'black'
}

# 6.4 页眉 (版本:0.9)

既然采用了naive-ui框架,那本着不重复造轮子的心态,打算直接采用……嗯?

naive-ui没有提供页眉或者顶部导航栏?

只提供了侧边导航栏,这个与期待不太一样。

那就只能动手自己做一个,虽然内容本身也不多

# 6.4.1 组件结构

<!-- header.vue -->
<div class="headerNav">
	<div class="navContainer">
        <!-- 个人logo -->
		<div class="logo">
			<img src="../assets/Logo.png">
		</div>
         <!-- 导航栏右侧内容 -->
		<div class="navRight">
			<n-button style="margin-right: 20px;" text v-if="isdark" @click="themechange()">浅色?</n-button>
			<n-button style="margin-right: 20px;" text v-if="!isdark" @click="themechange()">深色?</n-button>
			<a target="_blank"
				href="https://eve-wings.github.io/Eve-Wings/guide/personal-works/%E7%A8%8B%E5%BA%8F/%E5%B7%A5%E4%BD%9C%E6%B8%85%E5%8D%95/#_0-%E5%89%8D%E8%A8%80">
				<n-icon size="25">
					<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
						viewBox="0 0 496 512">
                          <!-- 太长了所以svg内容略微省略了 -->
						<path fill="currentColor"></path>
					</svg>
				</n-icon>
			</a>
		</div>
	</div>
</div>

# 6.4.2 样式

Logo的图片,是导入来自个人github的logo,然后再使用滤镜进行反色以适应暗色背景

.headerNav {
  width: 100%;
  position: relative;
  top: 0;
  left: 0;
  min-height: 50px;
  border-bottom: 2px solid #bbb;
  color: inherit;
  background: inherit;
  transition: all 1s;

  .navContainer {
    width: 85%;
    margin: 0 auto;
    padding: 0 15px;
    // 使用弹性盒进行布局
    display: flex;
    align-items: center;

    .logo {
      height: 60px;
      display: flex;

      img {
        transition: all 1s;
        max-height: 60px;
        // 反色滤镜
        filter: invert();
      }
    }

    .navRight {
      display: flex;
      margin-left: auto;
      margin-right: 5px;

      a {
        color: #aaa;

        &:hover {
          color: #363636
        }
      }
    }
  }
}

# 6.5 创建新分类 (版本:0.9)

# 6.5.1 主体 main

<headerNav></headerNav>
<div id="main">
    <!-- transition 为过度做准备 -->
    <transition name="Slide-left"  mode="out-in"></transition>
</div>
// 主体内容,左右两侧适当留空,以便添加其他的内容
// 当且尚未进行移动端适配
#main{
  width: 70%;
  margin: 20px auto;
}

# 6.5.2 卡片card

使用naive-ui提供的card卡片组件作为容器,先写没有内容的情况

<!-- 这个卡片将在没有任何分类的时候显示 -->
<n-card title="你尚未添加任何的分类,请您添加分类" id="NoList">
	<template #header-extra>
         <!-- 这里使用的是naive-ui提供的卡片的插槽功能 -->
		<n-button circle style="font-size: 1.5rem;" @click="showClassForm">+</n-button>
	</template>
</n-card>

# 6.5.3 遮罩层

我们需要通过这个按钮,弹出“新建分类”的面板,同时需要遮罩层

<!-- 遮罩层 -->
<div id="shadow" v-if="isShadow"></div>
// 遮罩层样式
#shadow {
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
  z-index: 998;
  background-color: rgba(0, 0, 0, 0.7);
}
// 在data中添加数据
isShadow: false
// 为按钮绑定事件,用来测试遮罩层的情况
createClass () {
    this.isShadow = !this.isShadow
}

可以正常显示遮罩层,但是直接添加还是有点突兀,我们给它添加动画

<transition name="fade-display">
	<div id="shadow" v-if="isShadow"></div>
</transition>

# 6.5.4 添加新分类表单

<transition name="Slide-bottom">
	<div id="createClassForm" v-if="isCreateClass">
		<n-card title="创建新的分类">
			<template #header-extra>
				<!-- 取消按钮 -->
				<n-button circle @click="isCreateClass=false;isShadow=false">X</n-button>
			</template>
			<n-form :model="createClassForm" ref="createClassFormRef">
				<n-form-item label="分类名">
					<n-input v-model:value="createClassForm.label"></n-input>
				</n-form-item>
				<n-form-item>
					<!-- 提交按钮 -->
					<n-button style="margin-left: auto;" @click="pushClass()" :disabled="createClassForm.label === ''">
						创建</n-button>
				</n-form-item>
			</n-form>
		</n-card>
	</div>
</transition>

样式:

// 表单样式
#createClassForm {
  border-radius: 3px;
  width: 45%;
  position: absolute;
  left: 28%;
  top: 25%;
  z-index: 999;
  box-shadow: darken(@lightGreen, 10%) 0px 0px 15px;
}

# 6.5.5 按钮绑定事件:

pushClass() {
	let label = this.createClassForm.label
    // 通过类创建实例对象
	let classification = new Classification(label)
    // 更改store数据
	this.$store.commit('pushClassification', classification)
    // 表单初始化
	this.createClassForm.label = ''
	this.isShadow = !this.isShadow
	this.isCreateClass = !this.isCreateClass
}

当分类被创建之后,是事项列表中的分类长度也不为零,不能再提示没有分类了

<!-- 当事项列表中没有任何事项时 -->
<!-- 在这里因为一开始v-if没有写在div上导致动画效果出不来……太蠢了 -->
<transition name="Slide-left" mode="out-in">
	<div v-if="$store.state.mattersList.classifications.length === 0">
		<n-card title="你尚未添加任何的分类,请您添加分类">
			<template #header-extra>
				<n-button circle style="font-size: 1.5rem;" @click="showClassForm()">+</n-button>
			</template>
		</n-card>
	</div>

	<!-- 当事项列表中有事项时 -->
	<div v-if="$store.state.mattersList.classifications.length !== 0">
		<n-card>
			<n-card v-for="(classification, classIndex) in $store.state.mattersList.classifications" :key="classIndex"
				class="classification">
				<n-collapse>
					<n-collapse-item :title="classification.label" :name="classIndex">
						<!-- 代办事项相关 -->
					</n-collapse-item>
					<!-- 用于修改分类的内容 -->
					<div class="classEditBtns">
						<n-button type="info" style="margin-right: 20px;" @click="showCreateMatterForm(classIndex)">+
							添加事项
						</n-button>
						<n-button type="warning" style="margin-right: 20px;" @click="showEditClassForm(classIndex)">编辑
						</n-button>
						<n-button type="error" @click="removeClass(classIndex)">删除</n-button>
					</div>
				</n-collapse>
			</n-card>
			<n-card title="创建新的分类" style="margin-top: 30px;">
				<template #header-extra>
					<n-button circle style="font-size: 1.5rem;" @click="showClassForm()">+</n-button>
				</template>
			</n-card>
		</n-card>
	</div>
</transition>

# 6.6 展示代办事项列表 (版本:0.9)

# 6.6.1 Collapse 折叠面板组件

<!-- 展示待办事项列表 -->
<transition name="Slide-left">
	<div v-if="$store.state.mattersList.classifications.length !== 0">
		<n-card>
			<n-card v-for="(classification, classIndex) in $store.state.mattersList.classifications" :key="classIndex"
				class="classification">
				<n-collapse>
					<n-collapse-item :title="classification.label" :name="classIndex">
						<!-- 代办事项相关 -->
					</n-collapse-item>
					<!-- 用于修改分类的内容 -->
					<div class="classEditBtns">
						<n-button type="info" style="margin-right: 20px;" @click="showCreateMatterForm(classIndex)">+
							添加事项
						</n-button>
						<n-button type="warning" style="margin-right: 20px;" @click="showEditClassForm(classIndex)">编辑
						</n-button>
						<n-button type="error" @click="removeClass(classIndex)">删除</n-button>
					</div>
				</n-collapse>
			</n-card>
			<n-card title="创建新的分类" style="margin-top: 30px;">
				<template #header-extra>
					<n-button circle style="font-size: 1.5rem;" @click="showClassForm()">+</n-button>
				</template>
			</n-card>
		</n-card>
	</div>
    
    <!-- 省略 -->
</transition>

<!-- 编辑分类名 -->
<transition name="Slide-bottom">
	<div id="editClassForm" v-if="isEditClass">
		<n-card title="修改分类名">
			<template #header-extra>
				<n-button circle @click="isEditClass=false;isShadow=false">X</n-button>
			</template>
			<n-form :model="editClassForm">
				<n-form-item label="分类名">
					<n-input v-model:value="editClassForm.label"></n-input>
				</n-form-item>
				<n-form-item>
					<n-button style="margin-left: auto;" @click="editClass()" :disabled="editClassForm.label === ''">修改
					</n-button>
				</n-form-item>
			</n-form>
		</n-card>
	</div>
</transition>

# 6.6.2 按钮绑定的事件

// 修改分类
showEditClassForm(index) {
	this.editClassForm.index = index
	this.editClassForm.label = this.$store.state.mattersList.classifications[index].label
	this.isShadow = !this.isShadow
	this.isEditClass = !this.isEditClass
},
    // 实际上写到这里想起,在vuex中,mutations中的方法,是不能传递两个以上的参数的
    // 知识点不牢固……回去重新修改mutations中的方法
editClass() {
	this.$store.commit('editClassification', this.editClassForm)
	this.editClassForm = {
		index: -1,
		label: ''
	}
	this.isShadow = !this.isShadow
	this.isEditClass = !this.isEditClass
	this.saveLocal()
},

# 6.7 添加事项 (版本:0.9)

# 6.7.1 按钮与表单

在分类上添加新的按钮,添加事项

<div class="classEditBtns">
	<n-button type="info" style="margin-right: 20px;" @click="showCreateMatterForm(classIndex)">+ 添加事项</n-button>
	<n-button type="warning" style="margin-right: 20px;" @click="showEditClassForm(classIndex)">编辑</n-button>
	<n-button type="error" @click="removeClass(classIndex)">删除</n-button>
</div>

同时还有添加事项的表单

<transition name="Slide-bottom">
	<div id="createMatterForm" v-if="isCreateMatter">
		<n-card title="添加事项">
			<template #header-extra>
				<n-button circle @click="isCreateMatter=false;isShadow=false">X</n-button>
			</template>
			<n-form :model="createMatterForm">
				<n-form-item label="事项名" show-require-mark>
					<n-input v-model:value="createMatterForm.label" placeholder="请输入事项名——比如,我想吃白切鸡?" maxlength="20"
						show-count clearable></n-input>
				</n-form-item>
                 <!-- 使用栅格系统 -->
				<n-grid x-gap="12" :cols="2">
					<n-grid-item>
						<n-form-item label="优先级">
							<n-rate v-model:value="createMatterForm.priority" />
						</n-form-item>
					</n-grid-item>
					<n-grid-item>
						<n-form-item label="拟定完成时间">
							<n-date-picker v-model:value="createMatterForm.completionTime" type="date" clearable>
							</n-date-picker>
						</n-form-item>
					</n-grid-item>
				</n-grid>
				<n-form-item label="描述">
					<n-input type="textarea" maxlength="100" show-count v-model:value="createMatterForm.describe"
						placeholder="不妨描述一下事项?当然,在这里不写任何东西也不会有任何影响,实际上,烤全鸡也不错"></n-input>
				</n-form-item>
				<n-form-item>
					<n-button style="margin-left: auto;" @click="pushMatter()"
						:disabled="createMatterForm.label === ''">添加事项</n-button>
				</n-form-item>
			</n-form>
		</n-card>
	</div>
</transition>

# 6.7.2 数据

我们需要获得当天的时间,因此在页面加载的时候,通过created钩子,获取当天时间

created() {
	this.today = new Date().setHours(0, 0, 0, 0)
}

表单的数据如下:

createMatterForm: {
	classIndex: -1,
	label: '',
	describe: '',
	completionTime: this.today,
	priority: 0
}

# 6.7.3按钮绑定的事件:

// 添加事项
	showCreateMatterForm(index) {
         // 显示遮罩层和表单
		this.isShadow = !this.isShadow
		this.isCreateMatter = !this.isCreateMatter
         // 同时获取所处分类的索引值
		this.createMatterForm.classIndex = index
	},
	pushMatter() {
		let createTime = new Date().setHours(0, 0, 0, 0)
		let cMF = this.createMatterForm
         // 实例化对象
		let matter = new Matter(cMF.classIndex, cMF.label, cMF.describe, createTime, cMF.completionTime,
			cMF.priority)
         // 修改数据
		this.$store.commit('pushMatter', matter)
         // 遮罩层和表单关闭
		this.isShadow = !this.isShadow
		this.isCreateMatter = !this.isCreateMatter
         // 初始化
		this.createMatterForm = {
			classIndex: -1,
			label: '',
			describe: '',
			completionTime: this.today,
			priority: 0
		}
	}

# 6.8 展示事项 (版本:0.9)

在分类的基础上,通过v-for循环遍历classifications中的matters,从而获取所有的待办事项,并且进行展示

<n-collapse-item :title="classification.label" :name="classIndex">
	<!-- 代办事项相关 -->
	<n-card v-for="(matter, matterIndex) in classification.matters" :key="matterIndex" class="matter"
		:class="{compMatter:matter.completion}" :title="matter.label">
		<!-- 待办事项操作相关 -->
		<template #header-extra>
			<div style="margin-right: 20px">
				<n-switch :default-value="matter.completion" @update:value="matterComplet(classIndex, matterIndex)"
					size="small" />
			</div>
			<div style="margin-right: 20px">
				<n-rate size="small" :value="matter.priority" />
			</div>
			<div>
				<n-button type="info" style="margin-right: 20px;"
					@click="showCreateMatterSonForm(classIndex, matterIndex)">+ 添加子事项
				</n-button>
				<n-button type="warning" style="margin-right: 20px;" @click="showEditMatter(matterIndex,matter)">编辑
				</n-button>
				<n-button type="error" @click="removeMatter(classIndex, matterIndex)">删除
				</n-button>
			</div>
		</template>
		<span v-if="matter.describe !== '' ">描述: {{matter.describe}}</span>
		<n-collapse>
			<n-collapse-item title="查看子事项" :name="matterIndex" v-if="matter.matterSons.length !== 0">
				<!-- 代办子事项相关 -->
			</n-collapse-item>
		</n-collapse>
	</n-card>
	<!-- 代办事项结束 -->
</n-collapse-item>

同时我们需要绑定四个事件,分别是:事项完成情况的修改(switch开关组件)、删除待办事项、编辑代办事项、以及添加子事项

# 6.8.1 事项完成情况修改

removeMatter(classIndex, matterIndex) {
	let removeMatterForm = {
		classIndex: classIndex,
		matterIndex: matterIndex
	}
	this.$store.commit('removeMatter', removeMatterForm)
}

// store/index.js
complateMatter(state, matter) {
	let classIndex = matter.classIndex
	let matterIndex = matter.matterIndex
	let comp = state.mattersList.classifications[classIndex].matters[matterIndex].completion
	if (comp === true) {
		state.mattersList.classifications[classIndex].matters[matterIndex].completion = false
	} else {
        state.mattersList.classifications[classIndex].matters[matterIndex].completion = true
	}
}

# 6.8.2 删除待办事项

removeMatter(classIndex, matterIndex) {
	let removeMatterForm = {
		classIndex: classIndex,
		matterIndex: matterIndex
	}
	this.$store.commit('removeMatter', removeMatterForm)
}

# 6.8.3 编辑代办事项

需要获取到待办事项的详细信息,并且将其在编辑表单中展示出来

showEditMatter(matterIndex, matter) {
	this.isShadow = !this.isShadow
	this.isEditMatter = !this.isEditMatter
	this.editMatterForm.classIndex = matter.classIndex
	this.editMatterForm.matterIndex = matterIndex
	this.editMatterForm.label = matter.label
	this.editMatterForm.describe = matter.describe
	this.editMatterForm.completionTime = matter.completionTime
	this.editMatterForm.completion = matter.completion
	this.editMatterForm.priority = matter.priority
}

表单:

<transition name="Slide-bottom">
	<div id="editMatterForm" v-if="isEditMatter">
		<n-card title="编辑事项">
			<template #header-extra>
				<n-button circle @click="isEditMatter=false;isShadow=false">X</n-button>
			</template>
			<n-form :model="editMatterForm">
				<n-form-item label="事项名" show-require-mark>
					<n-input v-model:value="editMatterForm.label" placeholder="请输入事项名" maxlength="20" show-count
						clearable></n-input>
				</n-form-item>
				<n-grid x-gap="12" :cols="2">
					<n-grid-item>
						<n-form-item label="优先级">
							<n-rate v-model:value="editMatterForm.priority" />
						</n-form-item>
					</n-grid-item>
					<n-grid-item>
						<n-form-item label="拟定完成时间">
							<n-date-picker v-model:value="editMatterForm.completionTime" type="date" clearable>
							</n-date-picker>
						</n-form-item>
					</n-grid-item>
				</n-grid>
				<n-form-item label="描述">
					<n-input type="textarea" maxlength="100" show-count v-model:value="editMatterForm.describe">
					</n-input>
				</n-form-item>
				<n-form-item>
					<n-button style="margin-left: auto;" @click="editMatter()" :disabled="editMatterForm.label === ''">
						修改事项</n-button>
				</n-form-item>
			</n-form>
		</n-card>
	</div>
</transition>

提交修改:

editMatter() {
	this.$store.commit('editMatter', this.editMatterForm)
	this.editMatterForm = {
		classIndex: -1,
		MatterIndex: -1,
		label: '',
		describe: '',
		completionTime: this.today,
		completion: false,
		priority: 0
	}
	this.isShadow = !this.isShadow
	this.isEditMatter = !this.isEditMatter
}

# 6.8.4 添加子事项

// 添加子事项
showCreateMatterSonForm(classIndex, matterIndex) {
		this.isShadow = !this.isShadow
		this.isCreateMatterSon = !this.isCreateMatterSon
		this.createMatterSonForm.classIndex = classIndex
		this.createMatterSonForm.matterIndex = matterIndex
	},
	pushMatterSon() {
		let createTime = new Date().setHours(0, 0, 0, 0)
		let cMSF = this.createMatterSonForm
		let matter_son = new Matter_son(cMSF.classIndex, cMSF.matterIndex, cMSF.label, cMSF.describe,
			createTime, cMSF.completionTime, cMSF.priority)
		this.$store.commit('pushMatterSon', matter_son)
		this.isShadow = !this.isShadow
		this.isCreateMatterSon = !this.isCreateMatterSon
		this.createMatterSonForm = {
			classIndex: -1,
			matterIndex: -1,
			label: '',
			describe: '',
			completionTime: this.today,
			priority: 0
		}
	}

# 6.9 子事项

布局基本上与事项一致,略过

# 6.10 本地缓存 (版本:0.9)

// 本地缓存
saveLocal() {
	// 对象JSON化
	let str = JSON.stringify(this.$store.state.mattersList)
	// 本地缓存
	localStorage.setItem('mattersList', str)
}

在created钩子中

// created()
let obj = localStorage.getItem('mattersList')
if (obj !== null) {
	let mattersList = JSON.parse(obj)
	this.$store.commit('getMattersList', mattersList)
}

在所有增删改的方法中,调用 saveLocal()

# 6.11 布局调整、内容细化 (版本:0.9)

# 6.11.1过渡动画

// 动画

// 淡入淡出
.fade-display-enter-active {
  opacity: 0;
  transition: all 1s;
}

.fade-display-enter-to {
  opacity: 1;
}

.fade-display-leave-active {
  transition: all 1s;
  opacity: 1;
}

.fade-display-leave-to {
  opacity: 0;
}

// 从下往上滑入、上往下滑出
.Slide-bottom-enter-active {
  opacity: 0;
  transform: translateY(40px);
  transition: all 1s;
}

.Slide-bottom-enter-to {
  opacity: 1;
  transform: translateY(0);
}

.Slide-bottom-leave-active {
  transition: all 1s;
}

.Slide-bottom-leave-to {
  opacity: 0;
  transform: translateY(40px);
}

// 从左往右滑入、从右往左滑出
.Slide-left-enter-active {
  opacity: 0;
  transform: translateX(-80px);
  transition: all 1s;
}

.Slide-left-enter-to {
  opacity: 1;
  transform: translateX(0);
}

.Slide-left-leave-active {
  opacity: 1;
  transform: translateX(0);
  transition: all 1s;
}

.Slide-left-leave-to {
  opacity: 0;
  transform: translateX(-80px);
}

# 6.11.2时钟组件

<template>
	<div class="clock">
		<transition name="clock-open">
			<div v-if="timeMessage">
				<n-card>
					<n-gradient-text size="22" type="danger">已经{{time.getHours()}}点了哦</n-gradient-text>
				</n-card>
			</div>
		</transition>
		<transition name="clock-open" >
			<div v-if="isOpenClock">
				<n-card title="我的天啊,你都看看几点了" closable @close="openClock()"
					:segmented="{content: 'hard'}">
					<div style=" text-align: center; font-size: 16px;">
						<n-gradient-text :size="24" type="success"><n-time :time="time" type="datetime " format="yyyy-MM-dd HH:mm:ss" /></n-gradient-text>
					</div>
				</n-card>
			</div>
		</transition>
		<n-button text @click="openClock()">
			<n-gradient-text :size="16" type="success"><n-time :time="time" type="datetime " format="yyyy-MM-dd HH:mm:ss" /></n-gradient-text>
		</n-button>
	</div>
</template>

<script>
	import '../assets/css/clock.css'
	import {
		NCard,
		NButton,
		NTime,
		NGradientText
	} from 'naive-ui'
	export default {
		name: 'clock',
		components: {
			NCard,
			NButton,
			NTime,
			NGradientText
		},
		data() {
			return {
				time: '',
				isOpenClock: false,
				timeMessage: false,
			}
		},
		methods: {
			openClock() {
				this.isOpenClock = !this.isOpenClock
			}
		},
		created() {
			this.time = new Date()
			setInterval(() => {
				this.time = new Date()
				if(this.time.getMinutes() === 0 && this.time.getSeconds() === 0){
					this.timeMessage = true
					setTimeout(() => {
						this.timeMessage = false
					},5000)
				}
			}, 500)
		}
	}
</script>
.clock {
  position: fixed;
  bottom: 0;
  left: 0;
}

// 时钟打开
.clock-open-enter-active {
  opacity: 0;
  transform: translateY(-300px);
  transition: all 1s;
}

.clock-open-enter-to {
  opacity: 1;
  transform: translateY(0);
}

.clock-open-leave-active {
  transition: all 1s;
}

.clock-open-leave-to {
  opacity: 0;
  transform: translateY(-300px);
}

# 6.12 后续更新维护(2021/07/14)

# 6.12.1 回车键触发提交(0.95版本)

实际上使用的是vue提供的 @keyup.enter 绑定事件

绑定的事件可以在代码中搜索 keyup 关键字

# 6.12.2 移动端适配(0.95版本)

使用媒体监听基于原本的样式再整了一套移动端的样式

@meadia screen and (max-width: 768px){}

# 6.12.3 变量名调整(0.95版本)

matter_son 统一改为 matterSon

# 6.12.4 bug修复——移动端下,输入框无法看到其输入的文本(2021/07/07)

修改样式,媒体查询移动端

@media screen and (max-width:768px) {
    .n-input{
    background-color: rgba(170, 170, 170, 0.5);
    .n-input-wrapper{
      .n-input__input{
        .n-input__input-el{
          color: darken(@lightGreen, 10%)
        }
      }
      .n-input__textarea{
        .n-input__textarea-el{
          color: darken(@lightGreen, 10%)
        }
      }
    }
  }
}

# 6.12.5 代码优化(2021/07/08)

const matterSon = state.mattersList.classifications[classIndex].matters[matterIndex].matterSons[matterSonIndex]
// 通过这种方式获取到对象之后,对matterSon操作即可,可以提高可读性

# 6.12.6 输入框输入空格的问题(2021/07/08)

str.trim()	// 可以删除字符串中的前后空格串,返回新数组

# 6.12.7 删除分类、事项修改(2021/07/14)

removeClassification(state, classIndex) {
	state.mattersList.classifications.splice(classIndex, 1)
	// 将分类之后的分类中所有的classID进行修改
	state.mattersList.classifications.forEach((classification, classIndex) => {
		classification.matters.forEach(matter => {
			matter.classIndex = classIndex
			matter.matterSons.forEach(matterSon => {
				matterSon.classIndex = classIndex
			})
		})
	})
}

removeMatter(state, removeMatterForm) {
	// classIndex, matterIndex
	let classIndex = removeMatterForm.classIndex
	let matterIndex = removeMatterForm
	const matters = state.mattersList.classifications[classIndex].matters
	matters.splice(matterIndex, 1)
	// 删除待办事项之后,对该事项之后的事项的ID进行修改
	matters.forEach((matter, matterIndex) => {
		matter.matterSons.forEach(matterSon => {
			matterSon.matterIndex = matterIndex
		})
	})
}

# 6.13 项目感想

这里开始不正经了

  • 我原本是想着画那个么线框图之类的,但实际上我发现是我天真了……虽然有一定的美术基础,但是就那么几十分钟画的玩意加上又不是学设计的,线框图基本上就没有好好遵守,因为画的太差了……
    • 所以导致移动端适配不是第一时间就进行开发
  • 真的!一定要好好思考数据到底是怎么一个结构……
  • 关于naive-ui :我对这个框架还是比较看好,虽然部分内容不太好用,但是整体来说美观且复用性良好,而且,官方的文档写的十分的有趣。
更新时间: 7/17/2021, 5:42:59 PM