用 ElementUI 的日历组件 Calendar 自定义渲染
文章目录
- 需求
- 分析
- 1. 页面渲染
- 2. 获取页面上的开始日期和结束日期
- 3. 总的代码
需求
之前实现过一版用 ElementPlus 的日历组件 Calendar 自定义渲染,是在 Vue3 项目中实现的,现在需求在 Vue2 中也实现一版
分析
1. 页面渲染
<el-calendar v-model="calendar" style="padding-bottom: 0">
<template slot="dateCell" slot-scope="{ data }">
<div class="calendar-day">
<div
:class="data.isSelected ? 'is-selected' : ''"
style="font-weight: bold"
>
{{ data.day.split("-").slice(1).join("-") }}
</div>
<div class="duty-text">
值班人员:
<span v-for="item in tableData" :key="item.id">
<span
v-if="item.dutyDate == data.day"
style="font-weight: bold"
>
{{ item.dutyUser }}
</span>
</span>
<div>
<el-button
size="mini"
type="text"
style="color: #1e80ff; margin-right: 10px"
@click.stop="handleEdit(data)"
>编辑</el-button
>
<el-button
slot="reference"
size="mini"
type="text"
style="color: #1e80ff"
@click="handleRemove(data)"
>删除</el-button
>
</div>
</div>
</div>
</template>
</el-calendar>
data(){
calendar: new Date(),
}
2. 获取页面上的开始日期和结束日期
methods: {
// 获取日历显示时间范围
getRange (date) {
if (date) {
} else {
this.calendar = new Date()
}
// 日历第一天
let firstDay = ''
// 日历最后一天
let lastDay = ''
// 今天
const today = date || new Date()
// 上月
const m = today.getMonth()
// 本月
const cm = m + 1
// 下月
const lm = m + 2 > 12 ? 1 : m + 2
// 要显示的本月
const currentMonth = cm < 10 ? '0' + cm : cm
// 要显示的本本年
const currentYear = today.getFullYear()
// 要显示的上个月的年份,m = 0 则当前1月,上月则是去年12月
const prevYear = m == 0 ? currentYear - 1 : currentYear
const prevMonth = m == 0 ? 12 : m < 10 ? '0' + m : m
// 上个月天数
const pmd = new Date(prevYear, m, 0).getDate()
// 下个月的年份,当前12月,则需要加一年
const lastYear = cm + 1 > 12 ? currentYear + 1 : currentYear
const lastMonth = lm < 10 ? '0' + lm : lm
// 1号是周几
const firstWeek = new Date(today.setDate(1)).getDay()
// 如果是周日,则不需要显示上个月
if (firstWeek == 0) {
firstDay = `${currentYear}-${currentMonth}-01`
}
// 其他周几,对应用上个月的天数往前推算
else {
firstDay = `${prevYear}-${prevMonth}-${pmd - (firstWeek - 1)}`
}
// 这个月天数
const currentMonthDate = new Date(currentYear, cm, 0).getDate()
// 最后一天是周几
const lastWeek = new Date(today.setDate(currentMonthDate)).getDay()
// 周六显示当月最后一天
if (lastWeek == 6) {
lastDay = `${currentYear}-${currentMonth}-${currentMonthDate}`
}
// 其他周几,对应往后推算
else {
const day = ['06', '05', '04', '03', '02', '01']
lastDay = `${lastYear}-${lastMonth}-${day[lastWeek]}`
}
this.timeList.startDate = firstDay
this.timeList.endDate = lastDay
this.getPageList()
},
}
3. 总的代码
- plan.vue
<template>
<div
v-loading="loading"
class="module-container"
element-loading-text="加载中...."
>
<!-- {{ timeList.startDate }}-{{ timeList.endDate }} -->
<MyCard :is-have-left="false">
<template slot="right-body">
<div class="split-head" style="margin-bottom: -40px">
<el-button
type="primary"
size="small"
style="margin-left: 20px"
@click="handlePlan()"
>
排班计划
</el-button>
</div>
<div class="split-body">
<template>
<el-calendar v-model="calendar" style="padding-bottom: 0">
<template slot="dateCell" slot-scope="{ data }">
<div class="calendar-day">
<div
:class="data.isSelected ? 'is-selected' : ''"
style="font-weight: bold"
>
{{ data.day.split("-").slice(1).join("-") }}
</div>
<div class="duty-text">
值班人员:
<span v-for="item in tableData" :key="item.id">
<span
v-if="item.dutyDate == data.day"
style="font-weight: bold"
>
{{ item.dutyUser }}
</span>
</span>
<div>
<el-button
size="mini"
type="text"
style="color: #1e80ff; margin-right: 10px"
@click.stop="handleEdit(data)"
>编辑</el-button
>
<el-button
slot="reference"
size="mini"
type="text"
style="color: #1e80ff"
@click="handleRemove(data)"
>删除</el-button
>
</div>
</div>
</div>
</template>
</el-calendar>
</template>
</div>
</template>
</MyCard>
<Edit
:scheduling-visible.sync="schedulingVisible"
:edit_visible.sync="edit_visible"
:duty-info="dutyInfo"
@getRange="getRange"
/>
</div>
</template>
<script>
import Edit from './child/edit.vue'
export default {
name: '',
components: {
Edit
},
props: {},
data () {
return {
projectId: this.$store.state.project.projectId,
structId: this.$store.state.struct.structId,
loading: false,
visible: false,
schedulingVisible: false,
calendar: new Date(),
editForm: {},
tableData: [],
timeList: {
startDate: '',
endDate: ''
},
edit_visible: false,
dutyInfo: {}
}
},
watch: {
calendar (n, o) {
if (n.getFullYear() !== o.getFullYear() || n.getMonth() !== o.getMonth()) {
this.getRange(n)
}
}
},
created () {
},
mounted () { this.getRange(this.calendar) },
beforeDestroy () { },
methods: {
// 获取日历显示时间范围
getRange (date) {
if (date) {
} else {
this.calendar = new Date()
}
// 日历第一天
let firstDay = ''
// 日历最后一天
let lastDay = ''
// 今天
const today = date || new Date()
// 上月
const m = today.getMonth()
// 本月
const cm = m + 1
// 下月
const lm = m + 2 > 12 ? 1 : m + 2
// 要显示的本月
const currentMonth = cm < 10 ? '0' + cm : cm
// 要显示的本本年
const currentYear = today.getFullYear()
// 要显示的上个月的年份,m = 0 则当前1月,上月则是去年12月
const prevYear = m == 0 ? currentYear - 1 : currentYear
const prevMonth = m == 0 ? 12 : m < 10 ? '0' + m : m
// 上个月天数
const pmd = new Date(prevYear, m, 0).getDate()
// 下个月的年份,当前12月,则需要加一年
const lastYear = cm + 1 > 12 ? currentYear + 1 : currentYear
const lastMonth = lm < 10 ? '0' + lm : lm
// 1号是周几
const firstWeek = new Date(today.setDate(1)).getDay()
// 如果是周日,则不需要显示上个月
if (firstWeek == 0) {
firstDay = `${currentYear}-${currentMonth}-01`
}
// 其他周几,对应用上个月的天数往前推算
else {
firstDay = `${prevYear}-${prevMonth}-${pmd - (firstWeek - 1)}`
}
// 这个月天数
const currentMonthDate = new Date(currentYear, cm, 0).getDate()
// 最后一天是周几
const lastWeek = new Date(today.setDate(currentMonthDate)).getDay()
// 周六显示当月最后一天
if (lastWeek == 6) {
lastDay = `${currentYear}-${currentMonth}-${currentMonthDate}`
}
// 其他周几,对应往后推算
else {
const day = ['06', '05', '04', '03', '02', '01']
lastDay = `${lastYear}-${lastMonth}-${day[lastWeek]}`
}
this.timeList.startDate = firstDay
this.timeList.endDate = lastDay
this.getPageList()
},
handlePlan () {
this.schedulingVisible = true
},
getPageList () {
this.loading = true
const url = `client/${this.projectId}/dh-duty-plan/list?startDate=${this.timeList.startDate}&endDate=${this.timeList.endDate}`
this.$axios
.get(url)
.then(res => {
if (res.code !== 200) return
this.tableData = res.data
// this.total = res.data.total / 1
})
.finally(() => {
this.loading = false
})
},
handleEdit (row) {
const url = `client/${this.projectId}/dh-duty-plan/list?dutyDate=${row.day}`
this.$axios
.get(url)
.then((res) => {
if (res.code !== 200) return
if (res.data.length) {
this.dutyInfo = res.data[0]
} else {
this.dutyInfo = {
id: '',
dutyUserId: '',
dutyDate: row.day
}
}
})
.finally(() => {
})
this.edit_visible = true
},
async handleRemove (row) {
const result = await this.$confirm(`确定要删除吗?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false
}).catch((err) => err)
if (result === 'confirm') {
const url = `client/${this.projectId}/dh-duty-plan/list?dutyDate=${row.day}`
this.$axios
.get(url)
.then((res) => {
if (res.code !== 200) return
if (res.data.length) {
const info = res.data[0]
this.$axios.post(`client/${this.projectId}/dh-duty-plan/delete`, { id: info.id }).then(response => {
if (response.code !== 200) return
this.getPageList()
})
} else {
this.$message('该日期下未查询到值班人员')
}
})
.finally(() => {
// this.getPageList()
})
} else {
// this.$message('取消了删除')
}
},
}
}
</script>
<style lang="scss" scoped>
.module-container {
// padding-top: 15px;
padding-left: 22px;
height: 100%;
// ::v-deep .card-body {
// background: #f6f9ff !important;
// display: flex;
// padding: 0px;
// }
.split-head {
display: flex;
justify-content: center;
flex-direction: row;
}
}
.duty-text {
text-align: center;
// color: #939fb6;
font-size: large;
line-height: 25px;
padding-top: 5px;
}
.is-selected {
color: #1989fa;
font-weight: bold;
}
:deep(.el-calendar-table thead th) {
font-weight: bold;
}
</style>
- 弹窗 edit.vue
<template>
<div class="container">
<el-dialog
:title="'排班计划'"
:visible="schedulingVisible"
width="30%"
top="20vh"
:close-on-click-modal="false"
:modal-append-to-body="false"
custom-class="alarm-strategy"
@update:visible="(bol) => $emit('update:schedulingVisible', bol)"
>
<div>
<el-button
v-show="activeName === '单人排班'"
type="text"
icon="el-icon-plus"
class="add-one-btn"
@click="handleaddNode"
>新增一条</el-button
>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="单人排班" name="单人排班">
<el-table :data="singleForm" style="width: 100%" height="250">
<el-table-column label="日期" align="center">
<template slot-scope="scope">
<el-date-picker
v-model="scope.row.dutyDate"
size="small"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期"
style="width: 70%"
/>
</template>
</el-table-column>
<el-table-column label="人员" align="center">
<template slot-scope="scope">
<el-select
v-model="scope.row.dutyUserId"
size="small"
clearable
placeholder="请选择"
style="width: 70%"
>
<el-option
v-for="item in person_options"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</template>
</el-table-column>
<el-table-column v-if="singleForm.length > 1" label="操作">
<template slot-scope="scope">
<div>
<el-button
type="text"
@click="handleremoveNode(scope.$index)"
>
移除
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="批量排班" name="批量排班">
<div class="time-range batch-box">
<span>日期范围</span>
<el-date-picker
v-model="multiForm.time"
size="small"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
value-format="yyyy-MM-dd"
/>
</div>
<div class="personnel batch-box">
<span>人员添加</span>
<el-select
v-model="multiForm.dutyUserIdList"
size="small"
multiple
collapse-tags
clearable
placeholder="请选择"
>
<el-option
v-for="item in person_options"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</div>
</el-tab-pane>
</el-tabs>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="$emit('update:schedulingVisible', false)"
>取 消</el-button
>
<el-button type="primary" :loading="loading" @click="handleOk"
>确 定</el-button
>
</span>
</el-dialog>
<el-dialog
:title="'编辑'"
:visible="edit_visible"
width="30%"
top="20vh"
:close-on-click-modal="false"
:modal-append-to-body="false"
custom-class="alarm-strategy"
@update:visible="handleEditOk"
>
<div class="time-range batch-box">
<span>值班人</span>
<el-select
v-model="dutyInfo.dutyUserId"
size="small"
placeholder="请选择"
>
<el-option
v-for="item in person_options"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<!-- {{ dutyInfo }} -->
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="$emit('update:edit_visible', false)"
>取 消</el-button
>
<el-button type="primary" @click="handleEditOk">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: '',
components: {},
props: {
schedulingVisible: {
type: Boolean,
default: false
},
edit_visible: {
type: Boolean,
default: false
},
dutyInfo: {
type: Object,
default: () => { }
}
},
data () {
return {
projectId: this.$store.state.project.projectId,
structId: this.$store.state.struct.structId,
loading: false,
activeName: '单人排班',
value: '',
person_options: [],
value1: '',
pickerOptions: {
shortcuts: [
{
text: '最近一周',
onClick (picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
},
{
text: '最近一个月',
onClick (picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
},
{
text: '最近三个月',
onClick (picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
}
]
},
value2: '',
singleForm: [{ dutyDate: '', dutyUserId: '' }],
multiForm: {
time: [],
dutyUserIdList: []
}
}
},
created () {
},
mounted () {
this.init()
},
beforeDestroy () { },
methods: {
handleClick (tab, event) {
if (tab.label == '单人排班') {
this.multiForm = {}
} else {
this.singleForm = [{ dutyDate: '', dutyUserId: '' }]
}
},
handleremoveNode (data) {
this.singleForm.splice(data, 1)
},
handleaddNode () {
this.singleForm.push({
dutyDate: null, // 日期
dutyUserId: null // 人员
})
},
// 提交
handleOk () {
if (this.activeName === '单人排班') {
const flag = this.singleForm.some(item =>
item.dutyDate == null || item.dutyUserId == null || item.dutyDate == undefined || item.dutyUserId == undefined || item.dutyDate == '' || item.dutyUserId == ''
)
if (!flag) {
const url = `client/${this.projectId}/dh-duty-plan/danGeAdd`
this.$axios
.post(url, { dhDutyPlanList: this.singleForm })
.then((res) => {
if (res.code === 200) {
this.$emit('getRange')
this.$emit('update:schedulingVisible', false)
}
})
.finally(() => {
this.handleClick('单人排班')
})
} else {
return this.$message.warning('请完整填写表单')
}
} else {
if (this.multiForm.time.length && this.multiForm.dutyUserIdList.length) {
const tempData = {
startDate: this.multiForm.time[0],
endDate: this.multiForm.time[1],
dutyUserIdList: this.multiForm.dutyUserIdList
}
const url = `client/${this.projectId}/dh-duty-plan/batchAdd`
this.$axios
.post(url, tempData)
.then((res) => {
if (res.code === 200) {
this.$emit('getRange')
this.$emit('update:schedulingVisible', false)
}
})
.finally(() => {
this.handleClick('单人排班')
})
} else {
return this.$message.warning('请完整填写表单')
}
}
},
getPersonList () {
const url = `client/${this.projectId}/dh-duty-user/list`
this.$axios
.get(url)
.then(res => {
if (res.code !== 200) return
this.person_options = res.data
})
.finally(() => {
})
},
handleEditOk () {
if (this.dutyInfo.dutyUserId) {
if (this.dutyInfo.id) {
const tempData = {
id: this.dutyInfo.id,
dutyUserId: this.dutyInfo.dutyUserId,
dutyDate: this.dutyInfo.dutyDate
}
const url = `client/${this.projectId}/dh-duty-plan/update`
this.$axios
.post(url, tempData)
.then((res) => {
if (res.code !== 200) return
this.$emit('getRange')
this.$emit('update:edit_visible', false)
})
.finally(() => {
})
} else {
const url = `client/${this.projectId}/dh-duty-plan/danGeAdd`
this.$axios
.post(url, { dhDutyPlanList: [{ dutyDate: this.dutyInfo.dutyDate, dutyUserId: this.dutyInfo.dutyUserId }] })
.then((res) => {
if (res.code === 200) {
this.$emit('getRange')
this.$emit('update:edit_visible', false)
}
})
.finally(() => {
})
}
} else {
return this.$message.warning('请完整填写表单')
}
},
init () {
this.getPersonList()
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .el-dialog__body {
padding: 0 15px;
padding-right: 0px;
position: relative;
margin-bottom: 30px;
overflow: hidden;
}
.add-one-btn {
position: absolute;
right: 20px;
z-index: 22;
}
.single-person-box {
padding-right: 20px;
max-height: 360px;
overflow: auto;
.limit {
display: flex;
margin-bottom: 12px;
> div {
flex: 1;
margin-left: 10px;
}
.flex {
display: flex;
align-items: center;
.el-date-editor {
width: 160px;
}
.el-select {
width: 160px;
}
}
.time {
margin-right: 10px;
}
.staff {
}
span {
margin-right: 10px;
}
}
}
.batch-box {
display: flex;
align-items: center;
width: 70%;
margin-left: 10%;
margin-top: 15px;
span {
margin-right: 10px;
}
.el-select,
.el-date-editor {
flex: 1;
}
}
</style>