vue 实现排班和日历功能table 左右定位 表格可滑动

个人收藏 vue 实现排班和日历功能table 左右定位 表格可滑动 项目中案例有一些垃圾代码。。。。
在这里插入图片描述

在这里插入图片描述
明白人 懂。。。。。。。
父级

<template>
  <div class="app-container" :style="{ padding: receivingOrdersId? '0' : '20px' }">
    <el-card class="search">
      <el-row>
        <el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="6" style="margin-bottom: 6px;">
          <div class="cardNameCss" style="width:22%;">时间:</div>
          <el-date-picker v-model="value2" type="month" placeholder="选择月"></el-date-picker>
        </el-col>
        
        
        <!-- 查询和其他操作 -->
        <el-col :xs="12" :sm="12" :md="12" :lg="12" :xl="5" style="text-align:right;">
          <el-button
            v-permission="['admin:worker:selectWorker']"
            class="filter-item"
            type="primary"
            icon="el-icon-search"
            @click="handleFilter"
            >查找</el-button
          >
          <el-button
            class="filter-item"
            type="primary"
            icon="el-icon-refresh"
            @click="cancelList"
            >重置</el-button
          >
          <el-button
            v-permission="['admin:worker:addWorker']"
            class="filter-item"
            type="primary"
            icon="el-icon-edit"
            @click="handleCreate"
            >添加</el-button
          >
        </el-col>
      </el-row>
    </el-card>
    <!-- 查询结果 -->
    <div class="newTable">
      <div class="newTableCss">
        <table class="workTable"  v-loading="listLoading">
          <tr class="workTableTr">
            <td class="tdFixed" style="width:50px">序号</td>
            <td class="tdFixed" style="margin-left:50px;">姓名</td>
            <td v-for="(item,index) in dataList[0].work">
              <div :class="index==0?'maL':''">
                <span class="disblock" style="font-size:10px">
                  {{item.day}}
               </span>
               <br>
               <span  class="disblock" style="font-size:10px">
                 {{formatDay(item.value)}}
               </span>
              </div>
              
            </td>
             <!-- 占位 -->
            <td>
              <div style="width:82px;"></div>
            </td>
            <td class="tdFixed2">操作</td>
          </tr>
          <tr v-for="(item,index ) in dataList">
            <td class="tdFixed" style="width:50px">{{index+1}}</td>
            <td class="tdFixed" style="margin-left:50px;">{{item.name}}</td>
            <td v-for="(item2,index2) in dataList[index].work" class="colorCss">
              <div :class="index2==0?'maL':''">
                <span v-if="item2.type==1" style="background:green;" class="tabbg">上班</span>
                <span v-if="item2.type==2" style="background:red;" class="tabbg">休息</span>
                <span v-if="item2.type==3" style="background:yellow;color:#000;" class="tabbg">调休</span>
              </div>
            </td>
            <!-- 占位 -->
            <td>
              <div style="width:82px;"></div>
            </td>
            <td class="tdFixed2">
              <el-button
              v-permission="['admin:worker:updateWorker']"
              type="primary"
              size="mini"
              @click="handleUpdate(item)"
              >编辑</el-button>
            </td>
          </tr>
        </table>
      </div>
      <pagination
        v-show="total > 0"
        :total="total"
        :page.sync="listQuery.pageNum"
        :limit.sync="listQuery.pageSize"
        @pagination="getList"
      />
    </div>

    <!-- 添加或修改对话框 -->
    <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
      <el-form
        ref="dataForm"
        :model="dataForm"
        status-icon
        label-position="left"
        label-width="150px"
        style=" margin-left:50px;"
      >
      <el-form-item label="姓名">
       <span>{{dataForm.name}}</span>
      </el-form-item>
      <vueCalendar :dataForm="dataForm" @getwork="getwork"></vueCalendar>
       
        
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取消</el-button>
        <el-button
          v-if="dialogStatus == 'create'"
          type="primary"
          @click="createData"
          :disabled="isDisabled"
          >确定</el-button
        >
        <el-button v-else type="primary" @click="updateData">确定</el-button>
      </div>
      <el-dialog
                title="调班"
                :visible.sync="dialogVisiblex"
                v-model="dialogVisiblex"
                :close-on-press-escape="false"
                :close-on-click-modal="false"
                append-to-body
                width="1000px"
                @close="cancel"
        >
        <div class="flex align-center justify-start tbCss" style="width:100%;">
          <div>姓名:{{dataForm.name}}</div>
          <div>时间:{{dataForm.day}}</div>
          <div>状态:
            <span v-if="dataForm.type==1">上班</span>
            <span v-if="dataForm.type==2">休息</span>
            <span v-if="dataForm.type==3">调休</span>
          </div>
        </div>
        <el-table
        :data="dataList2"
        border
        element-loading-text="正在查询中。。。"
        fit
        highlight-current-row
        size="small"
        @row-click="handleSelectionChange"
      >
        <el-table-column
          align="center"
          fixed="left"
          label="序号"
          type="index"
        />
        <el-table-column align="center" label="姓名" prop="name" />
        <el-table-column align="center" label="状态" prop="type" >
          <template slot-scope="scope">
            <span v-if="scope.row.type==1">上班</span>
            <span v-if="scope.row.type==2">休息</span>
            <span v-if="scope.row.type==3">调休</span>
          </template>
        </el-table-column>
        <el-table-column label="选择" width="55" align="center">
          <template slot-scope="scope">
            <el-radio
              v-model="tableRadio"
              :label="scope.row.nameId"
              class="radioCss"
            >
              <span></span>
            </el-radio>
          </template>
        </el-table-column>
      </el-table>
      <!-- <el-pagination
        :current-page="queryDispatch.pageNum"
        :page-size="queryDispatch.pageSize"
        :total="dispatchtotal"
        background
        layout="total, sizes, prev, pager, next, jumper"
        style="margin-top: 30px"
        @current-change="handleCurrentDispatch"
        @size-change="handleCurrentDispatch"
      /> -->
        <div style="margin-top:10px;text-align: right;">
          <el-button @click="dialogVisiblex = false">取消</el-button>
        <el-button
          type="primary"
          @click="dialogVisiblex = false"
          >确定</el-button
        >
        </div>
        </el-dialog>
    </el-dialog>
  </div>
</template>

<script>
import { listWork, createWork, updateWork, deleteWork } from "@/api/work";
import { getToken } from "@/utils/auth";
import Pagination from "@/components/Pagination"; // Secondary package based on el-pagination
import vueCalendar from "@/components/vueCalendar";//日历
export default {
  name: "Work",
  components: { Pagination,vueCalendar},
  data() {
    return {
      ordersList: [],
      total: 0,
      listLoading: true,
      listQuery: {
        pageNum: 1,
        pageSize: 10,
        storeId:''
      },
      dataForm: {
        id: undefined,
        mobile: undefined,
        password: undefined
      },
      dialogFormVisible: false,
      dialogStatus: "",
      textMap: {
        update: "编辑",
        create: "添加"
      },
      isDisabled:false,
      storeId:"",
      isReturnBtn:false,

      value2:'',
      //type 1上班 2休息 3调休 
      dataList:[{name:'张三',nameId:1,work:[
      {day:'星期二',value:'2023-08-01',type:'1'},
      {day:'星期三',value:'2023-08-02',type:'2'},
      {day:'星期四',value:'2023-08-03',type:'3'},
      {day:'星期五',value:'2023-08-04',type:'1'},
      {day:'星期六',value:'2023-08-05',type:'2'},
      {day:'星期日',value:'2023-08-06',type:'3'},
      {day:'星期一',value:'2023-08-07',type:'1'},
      {day:'星期二',value:'2023-08-08',type:'2'},
      {day:'星期三',value:'2023-08-09',type:'3'},
      {day:'星期四',value:'2023-08-10',type:'1'},
      {day:'星期五',value:'2023-08-11',type:'2'},
      {day:'星期六',value:'2023-08-12',type:'3'},
      {day:'星期日',value:'2023-08-13',type:'1'},
      {day:'星期一',value:'2023-08-14',type:'2'},
      {day:'星期二',value:'2023-08-15',type:'3'},
      {day:'星期三',value:'2023-08-16',type:'1'},
      {day:'星期四',value:'2023-08-17',type:'2'},
      {day:'星期五',value:'2023-08-18',type:'3'},
      {day:'星期六',value:'2023-08-19',type:'1'},
      {day:'星期日',value:'2023-08-20',type:'2'},
      {day:'星期一',value:'2023-08-21',type:'3'},
      {day:'星期二',value:'2023-08-22',type:'1'},
      {day:'星期三',value:'2023-08-23',type:'2'},
      {day:'星期四',value:'2023-08-24',type:'3'},
      {day:'星期五',value:'2023-08-25',type:'1'},
      {day:'星期六',value:'2023-08-26',type:'2'},
      {day:'星期日',value:'2023-08-27',type:'3'},
      {day:'星期一',value:'2023-08-28',type:'1'},
      {day:'星期二',value:'2023-08-29',type:'2'},
      {day:'星期三',value:'2023-08-30',type:'3'},
      {day:'星期四',value:'2023-08-31',type:'3'}]},
      {name:'李四',nameId:2,work:[{day:'星期六',value:'2023-08-01',type:'2'},
      {day:'星期日',value:'2023-08-02',type:'3'},{day:'星期一',value:'2023-08-03',type:'1'}]},
      {name:'王五',nameId:3,work:[{day:'星期六',value:'2023-08-01',type:'1'},
      {day:'星期日',value:'2023-08-02',type:'2'},{day:'星期一',value:'2023-08-03',type:'3'}]},
      ],
      dialogVisiblex:false,
      dataList2:[{name:'小刘',nameId:1,type:'2'},{name:'小李',nameId:2,type:'2'},{name:'小王',nameId:3,type:'2'}],
      tableRadio:''
    };
  },
  props: {
    //门店ID
    receivingOrdersId: {
      type: '',
      default: null
    },
  },
  computed: {
    headers() {
      return {
        accessToken: getToken()
      };
    }
  },
  created() {
    if(this.receivingOrdersId){ //有父级页面
      this.storeId=this.receivingOrdersId
      this.isReturnBtn=true
    }else{//没父级页面 门店账号登录
      this.storeId= this.$store.getters.storeId
      this.isReturnBtn=false
    }
    console.log('storeId',this.storeId)
    this.getDay()
    this.getList();
  },
  methods: {
    formatDay(value){
      if(value){
        return value.replace(/-/g,'/')
      }

    },
    getDay(){
      const now = new Date()
      const year = now.getFullYear()  //年
      const month = now.getMonth() + 1 //月
      const day = now.getDate()    //日
      this.value2=year+'-'+month
    },
    getList() {
      this.listLoading = true;
      this.listQuery.storeId=this.storeId//门店ID
      if(this.listQuery.startTime){
        let startTime=this.listQuery.startTime.slice(0,10)+' 00:00:00'
        this.listQuery.startTime = startTime;

      }
      if(this.listQuery.endTime){
        let endTime=this.listQuery.endTime.slice(0,10)+' 23:59:59'
        this.listQuery.endTime =endTime;

      }
      listWork(this.listQuery)
        .then(response => {
          this.ordersList = response.data.data.list;
          this.total = response.data.data.total;
          this.listLoading = false;
        })
        .catch(() => {
          this.ordersList = [];
          this.total = 0;
          this.listLoading = false;
        });
    },
    //重置
    cancelList() {
      this.listQuery = {};
      this.listQuery.pageNum = 1;
      this.listQuery.pageSize= 20;
      this.getList();
    },
    handleFilter() {
      this.listQuery.pageNum = 1;
      this.getList();
    },
    resetForm() {
      this.dataForm = {
        id: undefined,
        name: undefined
      };
    },
    handleCreate() {
      this.resetForm();
      this.dialogStatus = "create";
      this.dialogFormVisible = true;
      this.$nextTick(() => {
        this.$refs["dataForm"].clearValidate();
      });
    },
    createData() {
      this.dataForm.storeId=this.storeId
      this.$refs["dataForm"].validate(valid => {
        if (valid) {
          this.isDisabled=true
          createWork(this.dataForm)
            .then(response => {
              this.getList();
              this.dialogFormVisible = false;
              this.isDisabled=false
              this.$notify.success({
                title: "成功",
                message: "添加成功"
              });
            })
            .catch(response => {
              this.isDisabled=false
              this.$notify.error({
                title: "失败",
                message: response.data.msg
              });
            });
        }
      });
    },
    handleUpdate(row) {
      this.dataForm = Object.assign({}, row);
      this.dialogStatus = "update";
      this.dialogFormVisible = true;
      this.$nextTick(() => {
        this.$refs["dataForm"].clearValidate();
      });
    },
    updateData() {
      this.$refs["dataForm"].validate(valid => {
        if (valid) {
          updateWork(this.dataForm)
            .then(() => {
              this.getList();
              this.dialogFormVisible = false;
              this.$notify.success({
                title: "成功",
                message: "更新成功"
              });
            })
            .catch(response => {
              this.$notify.error({
                title: "失败",
                message: response.data.msg
              });
            });
        }
      });
    },
    //点击日历返回
    getwork(type,date){
      if(type){
        this.dataForm.day=date.value
        this.dataForm.type=date.type
        this.dialogVisiblex=true
      }

    },
    handleSelectionChange(){

    },
    cancel(){
      this.dialogVisiblex=false
    },
    getValue(){
      this.$forceUpdate()
    },
    goStoreList(){
      this.$emit("goStoreList");
    }
  }
};
</script>
<style>
.workTable{
  border-collapse: collapse;
  color: #606266;
  font-size: 14px;
  width: 100%;
  overflow: auto;
  display: inline-block;
  position: relative;
}
.workTable td{
  border: 1px solid #EBEEF5;
  padding: 8px;
  text-align: center;
}
.colorCss{
  padding: 0px !important;
  color: #fff;
}
.tabbg{
  width: 100%;
  display: inline-block;
  line-height: 48px;
}
.workTableTr{
  width: 100%;
  color: #909399;
  font-weight: bold;
}

.nowrapTab{
  overflow: auto;
  white-space: nowrap;
}
.workTabtd{
  display: inline-block;
  width: 100px;
}
.disblock{
  display: inline-block;
}
.tdFixed{
  position: fixed;
  display:flex;
  background: #fff;
  height: 50px;
  width: 100px;
  justify-content:center;
  align-items: center;
}
.maL{
  margin-left: 150px;
}
.tdFixed2{
  position: fixed;
  display:flex;
  background: #fff;
  height: 50px;
  width: 100px;
  justify-content:center;
  align-items: center;
  right: 30px;
}
.tbCss div{
  padding-left: 10px;

}
</style>

组件

<template>
    <div class="calendar-box">
        <div class="calendar-wrapper">
            <div class="calendar-toolbar">
                <div class="prev" @click="prevMonth">上个月</div>
                <div class="current">{{ currentDateStr }}</div>
                <div class="next" @click="nextMonth">下个月</div>
            </div>

            <div class="calendar-week">
                <div class="week-item calendarBorder" v-for="item of weekList" :key="item">
                    {{ item }}
                </div>
            </div>
            <div class="calendar-inner">
                <div class="calendar-item calendarBorder" v-for="(item, index) of calendarList" :key="index" :class="{
              'calendar-item': true,
              calendarBorder: true,
              'calendar-item-hover': !item.disable,
              'calendar-item-disabled': item.disable,
              'calendar-item-checked':
                dayChecked && dayChecked.value == item.value,
                'coG':item.type==1,
                'coR':item.type==2,
                'coY':item.type==3
            }" @click="handleClickDay(item)" style="position:relative;">
                    <span class="typeCss" v-if="item.type==1">上班</span>
                    <span class="typeCss" v-if="item.type==2">休息</span>
                    <span class="typeCss" v-if="item.type==3">调休</span>
                    {{ item.date }}
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: "vueCalendar",
        props: {
            dataForm: {
                type: "",
                default: null
            },
        },
        data() {
            return {
                showYearMonth: {}, // 显示的年月
                calendarList: [], // 用于遍历显示
                shareDate: new Date(), // 享元模式,用来做 日期数据转换 优化
                dayChecked: {}, // 当前选择的天
                weekList: ["一", "二", "三", "四", "五", "六", "日"], // 周
            };
        },
        created() {
            this.initDataFun(); // 初始化数据
        },
        computed: {
            // 显示当前时间
            currentDateStr() {
                let { year, month } = this.showYearMonth;
                return `${year}年${this.pad(month + 1)}月`;
            },
        },
        methods: {
            //#region 计算日历数据
            // 初始化数据
            initDataFun() {
                // 初始化当前时间
                this.setCurrentYearMonth(); // 设置日历显示的日期(年-月)
                this.createCalendar(); // 创建当前月对应日历的日期数据
                this.getCurrentDay(); // 获取今天
            },
            // 设置日历显示的日期(年-月)
            setCurrentYearMonth(d = new Date()) {
                let year = d.getFullYear();
                let month = d.getMonth();
                let date = d.getDate();
                this.showYearMonth = {
                    year,
                    month,
                    date,
                };
            },
            getCurrentDay(d = new Date()) {
                let year = d.getFullYear();
                let month = d.getMonth();
                let date = d.getDate();
                this.dayChecked = {
                    year,
                    month,
                    date,
                    value: this.stringify(year, month, date),
                    disable: false
                };
            },
            // 创建当前月对应日历的日期数据
            createCalendar() {
                // 一天有多少毫秒
                const oneDayMS = 24 * 60 * 60 * 1000;
                let list = [];
                let { year, month } = this.showYearMonth;

                // #region
                // ---------------仅仅只算某月的天---------------
                //   // 当前月,第一天和最后一天的毫秒数
                //   let begin = new Date(year, month, 1).getTime();
                //   let end = new Date(year, month + 1, 0).getTime();

                // ---------------计算某月前后需要填补的天---------------
                // 当前月份第一天是星期几, 0-6
                let firstDay = this.getFirstDayByMonths(year, month);
                // 填充多少天,因为我将星期日放到最后了,所以需要另外调整下
                let prefixDaysLen = firstDay === 0 ? 6 : firstDay - 1;
                // 向前移动之后的毫秒数
                let begin = new Date(year, month, 1).getTime() - oneDayMS * prefixDaysLen;
                // 当前月份最后一天是星期几, 0-6
                let lastDay = this.getLastDayByMonth(year, month);
                // 填充多少天,因为我将星期日放到最后了,所以需要另外调整下
                let suffixDaysLen = lastDay === 0 ? 0 : 7 - lastDay;
                // 向后移动之后的毫秒数
                let end = new Date(year, month + 1, 0).getTime() + oneDayMS * suffixDaysLen;
                // // 计算左侧时间段的循环数
                // let rowNum = Math.ceil((end - begin) / oneDayMS / 7);
                // let newPeriod = [];
                // for (let i = 0; i < rowNum; i++) {
                //   newPeriod.push({});
                // }
                // #endregion

                // 填充天
                while (begin <= end) {
                    // 享元模式,避免重复 new Date
                    this.shareDate.setTime(begin);
                    let year = this.shareDate.getFullYear();
                    let curMonth = this.shareDate.getMonth();
                    let date = this.shareDate.getDate();
                    list.push({
                        year: year,
                        month: curMonth + 1, // 月是从0开始的
                        date: date,
                        value: this.stringify(year, curMonth, date),
                        disable: curMonth !== month,
                    });
                    begin += oneDayMS;
                }
                let dataForm = this.dataForm.work//父级数据
                for (var i = 0; i < list.length; i++) {
                    for (var i2 = 0; i2 < dataForm.length; i2++) {
                        if (list[i].value == dataForm[i2].value) {
                            list[i].type = dataForm[i2].type
                        }

                    }
                }
                this.calendarList = list;
            },
            // 格式化时间
            stringify(year, month, date) {
                let str = [year, this.pad(month + 1), this.pad(date)].join("-");
                return str;
            },
            // 对小于 10 的数字,前面补 0
            pad(str) {
                return str < 10 ? `0${str}` : str;
            },
            // 点击上一月
            prevMonth() {
                this.showYearMonth.month--;
                this.recalculateYearMonth(); // 因为 month的变化 会超出 0-11 的范围, 所以需要重新计算
                this.createCalendar(); // 创建当前月对应日历的日期数据
            },
            // 点击下一月
            nextMonth() {
                this.showYearMonth.month++;
                this.recalculateYearMonth(); // 因为 month的变化 会超出 0-11 的范围, 所以需要重新计算
                this.createCalendar(); // 创建当前月对应日历的日期数据
            },
            // 重算:显示的某年某月
            recalculateYearMonth() {
                let { year, month, date } = this.showYearMonth;

                let maxDate = this.getDaysByMonth(year, month);
                // 预防其他月跳转到2月,2月最多只有29天,没有30-31
                date = Math.min(maxDate, date);

                let instance = new Date(year, month, date);
                this.setCurrentYearMonth(instance);
            },
            // 判断当前月有多少天
            getDaysByMonth(year, month) {
                return new Date(year, month + 1, 0).getDate();
            },
            // 当前月的第一天是星期几
            getFirstDayByMonths(year, month) {
                return new Date(year, month, 1).getDay();
            },
            // 当前月的最后一天是星期几
            getLastDayByMonth(year, month) {
                return new Date(year, month + 1, 0).getDay();
            },
            // #endregion 计算日历数据

            // 操作:点击了某天
            handleClickDay(item) {
                if (!item || item.disable) return;
                console.log(item);
                this.dayChecked = item;
                this.$emit("getwork", true, item);
            },
        },
    };
</script>

<style>
    .calendar-box {
        width: 90%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .calendar-week {
        width: 637px;
        border-left: 1px solid #eee;
        display: flex;
        flex-wrap: wrap;
        /* .week-item {
          width: 90px;
          height: 50px;
          border-top: 1px solid #eee;
        } */
    }

    .week-item {
        width: 90px;
        height: 50px;
        border-top: 1px solid #eee;
    }

    .calendar-inner {
        width: 637px;
        border-left: 1px solid #eee;
        display: flex;
        flex-wrap: wrap;
        /* .calendar-item {
          width: 90px;
          height: 60px;
        } */



    }

    .calendar-toolbar {
        width: 637px;
        height: 50px;
        display: flex;
        justify-content: space-between;
        align-items: center;

        .prev,
        .next,
        .current {
            cursor: pointer;

            &:hover {
                /* color: #438bef; */
            }
        }
    }

    .calendar-item-disabled {
        color: #acacac;
        cursor: not-allowed;
    }

    .calendar-item-checked {
        color: #fff;
        background-color: #438bef;
    }

    .calendar-item-hover {
        cursor: pointer;

        &:hover {
            color: #fff;
            background-color: #438bef;
        }
    }

    .calendar-item {
        width: 90px;
        height: 60px;
    }

    .calendarBorder {
        display: flex;
        justify-content: center;
        align-items: center;
        border-bottom: 1px solid #eee;
        border-right: 1px solid #eee;
    }

    .prev,
    .next {
        color: #FFF;
        background-color: #409EFF;
        border-color: #409EFF;
        padding: 10px 20px;
        font-size: 14px;
        border-radius: 4px;
    }

    .coG {
        background: green;
        color: #fff !important;
    }

    .coR {
        background: red;
    }

    .coY {
        background: yellow
    }

    .typeCss {
        display: inline-block;
        position: absolute;
        top: 6px;
        right: 6px;
        font-size: 12px;
    }
</style>
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值