Vue+Element-ui实例_日历排班(自定义)

在日常开发需求中,可能会遇到给员工进行排班的需求,如果只是在table表格中显示,会显得枯燥、不直观,今天我们就来写一个可以自定义的日历排班功能,用的是vue2+element-ui。

效果图如下:

(图一):日历中显示排班数据,排班数据可以使用鼠标进行拖动,改变排班的顺序。

 (图二):可以对日期范围进行批量、多班次排班

(图三):可以对单日排班信息进行操作,显示单日排班时间的具体信息等。

下面是代码部分: 

<template>
  <div id="app">
    <div class="calender-class">
      <div class="batch-add-Work-class">
        <el-button class="add-btn-class" size="small" type="primary" @click="batchAddDrawer = true">批量排班</el-button>
        <el-button class="add-btn-class" size="small" type="primary" @click="changeDateDrawer = true">日期选择</el-button>
      </div>
      <el-calendar>
        <!-- 这里使用的是 2.5 slot 语法,对于新项目请使用 2.6 slot 语法-->
        <template slot="dateCell" slot-scope="{ date, data }">
          <div class="day-content-class">
            <template v-if="viewDate[data.day]">
              <div class="header-class">
                <div class="day-class">
                  {{
                    data.day
                      .split("-")
                      .slice(1)
                      .join("-")
                  }}
                </div>
                <div class="handle-class">
                  <el-button icon="el-icon-edit" size="mini" circle @click="handleWorkInfo(viewDate[data.day], data)">
                  </el-button>
                </div>
              </div>
              <div class="paiban-class">
                <div v-for="(dayValue, i) in viewDate[data.day]" :class="[
                    'draggable-div' + i,
                    'each-paiban-class',
                    setWorkClass(dayValue.sort),
                  ]" draggable="true" @dragstart="handleDragStart($event, dayValue, data.day)"
                  @dragover.prevent="handleDragOver($event)" @dragenter="handleDragEnter($event, dayValue)"
                  @dragend="handleDragEnd()">
                  <i :class="[
                      setIconClass(dayValue.shiftName),
                      'paiban-icon-class',
                    ]"></i>
                  <div class="paiban-name-class">{{ dayValue.groupName }}</div>
                </div>
              </div>
            </template>
            <template v-else>
              <div class="header-class">
                <div class="day-class">
                  {{
                    data.day
                      .split("-")
                      .slice(1)
                      .join("-")
                  }}
                </div>
                <div class="handle-class">
                  <el-button icon="el-icon-edit" size="mini" circle @click="handleWorkInfo(viewDate[data.day], data)">
                  </el-button>
                </div>
              </div>
              <div class="no-work-class">
                <div class="icon-class"><i class="el-icon-date"></i></div>
                <div class="tips-class">
                  暂无排班
                </div>
              </div>
            </template>
          </div>
        </template>
      </el-calendar>
    </div>
    <!-- 批量排班抽屉弹窗 -->
    <div>
      <el-drawer title="批量排班" :visible.sync="batchAddDrawer" size="40%">
        <div class="demo-drawer__content">
          <el-form :model="batchAddForm">
            <el-form-item label="排班日期" label-width="80px">
              <el-date-picker v-model="batchAddForm.batchDate" value-format="yyyy-MM-dd" type="daterange"
                range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期">
              </el-date-picker>
            </el-form-item>
            <el-button type="primary" icon="el-icon-plus" circle @click="addDomain"></el-button>
            <el-form-item label-width="80px" v-for="(data, index) in batchAddForm.classData"
              :label="'排班' + (index + 1) + ':'" :key="data.key">
              <p></p>
              <span>班次:</span>
              <el-radio-group v-model="data.shiftName">
                <el-radio label="早">早</el-radio>
                <el-radio label="中">中</el-radio>
                <el-radio label="晚">晚</el-radio>
              </el-radio-group>
              <p></p>
              <span>班别:</span>
              <el-radio-group class="margin-left:80px" v-model="data.groupName">
                <el-radio label="甲">甲</el-radio>
                <el-radio label="乙">乙</el-radio>
                <el-radio label="丙">丙</el-radio>
              </el-radio-group>
              <el-button class="remove-domain-class" @click.prevent="removeDomain(data)" type="danger"
                icon="el-icon-delete" circle></el-button>
            </el-form-item>
          </el-form>
        </div>
        <div class="demo-drawer__footer">
          <el-button @click="handleBatchClose">取 消</el-button>
          <el-button type="primary" @click="batchAddWork()">
            确定
          </el-button>
        </div>
      </el-drawer>
    </div>
    <!-- 单独排班 -->
    <div>
      <el-drawer :title="'【' + hanleDay.day + '】排班'" :visible.sync="drawer" size="40%">
        <div class="add-work-class">
          <el-button class="add-btn-class" type="primary" @click="innerDrawer = true">添加</el-button>
          <el-drawer title="添加排班" :append-to-body="true" :before-close="handleClose" :visible.sync="innerDrawer">
            <div class="demo-drawer__content">
              <el-form :model="addForm">
                <el-form-item label="班次:" label-width="80px">
                  <el-radio-group v-model="addForm.shiftName">
                    <el-radio label="早">早</el-radio>
                    <el-radio label="中">中</el-radio>
                    <el-radio label="晚">晚</el-radio>
                  </el-radio-group>
                </el-form-item>
                <el-form-item label="班别:" label-width="80px">
                  <el-radio-group v-model="addForm.groupName">
                    <el-radio label="甲">甲</el-radio>
                    <el-radio label="乙">乙</el-radio>
                    <el-radio label="丙">丙</el-radio>
                  </el-radio-group>
                </el-form-item>
              </el-form>
            </div>
            <div class="demo-drawer__footer">
              <el-button @click="handleClose">取 消</el-button>
              <el-button type="primary" @click="addWork()">
                确定
              </el-button>
            </div>
          </el-drawer>
        </div>
        <el-table :data="workInfoList">
          <el-table-column property="date" label="日期" width="100"></el-table-column>
          <el-table-column property="shiftName" label="班次"></el-table-column>
          <el-table-column property="groupName" label="班别"></el-table-column>
          <el-table-column property="startTime" label="开始时间" width="160"></el-table-column>
          <el-table-column property="endTime" label="结束时间" width="160"></el-table-column>
          <el-table-column fixed="right" label="操作" width="120">
            <template slot-scope="scope">
              <el-button @click.native.prevent="deleteRow(scope, workInfoList)" type="text" size="small">
                移除
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-drawer>
    </div>
    <!-- 日期选择 -->
    <div>
      <el-drawer title="日期选择" :visible.sync="changeDateDrawer" class="change-date-drawer-class" size="30%">
        <el-calendar>
          <!-- 这里使用的是 2.5 slot 语法,对于新项目请使用 2.6 slot 语法-->
          <template slot="dateCell" slot-scope="{ date, data }">
            <div :class="['day-content-class',setDisabled(data.day)]">
              <template>
                <div class="header-class no-drop-class" v-show="data.type === 'current-month'"
                  @click="selectDate(date,data)">
                  <div class="day-class">
                    {{
                    data.day
                      .split("-")
                      .slice(1)
                      .join("-")
                  }}
                  </div>
                  <div :key="data.day" :id="data.day">{{ initHolidayDate(data) }}</div>
                </div>
              </template>
            </div>
          </template>
        </el-calendar>
        <div style="margin:10px">
          <div>选中的日期:</div>
          <span v-for="day in currentDate">
            {{day.date}} &nbsp
          </span>
        </div>
      </el-drawer>
    </div>
  </div>
</template>

<script>
  import moment from "moment";
  export default {
    data() {
      return {
        viewDate: {
          "2023-10-10": [{
              id: "2023-10-10" + Math.random(1000),
              ruleName: "三班两运转",
              shiftName: "早",
              groupName: "甲",
              startTime: "2023-10-10 08:30",
              endTime: "2023-10-10 20:30",
              isNotHoliday: 0,
              classId: 1,
              date: "2023-10-10",
              sort: 1,
            },
            {
              id: "2023-10-10" + Math.random(1000),
              ruleName: "三班两运转",
              shiftName: "中",
              groupName: "乙",
              startTime: "2023-10-10 20:30",
              endTime: "2023-10-08 08:30",
              isNotHoliday: 0,
              classId: 1,
              date: "2023-10-10",
              sort: 2,
            },
          ],
          "2023-10-08": [{
              id: "2023-10-08" + Math.random(1000),
              ruleName: "三班两运转",
              shiftName: "早",
              groupName: "甲",
              startTime: "2023-10-08 08:30",
              endTime: "2023-10-08 20:30",
              isNotHoliday: 0,
              classId: 1,
              date: "2023-10-08",
              sort: 1,
            },
            {
              id: "2023-10-08" + Math.random(1000),
              ruleName: "三班两运转",
              shiftName: "中",
              groupName: "乙",
              startTime: "2023-10-08 08:30",
              endTime: "2023-10-08 20:30",
              isNotHoliday: 0,
              classId: 1,
              date: "2023-10-08",
              sort: 2,
            },
            {
              id: "2023-10-08" + Math.random(1000),
              ruleName: "三班两运转",
              shiftName: "晚",
              groupName: "丙",
              startTime: "2023-10-08 08:30",
              endTime: "2023-10-09 20:30",
              isNotHoliday: 0,
              classId: 1,
              date: "2023-10-08",
              sort: 3,
            },
          ],
        },
        thisDay: null,
        thisDayWork: null,
        ending: null,
        dragging: null,

        batchAddDrawer: false,
        // 批量添加
        batchAddForm: {
          batchDate: [],
          classData: [{
            shiftName: "早",
            groupName: "甲",
          }, ],
        },
        // 单日添加
        addForm: {
          shiftName: "早",
          groupName: "甲",
          sort: 1,
        },
        drawer: false,
        innerDrawer: false,
        hanleDay: "",
        workInfoList: [],

        // 时间范围
        dateRange: ['2023-10-1', '2023-10-20'],
        changeDateDrawer: false,
        // 点击月中已选中的日期
        currentDate: [],
      };
    },
    watch: {
      "addForm.shiftName"(newVal, oldVal) {
        switch (newVal) {
          case "早":
            this.addForm.sort = 1;
            break;
          case "中":
            this.addForm.sort = 2;
            break;
          case "晚":
            this.addForm.sort = 3;
            break;
          default:
            break;
        }
      },
    },
    computed: {},
    methods: {
      handleDragStart(e, item, thisDay) {
        this.dragging = item;
        this.thisDay = thisDay;
        this.thisDayWork = this.viewDate[thisDay];
      },
      handleDragEnd() {
        if (this.ending.id === this.dragging.id) {
          return;
        }
        let newItems = [...this.thisDayWork];
        const src = newItems.indexOf(this.dragging);
        const dst = newItems.indexOf(this.ending);
        newItems.splice(src, 1, ...newItems.splice(dst, 1, newItems[src]));
        this.$set(this.viewDate, this.thisDay, newItems);
        this.$nextTick(() => {
          this.dragging = null;
          this.ending = null;
        });
        console.log(
          "🚀 ~ file: App.vue:286 ~ handleDragEnd ~ this.viewDate:",
          this.viewDate
        );
      },
      handleDragOver(e) {
        // 首先把div变成可以放置的元素,即重写dragenter/dragover
        e.dataTransfer.dropEffect = "move"; // e.dataTransfer.dropEffect="move";//在dragenter中针对放置目标来设置!
      },
      handleDragEnter(e, item) {
        e.dataTransfer.effectAllowed = "move"; // 为需要移动的元素设置dragstart事件
        this.ending = item;
      },
      // 获取时间范围中的所有日期
      enumerateDaysBetweenDates(startDate, endDate) {
        let daysList = [];
        let SDate = moment(startDate);
        let EDate = moment(endDate);
        daysList.push(SDate.format("YYYY-MM-DD"));
        while (SDate.add(1, "days").isBefore(EDate)) {
          daysList.push(SDate.format("YYYY-MM-DD"));
        }
        daysList.push(EDate.format("YYYY-MM-DD"));
        return daysList;
      },
      setSortValue(value) {
        let sort = 1;
        switch (value) {
          case "早":
            sort = 1;
            break;
          case "中":
            sort = 2;
            break;
          case "晚":
            sort = 3;
            break;
          default:
            break;
        }
        return sort;
      },
      setWorkClass(value) {
        let classValue = "no-work-class";
        switch (value) {
          case 1:
            classValue = "zao-work-class";
            break;
          case 2:
            classValue = "wan-work-class";
            break;
          case 3:
            classValue = "ye-work-class";
            break;
          default:
            break;
        }
        return classValue;
      },
      setIconClass(value) {
        let classValue = "el-icon-sunrise-1";
        switch (value) {
          case "早":
            classValue = "el-icon-sunrise-1";
            break;
          case "中":
            classValue = "el-icon-sunny";
            break;
          case "晚":
            classValue = "el-icon-moon";
            break;
          default:
            break;
        }
        return classValue;
      },
      // 编辑单日排班
      handleWorkInfo(info, data) {
        this.hanleDay = data;
        this.drawer = true;
        if (info && info.length > 0) {
          this.workInfoList = info;
        } else {
          this.workInfoList = [];
        }
      },
      handleClose() {
        this.innerDrawer = false;
      },
      // 添加单日排班
      addWork() {
        let info = {
          id: this.hanleDay.day + Math.random(1000),
          ruleName: "三班两运转",
          shiftName: this.addForm.shiftName,
          groupName: this.addForm.groupName,
          startTime: this.hanleDay.day + " 08:30",
          endTime: this.hanleDay.day + " 20:30",
          isNotHoliday: 0,
          classId: 1,
          date: this.hanleDay.day,
          sort: this.addForm.sort,
        };
        this.workInfoList.push(info);
        this.$set(this.viewDate, this.hanleDay.day, this.workInfoList);
        this.innerDrawer = false;
      },
      // 清除单日排班数据
      deleteRow(row, tableData) {
        let index = row.$index;
        tableData.splice(index, 1);
        if (tableData.length > 0) {
          this.$set(this.viewDate, this.hanleDay.day, tableData);
        } else {
          this.$delete(this.viewDate, this.hanleDay.day);
        }
      },
      addDomain() {
        this.batchAddForm.classData.push({
          shiftName: "早",
          groupName: "甲",
          key: Date.now(),
        });
      },
      removeDomain(item) {
        if (this.batchAddForm.classData.length > 1) {
          var index = this.batchAddForm.classData.indexOf(item);
          if (index !== -1) {
            this.batchAddForm.classData.splice(index, 1);
          }
        } else {
          this.$message({
            message: "请至少安排一个排班",
            type: "error",
          });
        }
      },
      // 批量添加排班数据
      batchAddWork() {
        let dateList = this.batchAddForm.batchDate;
        let classList = this.batchAddForm.classData;
        let list = [];
        if (dateList && dateList.length > 0) {
          list = this.enumerateDaysBetweenDates(dateList[0], dateList[1]);
        }
        list.forEach((item) => {
          let workList = [];
          classList.forEach((work) => {
            let info = {
              id: item + Math.random(1000),
              ruleName: "三班两运转",
              shiftName: work.shiftName,
              groupName: work.groupName,
              startTime: item + " 08:30",
              endTime: item + " 20:30",
              isNotHoliday: 0,
              classId: 1,
              date: item,
              sort: this.setSortValue(work.shiftName),
            };
            workList.push(info);
          });
          this.$set(this.viewDate, item, workList);
        });

        this.batchAddDrawer = false;
        this.batchAddForm = {
          batchDate: [],
          classData: [{
            shiftName: "早",
            groupName: "甲",
          }, ],
        };
      },
      handleBatchClose() {
        this.batchAddDrawer = false;
      },

      //初始化已选中的日期
      initHolidayDate(data) {
        for (let i in this.currentDate) {
          if (data.day === this.currentDate[i].date) {
            data.isSelected = true;
            // return '✔️'
            return '✔'
          }
        }
      },
      //点击选中或取消选中
      selectDate(date, data) {
        console.log("🚀 ~ file: App.vue:510 ~ selectDate ~ data:", data)
        let day = date.getDate();
        let span = document.getElementById(data.day);
        if (span.innerText) {
          span.innerText = ''
          for (let i in this.currentDate) {
            if (day === this.currentDate[i].day) {
              this.currentDate.splice(i, 1)
            }
          }
        } else {
          span.innerText = '✔';
          this.currentDate.push({
            day: day,
            date: data.day
          })
        }
        console.log("this.currentDate:", this.currentDate)
      },
      // 设置禁用值
      setDisabled(date) {
        // console.log("🚀 ~ file: App.vue:537 ~ setDisabled ~ date:", date)
        if (moment(date).isBefore(this.dateRange[0]) || moment(date).isAfter(this.dateRange[1])) {
          return 'disabled-date-class'
        }
      }
    },
  };
</script>

<style>
  #app {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
  }

  .el-table__fixed-right {
    height: 100% !important;
  }

  .calender-class {
    width: 100%;
    height: 100%;
  }

  .is-selected {
    color: #1989fa;
  }

  .el-calendar__body {
    height: 85vh;
  }

  .el-calendar-table {
    height: 100%;
  }

  .el-calendar-day {
    height: 100% !important;
  }

  .day-content-class {
    height: 100px;
    display: flex;
    flex-direction: column;
  }

  .header-class {
    flex: 1;
    display: flex;
    height: 28px;
    flex-direction: row;
    justify-content: space-between;
  }

  .day-class {
    flex: 4;
  }

  .handle-class {
    flex: 1;
  }

  .paiban-class {
    flex: 4;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: flex-end;
  }

  .paiban-icon-class {
    font-size: 22px;
    margin: 8px 0 10px 0;
  }

  .paiban-name-class {
    padding-top: 10px;
  }

  .each-paiban-class {
    text-align: center;
    max-width: 50px;
    margin: 5px 5px 0 5px;
    border-radius: 5px;
    padding: 0 0 5px 0;
    flex: 1;
  }

  .zao-work-class {
    background-color: #d9ffd9;
    color: #11be11;
  }

  .wan-work-class {
    background-color: #fff0bd;
    color: #fccb2c;
  }

  .ye-work-class {
    background-color: #ddeffb;
    color: #2dabff;
  }

  .no-work-class {
    text-align: center;
    color: #cacaca;
  }

  .icon-class {
    font-size: 20px;
    margin-bottom: 20px;
  }

  /* 侧边弹窗 */
  .add-btn-class {
    margin: 10px;
    float: right;
  }

  .change-date-drawer-class .el-calendar__body {
    height: 45%;
  }

  .change-date-drawer-class .day-content-class {
    height: 30px
  }

  .disabled-date-class {
    color: #ccc;
    pointer-events: none;
  }
</style>

  • 13
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
全自动排班表使用说明 1、在设置好年份与表头。 "2、每年1月1日,在中按一个排班周期设置好人员名单,将自动生成全年排班表,   第一行就是1月1日上班人员,如使用该表时不是1月1日,可以通过调整中   的排班次序来获得之后日期的正确排班表。单元格内的多行人员代表当天几个班   (如三行就是早中晚),当天增加一个班,就在人员单元格内按[Alt-Enter]换行。" 3、在表中,请自行增加本年的节日,假日会以红底黑字增亮显示 4、在中设置好班次与工时。 5、在中可以自动统计每人每月或全年的工作量 "6、对于不规则情况的排班,如换班,可以在当月人员名单中手动更改,另外,如果每天   的班次大于三个,会出现单元格显示不下的情况,这时可以用快捷键来修改字体大小。" "7、快捷键:   Ctrl-q 所有表保护状态,只有人员显示栏可以更改   Ctrl-e 取消所有表的保护状态,所有栏都可编辑   Ctrl-r 所有表人员名称字体加大   Ctrl-t 所有表人员名称字体减小 Ctrl-p 下打印排班表 Alt-F4 退出EXECL" "8、注意事项:理论上没有限制,但由于单元格显示宽高度有限(字体太小影响显示效果),       本表建议适用一个班最多三人,一天最多四个班上班(四行)的情况,不符合       该条件的可以使用《排班表(考勤版)》解决。"
好的,针对您的问题,以下是实现 Vue+Element-UI上传图片并压缩的基本步骤: 1. 安装依赖 在项目中安装 Element-UI 和插件 vue-image-crop-upload 以及图片压缩库,可使用以下命令: ``` npm install element-ui vue-image-crop-upload compressjs --save ``` 2. 引入 Element-UI 在 main.js 中引入 Element-UI: ```javascript import Vue from 'vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI) ``` 3. 使用 vue-image-crop-upload 在需要上传图片的组件中使用 vue-image-crop-upload,可使用以下代码: ```vue <template> <div> <vue-image-crop-upload ref="upload" :url="uploadUrl" :headers="uploadHeaders" :size="size" :accept="accept" :beforeUpload="beforeUpload" :cropConfig="cropConfig" :compressConfig="compressConfig" @input="handleInput" @crop-success="handleCropSuccess" > <el-button size="small" type="primary">上传图片</el-button> </vue-image-crop-upload> </div> </template> <script> import VueImageCropUpload from 'vue-image-crop-upload' export default { components: { VueImageCropUpload }, data() { return { uploadUrl: 'xxx', // 上传地址 uploadHeaders: { // 上传请求头 Authorization: 'Bearer ' + getToken() }, size: 1024 * 1024 * 2, // 上传图片大小限制 accept: '.jpg,.jpeg,.png', // 上传图片格式限制 cropConfig: { // 图片裁剪配置 aspectRatio: 1 / 1, autoCropArea: 1, viewMode: 1, zoomable: false, guides: false, dragMode: 'move', cropBoxResizable: false, crop: () => {} }, compressConfig: { // 图片压缩配置 targetSize: 1024 * 1024, // 目标大小 quality: 0.7, // 压缩质量 mimeType: 'image/jpeg' // 输出格式 } } }, methods: { beforeUpload(file) { // 文件上传前的回调函数 this.$refs.upload.startUpload() }, handleInput(file) { // 文件选择后的回调函数 this.$refs.upload.showCrop() }, handleCropSuccess(blob, file) { // 图片裁剪成功后的回调函数 this.compressImage(blob, file) // 压缩图片 }, compressImage(blob, file) { // 图片压缩 const reader = new FileReader() reader.readAsDataURL(blob) reader.onload = (e) => { const base64 = e.target.result const compressedBlob = Compress.compress(base64, this.compressConfig) const compressedFile = new File([compressedBlob], file.name, { type: compressedBlob.type }) this.$emit('upload', compressedFile) // 触发上传事件 } } } } </script> ``` 4. 完成上传 在父组件中监听上传事件,使用 axios 或其他方法上传文件至服务器: ```vue <template> <div> <upload :action="uploadUrl" @upload="handleUpload"></upload> </div> </template> <script> import axios from 'axios' import Upload from './Upload.vue' export default { components: { Upload }, data() { return { uploadUrl: 'xxx' // 上传地址 } }, methods: { handleUpload(file) { const formData = new FormData() formData.append('file', file) axios.post(this.uploadUrl, formData).then(response => { console.log(response.data) }) } } } </script> ``` 以上就是实现 Vue+Element-UI上传图片并压缩的基本步骤,您可以根据您的具体需求进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值