1、效果展示
1.1、添加日程
添加日程.png
1.2、显示已添加了的日程,并且在日历上标注
效果图.png
1.3、点击标注,弹出日程内容以及重要程度
查看.png
2、需求分析
在新项目中,需要在首页做个日历展示以及实现添加,查看,修改,删除日程的功能,ElementUI组件库中有一个calendar
组件可以拿来进行复用。
3、后台接口查看
一开始后台那边没有返回日期,在日历上进行标注的过程必须是需要后台那边返回添加了日程的时间过来的,前端这边再去将带有日程的时间存进一个数组,然后再去单独渲染到日历上去。
列表返回数据.png
4、标注渲染问题的难点
这里可以有很多不同的方式去实现这个难点,我这边是和后台开发人员协商好,前端传入年月给后台,然后后台返回那一个月份所有添加了日程的数据给我,然后通过给上一个月以及下一个月添加点击事件,通过事件的触发来完成获取年月的信息,从而调一次接口,拿到当月的数据。
4、代码实现过程
4.1、日历的初步渲染
这里的calendar_nolabel
类是没有做标注的时间部分,而calendar_label
是做了标注的部分
<el-calendar v-model="value" v-loading="loading" class="cal">
<template slot="dateCell" slot-scope="{ date, data }">
<p
v-if="handleSelected(data.day) == '1'"
class="calendar_nolabel"
@click="clickCalendar(data)"
>
{{ data.day.split("-").slice(2).join("-") }}
</p>
<p
v-if="handleSelected(data.day) == '2'"
class="calendar_label"
@click="clickCalendar(data)"
>
{{ data.day.split("-").slice(2).join("-") }}
</p>
</template>
</el-calendar>
4.2、添加日程按钮
<el-button class="add-canlendar" @click="handleAdd">+ 添加日程</el-button>
4.3、封装日程弹窗组件
这里有一点需要注意,在打开的时候我添加了重置表单的操作,这里在使用重置的时候需要使用this.$nextTick(()=>{})
,因为这一步操作需要在DOM
渲染完成时去执行。
<template>
<div class="canlendar">
<el-dialog :title="title" :visible.sync="dialogFormVisible">
<el-form :model="form" :rules="rules" ref="ruleForm">
<el-form-item
label="重要等级"
prop="level"
:label-width="formLabelWidth"
>
<el-select v-model="form.level" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item
label="日程日期"
prop="scheduleDate"
:label-width="formLabelWidth"
>
<el-date-picker
v-model="form.scheduleDate"
type="date"
format="yyyy-MM-dd"
placeholder="选择日程日期"
>
</el-date-picker>
</el-form-item>
<el-form-item
label="日程内容"
prop="content"
:label-width="formLabelWidth"
>
<el-input
type="textarea"
v-model="form.content"
autocomplete="off"
></el-input>
</el-form-item>
</el-form>
<div slot="footer" v-if="isAdd" class="dialog-footer">
<el-button @click="handleCancel">取 消</el-button>
<el-button type="primary" @click="handleDeterminer">确 定</el-button>
</div>
<div slot="footer" v-else-if="!isAdd" class="dialog-footer">
<el-button class="del-btn" @click="handleRemove">删除日程</el-button>
<el-button type="primary" @click="handleClose">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { postAddSchedule, deleteSchedule } from "@/api/home";
export default {
data() {
return {
dialogFormVisible: false,
form: {},
formLabelWidth: "80px",
title: "",
scheId: "",
isAdd: true,
options: [
{
value: 1,
label: "一般",
},
{
value: 2,
label: "重要",
},
{
value: 3,
label: "很重要",
},
],
// 表单校验
rules: {
level: [{ required: true, message: "请输入等级", trigger: "blur" }],
content: [{ required: true, message: "请输入内容", trigger: "change" }],
scheduleDate: [
{
type: "date",
required: true,
message: "请选择日期",
trigger: "change",
},
],
},
};
},
methods: {
// 打开弹窗
handleOpen(row) {
if (row) {
this.title = "查看日程";
//弹出日程信息
this.form = JSON.parse(JSON.stringify(row));
this.scheId = JSON.parse(JSON.stringify(row)).id;
this.isAdd = false;
} else {
this.title = "添加日程";
this.isAdd = true;
this.$nextTick(() => {
this.form = {};
this.$refs.ruleForm.resetFields();
});
}
this.dialogFormVisible = true;
},
// 删除日程
handleRemove() {
// 写上你的删除接口
});
},
handleClose() {
this.dialogFormVisible = false;
},
// 点击取消
handleCancel() {
this.form = {};
this.$refs.ruleForm.resetFields();
this.dialogFormVisible = false;
},
// 点击确定
handleDeterminer() {
this.$refs.ruleForm.validate((val) => {
if (val) {
// 写上你的添加日程的接口
this.dialogFormVisible = false;
} else {
this.dialogFormVisible = true;
}
});
},
},
};
</script>
4.4、添加子组件弹窗
将弹窗加入到日历组件代码中去,在日历组件中通过获取子组件的handleOpen()
方法,弹出日程框。
- 引入组件
import AddCanlendar from "./canlendarDialog";
- 弹窗组件
<!-- 日程弹窗 -->
<AddCanlendar
ref="canlendarDialog"
@handleSuccess="handleSuccess"
></AddCanlendar>
4.5、开始渲染当月日程并做标注
通过map遍历,取每个元素节点下的日期,然后返回到一个新数组中去。
// 获取当月的日程信息
handleSchedule() {
// 将日期传入接口,判断当前时间是否有日程
getScheduleList(this.moment(this.value).format("YYYY-MM"))
.then((res) => {
if (res.code == 0) {
// this.label.push(res.data);
this.label = res.data.map((item) => {
return item.scheduleDate;
});
this.notLabel = res.data;
this.loading = false;
}
})
.catch();
},
通过遍历数组,判断日期,有标注的日程数组中有数据的话返回flag为2
,否则就是默认的1。这里可以继续多重判断,比如可以根据重要的等级程度去返回不同的值去做渲染。
handleSelected(day) {
let flag = "1";
this.label.forEach((item) => {
if (item == day) {
flag = "2";
return;
}
});
return flag;
},
4.6、给上一月,下一月按钮添加点击事件
created() {
this.$nextTick(() => {
// 点击上个月
let prevBtn = document.querySelector(
".el-calendar__button-group .el-button-group>button:nth-child(1)"
);
prevBtn.addEventListener("click", () => {
this.value = this.moment(this.value).format("YYYY-MM");
console.info(this.value);
this.handleSchedule();
});
// 点击今天
let currBtn = document.querySelector(
".el-calendar__button-group .el-button-group>button:nth-child(2)"
);
currBtn.addEventListener("click", () => {
this.value = this.moment(this.value).format("YYYY-MM");
console.info(this.value);
this.handleSchedule();
});
// 点击下个月
let nextBtn = document.querySelector(
".el-calendar__button-group .el-button-group>button:nth-child(3)"
);
nextBtn.addEventListener("click", () => {
this.value = this.moment(this.value).format("YYYY-MM");
console.info(this.value);
this.handleSchedule();
});
});
},
5、完整代码:
<template>
<div>
<el-calendar v-model="value" v-loading="loading" class="cal">
<template slot="dateCell" slot-scope="{ date, data }">
<p
v-if="handleSelected(data.day) == '1'"
class="calendar_nolabel"
@click="clickCalendar(data)"
>
{{ data.day.split("-").slice(2).join("-") }}
</p>
<p
v-if="handleSelected(data.day) == '2'"
class="calendar_label"
@click="clickCalendar(data)"
>
{{ data.day.split("-").slice(2).join("-") }}
</p>
</template>
</el-calendar>
<el-button class="add-canlendar" @click="handleAdd">+ 添加日程</el-button>
<!-- 日程弹窗 -->
<AddCanlendar
ref="canlendarDialog"
@handleSuccess="handleSuccess"
></AddCanlendar>
</div>
</template>
<script>
import AddCanlendar from "./canlendarDialog";
import { getScheduleList, deleteSchedule } from "@/api/home";
export default {
components: {
AddCanlendar,
},
data() {
return {
value: new Date(),
notLabel: [],
label: [],
loading: true,
};
},
computed: {},
created() {
this.$nextTick(() => {
// 点击上个月
let prevBtn = document.querySelector(
".el-calendar__button-group .el-button-group>button:nth-child(1)"
);
prevBtn.addEventListener("click", () => {
this.value = this.moment(this.value).format("YYYY-MM");
console.info(this.value);
this.handleSchedule();
});
// 点击今天
let currBtn = document.querySelector(
".el-calendar__button-group .el-button-group>button:nth-child(2)"
);
currBtn.addEventListener("click", () => {
this.value = this.moment(this.value).format("YYYY-MM");
console.info(this.value);
this.handleSchedule();
});
// 点击下个月
let nextBtn = document.querySelector(
".el-calendar__button-group .el-button-group>button:nth-child(3)"
);
nextBtn.addEventListener("click", () => {
this.value = this.moment(this.value).format("YYYY-MM");
console.info(this.value);
this.handleSchedule();
});
});
},
mounted() {
this.handleSchedule();
console.log(this.label);
},
methods: {
// 添加日程
handleAdd() {
this.$refs.canlendarDialog.handleOpen();
},
// 添加成功
handleSuccess() {
// 添加日程成功后去刷新列表
this.handleSchedule();
},
handleSelected(day) {
let flag = "1";
this.label.forEach((item) => {
if (item == day) {
flag = "2";
return;
}
});
return flag;
},
// 获取当月的日程信息
handleSchedule() {
// 将日期传入接口,判断当前时间是否有日程
getScheduleList(this.moment(this.value).format("YYYY-MM"))
.then((res) => {
if (res.code == 0) {
// this.label.push(res.data);
this.label = res.data.map((item) => {
return item.scheduleDate;
});
this.notLabel = res.data;
this.loading = false;
}
})
.catch();
},
// 点击
clickCalendar(day) {
this.notLabel.forEach((item) => {
if (item.scheduleDate == day.day) {
// 展示日程
this.$refs.canlendarDialog.handleOpen(item);
}
});
},
},
};
</script>
<style scoped>
.cal ::v-deep.el-calendar-day .calendar_nolabel {
margin: 0 auto;
padding: 2px;
text-align: center;
}
.cal ::v-deep.el-calendar-day .calendar_label {
border: 1px solid #F93937;
border-radius: 50%;
width: 23px;
margin: 0 auto;
padding: 2px;
text-align: center;
}
.add-canlendar {
width: 90%;
height: 35px;
line-height: 13px;
background: #409eff;
color: #fff;
margin-top: 5px;
}
</style>