ToDoList
使用TS+vue实现如下效果。
包含内容块的增删改查,主要介绍过程中的一些重要的点和思想,代码在最后。
- 弹窗编辑块在添加和修改时均有使用,将弹框显示的状态放在vuex中管理;
- 增删改查各个方法在各个组件中使用,将封装的增删改查方法实例化放入vuex中;
- 编辑时编辑框的内容使用深拷贝;
- 将编辑时id和transData过渡数据放在vuex中;
- 通过editId判断是添加还是编辑,添加时editId初始化为null;
- 通过localStoreage存储数据,方法操作基于localStoreage
- itemData.ts、menu.ts存放最基本的数据结构及枚举结构,dataAction.ts存放操作方法
- 组件menuBar,menuList, Dialog三个组件
- 分类查询在dataAction.ts中添加方法,更改dataList,每次读取需要重新拿到所有的数据。
itemData.ts
import Category from "./enum";
// 数据类
class ItemData {
id!: number
categoryId!: number
title!: string
content!: string
createTime!: string
constructor(id: number = -1, categoryId: number = -1, title: string,
content: string, createTime: number = -1) {
this.id = id;
this.categoryId = categoryId;
this.title = title;
this.content = content;
this.createTime = this.dateFormate();
}
dateFormate(): string {
let time = new Date();
let str = time.getFullYear() + ":" + time.getMonth() + ":" + time.getDay();
return str;
}
}
export default ItemData;
dataAction.ts
import ItemData from "./itemData";
import Category from "@/store/enum";
// 数据操作
class dataAction {
dataKey!: string
dataList!: Array<ItemData>
constructor(dataKey: string = "dataList") {
this.dataKey = dataKey
// 读取数据
this.dataList = this.readData();
}
// 读取数据
readData(): any[] {
let strData: string | null = window.localStorage.getItem(this.dataKey);
let arr: any[] = strData ? JSON.parse(strData) : [];
return arr;
}
// 编辑数据
editData(itemData: ItemData) {
let editData: ItemData | undefined = this.dataList.find(item => {
return item.id === itemData.id;
});
if (editData) {
editData.categoryId = itemData.categoryId;
editData.title = itemData.title;
editData.content = itemData.content;
this.saveData(this.dataList);
}
}
// 删除数据
delData(id:number): boolean {
let index = this.dataList.findIndex(item => item.id === id);
if (index > -1) {
this.dataList.splice(index, 1);
this.saveData(this.dataList);
return true;
}
return false;
}
// 新增数据
addData(newData: ItemData): number {
let data = !this.dataList ? [] : this.dataList;
let newId = !data.length ? 1 : data[data.length - 1].id + 1;
newData.id = newId;
data.push(newData);
// 保存数据
this.saveData(data);
return newId;
}
saveData(strData: ItemData[]): void {
let str: string = JSON.stringify(strData);
window.localStorage.setItem(this.dataKey, str);
}
// 拿到类型
getCategory(cateId: Category): string {
const arrNames = ["生活", "学习", "娱乐"];
return arrNames[cateId];
}
// 分类查询
getTypeData(cateId: Category): Array<ItemData> {
this.dataList = this.readData();
cateId = Number(cateId);
if (cateId !== 3) {
this.dataList = this.dataList.filter(item => {
return item.categoryId === cateId;
});
}
return this.dataList;
}
}
export default dataAction;
store.ts
import Vue from 'vue';
import Vuex from 'vuex';
import dataAction from '@/store/dataAction';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
showDialog: false,
dataAction: new dataAction(),
transData: null,
editId: null
},
mutations: {
showEditData(state: any, editData: any) {
state.transData = editData;
},
},
actions: {
},
});
enum.ts
enum Category {
Work = 0,
Life = 1,
Study = 2,
All = 3
}
export default Category;
组件
home.vue
<template>
<div class="home">
<MenuBar />
<MenuList />
<Dialog />
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import Dialog from "@/components/Dialog.vue";
import MenuList from "@/components/MenuList.vue";
import MenuBar from "@/components/MenuBar.vue";
@Component({
components: {
Dialog,
MenuBar,
MenuList,
},
})
export default class Home extends Vue {}
</script>
menubar.vue
<template>
<div class="menu-bar">
<div class="logo">To Do List</div>
<div class="operate">
<button @click="add">添加</button>
<select v-model="type">
<option value = 3>全部</option>
<option value = 0>生活</option>
<option value = 1>学习</option>
<option value = 2>娱乐</option>
</select>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Watch } from 'vue-property-decorator';
@Component({
components: {}
})
export default class MenuBar extends Vue{
type: number = 3
@Watch("type") selectData(type) {
console.log(this.type);
this.$store.state.dataAction.getTypeData(this.type);
}
add(): void {
this.$store.state.editId = null;
this.$store.state.showDialog = true;
}
}
</script>
<style lang="scss">
.menu-bar {
display: flex;
justify-content: space-between;
align-items: center;
height: 50px;
width: 100%;
}
</style>
menuList.vue
<template>
<div class="menu-list">
<Block v-for="item in dataList" :key="item.id" :val="item" />
</div>
</template>
<script lang="ts">
import { Vue, Component, Watch } from 'vue-property-decorator';
import Block from "@/components/Block.vue";
import ItemData from "@/store/ItemData.ts";
@Component({
components: { Block }
})
export default class MenuList extends Vue{
dataList: Array<ItemData> = this.$store.state.dataAction.dataList;
@Watch("$store.state.dataAction.dataList") getData() {
this.dataList = this.$store.state.dataAction.dataList;
}
}
</script>
<style lang="scss">
.menu-list {
display: flex;
flex-wrap: wrap;
padding: 20px;
height: calc(100% - 50px);
box-sizing: border-box;
}
</style>
Block.vue
<template>
<div class="block">
<div class="head">
<div class="left">
<input class="title" v-model="val.title" />
</div>
<div class="right">
<button @click="edit(val.id)">编辑</button>
<button @click="del(val.id, val.title)">删除</button>
</div>
</div>
<div class="next">
<div class="left">
<span>{{val.createTime}}</span>
</div>
<div class="right">
<span>{{getCateName(val.categoryId)}}</span>
</div>
</div>
<div class="content">
<!-- <span>{{val.content}}</span> -->
<textarea v-model="val.content"></textarea>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator';
import {ItemData} from "@/store/itemData.ts";
@Component({
components: {}
})
export default class Block extends Vue{
@Prop(ItemData) val
getCateName(id: number): string {
return this.$store.state.dataAction.getCategory(id);
}
del(id: number, title: string) {
if (window.confirm(`确定删除${title}的内容吗?`)) {
this.$store.state.dataAction.delData(id);
}
}
edit(id: number) {
// 展示模态框
this.$store.state.showDialog = true;
this.$store.state.editId = id;
let item = this.$store.state.dataAction.dataList.find((item: any) => {
return id === item.id;
});
// 深拷贝
item = JSON.parse(JSON.stringify(item));
this.$store.commit("showEditData", item);
}
}
</script>
<style lang="scss">
.block {
flex: 0 1 auto;
width: 25%;
margin-right: 20px;
.head, .next {
display: flex;
justify-content: space-between;
align-items: center;
.left {
flex: auto;
input {
width: 100%;
}
}
.right {
}
}
.next {
.left {
text-align: left;
}
}
.content {
textarea {
width: 100%;
min-height: 100px;
}
}
}
</style>
dialog.vue
<template>
<div class="dialog" v-if="$store.state.showDialog">
<div class="form">
<div class="block">
<div class="head">
<div class="left">
<input class="title" v-model="form.title" />
</div>
<div class="right">
<select v-model="form.categoryId">
<option value = 0>生活</option>
<option value = 1>学习</option>
<option value = 2>娱乐</option>
</select>
<button @click="save">保存</button>
<button @click="cancle">取消</button>
</div>
</div>
<div class="content">
<textarea v-model="form.content">内容</textarea>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import Block from "@/components/Block.vue";
import itemData from "@/store/itemData.ts";
import category from "@/store/enum.ts";
import ItemData from '@/store/itemData.ts';
@Component({
components: { Block }
})
export default class Dialog extends Vue {
form: ItemData = new ItemData(-1,0);
created() {
if(this.$store.state.editId) {
this.form = this.$store.state.transData;
}
}
@Watch("$store.state.editId") editId() {
this.form = new ItemData(-1,0);
if(this.$store.state.editId) {
this.form = this.$store.state.transData;
}
}
save() {
if(this.form && this.form.categoryId > -1 && this.form.content.trim().length > 0 && this.form.title.trim().length > 0 ) {
this.form.categoryId = Number(this.form.categoryId);
if(!this.$store.state.editId) {
this.$store.state.dataAction.addData(this.form);
} else {
this.$store.state.dataAction.editData(this.form);
}
this.$store.state.showDialog = false;
} else {
window.alert("输入未完成");
}
}
cancle() {
this.$store.state.showDialog = false;
}
}
</script>
<style lang="scss" scoped>
.dialog {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0, 0.3);
.form {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.block {
.head {
display: flex;
justify-content: space-between;
align-items: center;
.left {
flex: auto;
margin-right: 10px;
input {
width: 100%;
}
}
.right {
}
}
.content {
textarea {
width: 100%;
min-height: 100px;
}
}
}
}
}
</style>