基于Element-UI日历组件的待办事项例子
想实现一个日历提醒待办事项的功能,主要实现的功能是:
- 日历上展示是否有待办的事情,如果有则为红色对勾,如果全部是已完成则用绿色对勾,没有事项时候则没有任何标识
- 日历组件上侧有展开待办的列表功能(以Table形式展现)
主要涉及的组件有 - todoCellCheck.vue(用来检查每个日期是否有待办且是否全部完成)
- todoItem.vue(点击日期时,弹出事项的列表)
- todoItemTable.vue(通过折叠功能,展示Table形式的待办)
测试数据中,Mock返回待办事项的数组,例如以下数据:
/**
* abstract:概要
* detail:明细
* status:
* 0 已办
* 1 待办
* 999 最大状态
*/
const todoThings = [
{
date: "2020-04-14",
abstract: "王小虎1",
detail: "上海市普陀区金沙江路 1518 弄",
status: 1
},
{
date: "2020-04-14",
abstract: "王小虎2",
detail: "上海市普陀区金沙江路 1518 弄",
status: 0
}]
业务相关的内容在src下新建bear,配置相关的modules扫描,并且加入到store中src>bear>index.js如下:
const modulesFiles = require.context("./modules", true, /\.js$/);
const bearmodules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, "$1");
const value = modulesFiles(modulePath);
modules[moduleName] = value.default;
return modules;
}, {});
export default bearmodules;
在store的index.js中导入bearmodules。业务代码的modules在src>bear>modules>todoThings.js如下:
import { getTodoThingsByDate } from "@/api/todoThings/todoThings";
const actions = {
getTodoThingsByDate(context, { beginDate, endDate }) {
return new Promise((resolve, reject) => {
getTodoThingsByDate(beginDate, endDate)
.then(response => {
const { data } = response;
const resp = { code: 200, message: "Success", data: data };
resolve(resp);
})
.catch(error => {
reject(error);
});
});
}
};
export default {
namespaced: true,
actions
};
在src>views下新建todothings目录,待办日历的index.vue如下:
<template>
<el-row>
<el-row>
<el-button type="primary" :icon="collapseListIcon" @click="collapseList">
{{ collapseListTitel }}
</el-button>
</el-row>
<el-row>
<el-col :span="calendarSpan">
<div class="grid-content bg-purple">
<el-calendar v-model="calendarValue">
<template v-slot:dateCell="{ date, data }">
<todoItem
v-if="data.isSelected"
:itemsDataGroupByDate="todoDataGroupByDate"
:cellDate="data.day"
>
</todoItem>
<!-- 初始化标记是否有事情要办 -->
<todoCellCheck
:itemsDataGroupByDate="todoDataGroupByDate"
:cellDate="data.day"
></todoCellCheck>
<p :class="data.isSelected ? 'is-selected' : ''">
{{
data.day
.split("-")
.slice(1)
.join("-")
}}
</p>
</template>
</el-calendar>
</div>
</el-col>
<el-col :span="todoItemSpan" v-if="isCollapseList">
<todoItemTable :tableData="todoItemThings"></todoItemTable>
</el-col>
</el-row>
</el-row>
</template>
<script>
import todoItem from "./components/todoItem";
import todoItemTable from "./components/todoItemTable";
import todoCellCheck from "./components/todoCellCheck";
import { jsonArraysGroupByValue } from "@/utils/jsonUtils.js";
import { prevMonthFirstDate, nextMonthLastDate } from "@/utils/date";
export default {
created() {
this.initTodoThings();
},
data() {
return {
/**
* calendarSpan 日历宽度
* todoItemSpan 右侧列表宽度
* isCollapseList 右侧折叠
* cellDate 选中单元格的日期
* cellData 选中单元格的数据
* cellItemThings 选中单元格弹出层的待办事项
* todoItemThings 当前月及前后各一月的事项
* calendarValue 日历当前月
* ---------------
* todoDataGroupByDate 按日期分组的待办事项
* ---------------
*
*/
calendarSpan: 12,
todoItemSpan: 12,
isCollapseList: true,
cellDate: null,
cellData: null,
collapseListTitel: "收起列表",
collapseListIcon: "el-icon-arrow-left",
cellItemThings: null,
// calendarData: null,
calendarValue: new Date(),
todoItemThings: null,
todoDataGroupByDate: null,
contextmenuVisible: false,
top: 0,
left: 0,
editFormVisible: false
};
},
components: { todoItem, todoItemTable, todoCellCheck },
watch: {
calendarValue() {
this.initTodoThings();
}
},
methods: {
//查询选中日期及前后各一个月的todo
initTodoThings() {
const beginDate = prevMonthFirstDate(this.calendarValue);
const endDate = nextMonthLastDate(this.calendarValue);
this.$store
.dispatch("todoThings/getTodoThingsByDate", {
beginDate: beginDate,
endDate: endDate
})
.then(response => {
if (response.code === 200) {
this.todoItemThings = response.data;
this.todoDataGroupByDate = jsonArraysGroupByValue(
this.todoItemThings,
"date"
);
}
});
},
collapseList() {
if (this.isCollapseList) {
this.calendarSpan = 24;
this.todoItemSpan = 0;
} else {
this.calendarSpan = 12;
this.todoItemSpan = 12;
this.cellDate = null;
this.cellData = null;
}
this.isCollapseList = !this.isCollapseList;
this.collapseListTitel = !this.isCollapseList ? "展开列表" : "收起列表";
this.collapseListIcon = !this.isCollapseList
? "el-icon-arrow-right el-icon--right"
: "el-icon-arrow-left";
}
}
};
</script>
<style lang="scss" scoped>
.is-selected {
color: #1989fa;
}
</style>
日历组件在初始化时查询相应的待办,并且将所有待办数据按日期进行分组,同时布局方面默认展开右侧的Table
作为检查日历中日期是否有待办,使用了todoCellCheck.vue组件,思路是先将所有待办事项按日期分组,之后和加载的日期想对比,根据分组内的status属性求和作为结果,status和为0则说明所有事项均以完成。代码如下:
<script>
export default {
name: "todoCellCheck",
functional: true,
props: {
itemsDataGroupByDate: {
type: Object,
default: null
},
cellDate: {
type: String,
default: ""
}
},
render(h, context) {
const { itemsDataGroupByDate, cellDate } = context.props;
const vnodes = [];
let status = 999;
if (itemsDataGroupByDate !== null) {
Object.keys(itemsDataGroupByDate).forEach(function(category) {
if (category === cellDate) {
status = itemsDataGroupByDate[category].reduce(
(prev, currentValue) => {
return prev + currentValue.status;
},
0
);
status === 0 && status !== 999
? // ? vnodes.push(<svg-icon icon-class="greenmark" />)
vnodes.push("✔️")
: vnodes.push(<svg-icon icon-class="redmark" />);
}
});
}
return vnodes;
}
};
</script>
todoItem.vue组件绑定实现当点击日期时,弹出一个页面提示当前日期的待办事项。代码如下:
<template>
<div class="todocell">
<slot name="todoItems" :itemThings="cellItemThings"></slot>
<el-popover placement="right" width="800" trigger="click" v-model="visible">
<el-table :data="cellItemThings" :row-class-name="tableRowClassName">
<el-table-column
width="150"
property="date"
label="日期"
></el-table-column>
<el-table-column
width="100"
property="abstract"
label="概要"
></el-table-column>
<el-table-column
width="300"
property="detail"
label="明细"
></el-table-column>
<el-table-column label="状态" width="100">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span style="margin-left: 10px">{{
scope.row.status === 0 ? "已完成" : "待完成"
}}</span>
</template>
</el-table-column>
</el-table>
</el-popover>
</div>
</template>
<script>
export default {
props: {
itemsDataGroupByDate: {
type: Object,
default: null
},
cellDate: {
type: String,
default: ""
}
},
created() {
this.initCellItemThings();
},
data() {
return {
visible: false,
cellItemThings: null
};
},
methods: {
initCellItemThings() {
if (this.itemsDataGroupByDate !== null) {
this.cellItemThings = this.itemsDataGroupByDate[this.cellDate];
if (
this.cellItemThings !== null &&
typeof this.cellItemThings !== "undefined"
) {
this.visible = true;
}
}
},
tableRowClassName({ row }) {
if (row.status === 0) {
return "success-row";
} else {
return "warning-row";
}
}
}
};
</script>
<style scoped>
.todocell {
color: #1989fa;
}
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>
目前展示效果如下截图
样式比较难弄,准备修改成vue-element-admin的Table样式