先看效果图
该日历接口入参是当前面板第一天和最后一天时间,查询区间数据。
接口返回数据格式:
[{
"date": "2023-05-29", // 日期
"type": "nonWorkday", // 类型
"description": "备注" // 备注
}]
工作有点忙直接上原始代码了,有疑惑的地方可以评论留言讨论
<template>
<div class="calendar-setting">
<a-calendar :header-render="headerRender" @select="handleSelect" @panelChange="handlePanelChange">
<div v-if="calendarList?.length" class="ant-fullcalendar-cell" slot="dateFullCellRender" slot-scope="value">
<calendar-item :data="getListData(value)" />
</div>
</a-calendar>
<supcon-modal
ref="calendar_modal"
:modal_attrs="{
width: 475,
height: 404,
}"
modal_title="日期设置"
@modal_handle_ok="form_submit"
>
<div slot="content">
<div class="modal-date">
选择日期:<span>{{ this.selectData }}</span>
</div>
<supcon-form
ref="calendar_form"
slot="content"
:form_attrs="{ action: 'modal' }"
:form_default="form_default"
:form_setting="calendar_form_person(calendarEnums)"
/>
</div>
</supcon-modal>
</div>
</template>
<script>
import API from '@/api/workforce-management/duty-scheduling'
import moment from 'moment'
import { calendar_form_person } from './../../config/duty-setting.js'
export default {
components: {
supconForm: () => import('@/components/supcon-form/supcon-form'),
supconModal: () => import('@/components/supcon-modal/supcon-modal'),
calendarItem: () => import('./calendar-item.vue'),
},
data() {
return {
calendar_form_person,
form_default: {},
selectData: '',
calendarEnums: [],
calendarList: [],
firstDate: '',
lastDate: '',
}
},
computed: {},
created() {
},
methods: {
handlePanelChange(value) {
const { firstDate, lastDate } = this.getFirstDateAndLastDateOnThePanel(value)
this.firstDate = firstDate
this.lastDate = lastDate
this.queryList()
},
getListData(value) {
let date = moment(value).format('yyyy-MM-DD')
if (this.calendarList?.length) {
let data = moment(value).format('DD')
let day = moment(value).day()
let target = this.calendarList?.find((item) => item?.date === date)
return {
type: target?.type,
desc: target?.description,
bg: target?.type,
data,
isRed: day === 0 || day === 6,
}
} else {
return {}
}
},
queryList() {
},
// 单个日期点击
handleSelect(value) {
this.selectData = moment(value).format('yyyy-MM-DD')
let target = this.calendarList.find((item) => item?.date === this.selectData)
if (target.id) {
this.form_default = target
}
this.$refs.calendar_modal.modal_visible = true
},
async form_submit() {
this.$refs.calendar_form.supcon_form.validateFields((err, fields_value) => {
if (err !== null) {
return false
}
console.log(fields_value)
})
},
// 自定义头部
headerRender({ value, type, onChange, onTypeChange }) {
this.handlePanelChange(value)
const start = 0
const end = 12
const monthOptions = []
const current = value.clone()
const localeData = value.localeData()
const months = []
for (let i = 0; i < 12; i++) {
current.month(i)
months.push(localeData.monthsShort(current))
}
for (let index = start; index < end; index++) {
monthOptions.push(
<a-select-option class="month-item" key={`${index}`}>
{months[index]}
</a-select-option>
)
}
const month = value.month()
const year = value.year()
const options = []
for (let i = year - 10; i < year + 10; i += 1) {
options.push(
<a-select-option key={i} value={i} class="year-item">
{i}
</a-select-option>
)
}
return (
<div style={{ padding: '10px' }}>
<a-row type="flex" justify="space-between">
<a-col></a-col>
<a-col></a-col>
<a-col>
<a-select
size="large"
dropdownMatchSelectWidth={false}
class="my-year-select"
onChange={(newYear) => {
const now = value.clone().year(newYear)
onChange(now)
}}
value={String(year)}
>
{options}
</a-select>
<a-select
style="margin: 0 12px"
size="large"
dropdownMatchSelectWidth={false}
value={String(month)}
onChange={(selectedMonth) => {
const newValue = value.clone()
newValue.month(parseInt(selectedMonth, 10))
onChange(newValue)
}}
>
{monthOptions}
</a-select>
<a-date-picker
size="large"
defaultValue={value}
onChange={(selectedMonth) => {
onChange(selectedMonth)
}}
/>
</a-col>
</a-row>
</div>
)
},
// 获取面板第一个日期和最后一个日期
getFirstDateAndLastDateOnThePanel(date) {
const firstDate = moment(date).startOf('month')
const lastDate = moment(date).endOf('month')
const firstDateDay = firstDate.day()
firstDate.subtract(firstDateDay, 'days')
lastDate.add(43 - Number(lastDate.format('DD')) - firstDateDay, 'days')
return {
firstDate: firstDate.format('YYYY-MM-DD'),
lastDate: lastDate.format('YYYY-MM-DD'),
}
},
},
}
</script>
<style lang="less" scoped>
.calendar-setting {
width: 100%;
height: 100%;
padding: 12px 24px;
/deep/ .ant-fullcalendar-column-header {
background: #e6f7ff;
position: relative;
top: 3px;
}
}
.events {
list-style: none;
margin: 0;
padding: 0;
}
.events .ant-badge-status {
overflow: hidden;
white-space: nowrap;
width: 100%;
text-overflow: ellipsis;
font-size: 12px;
}
.notes-month {
text-align: center;
font-size: 28px;
}
.notes-month section {
font-size: 28px;
}
.modal-date {
margin-bottom: 16px;
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 14px;
color: #000000;
opacity: 0.85;
span {
opacity: 0.65;
}
}
.fontColor {
color: #f00;
}
.nonWorkday {
background: #fff1f0;
}
.workday {
background: rgba(0, 0, 0, 0.04);
}
.full-header-no {
padding: 2px 6px;
background: #ff4d4f;
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
color: #ffffff;
}
.full-header {
padding: 2px 6px;
background: rgba(0, 0, 0, 0.45);
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
color: #ffffff;
}
.ant-fullcalendar-value {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.ant-fullcalendar-content {
margin-top: 12px;
}
</style>
calendar-item子组件
<template>
<div :class="['ant-fullcalendar-date', data?.bg]">
<div :class="['ant-fullcalendar-value', data?.isRed && 'fontColor']">
<template v-if="data?.type">
<div v-if="data?.type === 'nonWorkday'" class="full-header-no">休</div>
<div v-if="data?.type === 'workday'" class="full-header">班</div>
</template>
<div v-else></div>
{{ data?.data }}
</div>
<div class="ant-fullcalendar-content" v-if="data?.desc">备注:{{ data?.desc }}</div>
</div>
</template>
<script>
export default {
props: {
data: {
type: Object,
default: () => {},
},
},
components: {},
data() {
return {}
},
computed: {},
created() {},
methods: {},
}
</script>
<style lang="less" scoped>
.fontColor {
color: #f00;
}
.nonWorkday {
background: #fff1f0;
}
.workday {
background: rgba(0, 0, 0, 0.04);
}
.full-header-no {
padding: 2px 6px;
background: #ff4d4f;
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
color: #ffffff;
}
.full-header {
padding: 2px 6px;
background: rgba(0, 0, 0, 0.45);
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
color: #ffffff;
}
.ant-fullcalendar-value {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.ant-fullcalendar-content {
margin-top: 12px;
}
</style>