基於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樣式