更新班组

This commit is contained in:
朱文强 2025-10-19 00:38:48 +08:00
parent 6c46083d4a
commit 4e801873b9
23 changed files with 3332 additions and 0 deletions

View File

@ -47,6 +47,7 @@
"axios": "0.27.2",
"benz-amr-recorder": "^1.1.5",
"bpmn-js-token-simulation": "0.10.0",
"chinese-lunar": "^0.1.4",
"clipboard": "2.0.8",
"code-brick-zj": "^1.0.2",
"core-js": "^3.26.0",

75
src/api/group/Schedule.js Normal file
View File

@ -0,0 +1,75 @@
import request from '@/utils/request'
// 删除排班计划配置基础信息
export function deleteGroupPlan(id) {
return request({
url: '/base/group-scheduling-plan/delete?id=' + id,
method: 'delete'
})
}
// 获得排班计划配置基础信息分页
export function getGroupPlanPage(query) {
return request({
url: '/base/group-scheduling-plan/page',
method: 'get',
params: query
})
}
// 获取code
export function getCode() {
return request({
url: '/base/group-scheduling-plan/getCode',
method: 'get'
})
}
// 获得产线工段树形结构
export function getGroupPlanTree() {
return request({
url: '/base/factory/getTreeSimple',
method: 'get'
})
}
// 基础信息下一步至班组班次
export function createStepOne(data) {
return request({
url: '/base/group-scheduling-plan/createStepOne',
method: 'post',
data:data
})
}
// 班组班次上一步至基础信息
export function returnStepOne(id) {
return request({
url: '/base/group-scheduling-plan/returnStepOne?id='+id,
method: 'delete',
})
}
// 获取部门下可用班组
export function listByDeptId(id) {
return request({
url: '/base/group-team/listByDeptId?id='+id,
method: 'get'
})
}
// 班组班次下一步至获取预览
export function createStepTwo(data) {
return request({
url: '/base/group-scheduling-plan/createStepTwo',
method: 'post',
data:data
})
}
// 获取预览上一步至班组班次
export function returnStepTwo(id) {
return request({
url: '/base/group-scheduling-plan/returnStepTwo?id='+id,
method: 'delete',
})
}

View File

@ -0,0 +1,47 @@
import request from '@/utils/request'
// 创建班组基础信息
export function createGroup(data) {
return request({
url: '/base/group-team/create',
method: 'post',
data: data
})
}
// 更新班组基础信息
export function updateGroup(data) {
return request({
url: '/base/group-team/update',
method: 'put',
data: data
})
}
// 获得班组基础信息
export function getGroup(id) {
return request({
url: '/base/group-team/get?id=' + id,
method: 'get'
})
}
// 获得班组基础信息分页
export function getGroupPage(query) {
return request({
url: '/base/group-team/page',
method: 'get',
params: query
})
}
// 获取班组code
export function getCode() {
return request({
url: '/base/group-team/getCode',
method: 'get'
})
}

View File

@ -0,0 +1,51 @@
/*
* @Author: zwq
* @Date: 2025-10-18 21:24:37
* @LastEditors: zwq
* @LastEditTime: 2025-10-18 23:07:37
* @Description:
*/
import request from '@/utils/request'
// 获得节假日基础信息分页
export function deptHolidayList(query) {
return request({
url: '/base/group-holiday/page',
method: 'get',
params: query
})
}
// 创建节假日基础信息
export function createHoliday(data) {
return request({
url: '/base/group-holiday/create',
method: 'post',
data: data
})
}
// 更新节假日基础信息
export function updateHoliday(data) {
return request({
url: '/base/group-holiday/update',
method: 'put',
data: data
})
}
// 删除
export function deleteHolidayn(id) {
return request({
url: '/base/group-holiday/delete?id=' + id,
method: 'delete'
})
}
// 获得节假日变动日志分页
export function deptHolidayLogList(query) {
return request({
url: '/base/group-holiday-log/page',
method: 'get',
params: query
})
}

BIN
src/assets/images/banzu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,65 @@
<!--
* @Author: zwq
* @Date: 2023-08-01 15:27:31
* @LastEditors: zwq
* @LastEditTime: 2023-08-01 16:25:54
* @Description:
-->
<template>
<div :class="[className, { 'p-0': noPadding }]">
<slot />
</div>
</template>
<script>
export default {
props: {
size: {
// : xl lg md sm
type: String,
default: 'de',
validator: function (val) {
return ['xl', 'lg', 'de', 'md', 'sm'].indexOf(val) !== -1;
},
},
noPadding: {
type: Boolean,
default: false,
},
},
computed: {
className: function () {
return `${this.size}-title`;
},
},
};
</script>
<style lang="scss" scoped>
$pxls: (xl, 28px) (lg, 24px) (de, 20px) (md, 18px) (sm, 16px);
$mgr: 8px;
@each $size, $height in $pxls {
.#{$size}-title {
font-size: $height;
line-height: $height;
color: #000;
font-weight: 500;
font-family: '微软雅黑', 'Microsoft YaHei', Arial, Helvetica, sans-serif;
&::before {
content: '';
display: inline-block;
vertical-align: top;
width: 4px;
height: $height + 2px;
border-radius: 1px;
margin-right: $mgr;
background-color: #0b58ff;
}
}
}
.p-0 {
padding: 0;
}
</style>

View File

@ -0,0 +1,125 @@
<!--
* @Author: zwq
* @Date: 2025-10-11 14:27:37
* @LastEditors: zwq
* @LastEditTime: 2025-10-15 16:47:28
* @Description:
-->
<template>
<div class="app-container">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick" />
<base-table
:selectWidth="55"
@selection-change="selectChange"
:table-props="tableProps"
:page="1"
:limit="999"
:table-data="tableData"></base-table>
</div>
</template>
<script>
import { listByDeptId } from '@/api/group/Schedule';
const tableProps = [
{
prop: 'code',
label: '班组编号',
width: 140,
},
{
prop: 'name',
label: '班组名称',
width: 100,
},
{
prop: 'deptId',
label: '所属部门',
},
{
prop: 'leaderName',
label: '组长',
},
{
prop: 'remark',
label: '备注',
},
];
export default {
data() {
return {
urlOptions: {
getDataListURL: listByDeptId,
},
tableProps,
tableData: [],
formConfig: [
{
type: 'input',
label: '班组编号',
placeholder: '班组编号',
param: 'code',
},
{
type: 'input',
label: '班组名称',
placeholder: '班组名称',
param: 'name',
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary',
},
],
formInline: {
pageNo: 1,
pageSize: 100,
code: '',
name: '',
},
selectedList: [],
deptId: undefined
};
},
created() {},
methods: {
init(id){
this.deptId = id
this.getDataList()
},
//
getDataList() {
this.urlOptions
.getDataListURL(this.deptId)
.then((response) => {
this.tableData = response.data;
});
},
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.formInline.name = val.name;
this.formInline.code = val.code;
this.getDataList();
break;
default:
console.log(val);
}
},
selectChange(val) {
this.selectedList = val;
},
dataFormSubmit() {
if (this.selectedList && this.selectedList.length > 0) {
this.$emit('refreshTableData', this.selectedList);
}
},
},
};
</script>

View File

@ -0,0 +1,806 @@
<!--
* @Author: zwq
* @Date: 2025-10-13 15:07:24
* @LastEditors: zwq
* @LastEditTime: 2025-10-17 17:01:35
* @Description:
-->
<template>
<div>
<!-- 产线信息-工艺工序圆圈 -->
<div class="circle-container">
<div v-for="(item, index) in dotArr" :key="index" class="circle-wrapper">
<div
class="circle"
:style="{
background: stepNum == index + 1 ? '#0B58FF' : '',
}">
{{ index + 1 }}
</div>
<div
class="circle-text"
:style="{
color: stepNum == index + 1 ? '#0B58FF' : '',
}">
{{ item.name }}
</div>
<!-- 圆点后面的虚线 -->
<div v-if="index < 2" class="connector" />
</div>
</div>
<div v-if="stepNum == 1">
<el-form
:model="dataForm"
:rules="dataRule"
ref="dataForm"
@keyup.enter.native="dataFormSubmit()"
label-position="top"
label-width="80px">
<el-row :gutter="10">
<el-col :span="6">
<el-form-item label="计划编号" prop="code">
<el-input
v-model="dataForm.code"
clearable
placeholder="请输入计划编号" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="计划名称" prop="name">
<el-input
v-model="dataForm.name"
clearable
placeholder="请输入计划名称" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="部门">
<dept-select
style="width: 100%"
ref="deptSelect"
@DeptId="setDeptId"></dept-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="周末休假方式" prop="weekType">
<el-select
style="width: 100%"
v-model="dataForm.weekType"
placeholder="请选择周末休假方式">
<el-option
v-for="item in options1"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="6">
<el-form-item label="开始时间" prop="startDay">
<el-date-picker
style="width: 100%"
v-model="dataForm.startDay"
type="datetime"
value-format="timestamp"
placeholder="选择日期时间"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="结束时间" prop="endDay">
<el-date-picker
style="width: 100%"
v-model="dataForm.endDay"
type="datetime"
value-format="timestamp"
placeholder="选择日期时间"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="倒班方式" prop="shiftType">
<el-select
style="width: 100%"
v-model="dataForm.shiftType"
placeholder="请选择倒班方式">
<el-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="同班次连排" prop="shiftSustainedNum">
<el-input
v-model="dataForm.shiftSustainedNum"
oninput="value=value.replace(/^(0+)|[^\d]+/g,'')">
<el-select
style="width: 80px"
v-model="dataForm.shiftSustainedType"
slot="append">
<el-option label="日" :value="1"></el-option>
<el-option label="周" :value="2"></el-option>
<el-option label="月" :value="3"></el-option>
<el-option label="季" :value="4"></el-option>
</el-select>
</el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input
v-model="dataForm.remark"
clearable
placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div v-if="stepNum == 2">
<small-title style="margin: 16px 0" size="sm" :no-padding="true">
班次
</small-title>
<base-table
:table-props="tableProps1"
:page="1"
:limit="10"
:table-data="tableData1">
<method-btn
v-if="tableBtn1.length"
slot="handleBtn"
:width="80"
label="操作"
:method-list="tableBtn1"
@clickBtn="handleClick1" />
</base-table>
<small-title style="margin: 16px 0" size="sm" :no-padding="true">
班组
</small-title>
<base-table
:table-props="tableProps2"
:page="1"
:limit="10"
:table-data="tableData2"
:add-button-show="'新增'"
@emitButtonClick="addNewGroup">
<method-btn
v-if="tableBtn2.length"
slot="handleBtn"
:width="110"
label="操作"
:method-list="tableBtn2"
@clickBtn="handleClick2" />
</base-table>
</div>
<div v-if="stepNum == 3">
<small-title style="margin: 16px 0" size="sm" :no-padding="true">
排班计划预览
<span style="font-size: 14px; color: #ff1c15">
系统将根据以下设置按班组轮班顺序依次循环每日班次生成正式排班计划请确认
<el-popover
placement="right"
title="比如三班两倒同班次连排2天不休周末排班如下"
width="300"
trigger="hover">
<el-table :data="gridData">
<el-table-column property="date"></el-table-column>
<el-table-column
property="class1"
label="班次一"></el-table-column>
<el-table-column
property="class2"
label="班次二"></el-table-column>
</el-table>
<i
slot="reference"
class="el-icon-warning-outline"
style="color: #ff1c15"></i>
</el-popover>
</span>
</small-title>
<el-tag>
{{
'计划时间:' +
parseTime(dataForm.startDay) +
' 至 ' +
parseTime(dataForm.endDay)
}}
</el-tag>
<div
style="
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 10px;
margin: 15px;
">
<div style="display: grid; grid-template-columns: 40px auto; gap: 10px">
<div class="daobanpng" />
<div>
<div style="color: rgba(0, 0, 0, 0.6)">倒班方式</div>
<div style="color: #000000">
{{ ['长白班', '两班倒', '三班倒'][dataForm.shiftType - 1] }}
</div>
</div>
</div>
<div style="display: grid; grid-template-columns: 40px auto; gap: 10px">
<div class="lianpaipng" />
<div>
<div style="color: rgba(0, 0, 0, 0.6)">同班次连排</div>
<div style="color: #000000">
{{
dataForm.shiftSustainedNum +
['日', '周', '月', '季'][dataForm.shiftSustainedType - 1]
}}
</div>
</div>
</div>
<div style="display: grid; grid-template-columns: 40px auto; gap: 10px">
<div class="xiujiapng" />
<div>
<div style="color: rgba(0, 0, 0, 0.6)">周末休假方式</div>
<div style="color: #000000">
{{ ['双休', '周六休', '周日休', '不休'][dataForm.weekType - 1] }}
</div>
</div>
</div>
<div style="display: grid; grid-template-columns: 40px auto; gap: 10px">
<div class="banzupng" />
<div>
<div style="color: rgba(0, 0, 0, 0.6)">参与班组</div>
<div style="color: #000000">
{{
tableData2
.map((item) => {
return item.name;
})
.join('/')
}}
</div>
</div>
</div>
</div>
</div>
<base-dialog
:dialogTitle="'修改班次'"
:dialogVisible="addOrUpdateVisible1"
@cancel="cancel1"
@confirm="handleConfirm1"
:destroy-on-close="true"
append-to-body
width="40%">
<edit-class
ref="editClassRef"
@refreshTableData="refreshTableData1"></edit-class>
</base-dialog>
<base-dialog
:dialogTitle="'班组选择'"
:dialogVisible="addOrUpdateVisible2"
@cancel="cancel2"
@confirm="handleConfirm2"
:destroy-on-close="true"
append-to-body
width="40%">
<add-group
ref="addGroupRef"
@refreshTableData="refreshTableData2"></add-group>
</base-dialog>
<base-dialog
:dialogTitle="'绑定产线'"
:dialogVisible="addOrUpdateVisible3"
@cancel="cancel3"
@confirm="handleConfirm3"
:destroy-on-close="true"
append-to-body
width="50%">
<bind-line
ref="bindLineRef"
@refreshTableData="refreshTableData3"></bind-line>
</base-dialog>
</div>
</template>
<script>
import deptSelect from './../deptSelect.vue';
import SmallTitle from './SmallTitle';
import { parseTime } from '@/filter/code-filter';
import editClass from './edit-class.vue';
import addGroup from './add-group.vue';
import bindLine from './bind-line.vue';
import { getCode, createStepOne, returnStepOne } from '@/api/group/Schedule';
const tableProps1 = [
{
prop: 'name',
label: '班次名称',
},
{
prop: 'startDay',
label: '开始时间',
// filter: (val) => {
// parseTime(val, '{H}:{mm}');
// },
},
{
prop: 'endDay',
label: '结束时间',
// filter: (val) => {
// parseTime(val, '{H}:{mm}');
// },
},
{
prop: 'remark',
label: '备注',
},
];
const tableProps2 = [
{
prop: 'code',
label: '班组编号',
width: 140,
},
{
prop: 'name',
label: '班组名称',
},
{
prop: 'isProduction',
label: '是否生产班组',
filter: (val) => {
return val ? '是' : '否';
},
width: 110,
},
{
prop: 'code1',
label: '产线及工段',
},
];
export default {
components: {
deptSelect,
SmallTitle,
editClass,
addGroup,
bindLine,
},
data() {
return {
dotArr: [
{
name: '基础信息',
},
{
name: '班组班次',
},
{
name: '计划确认',
},
],
stepNum: 1, //
//
dataForm: {
id: undefined,
code: undefined,
name: undefined,
startDay: undefined,
endDay: undefined,
deptId: undefined,
weekType: undefined,
shiftType: undefined,
shiftSustainedNum: undefined,
shiftSustainedType: 1,
remark: undefined,
},
options1: [
{
value: 1,
label: '双休',
},
{
value: 2,
label: '周六休',
},
{
value: 3,
label: '周日休',
},
{
value: 4,
label: '不休',
},
],
options2: [
{
value: 1,
label: '长白班',
},
{
value: 2,
label: '两班倒',
},
{
value: 3,
label: '三班倒',
},
],
dataRule: {
code: [
{ required: true, message: '计划编号不能为空', trigger: 'blur' },
],
name: [
{ required: true, message: '计划名称不能为空', trigger: 'blur' },
],
startDay: [
{ required: true, message: '开始时间不能为空', trigger: 'change' },
],
endDay: [
{ required: true, message: '结束时间不能为空', trigger: 'change' },
],
weekType: [
{
required: true,
message: '周末休假方式不能为空',
trigger: 'change',
},
],
shiftType: [
{
required: true,
message: '倒班方式方式不能为空',
trigger: 'change',
},
],
shiftSustainedNum: [
{ required: true, message: '同班次连排不能为空', trigger: 'blur' },
],
},
//
tableProps1,
tableBtn1: [
{
type: 'edit',
btnName: '编辑',
},
].filter((v) => v),
tableData1: [],
tableProps2,
tableBtn2: [
{
type: 'bind',
btnName: '绑定产线',
},
{
type: 'delete',
btnName: '删除',
},
].filter((v) => v),
tableData2: [],
addOrUpdateVisible1: false,
addOrUpdateVisible2: false,
addOrUpdateVisible3: false,
//
gridData: [
{
date: '第1天',
class1: 'A',
class2: 'B',
},
{
date: '第2天',
class1: 'A',
class2: 'B',
},
{
date: '第3天',
class1: 'C',
class2: 'A',
},
{
date: '第4天',
class1: 'C',
class2: 'A',
},
{
date: '第5天',
class1: 'B',
class2: 'C',
},
{
date: '第6天',
class1: 'B',
class2: 'C',
},
{
date: '第7天',
class1: 'A',
class2: 'B',
},
{
date: '第8天',
class1: 'A',
class2: 'B',
},
],
};
},
methods: {
//id
setDeptId(val) {
this.dataForm.deptId = val;
},
//id
setDataForm() {
this.$refs.deptSelect.setID(this.dataForm.deptId);
},
init() {
this.$nextTick(() => {
this.$refs['dataForm'].resetFields();
getCode().then((res) => {
this.dataForm.code = res.data;
});
});
},
handleClick1(val) {
this.addOrUpdateVisible1 = true;
this.$nextTick(() => {
this.$refs.editClassRef.init(val.data);
});
},
handleClick2(val) {
if (val.type === 'bind') {
this.addOrUpdateVisible3 = true;
this.$nextTick(() => {
this.$refs.bindLineRef.init(val.data.id);
});
} else if (val.type === 'delete') {
this.tableData2.splice(val.data._pageIndex - 1, 1);
}
},
upSubmit() {
if (this.stepNum == 2) {
returnStepOne(this.dataForm.id).then((res) => {
this.stepNum -= 1;
this.$emit('setSN', this.stepNum);
this.$nextTick(() => {
this.setDataForm();
});
});
}
},
nextSubmit() {
if (this.stepNum == 1) {
this.$refs['dataForm'].validate((valid) => {
if (!valid) {
return false;
}
if (!this.dataForm.deptId) {
this.$message('部门不能为空');
return;
}
createStepOne(this.dataForm).then((res) => {
this.dataForm.id = res.data;
this.stepNum += 1;
this.$emit('setSN', this.stepNum);
this.setTWOclass(); //
});
});
return;
}
if (this.stepNum == 2) {
let data = {}
data.planId = this.dataForm.id
data.groupPlanClassesBaseVOList = [] //-list
this.tableData1.forEach((item,index)=>{
const obj = {
sort: index+1,
name : item.name,
remark: item.remark,
startTime: {
hour:item.startDay.split(':')[0],
minute:item.startDay.split(':')[1],
},
endTime: {
hour:item.endDay.split(':')[0],
minute:item.endDay.split(':')[1],
}
}
data.groupPlanClassesBaseVOList.push(obj)
})
data.groupPlanTeamBaseVOList = [] //-list
this.tableData2.forEach(item=>{
const obj = {
teamId:item.id,
bindData: ''
}
data.groupPlanTeamBaseVOList.push(obj)
})
this.stepNum += 1;
this.$emit('setSN', this.stepNum);
return;
}
if (this.stepNum == 3) {
this.stepNum = 1;
this.$emit('setSN', this.stepNum);
return;
}
},
//
setTWOclass() {
this.tableData1 = [];
if (this.dataForm.shiftType == 1) {
const obj = {
name: '长白班',
startDay: '8:00',
endDay: '17:00',
};
this.tableData1.push(obj);
} else if (this.dataForm.shiftType == 2) {
const obj1 = {
name: '白班',
startDay: '8:00',
endDay: '20:00',
};
const obj2 = {
name: '夜班',
startDay: '20:00',
endDay: '8:00',
};
this.tableData1.push(obj1, obj2);
} else if (this.dataForm.shiftType == 3) {
const obj1 = {
name: '早班',
startDay: '8:00',
endDay: '16:00',
};
const obj2 = {
name: '中班',
startDay: '16:00',
endDay: '00:00',
};
const obj3 = {
name: '夜班',
startDay: '00:00',
endDay: '8:00',
};
this.tableData1.push(obj1, obj2, obj3);
}
},
//
addNewGroup() {
this.addOrUpdateVisible2 = true;
this.$nextTick(() => {
this.$refs.addGroupRef.init(this.dataForm.deptId);
});
},
cancel1() {
this.addOrUpdateVisible1 = false;
},
cancel2() {
this.addOrUpdateVisible2 = false;
},
cancel3() {
this.addOrUpdateVisible3 = false;
},
handleConfirm1() {
this.$refs.editClassRef.dataFormSubmit();
},
handleConfirm2() {
this.$refs.addGroupRef.dataFormSubmit();
},
handleConfirm3() {
this.$refs.bindLineRef.dataFormSubmit();
},
refreshTableData1(index, val) {
this.tableData1.splice(index, 1, val);
this.cancel1();
},
refreshTableData2(val) {
this.tableData2.push(...val);
this.cancel2();
},
refreshTableData3(val) {
console.log(val);
this.cancel3();
},
},
};
</script>
<style scoped>
.daobanpng {
width: 40px;
height: 40px;
background-image: url('~@/assets/images/daoban.png');
background-size: 100% 100%;
}
.lianpaipng {
width: 40px;
height: 40px;
background-image: url('~@/assets/images/lianpai.png');
background-size: 100% 100%;
}
.xiujiapng {
width: 40px;
height: 40px;
background-image: url('~@/assets/images/xiujia.png');
background-size: 100% 100%;
}
.banzupng {
width: 40px;
height: 40px;
background-image: url('~@/assets/images/banzu.png');
background-size: 100% 100%;
}
</style>
<!-- // -->
<style scoped>
.circle-container {
height: 110px;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
width: 90%;
overflow-x: auto;
margin: auto;
}
.circle-wrapper {
position: relative;
display: flex;
align-items: center;
}
.circle {
width: 52px;
height: 52px;
border-radius: 50%;
background: #8db1ff;
font-weight: 500;
font-size: 31px;
color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
position: relative;
z-index: 2;
cursor: pointer;
}
.circle-text {
position: absolute;
top: 60px;
left: -15px;
color: #8db1ff;
width: 82px;
text-align: center;
}
/* 下半圆虚线边框 */
.circle::after {
content: '';
position: absolute;
bottom: -6px; /* 2px边框 + 2px间隙 */
left: -4px;
right: -4px;
height: 30px; /* 半圆高度 */
border-radius: 0 0 60px 60px;
border: 1px dashed #0b58ff;
border-top: none;
z-index: 1;
}
.connector {
width: 160px; /* 计算连接线长度 */
height: 2px;
border-bottom: 1px dashed rgb(11, 88, 255, 1);
margin: 0 5px;
z-index: 1;
}
</style>

View File

@ -0,0 +1,69 @@
<!--
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: zwq
* @LastEditTime: 2025-10-16 16:37:40
* @Description:
-->
<template>
<div style="width: 100%; display: flex; justify-content: center">
<tree-transfer
:title="title"
:from_data="fromData"
:to_data="toData"
@add-btn="add"
@remove-btn="remove"
pid="pid"
:defaultProps="{ label: 'name' }"
height="450px"
style="padding-bottom:20px"
:mode="mode"
filter
openAll></tree-transfer>
</div>
</template>
<script>
import treeTransfer from 'el-tree-transfer';
import { getGroupPlanTree } from '@/api/group/Schedule';
export default {
components: { treeTransfer },
data() {
return {
groupId: undefined,
title: ['待选', '已选'],
mode: 'transfer',
fromData: [], //
toData: [], //
};
},
methods: {
init(id) {
this.fromData = [];
this.toData = [];
this.groupId = id;
getGroupPlanTree().then((res) => {
this.fromData = res.data;
this.fromData.forEach((item) => {
item.productionLineId = 0;
});
});
},
// 穿
add(fromData, toData, obj) {
console.log('fromData:', fromData);
console.log('toData:', toData,obj);
},
// 穿
remove(fromData, toData, obj) {
console.log('fromData:', fromData);
console.log('toData:', toData);
},
//
dataFormSubmit() {
this.$emit('refreshTableData',this.toData);
},
},
};
</script>

View File

@ -0,0 +1,114 @@
<!--
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: zwq
* @LastEditTime: 2025-10-15 16:31:11
* @Description:
-->
<template>
<el-form
:model="dataForm"
:rules="dataRule"
ref="dataForm"
@keyup.enter.native="dataFormSubmit()"
label-position="top"
label-width="80px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="序号" prop="index">
<el-input-number
v-model="dataForm.index"
:step="1"
:min="1"
step-strictly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班次名称" prop="name">
<el-input
v-model="dataForm.name"
clearable
placeholder="请输入班次名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="开始时间" prop="startDay">
<el-time-picker
style="width: 100%"
format="H:mm"
value-format="H:mm"
v-model="dataForm.startDay"
placeholder="选择日期时间"></el-time-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束时间" prop="endDay">
<el-time-picker
style="width: 100%"
format="H:mm"
value-format="H:mm"
v-model="dataForm.endDay"
placeholder="选择日期时间"></el-time-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注" prop="remark">
<el-input
v-model="dataForm.remark"
clearable
placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
export default {
components: {},
data() {
return {
dataForm: {
id: undefined,
index: undefined,
name: undefined,
startDay: undefined,
endDay: undefined,
remark: undefined,
},
_pageIndex: 1,
dataRule: {
index: [{ required: true, message: '序号不能为空', trigger: 'blur' }],
name: [
{ required: true, message: '班次名称不能为空', trigger: 'blur' },
],
startDay: [
{ required: true, message: '开始时间不能为空', trigger: 'change' },
],
endDay: [
{ required: true, message: '结束时间不能为空', trigger: 'change' },
],
},
};
},
methods: {
init(val) {
this._pageIndex = val._pageIndex-1;
this.dataForm.index = val._pageIndex || 1;
this.dataForm.name = val.name || undefined;
this.dataForm.startDay = val.startDay || undefined;
this.dataForm.endDay = val.endDay || undefined;
this.dataForm.remark = val.remark || undefined;
},
//
dataFormSubmit() {
this.$refs['dataForm'].validate((valid) => {
if (!valid) {
return false;
}
this.$emit('refreshTableData', this._pageIndex, this.dataForm);
});
},
},
};
</script>

View File

@ -0,0 +1,469 @@
<!--
* @Author: zwq
* @Date: 2025-10-11 14:27:37
* @LastEditors: zwq
* @LastEditTime: 2025-10-15 16:42:28
* @Description:
-->
<template>
<div class="app-container">
<div class="searchBarBox">
<el-form
:inline="true"
ref="searchBarForm"
:model="formInline"
class="searchBar">
<span class="blue-block" />
<el-form-item label="计划编号" prop="code">
<el-input
v-model="formInline.code"
clearable
size="small"
placeholder="请输入计划编号" />
</el-form-item>
<el-form-item label="计划名称" prop="name">
<el-input
v-model="formInline.name"
clearable
size="small"
placeholder="请输入计划名称" />
</el-form-item>
<el-form-item label="开始时间" prop="startDay">
<el-date-picker
v-model="formInline.startDay"
type="datetime"
placeholder="选择日期时间"></el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="endDay">
<el-date-picker
v-model="formInline.endDay"
type="datetime"
placeholder="选择日期时间"></el-date-picker>
</el-form-item>
<el-form-item label="部门" prop="deptId">
<dept-select
style="width: 200px"
ref="deptSelect"
@DeptId="setDeptId"></dept-select>
</el-form-item>
<el-form-item label="状态" prop="leaderName">
<el-select v-model="formInline.status" placeholder="请选择状态">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="small"
@click="buttonClick({ btnName: 'search' })">
查询
</el-button>
</el-form-item>
<el-form-item>
<span class="separateStyle"></span>
</el-form-item>
<el-form-item>
<el-button size="small" @click="buttonClick({ btnName: 'reset' })">
重置
</el-button>
</el-form-item>
<el-form-item>
<span class="separateStyle"></span>
</el-form-item>
<el-form-item>
<el-button
type="success"
size="small"
:plain="true"
@click="buttonClick({ btnName: 'add' })">
新增
</el-button>
</el-form-item>
</el-form>
</div>
<base-table
v-loading="dataListLoading"
:table-props="tableProps"
:page="listQuery.pageNo"
:limit="listQuery.pageSize"
:table-data="tableData"
@emitFun="getDataList">
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="80"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination
:limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo"
:total="listQuery.total"
@pagination="getDataList" />
<base-dialog
:dialogTitle="addOrEditTitle"
:dialogVisible="addOrUpdateVisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
:destroy-on-close="true"
append-to-body
width="60%">
<add-or-update
ref="addOrUpdate"
@setSN="setStepNum"
@refreshDataList="successSubmit"></add-or-update>
<template #footer>
<slot name="footer">
<el-row slot="footer" type="flex" justify="end">
<el-col :span="24">
<el-button
v-if="stepNum > 1"
size="small"
class="btnTextStyle"
@click="handleConfirm('up')">
上一步
</el-button>
<el-button
size="small"
class="btnTextStyle"
@click="handleCancel">
取消
</el-button>
<el-button
v-if="stepNum == 3"
type="primary"
class="btnTextStyle"
size="small"
plain
@click="handleConfirm">
保存草稿
</el-button>
<el-button
type="primary"
class="btnTextStyle"
size="small"
@click="handleConfirm">
{{ stepNum < 3 ? '下一步' : '确认并执行' }}
</el-button>
</el-col>
</el-row>
</slot>
</template>
</base-dialog>
</div>
</template>
<script>
import AddOrUpdate from './add-or-updata';
import deptSelect from './../deptSelect.vue';
import basicPage from '@/mixins/basic-page';
import subSpan from './subSpan.vue';
import subStatus from './subStatus.vue';
import { parseTime } from '@/filter/code-filter';
import { getGroupPlanPage, deleteGroupPlan } from '@/api/group/Schedule';
const tableProps = [
{
prop: 'code',
label: '计划编号',
width: 140,
},
{
prop: 'name',
label: '计划名称',
width: 100,
},
{
prop: 'startDay',
label: '开始时间',
filter: parseTime,
width: 150,
},
{
prop: 'endDay',
label: '结束时间',
filter: parseTime,
width: 150,
},
{
prop: 'shiftType',
label: '倒班方式',
filter: (val) => {
return val ? ['', '长白班', '两班倒', '三班倒'][val] : '-';
},
width: 110,
},
{
prop: 'shiftSustainedNum',
label: '同班次连排',
width: 110,
subcomponent: subSpan,
},
{
prop: 'deptId',
label: '部门',
},
{
prop: 'status',
label: '计划状态',
width: 110,
subcomponent: subStatus,
},
{
prop: 'createTime',
label: '创建时间',
filter: parseTime,
width: 150,
},
];
export default {
mixins: [basicPage],
data() {
return {
urlOptions: {
getDataListURL: getGroupPlanPage,
deleteURL: deleteGroupPlan,
},
tableProps,
tableBtn: [
{
type: 'edit',
btnName: '编辑',
showParam: {
type: '&',
data: [
{
type: 'equal',
name: 'status',
value: 1,
},
],
},
},
{
type: 'delete',
btnName: '删除',
showParam: {
type: '&',
data: [
{
type: 'equal',
name: 'status',
value: 1,
},
],
},
},
{
type: 'detail',
btnName: '查看',
showParam: {
type: '&',
data: [
{
type: 'unequal',
name: 'status',
value: 1,
},
],
},
},
{
type: 'cancel',
btnName: '作废',
showParam: {
type: '&',
data: [
{
type: 'equal',
name: 'status',
value: 2,
},
],
},
},
{
type: 'sync',
btnName: '同步节假日',
showParam: {
type: '&',
data: [
{
type: 'equal',
name: 'status',
value: 2,
},
{
type: 'updateFlag',
name: 'status',
value: true,
},
],
},
},
{
type: 'copy',
btnName: '复制',
},
].filter((v) => v),
tableData: [],
options: [
{
value: '1',
label: '草稿',
},
{
value: '2',
label: '已确认',
},
{
value: '3',
label: '已作废',
},
],
formInline: {
code: '',
name: '',
startDay: '',
endDay: '',
deptId: '',
status: '',
},
stepNum: 1, //
};
},
components: {
AddOrUpdate,
deptSelect,
},
created() {},
methods: {
buttonClick(val) {
switch (val.btnName) {
case 'search':
const date1 = new Date(this.formInline.startDay).getTime();
const date2 = new Date(this.formInline.endDay).getTime();
if (date1 > date2) {
this.$message('开始时间不得晚于结束时间');
return;
}
this.listQuery = {
pageNo: 1,
pageSize: 10,
total: 1,
...this.formInline,
};
this.getDataList();
break;
case 'reset':
this.formInline.name = null;
this.formInline.code = null;
this.formInline.deptId = null;
this.$refs.deptSelect.clear();
this.formInline.leaderName = null;
this.listQuery = {
pageSize: 10,
pageNo: 1,
total: 1,
};
this.getDataList();
break;
case 'add':
this.addOrEditTitle = '添加排班计划';
this.addOrUpdateVisible = true;
this.$nextTick(() => {
this.$refs.addOrUpdate.init();
});
break;
case 'export':
this.handleExport();
break;
default:
console.log(val);
}
},
setDeptId(val) {
this.formInline.deptId = val;
},
setStepNum(val) {
this.stepNum = val;
},
// dialog
handleCancel() {
this.addOrUpdateVisible = false;
this.addOrEditTitle = '';
},
handleConfirm(val) {
if (val == 'up') {
this.$refs.addOrUpdate.upSubmit();
} else {
this.$refs.addOrUpdate.nextSubmit();
}
},
},
};
</script>
<style scope>
.searchBarBox {
width: 100%;
position: relative;
margin-bottom: 8px;
}
.searchBarBox::after {
content: '';
display: block;
clear: both;
}
.searchBar .blue-block {
display: inline-block;
float: left;
width: 4px;
height: 16px;
background-color: #0b58ff;
border-radius: 1px;
margin-right: 8px;
margin-top: 12px;
}
.searchBar .el-form-item {
margin-bottom: 4px;
}
.searchBar .separateStyle {
display: inline-block;
width: 1px;
height: 24px;
background: #e8e8e8;
vertical-align: middle;
}
.searchBar .vue-treeselect__control {
height: 32px !important;
line-height: 32px !important;
margin: 4px 0;
}
body .el-dialog__header {
font-size: 16px;
color: rgba(0, 0, 0, 0.85);
font-weight: 500;
padding: 13px 24px;
border-bottom: 1px solid #e9e9e9;
}
body .el-dialog__header .titleStyle::before{
content: '';
display: inline-block;
width: 4px;
height: 16px;
background-color: #0B58FF;
border-radius: 1px;
margin-right: 8px;
position: relative;
top: 2px;
}
</style>

View File

@ -0,0 +1,26 @@
<template>
<span>
{{
injectData.shiftSustainedNum +
(injectDatashiftSustainedType
? ['', '日', '周', '月', '季'][injectDatashiftSustainedType]
: '')
}}
</span>
</template>
<script>
export default {
props: {
injectData: {
type: Object,
default: () => ({}),
},
},
data() {
return {};
},
created() {},
methods: {},
};
</script>

View File

@ -0,0 +1,31 @@
<!--
* @Author: zwq
* @Date: 2025-10-13 16:40:08
* @LastEditors: zwq
* @LastEditTime: 2025-10-13 16:43:11
* @Description:
-->
<template>
<el-tag
v-if="injectData.status"
size="medium"
:type="['', '', 'success', 'warning'][injectData.status]">
{{ ['', '草稿', '已确认', '已作废'][injectData.status] }}
</el-tag>
</template>
<script>
export default {
props: {
injectData: {
type: Object,
default: () => ({}),
},
},
data() {
return {};
},
created() {},
methods: {},
};
</script>

View File

@ -0,0 +1,64 @@
<template>
<treeselect
v-model="deptId"
:options="deptOptions"
:show-count="true"
sise="small"
ref="treeselect"
placeholder="请选择归属部门"
@input="setId"
:normalizer="normalizer" />
</template>
<script>
import Treeselect from '@riophae/vue-treeselect';
import '@riophae/vue-treeselect/dist/vue-treeselect.css';
import { listSimpleDepts } from '@/api/system/dept';
export default {
components: { Treeselect },
data() {
return {
deptId: undefined,
deptOptions: undefined,
defaultProps: {
children: 'children',
label: 'name',
},
};
},
created() {
this.getTreeselect();
},
methods: {
/** 查询部门下拉树结构 */
getTreeselect() {
listSimpleDepts().then((response) => {
// deptOptions
this.deptOptions = [];
this.deptOptions.push(...this.handleTree(response.data, 'id'));
});
},
// id
setId(val) {
this.$emit('DeptId', val);
},
// id
setID(id) {
this.deptId = id;
},
clear() {
console.log(this.$refs.treeselect);
this.$refs.treeselect.clear();
},
//
normalizer(node) {
return {
id: node.id,
label: node.name,
children: node.children,
};
},
},
};
</script>

View File

@ -0,0 +1,214 @@
<!--
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: zwq
* @LastEditTime: 2025-10-13 15:44:18
* @Description:
-->
<template>
<el-form
:model="dataForm"
:rules="dataRule"
ref="dataForm"
@keyup.enter.native="dataFormSubmit()"
label-position="top"
label-width="80px">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="班组编号" prop="code">
<el-input
v-model="dataForm.code"
clearable
placeholder="请输入班组编号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="班组名称" prop="name">
<el-input
v-model="dataForm.name"
clearable
placeholder="请输入班组名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="部门">
<dept-select
style="width: 200px"
ref="deptSelect"
@DeptId="setDeptId"></dept-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="组长" prop="leaderId">
<el-select
v-model="dataForm.leaderId"
@change="setLeaderName"
placeholder="请选择组长">
<el-option
v-for="item in leaderArr"
:key="item.id"
:label="item.nickname"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="组长电话" prop="leaderPhone">
<el-input
v-model="dataForm.leaderPhone"
clearable
placeholder="请输入组长电话" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="班组人数" prop="num">
<el-input-number
v-model="dataForm.num"
:step="1"
:min="0"
step-strictly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="生产班组" prop="isProduction">
<el-select
v-model="dataForm.isProduction"
placeholder="请选择是否为生产班组">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="备注" prop="remark">
<el-input
v-model="dataForm.remark"
clearable
placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
import basicAdd from '@/mixins/basic-add';
import deptSelect from './../deptSelect.vue';
import { listSimpleUsers } from '@/api/system/user';
import {
createGroup,
updateGroup,
getGroup,
getCode,
} from '@/api/group/groupSetting';
export default {
mixins: [basicAdd],
components: {
deptSelect,
},
data() {
return {
urlOptions: {
isGetCode: true,
codeURL: getCode,
createURL: createGroup,
updateURL: updateGroup,
infoURL: getGroup,
optionArrUrl: [listSimpleUsers], //
},
dataForm: {
id: undefined,
code: undefined,
name: undefined,
leaderId: undefined,
leaderName: undefined,
leaderPhone: undefined,
deptId: undefined,
isProduction: undefined,
num: undefined,
remark: undefined,
},
setData: true,
leaderArr: [],
options: [
{
value: true,
label: '是',
},
{
value: false,
label: '否',
},
],
dataRule: {
code: [
{ required: true, message: '班组编码不能为空', trigger: 'blur' },
],
name: [
{ required: true, message: '班组名称不能为空', trigger: 'blur' },
],
leaderId: [
{ required: true, message: '组长不能为空', trigger: 'change' },
],
isProduction: [
{ required: true, message: '生产班组不能为空', trigger: 'change' },
],
},
};
},
methods: {
setDeptId(val) {
this.dataForm.deptId = val;
},
/** 获取下拉框数组 */
getArr() {
this.urlOptions.optionArrUrl[0]()
.then(({ data: res }) => {
this.leaderArr = res;
})
.catch(() => {});
},
setDataForm(){
this.$refs.deptSelect.setID(this.dataForm.deptId)
},
setLeaderName(val) {
this.leaderArr.map((item) => {
if (val === item.id) {
this.dataForm.leaderName = item.nickname;
}
});
},
//
dataFormSubmit() {
this.$refs["dataForm"].validate((valid) => {
if (!valid) {
return false;
}
if(!this.dataForm.deptId){
this.$message('部门不能为空');
return
}
//
if (this.dataForm.id) {
this.urlOptions.updateURL(this.dataForm).then(response => {
this.$modal.msgSuccess("修改成功");
this.visible = false;
this.$emit("refreshDataList");
});
return;
}
//
this.urlOptions.createURL(this.dataForm).then(response => {
this.$modal.msgSuccess("新增成功");
this.visible = false;
this.$emit("refreshDataList");
});
});
},
},
};
</script>

View File

@ -0,0 +1,35 @@
<template>
<el-switch
@change="changeStatus"
size="small"
v-model="list.enabled"
:active-value="1"
:inactive-value="0"></el-switch>
</template>
<script>
import { updateGroup } from '@/api/group/groupSetting';
export default {
props: {
injectData: {
type: Object,
default: () => ({}),
},
},
data() {
return {
list: this.injectData,
};
},
created() {},
methods: {
changeStatus(val) {
const data = { ...this.injectData, enabled: val };
updateGroup(data).then((res) => {
this.$modal.msgSuccess('修改成功');
this.$emit('emitData');
});
},
},
};
</script>

View File

@ -0,0 +1,277 @@
<!--
* @Author: zwq
* @Date: 2025-10-11 14:27:37
* @LastEditors: zwq
* @LastEditTime: 2025-10-13 14:44:29
* @Description:
-->
<template>
<div class="app-container">
<div class="searchBarBox">
<el-form
:inline="true"
ref="searchBarForm"
:model="formInline"
class="searchBar">
<span class="blue-block" />
<el-form-item label="班组编号" prop="code">
<el-input
v-model="formInline.code"
clearable
size="small"
placeholder="请输入班组编号" />
</el-form-item>
<el-form-item label="部门" prop="deptId">
<dept-select
style="width: 200px"
ref="deptSelect"
@DeptId="setDeptId"></dept-select>
</el-form-item>
<el-form-item label="班组名称" prop="name">
<el-input
v-model="formInline.name"
clearable
size="small"
placeholder="请输入班组名称" />
</el-form-item>
<el-form-item label="组长" prop="leaderName">
<el-input
v-model="formInline.leaderName"
clearable
size="small"
placeholder="请输入组长" />
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="small"
@click="buttonClick({ btnName: 'search' })">
查询
</el-button>
</el-form-item>
<el-form-item>
<span class="separateStyle"></span>
</el-form-item>
<el-form-item>
<el-button size="small" @click="buttonClick({ btnName: 'reset' })">
重置
</el-button>
</el-form-item>
<el-form-item>
<span class="separateStyle"></span>
</el-form-item>
<el-form-item>
<el-button
type="success"
size="small"
:plain="true"
@click="buttonClick({ btnName: 'add' })">
新增
</el-button>
</el-form-item>
</el-form>
</div>
<base-table
v-loading="dataListLoading"
:table-props="tableProps"
:page="listQuery.pageNo"
:limit="listQuery.pageSize"
:table-data="tableData"
@emitFun="getDataList">
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="80"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination
:limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo"
:total="listQuery.total"
@pagination="getDataList" />
<base-dialog
:dialogTitle="addOrEditTitle"
:dialogVisible="addOrUpdateVisible"
@cancel="handleCancel"
@confirm="handleConfirm"
:before-close="handleCancel"
:destroy-on-close="true"
width="50%">
<add-or-update
ref="addOrUpdate"
@refreshDataList="successSubmit"></add-or-update>
</base-dialog>
</div>
</template>
<script>
import AddOrUpdate from './add-or-updata';
import deptSelect from './../deptSelect.vue';
import basicPage from '@/mixins/basic-page';
import changeStatus from './changeStatus.vue';
import { parseTime } from '@/filter/code-filter';
import { getGroupPage } from '@/api/group/groupSetting';
const tableProps = [
{
prop: 'code',
label: '班组编号',
width: 140,
},
{
prop: 'name',
label: '班组名称',
width: 100,
},
{
prop: 'deptId',
label: '所属部门',
},
{
prop: 'leaderName',
label: '组长',
},
{
prop: 'leaderPhone',
label: '组长电话',
},
{
prop: 'num',
label: '人数',
width: 70,
},
{
prop: 'isProduction',
label: '是否生产班组',
filter: val=>{return val?'是':'否'},
width: 110,
},
{
prop: 'remark',
label: '备注',
},
{
prop: 'createTime',
label: '创建时间',
filter: parseTime,
width: 150,
},
{
prop: 'enabled',
label: '班组状态',
subcomponent: changeStatus,
width: 80,
},
];
export default {
mixins: [basicPage],
data() {
return {
urlOptions: {
getDataListURL: getGroupPage,
},
tableProps,
tableBtn: [
{
type: 'edit',
btnName: '编辑',
},
].filter((v) => v),
tableData: [],
formInline: {
code: '',
name: '',
deptId: '',
leaderName: '',
},
};
},
components: {
AddOrUpdate,
deptSelect,
},
created() {},
methods: {
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.listQuery = {
pageNo: 1,
pageSize: 10,
total: 1,
...this.formInline,
};
this.getDataList();
break;
case 'reset':
this.formInline.name = null;
this.formInline.code = null;
this.formInline.deptId = null;
this.$refs.deptSelect.clear();
this.formInline.leaderName = null;
this.listQuery = {
pageSize: 10,
pageNo: 1,
total: 1,
};
this.getDataList();
break;
case 'add':
this.addOrEditTitle = '新增';
this.addOrUpdateVisible = true;
this.addOrUpdateHandle();
break;
case 'export':
this.handleExport();
break;
default:
console.log(val);
}
},
setDeptId(val){
this.formInline.deptId = val
}
},
};
</script>
<style scope>
.searchBarBox {
width: 100%;
position: relative;
margin-bottom: 8px;
}
.searchBarBox::after {
content: '';
display: block;
clear: both;
}
.searchBar .blue-block {
display: inline-block;
float: left;
width: 4px;
height: 16px;
background-color: #0b58ff;
border-radius: 1px;
margin-right: 8px;
margin-top: 12px;
}
.searchBar .el-form-item {
margin-bottom: 4px;
}
.searchBar .separateStyle {
display: inline-block;
width: 1px;
height: 24px;
background: #e8e8e8;
vertical-align: middle;
}
.searchBar .vue-treeselect__control {
height: 32px !important;
line-height: 32px !important;
margin: 4px 0;
}
</style>

View File

@ -0,0 +1,209 @@
<!--
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: zwq
* @LastEditTime: 2025-10-19 00:09:11
* @Description:
-->
<template>
<el-form
:model="dataForm"
:rules="dataRule"
ref="dataForm"
@keyup.enter.native="dataFormSubmit()"
label-position="top"
label-width="80px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="节假日名称" prop="name">
<el-input
v-model="dataForm.name"
clearable
:disabled="detail"
placeholder="请输入节假日名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="日历类型" prop="calendarType">
<el-select
v-model="dataForm.calendarType"
:disabled="detail"
style="width: 100%">
<el-option
v-for="item in options1"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="日期类型" prop="dateType">
<el-select
style="width: 100%"
:disabled="detail"
v-model="dataForm.dateType">
<el-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="日期" prop="dateTime">
<el-date-picker
:disabled="detail"
style="width: 100%"
v-if="dataForm.dateType == 1"
key="date"
v-model="dataForm.dateDay"
type="date"
placeholder="选择日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"></el-date-picker>
<el-date-picker
:disabled="detail"
style="width: 100%"
v-else
key="daterange"
v-model="dataForm.dateDayArr"
type="daterange"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否每年重复" prop="repeat">
<el-select
v-model="dataForm.repeat"
:disabled="detail"
style="width: 100%">
<el-option
v-for="item in options3"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
import { createHoliday, updateHoliday,deleteHolidayn } from '@/api/group/holidaySetting';
export default {
components: {},
data() {
return {
dataForm: {
id: undefined,
name: undefined,
calendarType: 1,
dateType: 1,
dateDay: undefined,
dateDayArr: [],
repeat: true,
},
detail: false,
options1: [
{
value: 1,
label: '公历(阳历)',
},
{
value: 2,
label: '农历(阴历)',
},
],
options2: [
{
value: 1,
label: '单天',
},
{
value: 2,
label: '时间段',
},
],
options3: [
{
value: false,
label: '否,本年度有效',
},
{
value: true,
label: '是,每年循环生效',
},
],
dataRule: {
name: [
{ required: true, message: '节假日名称不能为空', trigger: 'blur' },
],
calendarType: [
{ required: true, message: '日历类型不能为空', trigger: 'change' },
],
dateType: [
{ required: true, message: '日期类型不能为空', trigger: 'change' },
],
dateDay: [
{ required: true, message: '日期不能为空', trigger: 'change' },
],
dateDayArr: [
{ required: true, message: '日期不能为空', trigger: 'change' },
],
repeat: [
{
required: true,
message: '是否每年重复不能为空',
trigger: 'change',
},
],
},
};
},
methods: {
init(id, detail) {
this.dataForm.id = id || undefined;
this.detail = detail || false;
},
editHoliday(){
this.detail = false
},
deleteHoliday(){
deleteHolidayn(this.dataForm.id).then(res=>{
console.log(res)
})
},
//
dataFormSubmit() {
this.$refs['dataForm'].validate((valid) => {
if (!valid) {
return false;
}
//
if (this.dataForm.id) {
updateHoliday(this.dataForm).then((response) => {
this.$modal.msgSuccess('修改成功');
this.visible = false;
this.$emit('refreshPage');
});
return;
}
//
createHoliday(this.dataForm).then((response) => {
this.$modal.msgSuccess('新增成功');
this.visible = false;
this.$emit('refreshPage');
});
});
},
},
};
</script>

View File

@ -0,0 +1,181 @@
<template>
<div class="app-container">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick" />
<base-table
:table-props="tableProps"
:page="listQuery.pageNo"
:limit="listQuery.pageSize"
:table-data="tableData">
</base-table>
<pagination
:limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo"
:total="listQuery.total"
@pagination="getDataList" />
</div>
</template>
<script>
import { parseTime } from '@/filter/code-filter';
import { deptHolidayLogList } from '@/api/group/holidaySetting';
const tableProps = [
{
prop: 'createTime',
label: '操作时间',
filter: parseTime
},
{
prop: 'opPeopleName',
label: '操作人'
},
{
prop: 'opType',
label: '操作类型',
filter: (val)=>{return val?['','创建','删除','修改','-','-'][val]:'-'}
},
{
prop: 'name',
label: '节假日名称'
},
{
prop: 'opDet',
label: '修改内容'
},
{
prop: 'planType',
label: '更新排班计划',
filter: (val)=>{return val?['','是','否','无排班计划'][val]:'-'}
},
];
export default {
data() {
return {
listQuery: { //
pageSize: 10,
pageNo: 1,
total: 1,
},
tableProps,
tableData: [],
formConfig: [
{
type: 'datePicker',
label: '时间范围',
dateType: 'daterange',
format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd HH:mm:ss',
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'createTime',
},
{
type: 'input',
label: '操作人',
placeholder: '操作人',
param: 'opPeopleName',
},
{
type: 'select',
label: '操作类型',
selectOptions: [
{
id: 1,
name: '创建',
},
{
id: 2,
name: '删除',
},
{
id: 3,
name: '修改',
},
{
id: 4,
name: '解除继承',
},
{
id: 5,
name: '恢复继承',
},
],
param: 'opType',
},
{
type: 'select',
label: '更新排班计划',
selectOptions: [
{
id: 1,
name: '是',
},
{
id: 2,
name: '否',
},
{
id: 3,
name: '无排班计划',
},
],
param: 'planType',
},
{
type: 'button',
btnName: '搜索',
name: 'search',
color: 'primary',
},
{
type: 'separate',
},
{
type: 'button',
btnName: '重置',
name: 'reset',
},
],
};
},
created() {},
methods: {
//
getDataList() {
deptHolidayLogList(this.listQuery).then(response => {
this.tableData = response.data.list;
this.listQuery.total = response.data.total;
});
},
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.listQuery.pageNo = 1;
this.listQuery.pageSize = 10;
this.listQuery.createTime = val.createTime || [];
this.listQuery.planType = val.planType || undefined;
this.listQuery.opType = val.opType || undefined;
this.listQuery.opPeopleName = val.opPeopleName || undefined;
this.getDataList();
break;
case 'reset':
this.$refs.searchBarForm.resetForm();
this.listQuery = {
pageSize: 10,
pageNo: 1,
total: 1,
};
this.getDataList();
break;
default:
console.log(val);
}
},
},
};
</script>

View File

@ -0,0 +1,473 @@
<!--
* @Author: zwq
* @Date: 2024-07-01 14:54:06
* @LastEditors: zwq
* @LastEditTime: 2025-10-19 00:35:24
* @Description:
-->
<template>
<el-row :gutter="10" style="background-color: #f2f4f9">
<!--部门数据-->
<el-col :span="4">
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px" />
<el-tree
:data="deptOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree"
default-expand-all
highlight-current
@node-click="handleNodeClick" />
</div>
</el-col>
<el-col :span="20">
<div class="groupTeamScheduling">
<div class="operationArea">
<el-row>
<el-col :span="16">
<div style="height: 40px; font-size: 16px">
<span style="color: #409eff; font-weight: 600">
{{ showDeptName }}
</span>
节假日设置
</div>
</el-col>
<el-col :span="8">
<div style="float: right">
<el-button
size="small"
type="primary"
style="margin-right: 10px"
@click="addHoliday">
新增节假日
</el-button>
<el-button size="small" @click="holidayLog">
节假日变更记录
</el-button>
</div>
</el-col>
</el-row>
</div>
<div class="operationArea">
<el-form :inline="true" class="demo-form-inline">
<span class="blue-block"></span>
<el-form-item label="月份选择">
<el-date-picker
v-model="startDay"
type="month"
placeholder="选择月"
size="small"
@change="selectMonth"
:clearable="false"
style="width: 120px"></el-date-picker>
</el-form-item>
<el-form-item style="float: right">
<el-button
size="small"
type="primary"
@click="startDay = new Date()">
跳转到今天
</el-button>
</el-form-item>
</el-form>
<el-tag
type="warning"
closable
style="margin-bottom: 10px; color: black">
<i class="el-icon-warning" style="color: #ffbd02"></i>
当前节假日设置为组织架构中最高层级配置默认自动继承至下属部门子部门可选择
<span style="color: red">解除继承</span>
设置后进行独立修改复制后不再继承上级设置
</el-tag>
</div>
<!-- 日历区域 -->
<div class="calenderArea">
<el-calendar v-model="startDay">
<template slot="dateCell" slot-scope="{ data }">
<div v-if="data.type === 'current-month'" @click="showDetail">
<!-- 日期 -->
<div class="dateStyle">
<el-row :gutter="20">
<el-col :span="18">
{{ Number(data.day.split('-')[2]) }}
<div class="lunar-date">{{ getLunarDate(data.day) }}</div>
</el-col>
<el-col :span="6"><div class="work-tip"></div></el-col>
</el-row>
</div>
</div>
<div
v-else
style="font-size: 20px; font-weight: 500; text-align: left">
<el-row :gutter="20">
<el-col :span="24">
{{ Number(data.day.split('-')[2]) }}
<span style="font-size: 12px">
{{ getLunarDate(data.day) }}
</span>
</el-col>
</el-row>
</div>
</template>
</el-calendar>
</div>
</div>
</el-col>
<base-dialog
:dialogTitle="dialogTitle"
:dialogVisible="addOrUpdateVisible"
@cancel="cancel"
@confirm="handleConfirm"
:destroy-on-close="true"
width="40%">
<add-or-updata
ref="addOrUpdataRef"
@refreshPage="getHolidayPage"></add-or-updata>
<template #footer>
<slot name="footer">
<el-row slot="footer" type="flex" justify="end">
<el-col :span="24">
<el-button
v-if="!detail"
size="small"
class="btnTextStyle"
@click="cancel">
取消
</el-button>
<el-button
v-if="!detail"
type="primary"
class="btnTextStyle"
size="small"
@click="handleConfirm">
确定
</el-button>
<el-button
v-if="detail"
size="small"
type="primary"
class="btnTextStyle"
@click="editHoliday">
修改
</el-button>
<el-button
v-if="detail"
class="btnTextStyle"
size="small"
@click="deleteHoliday">
删除
</el-button>
</el-col>
</el-row>
</slot>
</template>
</base-dialog>
<base-dialog
dialogTitle="节假日变更记录"
:dialogVisible="logVisible"
@cancel="cancelLog"
:destroy-on-close="true"
width="70%">
<holiday-log ref="holidayLogRef"></holiday-log>
<template #footer>
<slot name="footer">
<el-row slot="footer" type="flex" justify="end">
<el-col :span="24">
<el-button size="small" class="btnTextStyle" @click="cancelLog">
取消
</el-button>
</el-col>
</el-row>
</slot>
</template>
</base-dialog>
</el-row>
</template>
<script>
import moment from 'moment';
import { solarToLunar } from 'chinese-lunar';
import { listSimpleDepts } from '@/api/system/dept';
import { getUserProfile } from '@/api/system/user';
import { deptHolidayList } from '@/api/group/holidaySetting';
import addOrUpdata from './add-or-updata.vue';
import holidayLog from './holidayLog';
export default {
name: '',
components: {
addOrUpdata,
holidayLog,
},
data() {
return {
startDay: '', //
//
deptOptions: undefined,
//
deptName: undefined,
//
showDeptName: undefined,
deptId: undefined,
defaultProps: {
children: 'children',
label: 'name',
},
dialogTitle: undefined,
addOrUpdateVisible: false,
detail: false,
logVisible: false,
};
},
watch: {
//
deptName(val) {
this.$refs.tree.filter(val);
},
},
created() {
this.startDay = new Date();
//
getUserProfile().then((response) => {
this.showDeptName = response.data.dept.name || '';
this.deptId = response.data.dept.id || '';
this.getHolidayPage();
});
this.getTreeselect();
},
methods: {
getHolidayPage() {
deptHolidayList({ deptId: this.deptId }).then((response) => {});
},
//
selectMonth() {
console.log(this.startDay);
this.getHolidayPage();
},
/** 查询部门下拉树结构 */
getTreeselect() {
listSimpleDepts().then((response) => {
// deptOptions
this.deptOptions = [];
this.deptOptions.push(...this.handleTree(response.data, 'id'));
});
},
//
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
//
handleNodeClick(data) {
this.deptId = data.id;
this.showDeptName = data.name;
this.getHolidayPage();
},
//
getLunarDate(solarDate) {
try {
const [year, month, day] = solarDate.split('-').map(Number);
const date = new Date(year, month - 1, day);
const lunar = solarToLunar(date);
//
const monthMap = {
1: '正',
2: '二',
3: '三',
4: '四',
5: '五',
6: '六',
7: '七',
8: '八',
9: '九',
10: '十',
11: '冬',
12: '腊',
};
const dayMap = {
1: '初一',
2: '初二',
3: '初三',
4: '初四',
5: '初五',
6: '初六',
7: '初七',
8: '初八',
9: '初九',
10: '初十',
11: '十一',
12: '十二',
13: '十三',
14: '十四',
15: '十五',
16: '十六',
17: '十七',
18: '十八',
19: '十九',
20: '二十',
21: '廿一',
22: '廿二',
23: '廿三',
24: '廿四',
25: '廿五',
26: '廿六',
27: '廿七',
28: '廿八',
29: '廿九',
30: '三十',
};
// ""
return `${monthMap[lunar.month]}${dayMap[lunar.day]}`;
} catch (error) {
console.log(error);
return '';
}
},
addHoliday() {
this.addOrUpdateVisible = true;
this.detail = false;
this.dialogTitle = '新增节假日';
this.$nextTick(() => {
this.$refs.addOrUpdataRef.init();
});
},
holidayLog() {
this.logVisible = true;
this.$nextTick(() => {
this.$refs.holidayLogRef.getDataList();
});
},
cancel() {
this.addOrUpdateVisible = false;
},
handleConfirm() {
this.$refs.addOrUpdataRef.dataFormSubmit();
},
showDetail(id) {
this.addOrUpdateVisible = true;
this.detail = true;
this.dialogTitle = '节假日详情';
this.$nextTick(() => {
this.$refs.addOrUpdataRef.init(id, true);
});
},
editHoliday() {
this.detail = false;
this.$nextTick(() => {
this.$refs.addOrUpdataRef.editHoliday();
});
},
deleteHoliday() {
this.$nextTick(() => {
this.$refs.addOrUpdataRef.deleteHoliday();
});
},
cancelLog() {
this.logVisible = false;
},
},
};
</script>
<style lang="scss">
.head-container {
padding: 20px 10px 0;
background-color: #fff;
min-height: calc(100vh - 120px - 8px);
border-radius: 8px;
}
.groupTeamScheduling {
.operationArea {
padding: 14px 10px 0 16px;
margin-bottom: 8px;
background-color: #fff;
border-radius: 8px;
.blue-block {
display: inline-block;
width: 4px;
height: 16px;
background-color: #0b58ff;
border-radius: 1px;
margin-right: 8px;
margin-top: 10px;
}
.el-form-item {
margin-bottom: 10px;
}
}
//
.calenderArea {
padding: 14px 10px 0 20px;
background-color: #fff;
border-radius: 8px;
min-height: calc(100vh - 120px - 8px - 168px);
.el-calendar__body {
padding: 10px 16px 16px 0;
}
.el-calendar__header {
display: none;
}
.el-calendar-table > thead {
height: 48px;
font-size: 20px;
font-weight: 500;
color: #000000;
background-color: rgba(242, 244, 249, 1);
}
.el-calendar-table__row {
height: 133px;
.prev,
.next {
pointer-events: none;
}
.is-selected,
.is-today {
background-color: #e4f0fd;
}
.el-calendar-day {
padding: 0;
height: 100%;
:hover {
background-color: #e4f0fd;
}
.dateStyle {
font-size: 20px;
font-weight: 500;
color: #000000;
text-align: left;
height: 133px;
line-height: 28px;
padding: 10px;
.lunar-date {
display: inline-block;
font-size: 12px;
color: #909399;
}
.work-tip {
background: #87c1ff;
color: white;
font-size: 18px;
width: 30px;
text-align: center;
float: right;
}
}
}
}
}
}
</style>