个人收藏 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>