vue可ctrl,shift多選,可添加標記日歷組件詳細
需要寫一個日歷組件有以下功能,element無法滿足,自己造一個
只顯示當前月日期,前后月日期不顯示也不可選擇;周六、周日顏色與工作日區(qū)分日期可以打標記(左上角標記)可ctrl+shift+鼠標左鍵多選
一、 按照 "日", "一", "二", "三", "四", "五", "六" 把一個月的日期排列
頁面:
<template> <div class="calendar"> <div class="calendar_header">{{currentMonth}}月</div> <table cellspacing="0" cellpadding="0" class="calendar_table"> <thead> <th v-for="day in WEEK_DAYS" :key="day" :class="['thead_th',day=='六'||day=='日'?'thead_th_red':'']"> {{day}}</th> </thead> <tbody> <tr v-for="(row,index) in rows" :key="index"> <td v-for="(cell,key) in row" :key="key" class="td" @click="handlePickDay(cell)"> <div :class="getCellClass(cell,key)"> <div v-if="cell.type=='current'" class="triangle" :style="getCelltriangleStyle(cell)"></div> <span class="cell_text">{{cell.text==0?'':cell.text}}</span> </div> </td> </tr> </tbody> </table> </div> </template>
組件定義props、emits
const props = defineProps({ markList: {//標記 type: Array<any>, default: (): any[] => { return []; }, }, month: { type: Date, required: true, }, disabled: { type: Boolean, default: false, }, disableBefore: {//禁用今天之前的日期 type: Boolean, default: true, }, }); const emits = defineEmits(['change']);
當前月的日期數(shù)組:
type CalendarDateCellType = 'next' | 'prev' | 'current' type CalendarDateCell = { text: number, type: CalendarDateCellType } const WEEK_DAYS = ref(["日", "一", "二", "三", "四", "五", "六"]); const currentMonth = computed(() => { return moment(props.month).format('M'); }) onMounted(() => { onKeyEvent(); }) const rows = computed(() => { let days: CalendarDateCell[] = [] const firstDay = moment(props.month).startOf("month").date(); // const endDay = moment(props.month).endOf('month').date();//當前月的最后一天 const firstDayOfWeek = moment(props.month).startOf("month").day(); // const daysInMonth = moment(props.month).daysInMonth();//當前月的天數(shù) const prevMonthDays: CalendarDateCell[] = getPrevMonthLastDays( firstDay, firstDayOfWeek - firstDay ).map((day) => ({ text: 0,//上月補0 type: 'prev', })) const currentMonthDays: CalendarDateCell[] = getMonthDays(moment(props.month).daysInMonth()).map( (day) => ({ text: day, type: 'current', }) ) days = [...prevMonthDays, ...currentMonthDays] const remaining = 7 - (days.length % 7 || 7) const nextMonthDays: CalendarDateCell[] = rangeArr(remaining).map( (_, index) => ({ text: 0,//下月補0 type: 'next', }) ) days = days.concat(nextMonthDays) // console.log(currentMonth.value, firstDay, endDay, moment(props.month).startOf("month").day()); return toNestedArr(days) })
二、單元格樣式處理
- 禁用
- 選中
- 周六、周日紅色字體
const getCellClass = (cell: CalendarDateCell, key: number) => { let date = getCellDate(cell); if (props.disableBefore && date.getTime() < new Date().getTime()) { return ['cell', 'cell_disabled'];//禁用 } let classes: string[] = ['cell', 'cell_enabled']; if (key == 0 || key == 6) {//周六、周日 classes.push('cell_red'); } let index = selectList.value.indexOf(moment(date).format('YYYY-MM-DD')); if (index != -1) { classes.push('cell_active');//選中 } return classes; }
左上角三角形標記
.triangle { position: absolute; left: 0; top: 0; width: 0; height: 0; // border-top: 30px solid #e6e911; border-right: 30px solid transparent; }
const getCelltriangleStyle = (cell: CalendarDateCell) => { let date = getCellDate(cell); let day = props.markList.find(item => item.date == moment(date).format('YYYY-MM-DD')); return day ? { borderTop: '30px solid #e6e911', } : {}; }
三、單機、按住ctrl點擊、按住shift點擊事件處理
1.記錄鍵盤按下ctrl、shift事件
const isCtrl = ref(false) const isShift = ref(false) const onKeyEvent = () => { window.addEventListener('keydown', e => { e.preventDefault();//取消默認事件 let e1 = e || window.event switch (e1.keyCode) { case 16: isShift.value = true; break; case 17: isCtrl.value = true; break; } }) window.addEventListener('keyup', e => { e.preventDefault(); let e1 = e || window.event switch (e1.keyCode) { case 16: isShift.value = false; break; case 17: isCtrl.value = false; break; } }) }
2.點擊事件處理
const selectList = ref<any[]>([]);//已選擇的 const shiftNum = ref(0);//shift復制的起始位置 const lastSelect = ref<any[]>([]);//按住shift倒數(shù)第二次復制的 //遵循excel點擊、ctrl、shift組合操作規(guī)范 const handlePickDay = (cell: CalendarDateCell) => { let date = getCellDate(cell); if (cell.type != 'current') { return; } if (props.disableBefore && date.getTime() < new Date().getTime()) { return } // console.log(isCtrl.value, isShift.value); let dateStr = moment(date).format('YYYY-MM-DD'); let currentSelect: string[] = []; //按住ctrl if (isCtrl.value) { if (selectList.value.includes(dateStr)) { selectList.value.splice(selectList.value.indexOf(dateStr), 1); } else { selectList.value.push(dateStr); } lastSelect.value = []; } else if (isShift.value) {//按住shift if (shiftNum.value == 0) {//無上次點擊 shiftNum.value = cell.text; if (selectList.value.includes(dateStr)) { selectList.value.splice(selectList.value.indexOf(dateStr), 1); } else { selectList.value.push(dateStr); } } else { if (shiftNum.value < cell.text) { currentSelect = getDatesInRange(shiftNum.value, cell.text); } else if (shiftNum.value > cell.text) { currentSelect = getDatesInRange(cell.text, shiftNum.value); } else { currentSelect = [dateStr]; } selectList.value = selectList.value.filter(item => !lastSelect.value.includes(item));//移除上次按shift復制的 selectList.value = selectList.value.concat(currentSelect);//添加本次按shift復制的 lastSelect.value = currentSelect; } } else { selectList.value = [dateStr]; } if (!isShift.value) { shiftNum.value = cell.text; } selectList.value = [...new Set(selectList.value)].sort();//去重、排序 console.log(shiftNum.value, selectList.value); emits('change', selectList.value); }
3.遵循excel點擊的操作方式:
- 未按ctrl、shift點擊=>只選擇當前點擊的;
- 按ctrl點擊=>未選中則選中,已選中則取消選中;
- 按住shift點擊=>記錄shift按下時點擊位置-->再次點擊時把期間內的選中,并移除倒數(shù)第二次的選中(否則都會選中,并在按下ctrl時釋放上次選中)
四、組件代碼:
<template> <div class="calendar"> <div class="calendar_header">{{currentMonth}}月</div> <table cellspacing="0" cellpadding="0" class="calendar_table"> <thead> <th v-for="day in WEEK_DAYS" :key="day" :class="['thead_th',day=='六'||day=='日'?'thead_th_red':'']"> {{day}}</th> </thead> <tbody> <tr v-for="(row,index) in rows" :key="index"> <td v-for="(cell,key) in row" :key="key" class="td" @click="handlePickDay(cell)"> <div :class="getCellClass(cell,key)"> <div v-if="cell.type=='current'" class="triangle" :style="getCelltriangleStyle(cell)"></div> <span class="cell_text">{{cell.text==0?'':cell.text}}</span> </div> </td> </tr> </tbody> </table> </div> </template> <script lang="ts" setup> import { ref, computed, onMounted } from "vue"; import moment from "moment"; const props = defineProps({ markList: {//標記 type: Array<any>, default: (): any[] => { return []; }, }, month: { type: Date, required: true, }, disabled: { type: Boolean, default: false, }, disableBefore: {//禁用今天之前的日期 type: Boolean, default: true, }, }); const emits = defineEmits(['change']); type CalendarDateCellType = 'next' | 'prev' | 'current' type CalendarDateCell = { text: number, type: CalendarDateCellType } const WEEK_DAYS = ref(["日", "一", "二", "三", "四", "五", "六"]); const currentMonth = computed(() => { return moment(props.month).format('M'); }) onMounted(() => { onKeyEvent(); }) const rows = computed(() => { let days: CalendarDateCell[] = [] const firstDay = moment(props.month).startOf("month").date(); // const endDay = moment(props.month).endOf('month').date();//當前月的最后一天 const firstDayOfWeek = moment(props.month).startOf("month").day(); // const daysInMonth = moment(props.month).daysInMonth();//當前月的天數(shù) const prevMonthDays: CalendarDateCell[] = getPrevMonthLastDays( firstDay, firstDayOfWeek - firstDay ).map((day) => ({ text: 0,//上月補0 type: 'prev', })) const currentMonthDays: CalendarDateCell[] = getMonthDays(moment(props.month).daysInMonth()).map( (day) => ({ text: day, type: 'current', }) ) days = [...prevMonthDays, ...currentMonthDays] const remaining = 7 - (days.length % 7 || 7) const nextMonthDays: CalendarDateCell[] = rangeArr(remaining).map( (_, index) => ({ text: 0,//下月補0 type: 'next', }) ) days = days.concat(nextMonthDays) // console.log(currentMonth.value, firstDay, endDay, moment(props.month).startOf("month").day()); return toNestedArr(days) }) const rangeArr = (n: number) => { return Array.from(Array.from({ length: n }).keys()) } const toNestedArr = (days: CalendarDateCell[]) => rangeArr(days.length / 7).map((index) => { const start = index * 7 return days.slice(start, start + 7) }); const getPrevMonthLastDays = (lastDay: number, count: number) => { return rangeArr(count).map((_, index) => lastDay - (count - index - 1)) } const getMonthDays = (days: number) => { return rangeArr(days).map((_, index) => index + 1) } /** * 獲取范圍期間所有日期 * @ return array['YYYY-MM-DD'] */ const getDatesInRange = (start: number, end: number): string[] => { let list = []; for (let i = start; i <= end; i++) { let dateStr = moment(getCellDate({ text: i, type: 'current' })).format('YYYY-MM-DD'); list.push(dateStr); } return list; } const isCtrl = ref(false) const isShift = ref(false) const onKeyEvent = () => { window.addEventListener('keydown', e => { e.preventDefault();//取消默認事件 let e1 = e || window.event switch (e1.keyCode) { case 16: isShift.value = true; break; case 17: isCtrl.value = true; break; } }) window.addEventListener('keyup', e => { e.preventDefault(); let e1 = e || window.event switch (e1.keyCode) { case 16: isShift.value = false; break; case 17: isCtrl.value = false; break; } }) } const getCellClass = (cell: CalendarDateCell, key: number) => { let date = getCellDate(cell); if (props.disableBefore && date.getTime() < new Date().getTime()) { return ['cell', 'cell_disabled'];//禁用 } let classes: string[] = ['cell', 'cell_enabled']; if (key == 0 || key == 6) {//周六、周日 classes.push('cell_red'); } let index = selectList.value.indexOf(moment(date).format('YYYY-MM-DD')); if (index != -1) { classes.push('cell_active');//選中 } return classes; } const getCelltriangleStyle = (cell: CalendarDateCell) => { let date = getCellDate(cell); let day = props.markList.find(item => item.date == moment(date).format('YYYY-MM-DD')); return day ? { borderTop: '30px solid #e6e911', } : {}; } const getCellDate = (cell: CalendarDateCell) => new Date(props.month.getFullYear(), props.month.getMonth(), cell.text) const selectList = ref<any[]>([]);//已選擇的 const shiftNum = ref(0);//shift復制的起始位置 const lastSelect = ref<any[]>([]);//按住shift倒數(shù)第二次復制的 //遵循excel點擊、ctrl、shift組合操作規(guī)范 const handlePickDay = (cell: CalendarDateCell) => { let date = getCellDate(cell); if (cell.type != 'current') { return; } if (props.disableBefore && date.getTime() < new Date().getTime()) { return } // console.log(isCtrl.value, isShift.value); let dateStr = moment(date).format('YYYY-MM-DD'); let currentSelect: string[] = []; //按住ctrl if (isCtrl.value) { if (selectList.value.includes(dateStr)) { selectList.value.splice(selectList.value.indexOf(dateStr), 1); } else { selectList.value.push(dateStr); } lastSelect.value = []; } else if (isShift.value) {//按住shift if (shiftNum.value == 0) {//無上次點擊 shiftNum.value = cell.text; if (selectList.value.includes(dateStr)) { selectList.value.splice(selectList.value.indexOf(dateStr), 1); } else { selectList.value.push(dateStr); } } else { if (shiftNum.value < cell.text) { currentSelect = getDatesInRange(shiftNum.value, cell.text); } else if (shiftNum.value > cell.text) { currentSelect = getDatesInRange(cell.text, shiftNum.value); } else { currentSelect = [dateStr]; } selectList.value = selectList.value.filter(item => !lastSelect.value.includes(item));//移除上次按shift復制的 selectList.value = selectList.value.concat(currentSelect);//添加本次按shift復制的 lastSelect.value = currentSelect; } } else { selectList.value = [dateStr]; } if (!isShift.value) { shiftNum.value = cell.text; } selectList.value = [...new Set(selectList.value)].sort();//去重、排序 console.log(shiftNum.value, selectList.value); emits('change', selectList.value); } </script> <style lang="scss" scoped> .calendar { width: 100%; padding: 12px 20px 35px; &_header { display: flex; justify-content: center; border: 1px solid var(--el-border-color-lighter); padding: 12px 20px; } &_table { width: 100%; .thead_th { padding: 12px 0; color: var(--el-text-color-regular); font-weight: 400; &_red { color: red; } } } .td { border: 1px solid var(--el-border-color-lighter); -moz-user-select: none; /*火狐*/ -webkit-user-select: none; /*webkit瀏覽器*/ -ms-user-select: none; /*IE10*/ -khtml-user-select: none; /*早期瀏覽器*/ user-select: none; } .cell { // background-color: #409eff; position: relative; text-align: center; min-height: 50px; display: flex; justify-content: center; &_enabled { cursor: pointer; color: #373737 } &_disabled { cursor: not-allowed; color: #9b9da1; } &_red { color: red; } &_active { background-color: #409eff; } .triangle { position: absolute; left: 0; top: 0; width: 0; height: 0; // border-top: 30px solid #e6e911; border-right: 30px solid transparent; } &_text { margin: auto; } } } </style>
使用:
<Calendar :month="month" :markList="list" @change="onChange"></Calendar>
到此這篇關于vue可ctrl,shift多選,可添加標記的日歷組件的文章就介紹到這了,更多相關vue ctrl shift內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue中數(shù)組常用的6種循環(huán)方法代碼示例
在vue項目開發(fā)中,我們需要對數(shù)組進行處理等問題,這里簡單記錄遍歷數(shù)組的幾種方法,這篇文章主要給大家介紹了關于vue中數(shù)組常用的6種循環(huán)方法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-03-03如何在Vue3中正確使用ElementPlus,親測有效,避坑
這篇文章主要介紹了如何在Vue3中正確使用ElementPlus,親測有效,避坑!具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03基于 Vue.js 2.0 酷炫自適應背景視頻登錄頁面實現(xiàn)方式
本文講述如何實現(xiàn)擁有酷炫背景視頻的登錄頁面,瀏覽器窗口隨意拉伸,背景視頻及前景登錄組件均能完美適配,背景視頻可始終鋪滿窗口,前景組件始終居中,視頻的內容始終得到最大限度的保留,可以得到最好的視覺效果2018-01-01vue項目接入高德地圖點擊地圖獲取經緯度以及省市區(qū)功能
這篇文章主要給大家介紹了關于vue項目接入高德地圖點擊地圖獲取經緯度以及省市區(qū)功能的相關資料,開發(fā)中我們需要地圖定位,就是用戶輸入位置,自動定位獲取經緯度,需要的朋友可以參考下2023-08-08