让前端飞js css htmlweb前端技术分享

vue3 中 element-plus Calendar 实现自

2023-04-05  本文已影响0人  阿巳交不起水电费

效果图:

image.png

版本与依赖:注意这里引入了webpack-merge

image.png

大概思路如下

1.修改日历周一至周五的文本:

解决办法:使用Config Provider

2.自定义日历头部:

首先element-plus官网提供了demo,但是我未使用这种方式,经测试发现这样不能通过顶部的日期组件选择日期

image.png

解决办法:使用dayjs自己实现日期快捷切换,这里把这部分逻辑抽离到myCalendar.ts中

image.png
注意Element-plus (opens new window)组件库默认支持 dayjs 进行日期时间处理,所以可以直接导入使用。

3.使用date-cell 的 scoped-slot 来自定义日历单元格,从而接入拖拽事件

4.使用 data.type === 'current-month'实现只显示当月日期

5.若你的element-plus版本使用#date-cell不能实现自定义日历单元格,可改为#dateCell试下

完整代码如下:

index.vue

<template>
  <div class="panel-content">
    <ELPlusLanguageConfig :local="local">
            <el-calendar :model-value="new Date(currentMonth)">
              <template #header="{ date }">

                <div class="button-box">
                  <div class="control-list">
                    <el-button round size="small" @click="selectDate('prev-year')">上一年</el-button>
                    <el-button round size="small" @click="selectDate('prev-month')">上一月</el-button>
                    <el-date-picker class="date-picker" v-model="currentMonth" :value-format="valueFormat" type="month"
                      placeholder="选择月份" />
                    <el-button round size="small" @click="selectDate('next-month')">下一月</el-button>
                    <el-button round size="small" @click="selectDate('next-year')">下一年</el-button>
                    <el-button round size="small" @click="selectDate('today')">今天</el-button>
                  </div>
                  <div>
                    <!-- <el-button type="primary" size="small">导入</el-button> -->
                  </div>
                </div>

              </template>
              <template #dateCell="{ data }">
                <div :class="{ 'dayBox': true }">
                  <div v-if="isCurrentMonth(data)" :class="{ 'dargIn': retData.dragIn === data.day }"
                    @click.stop="handleClick(data)" @drop="onDrop" @dragover="$event => onDragover($event, data)">
                    <h1>{{ formatDay(data) }}</h1>
                    <!-- 删除 -->
                    <el-icon class="del" @click.stop="del(data)">
                      <Delete />
                    </el-icon>
                  </div>
                </div>
              </template>
            </el-calendar>
          </ELPlusLanguageConfig>

          <div class="right-box">
            <div class="d-title">选择值班人员</div>
            <div class="content-box">

              <div class="t-one" v-for="(item, index) in retData.people" :key="index">
                <!-- 组 -->
                <div class="draggable" v-if="item.type" :draggable="true" @dragstart="onDragStart(item)">
                  {{ item.name }}
                  <p><span v-for="(sub, subIndex) in item.children" :key="subIndex">{{ sub.name }}</span></p>
                </div>
                <!-- 未分组 -->
                <div v-else>
                  {{ item.name }}
                  <p>
                    <span class="draggable" v-for="(sub, subIndex) in item.children" :key="subIndex" :draggable="true"
                      @dragstart="onDragStart(sub)">{{ sub.name }}</span>
                  </p>
                </div>
              </div>

            </div>

          </div>

  </div>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue'
import ELPlusLanguageConfig from '../components/ELPlusLanguageConfig.vue'
import { useCalendar } from '../components/myCalendar'

type calendarDateCell = { type: 'prev-month' | 'current-month' | 'next-month', isSelected: boolean, day: string, date: Date }

// 日历相关 start --------
let valueFormat = 'YYYY-MM'
const { currentMonth, selectDate, local } = useCalendar(valueFormat)

// 日期格式化显示
const formatDay = ({ day }:calendarDateCell) => {
  return Number(day.split('-')[2])
}
// 判断是否是当前月
const isCurrentMonth = (data:calendarDateCell) => {
  return data.type === 'current-month'
}

const handleClick = (data:calendarDateCell) => {
  console.log(data, 'click')
}

// 删除
const del = (data:calendarDateCell) => {
  console.log(data, 'del')
}

// 日历相关 end --------

// 拖拽相关 start ------- 

const retData = reactive({
  people: [
    {
      type: 'group',
      name: '组1',
      children: [
        {
          name: '李xx'
        },
        {
          name: '李bbb'
        }
      ]
    },
    {
      type: 'group',
      name: '组2',
      children: [
        {
          name: '李xx2'
        },
        {
          name: '李xxx3'
        }
      ]
    },
    {
      name: '未分配人员',
      children: [
        {
          name: '张三'
        },
        {
          name: '李四'
        }
      ]
    }
  ],
  dragIn: null, // 拖入的盒子,设置样式使用
  dragData: {} // 拖入的数据
})

const onDragStart = (data:any) => {
  console.log('onDragStart', data)
  retData.dragData = data
}

const onDragover = (e:DragEvent, data:any) => {
  // console.log('onDragover', e)
  e.preventDefault();
  retData.dragIn = data.day
}
const onDrop = (ev:DragEvent) => {
  // console.log('onDrop', ev)
  console.log('onDrop', retData.dragIn, retData.dragData)
  retData.dragIn = null
}

// 拖拽相关 end -------

</script>

<style lang="less" scoped>

.panel-content {
  background-color: #122645;
  display: flex;
  justify-content: space-between;
  align-items: stretch;
  height: 100%;
  color: #ffffff;

  .button-box {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;

    .control-list {
      display: flex;
      justify-content: flex-start;
      align-items: center;

      button {
        height: 34px;
        width: 88px;
        font-size: 16px;
        border: none;
        transition: all .3s;
      }

      :deep(.date-picker) {
        width: 188px;
        height: 40px;
        margin: 0 15px;
      }
    }


  }

  .dayBox {
    position: relative;
    height: 100%;
    width: 100%;
    font-size: 30px;
    font-family: MicrosoftYaHei-Bold-, MicrosoftYaHei-Bold;
    cursor: default;

    >div {
      height: 100%;
    }

    h1 {
      padding: 10px 16px 0 16px;
      font-size: 30px;
      margin: 0;
    }

    .del {
      cursor: pointer;
      position: absolute;
      bottom: 9px;
      right: 9px;
      font-size: 16px;

      &:hover {
        color: #0d84ff;
      }
    }
  }

  .right-box {
    width: 729px;
    flex: none;
    background-color: rgba(45, 103, 254, 0.12);
    margin-top: 64px;

    .d-title {
      background-color: #1C4294;
      font-size: 18px;
      padding: 16px 38px;
    }

    @PADDING: 15px;

    .content-box {
      padding: 0 (38px - @PADDING);
      overflow-y: auto;
      box-sizing: border-box;
      max-height: calc(100vh - 300px);
      ;
    }

    .t-one {
      margin-top: 44px - 2*@PADDING;

      &:first-child {
        margin-top: 44px - @PADDING;
      }

      >div {
        padding: @PADDING;
      }


      p {
        display: flex;
        justify-content: flex-start;
        flex-wrap: wrap;
        flex-direction: row;
        margin: 0;
      }

      span {
        background-color: #122645;
        width: 138px;
        height: 40px;
        line-height: 40px;
        padding: 0 16px;
        border: 1px solid #196BD1;
        margin-right: 13px;
        margin-top: 17px;
      }
    }
  }
}

.draggable {
  cursor: move;

  &:hover {
    outline: 1px dashed #196BD1;
    box-shadow: 0px 0px 20px 1px rgb(25, 107, 209,0.5);
  }
}

.dargIn {
  outline: 1px dashed #196BD1;
}
</style>

<!-- 全局样式 -->
<style lang="less">
@themeColor:#ffffff;
.el-calendar {
  background-color: transparent;

  .el-calendar__header {
    margin-bottom: 0;
    padding-bottom: 0;
  }

  .el-calendar__body {
    padding-top: 5px;
    padding-bottom: 0;
  }

  .el-calendar-day {
    padding: 0;
    // border: 1px solid #b4bbc5;
    border-radius: 4px;
    height: 126px;
    background-color: #081827;
  }

  // 其他月隐藏
  // .el-calendar-table:not(.is-range) td.prev,
  // .el-calendar-table:not(.is-range) td.next {
  //   color: transparent;
  // }

  // 顶部周一、周日
  .el-calendar-table thead {
    background-color: #061529;

    th {
      font-weight: normal;
      color: @themeColor;
      font-size: 18px;
      padding: 10px 0;
    }
  }

  .el-calendar-table tr td:first-child {
    border-left: none;
  }

  .el-calendar-table tr td:last-child {
    border-right: none;
  }

  .el-calendar-table td.is-today {
    color: @themeColor;

    .el-calendar-day {
      background-color: rgba(45, 103, 254, 0.35);
    }
  }

  .el-calendar-table__row:last-child {
    td {
      border-bottom: none;
    }
  }

  // hover
  .el-calendar-table .el-calendar-day:hover {
    // .dayBox{
    //   background-color: rgba(102, 177, 255,0.5);
    // }
  }

  // 选中项设置
  .el-calendar-table td.is-selected {
    background-color: transparent;

    .el-calendar-day {
      border-color: #0d84ff;
    }
  }
}

</style>

ELPlusLanguageConfig.vue

<!-- 给elementplus 单独设置国际化内容 -->
<template>
    <el-config-provider :locale="locale">
        <slot></slot>
    </el-config-provider>
</template>

<script setup lang="ts">

// import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
// import en from 'element-plus/dist/locale/en.mjs'

import zhCn from 'element-plus/lib/locale/lang/zh-cn';
// import en from 'element-plus/lib/locale/lang/en';
import { merge } from 'webpack-merge'; 
import {reactive,watch,ref} from 'vue'

const props = defineProps({
    local: { // 国际化
        type: Object,
        default() {
            return {}
        }
    }
})
const locale = ref({})
watch(()=>props.local,(newVal,oldVal)=>{
    locale.value = merge({}, zhCn, newVal)
},{
    immediate:true
})
</script>

<style scoped></style>

myCalendar.ts

import { ref, reactive } from 'vue'
import {dayjs} from "element-plus"
/**
 * 日历切换,快捷时间切换改为结合dayjs实现
 * @param {*} valueFormat 
 * @returns 
 */
export function useCalendar(valueFormat = 'YYYY-MM') {
    // 修改element-plus日历文案
    const local = {
        el: {
            datepicker: {
                weeks: {
                    fri: "周五",
                    mon: "周一",
                    sat: "周六",
                    sun: "周日",
                    thu: "周四",
                    tue: "周二",
                    wed: "周三",
                }
            }
        }
    }

    // 日期
    const currentMonth = ref(dayjs(new Date()).format(valueFormat))
    // 这里没有使用element-plus案例中的方式【因为没有同时生效】,这里改为自己实现快捷切换
    const selectDate = (val:'prev-year'|'prev-month'|'next-month'|'next-year'|'today') => {
        let ploy = {
            // 上一年
            'prev-year': () => {
                currentMonth.value = dayjs(currentMonth.value).add(-1, 'year').format(valueFormat)
            },
            // 上一月
            'prev-month': () => {
                currentMonth.value = dayjs(currentMonth.value).add(-1, 'month').format(valueFormat)
            },
            // 下一月
            'next-month': () => {
                currentMonth.value = dayjs(currentMonth.value).add(1, 'month').format(valueFormat)
            },
            // 下一年
            'next-year': () => {
                currentMonth.value = dayjs(currentMonth.value).add(1, 'year').format(valueFormat)
            },
            // 今天
            'today': () => {
                currentMonth.value = dayjs(new Date()).format(valueFormat)
            },
        }

        ploy[val]()
    }
    return {
        local,
        currentMonth, // 日历组件上的modelValue值,也是日期组件的-model值
        selectDate // 快捷切换时间的事件
    }
}

若对你有帮助,请点个赞吧,若能打赏不胜感激,谢谢支持!
本文地址:https://www.jianshu.com/p/ac88cabc0af5?v=1680746028292,转载请注明出处,谢谢。

参考:
element-plus日历组件

上一篇下一篇

猜你喜欢

热点阅读