Compare commits

...

6 Commits

Author SHA1 Message Date
zwq
4383067eb1 更新 2025-12-11 12:51:41 +08:00
0e343279ac Merge pull request '更新' (#456) from projects/line-new-zwq into projects/line-new
Reviewed-on: #456
2025-12-01 14:52:38 +08:00
zwq
00ac3dec45 更新 2025-12-01 14:52:21 +08:00
a84f765e73 Merge pull request '班组bug' (#455) from projects/line-new-zwq into projects/line-new
Reviewed-on: #455
2025-11-28 16:29:25 +08:00
zwq
ffa0b2e8dd 班组bug 2025-11-28 16:29:06 +08:00
1e6c6c5656 Merge pull request '班组bug' (#448) from projects/line-new-zwq into projects/line-new
Reviewed-on: #448
2025-11-11 15:38:56 +08:00
17 changed files with 1392 additions and 1018 deletions

View File

@@ -26,6 +26,13 @@ export function getGroup(id) {
method: 'get'
})
}
// 检查更新-生产班组
export function updateIsProduction(id) {
return request({
url: '/base/group-team/updateIsProduction?id=' + id,
method: 'get'
})
}
// 获得班组基础信息分页
export function getGroupPage(query) {

View File

@@ -16,7 +16,7 @@ export default {
},
tableData: [], //table数据
listQuery: { //分页
pageSize: 10,
pageSize: 20,
pageNo: 1,
total: 1,
},

View File

@@ -1,10 +1,10 @@
import axios from 'axios'
import {Message, MessageBox, Notification, Loading} from 'element-ui'
import { Message, MessageBox, Notification, Loading } from 'element-ui'
import store from '@/store'
import {getAccessToken, getRefreshToken, getTenantId, setToken} from '@/utils/auth'
import { getAccessToken, getRefreshToken, getTenantId, setToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import {getPath, getTenantEnable} from "@/utils/ruoyi";
import {refreshToken} from "@/api/login";
import { getPath, getTenantEnable } from "@/utils/ruoyi";
import { refreshToken } from "@/api/login";
// 需要忽略的提示。忽略后,自动 Promise.reject('error')
const ignoreMsgs = [
@@ -86,7 +86,7 @@ service.interceptors.request.use(config => {
for (const propName of Object.keys(config.params)) {
const value = config.params[propName];
const part = encodeURIComponent(propName) + '='
if (value !== null && typeof(value) !== "undefined") {
if (value !== null && typeof (value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
let params = propName + '[' + key + ']';
@@ -104,9 +104,9 @@ service.interceptors.request.use(config => {
}
return config
}, error => {
tryHideFullScreenLoading()
console.log(error)
Promise.reject(error)
tryHideFullScreenLoading()
console.log(error)
Promise.reject(error)
})
// 响应拦截器
@@ -176,36 +176,38 @@ service.interceptors.response.use(async res => {
+ '<div>5 分钟搭建本地环境</div>',
})
return Promise.reject(new Error(msg))
} else if (code === 400) {
//【班组管理】【排班计划】提交的校验按照原型图补充完整,排班计划是否重叠code400 有两个不同的返回信息
return res.data
} else if (code !== 200) {
if (msg === '无效的刷新令牌') { // hard coding忽略这个提示直接登出
console.log(msg)
} else {
Notification.error({
title: msg
})
//【班组管理】【排班计划】提交的校验按照原型图补充完整,排班计划是否重叠code400 有两个不同的返回信息
return res.data
}
return Promise.reject('error')
} else {
return res.data
}
}, error => {
tryHideFullScreenLoading()
console.log('err' + error)
let {message} = error;
if (message === "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
tryHideFullScreenLoading()
console.log('err' + error)
let { message } = error;
if (message === "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export function getBaseHeader() {
@@ -219,10 +221,10 @@ function handleAuthorized() {
if (!isRelogin.show) {
isRelogin.show = true;
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
isRelogin.show = false;
store.dispatch('LogOut').then(() => {

View File

@@ -1,423 +1,430 @@
<template>
<div class="home-page">
<div class="date-tabs">
<!-- @tab-click="handleClick" -->
<el-tabs v-model="activeName" @tab-click="timedayChange" :stretch="true">
<el-tab-pane
:label="'\u2002\u2002日\u2002\u2002'"
name="日"></el-tab-pane>
<el-tab-pane
:label="'\u2002\u2002周\u2002\u2002'"
name="周"></el-tab-pane>
<el-tab-pane
:label="'\u2002\u2002月\u2002\u2002'"
name="月"></el-tab-pane>
<el-tab-pane
:label="'\u2002\u2002年\u2002\u2002'"
name="年"></el-tab-pane>
</el-tabs>
<div class="detail">
<div class="app-container">
<!-- 顶部日期&筛选栏 -->
<el-row :gutter="20" class="top-bar">
<el-col :span="8">
<el-date-picker
v-model="timeday"
align="right"
type="date"
format="yyyy-MM-dd"
valueFormat="yyyy-MM-dd"
:clearable="false"
@change="timedayChange"
placeholder="选择日期"
:picker-options="pickerOptions"></el-date-picker>
</div>
</div>
<el-row class="main-top" :gutter="16">
<el-col :span="24" style="position: relative">
<div class="title">
<svg-icon icon-class="home-produce" />
<span class="title-inner">生产总成本</span>
</div>
<el-row class="box">
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.priceS }}万元</div>
<div class="unit-style">总计成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.matPriceS }}万元</div>
<div class="unit-style">原料成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.energyPriceS }}万元</div>
<div class="unit-style">能源成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.otherPriceS }}万元</div>
<div class="unit-style">其他成本</div>
</el-col>
<!-- <el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.ratioS }}</div>
<div class="unit-style">综合良品率/%</div>
</el-col> -->
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.areaPriceS }}</div>
<div class="unit-style">综合每平米成本</div>
</el-col>
</el-row>
v-model="dateRange"
type="daterange"
range-separator="~"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd" />
<el-button type="primary" style="margin-left: 10px">查询</el-button>
</el-col>
<el-col :span="24" style="position: relative">
<div class="title">
<svg-icon icon-class="home-produce" />
<span class="title-inner">原片成本</span>
</div>
<el-row class="box">
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.priceO }}万元</div>
<div class="unit-style">原片总成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.matPriceO }}万元</div>
<div class="unit-style">原料成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.energyPriceO }}万元</div>
<div class="unit-style">能源成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.otherPriceO }}万元</div>
<div class="unit-style">其他成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.ratioO }}</div>
<div class="unit-style">原片良品率/%</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.areaPriceO }}</div>
<div class="unit-style">原片每平米成本</div>
</el-col>
</el-row>
</el-col>
<el-col :span="24" style="position: relative">
<div class="title">
<svg-icon icon-class="home-produce" />
<span class="title-inner">深加工成本</span>
</div>
<el-row class="box">
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.priceD }}万元</div>
<div class="unit-style">深加工总成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.energyPriceD }}万元</div>
<div class="unit-style">能源成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.otherPriceD }}万元</div>
<div class="unit-style">其他成本</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.ratioD }}</div>
<div class="unit-style">深加工良品率/%</div>
</el-col>
<el-col :span="4" class="num-box shadow">
<div class="num-style">{{ homeData.areaPriceD }}</div>
<div class="unit-style">深加工每平米成本</div>
</el-col>
</el-row>
<el-col :span="16">
<el-tag type="info" @click="changeTime('yesterday')">昨日</el-tag>
<el-tag type="info" @click="changeTime('week')">本周</el-tag>
<el-tag type="info" @click="changeTime('month')">本月</el-tag>
<el-tag type="info" @click="changeTime('year')">本年</el-tag>
</el-col>
</el-row>
<el-row class="main-bottom" :gutter="16" v-if="false">
<el-col :span="9">
<div class="chart-wrapper">
<line-chart :chart-data="lineChartData" />
</div>
</el-col>
<el-col :span="7">
<div class="chart-wrapper">
<pie-chart />
</div>
<!-- 成本概览卡片 -->
<el-row :gutter="20" class="card-row">
<el-col :span="8">
<el-card class="cost-card">
<div class="card-title">总制造成本/平米</div>
<div class="card-content">
<div>
目标
<span class="target-val">16</span>
</div>
<div>
实际
<span class="actual-val">12.8</span>
</div>
<el-progress :stroke-width="13" :percentage="80" color="#42b983" />
</div>
</el-card>
</el-col>
<el-col :span="8">
<div class="chart-wrapper">
<bar-chart />
</div>
<el-card class="cost-card">
<div class="card-title">原片成本/平米</div>
<div class="card-content">
<div>
目标
<span class="target-val">16</span>
</div>
<div>
实际
<span class="actual-val">12.8</span>
</div>
<el-progress :stroke-width="13" :percentage="99" color="#42b983" />
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card class="cost-card">
<div class="card-title">加工成本/平米</div>
<div class="card-content">
<div>
目标
<span class="target-val">16</span>
</div>
<div>
实际
<span class="actual-val">12.8</span>
</div>
<el-progress :stroke-width="13" :percentage="99" color="#42b983" />
</div>
</el-card>
</el-col>
</el-row>
<!-- 成本构成区域 -->
<el-row :gutter="20" class="composition-row">
<!-- 饼图 -->
<el-col :span="6">
<el-card class="composition-card">
<div class="card-title">成本构成</div>
<div id="costPie" class="chart" style="height: 350px"></div>
</el-card>
</el-col>
<!-- 原片/加工成本子项 -->
<el-col :span="18">
<el-card class="composition-card">
<div class="card-title">成本构成</div>
<!-- 原片成本子项 -->
<div class="sub-cost-group">
<div class="sub-cost-title">原片成本</div>
<div class="sub-cost">
<div
v-for="(item, idx) in rawSubCosts"
:key="idx"
class="sub-cost-div">
<div class="sub-cost-name">
{{ item.name }}
<div class="sub-cost-val">{{ item.val }}</div>
</div>
<div style="text-align: center">
<el-progress
type="circle"
:percentage="89"
:width="60"
:stroke-width="10"
:show-text="false"
color="#e74c3c" />
</div>
<div class="sub-cost-name">
目标
<span class="red-text">{{ item.target }}</span>
</div>
<div class="sub-cost-name">
完成率
<span class="red-text">89%</span>
</div>
</div>
</div>
</div>
<el-divider></el-divider>
<!-- 加工成本子项 -->
<div class="sub-cost-group" style="margin-top: 20px">
<div class="sub-cost-title">加工成本</div>
<div class="sub-cost">
<div
v-for="(item, idx) in processSubCosts"
:key="idx"
class="sub-cost-div">
<div class="sub-cost-name">
{{ item.name }}
<div class="sub-cost-val">{{ item.val }}</div>
</div>
<div style="text-align: center">
<el-progress
type="circle"
:percentage="89"
:width="60"
:stroke-width="10"
:show-text="false"
color="#e74c3c" />
</div>
<div class="sub-cost-name">
目标
<span class="red-text">{{ item.target }}</span>
</div>
<div class="sub-cost-name">
完成率
<span class="red-text">89%</span>
</div>
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 趋势图区域 -->
<el-row :gutter="20" class="trend-row">
<!-- 总制造成本趋势 -->
<el-col :span="8">
<el-card class="trend-card">
<div class="card-title">总制造成本趋势</div>
<div id="totalTrend" class="chart" style="height: 300px"></div>
</el-card>
</el-col>
<!-- 原片/加工成本趋势 -->
<el-col :span="8">
<el-card class="trend-card">
<div class="card-title">原片/加工成本趋势</div>
<el-tabs v-model="costTab" type="card" style="margin-bottom: 10px">
<el-tab-pane label="原片成本" name="raw"></el-tab-pane>
<el-tab-pane label="加工成本" name="process"></el-tab-pane>
</el-tabs>
<div id="rawProcessTrend" class="chart" style="height: 300px"></div>
</el-card>
</el-col>
<!-- 单项成本趋势 -->
<el-col :span="8">
<el-card class="trend-card">
<div class="card-title">单项成本趋势</div>
<el-tabs v-model="itemTab" type="card" style="margin-bottom: 10px">
<el-tab-pane label="人工成本" name="labor"></el-tab-pane>
<el-tab-pane label="原料成本" name="material"></el-tab-pane>
<el-tab-pane label="制造费用" name="expense"></el-tab-pane>
<el-tab-pane label="玻璃热耗" name="heat"></el-tab-pane>
<el-tab-pane label="原片成品率" name="yield"></el-tab-pane>
</el-tabs>
<div id="itemTrend" class="chart" style="height: 300px"></div>
</el-card>
</el-col>
</el-row>
<div
class="main-footer"
style="
color: #c7c7c7;
user-select: none;
font-size: 14px;
letter-spacing: 1px;
height: 30px;
display: grid;
place-content: center;
">
&copy; 中建材智能自动化研究院有限公司
</div>
</div>
</template>
<script>
import moment from 'moment';
import tableHeightMixin from '@/mixins/lb/tableHeightMixin';
import LineChart from '../dashboard/LineChart';
import PieChart from '../dashboard/PieChart';
import BarChart from '../dashboard/BarChart';
import PanelGroup from '../dashboard/PanelGroup';
import { getData } from '@/api/cost/allCost';
import { getUserProfile } from '@/api/system/user';
const lineChartData = {
newVisitis: {
expectedData: [100, 120, 161, 134, 105, 160, 165],
actualData: [120, 82, 91, 154, 162, 140, 145],
},
messages: {
expectedData: [200, 192, 120, 144, 160, 130, 140],
actualData: [180, 160, 151, 106, 145, 150, 130],
},
purchases: {
expectedData: [80, 100, 121, 104, 105, 90, 100],
actualData: [120, 90, 100, 138, 142, 130, 130],
},
shoppings: {
expectedData: [130, 140, 141, 142, 145, 150, 160],
actualData: [120, 82, 91, 154, 162, 140, 130],
},
};
import * as echarts from 'echarts';
export default {
name: 'Home',
mixins: [tableHeightMixin],
components: {
LineChart,
PieChart,
PanelGroup,
BarChart,
},
computed: {
gradientBackground() {
return {
'background-image':
'linear-gradient(90deg, #f0f0f0 25%, rgba(255, 255, 255, 0) 25%, rgba(255, 255, 255, 0) 50%, #f0f0f0 50%, #f0f0f0 75%, rgba(255, 255, 255, 0) 75%, rgba(255, 255, 255, 0))',
'background-size': '100px 100px', // 调整条纹的大小
};
},
},
name: 'CostDashboard',
data() {
return {
activeName: '日',
user: '',
lineChartData: lineChartData.newVisitis,
homeData: {},
timeday: moment(new Date()).subtract(1, 'days').format('YYYY-MM-DD'),
startTime:
moment(new Date()).subtract(0, 'days').format('YYYY-MM-DD') +
' 00:00:00',
endTime:
moment(new Date()).subtract(-1, 'days').format('YYYY-MM-DD') +
' 00:00:00',
pickerOptions: {
disabledDate(time) {
return time.getTime() + 3600 * 1000 * 24 > Date.now();
},
shortcuts: [
{
text: '今天',
onClick(picker) {
picker.$emit('pick', new Date());
},
},
{
text: '昨天',
onClick(picker) {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24);
picker.$emit('pick', date);
},
},
{
text: '一周前',
onClick(picker) {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', date);
},
},
],
},
dateRange: ['2023-05-06', '2023-05-06'],
costTab: 'raw',
itemTab: 'labor',
// 原片成本子项
rawSubCosts: [
{ name: '人工成本', val: 4.56, target: 4.32 },
{ name: '原料成本', val: 4.56, target: 4.32 },
{ name: '原片制造费用', val: 4.56, target: 4.32 },
{ name: '玻璃热耗', val: 4.56, target: 4.32 },
{ name: '原片成品率', val: 4.56, target: 4.32 },
],
// 加工成本子项
processSubCosts: [
{ name: '人工成本', val: 4.56, target: 4.32 },
{ name: '加工动力成本', val: 4.56, target: 4.32 },
{ name: '加工制造费用', val: 4.56, target: 4.32 },
{ name: '加工投入产出率', val: 4.56, target: 4.32 },
],
// 趋势图模拟数据
trendX: ['00:00', '01:00', '02:00'],
trendY: [20, 40, 50],
targetY: 12.54,
};
},
created() {
},
beforeDestroy() {
mounted() {
this.initCostPie();
this.initTotalTrend();
this.initRawProcessTrend();
this.initItemTrend();
},
methods: {
getData() {
let listQuery = {
//分页
pageSize: 10,
pageNo: 1,
statisticType: ['', '日', '周', '月', '年'].indexOf(this.activeName),
startTime: this.timeday + ' 00:00:00',
};
getData(listQuery).then((response) => {
this.homeData = response.data;
for (let i in this.homeData) {
this.homeData[i] = Number(this.homeData[i]).toFixed(2);
}
// 切换时间范围(示例)
changeTime(type) {
// 实际项目中需根据type请求对应数据
console.log('切换时间:', type);
},
// 初始化成本构成饼图
initCostPie() {
const pieChart = echarts.init(document.getElementById('costPie'));
pieChart.setOption({
tooltip: { trigger: 'item' },
legend: {
orient: 'horizontal', // 水平排列
bottom: 0, // 放在底部
icon: 'circle',
left: 'center',
formatter: function (name) {
return name;
},
},
series: [
{
type: 'pie',
data: [
{ name: '原料成本(4.2元)', value: 29.9 },
{ name: '动力成本(2.1元)', value: 24 },
{ name: '人工成本(1.5元)', value: 24 },
{ name: '辅料成本(0.5元)', value: 21.2 },
{ name: '制造费用(0.5元)', value: 21.2 },
],
label: { show: false },
labelLine: { show: false },
bottom: 10,
},
],
});
window.addEventListener('resize', () => pieChart.resize());
},
timedayChange() {
let listQuery = {
//分页
pageSize: 10,
pageNo: 1,
statisticType: ['', '日', '周', '月', '年'].indexOf(this.activeName),
startTime: this.timeday + ' 00:00:00',
};
getData(listQuery).then((response) => {
this.homeData = response.data;
for (let i in this.homeData) {
this.homeData[i] = Number(this.homeData[i]).toFixed(2);
}
});
// 初始化总制造成本趋势图
initTotalTrend() {
const totalChart = echarts.init(document.getElementById('totalTrend'));
totalChart.setOption({
grid: { top: 20, bottom: 30, left: 30, right: 10 },
xAxis: { type: 'category', data: this.trendX },
yAxis: { type: 'value', min: -20, max: 60 },
series: [
{
name: '实际值',
type: 'line',
data: this.trendY,
lineStyle: { color: '#409eff' },
},
{
name: '目标值',
type: 'line',
data: [this.targetY, this.targetY, this.targetY],
lineStyle: { type: 'dashed', color: '#e6a23c' },
},
],
});
window.addEventListener('resize', () => totalChart.resize());
},
goDetail() {
this.$router.push({ path: 'indexDetail' });
// 初始化原片/加工成本趋势图
initRawProcessTrend() {
const rpChart = echarts.init(document.getElementById('rawProcessTrend'));
rpChart.setOption({
grid: { top: 10, bottom: 20, left: 20, right: 10 },
xAxis: { type: 'category', data: this.trendX },
yAxis: { type: 'value', min: -20, max: 60 },
series: [
{
name: '实际值',
type: 'line',
data: this.trendY,
lineStyle: { color: '#409eff' },
},
{
name: '目标值',
type: 'line',
data: [this.targetY, this.targetY, this.targetY],
lineStyle: { type: 'dashed', color: '#e6a23c' },
},
],
});
window.addEventListener('resize', () => rpChart.resize());
},
// 初始化单项成本趋势图
initItemTrend() {
const itemChart = echarts.init(document.getElementById('itemTrend'));
itemChart.setOption({
grid: { top: 10, bottom: 20, left: 20, right: 10 },
xAxis: { type: 'category', data: this.trendX },
yAxis: { type: 'value', min: -20, max: 60 },
series: [
{
name: '实际值',
type: 'line',
data: this.trendY,
lineStyle: { color: '#409eff' },
},
{
name: '目标值',
type: 'line',
data: [this.targetY, this.targetY, this.targetY],
lineStyle: { type: 'dashed', color: '#e6a23c' },
},
],
});
window.addEventListener('resize', () => itemChart.resize());
},
},
};
</script>
<style lang="scss" scoped>
.home-page::before {
background-image: url('~@/assets/img/home-bg.png');
background-size: cover;
background-repeat: no-repeat;
background-position: center;
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: rotate(180deg);
}
.date-tabs {
padding-left: 40px;
padding-top: 20px;
position: relative;
<style scoped>
.top-bar {
margin-bottom: 20px;
}
:deep(.date-tabs) {
.el-tabs__header {
margin-bottom: 8px;
display: inline-block;
transform: translateY(-12px);
}
.el-tabs__content {
overflow: visible;
}
.el-tabs__item {
font-size: 18px;
color: #fff;
padding-left: 0 !important;
padding-right: 0 !important;
line-height: 36px !important;
height: 36px;
}
.el-tabs__item.is-active {
color: #0b58ff;
}
.card-row,
.composition-row {
margin-bottom: 20px;
}
.detail {
display: inline-block;
position: absolute;
left: 260px;
top: 10px;
.cost-card .card-title {
font-size: 22px;
font-weight: bold;
margin-bottom: 15px;
}
:deep(.detail) {
.el-input__inner {
background-color: transparent;
color: white;
}
}
// .current-date {
// color: #fff;
// font-size: 18px;
// position: absolute;
// left: 260px;
// top: 14px;
// }
.current-time {
color: #fff;
.cost-card .card-content {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 10px;
align-items: center;
font-size: 18px;
position: absolute;
right: 38px;
top: 14px;
}
.main-top {
width: 100%;
padding: 0 20px 0 40px;
.title {
position: absolute;
left: 34px;
top: 20px;
width: 180px;
font-size: 32px;
z-index: 10;
.title-inner {
position: absolute;
left: 42px;
top: 5px;
font-size: calc(100vw * 20 / 1920);
}
}
.box {
background-color: #fff;
border-radius: 24px;
height: 192px;
padding: 40px 24px 32px 24px;
margin-bottom: 20px;
box-shadow: 0 8px 8px 0 gray;
.num-box {
height: 120px;
padding-top: 26px;
text-align: center;
.num-style {
color: #000;
font-size: calc(100vw * 40 / 1920);
}
.unit-style {
color: rgba(0, 0, 0, 0.7);
font-size: calc(100vw * 18 / 1920);
}
}
.shadow {
background: linear-gradient(90deg, #ffffff 80%, #f2f4f9 100%);
}
}
.target-val {
color: #666;
margin-left: 5px;
font-size: 20px;
}
.main-bottom {
width: 100%;
margin-top: 20px;
.chart-wrapper {
margin-left: 10px;
height: 425px;
background: #fff;
border-radius: 5px;
box-shadow: 0 3px 3px 0 gray;
}
}
</style>
<style lang="scss">
.home-page {
.el-progress-bar__inner {
background-image: url('~@/assets/img/home-progress-bg.png');
background-size: cover;
}
.actual-val {
color: #42b983;
margin-left: 5px;
font-size: 20px;
}
.composition-card {
height: 480px;
}
.composition-card .card-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
}
.sub-cost {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
gap: 10px;
align-items: center;
}
.sub-cost-group .sub-cost-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
}
.sub-cost-group .sub-cost-div {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
align-items: center;
border: 1px solid rgb(220, 220, 220, 0.5);
padding: 10px;
}
.sub-cost-name {
text-align: center;
margin-top: 5px;
font-size: 14px;
}
.sub-cost-val {
font-size: 16px;
font-weight: bold;
}
.completion {
text-align: center;
font-size: 12px;
color: #666;
}
.red-text {
color: #e74c3c;
}
.trend-card {
height: 450px;
}
.trend-card .card-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
}
</style>

View File

@@ -2,14 +2,14 @@
* @Author: zwq
* @Date: 2025-10-23 13:43:55
* @LastEditors: zwq
* @LastEditTime: 2025-11-10 10:39:33
* @LastEditTime: 2025-11-14 21:13:10
* @Description:
-->
<template>
<div class="app-container" style="padding-top:0">
<div class="app-container" style="padding-top: 0">
<div v-for="item in groupClassArr" :key="item.planId">
<small-title style="margin: 16px 0" size="sm" :no-padding="true">
{{item.deptName + ' - ' +item.planName}}
{{ item.deptName + ' - ' + item.planName }}
</small-title>
<base-table
:table-props="tableProps"
@@ -59,7 +59,7 @@ export default {
},
created() {},
methods: {
init(det) {
init(det, detSort) {
this.tableData = det;
//返回计划名和id
const arr = det.map((item) => {
@@ -77,7 +77,19 @@ export default {
map.set(item.planId, item);
}
});
this.groupClassArr = Array.from(map.values());
const newArr = Array.from(map.values());
// 创建B数组中id的顺序映射
const orderMap = new Map();
detSort.forEach((item, index) => {
orderMap.set(item, index);
});
// 根据B的顺序对A进行排序
this.groupClassArr = [...newArr].sort((a, b) => {
const orderA = orderMap.get(a.planId);
const orderB = orderMap.get(b.planId);
return orderA - orderB;
});
},
},
};

View File

@@ -2,212 +2,221 @@
* @Author: zwq
* @Date: 2025-10-23 13:43:55
* @LastEditors: zwq
* @LastEditTime: 2025-11-10 10:40:13
* @LastEditTime: 2025-11-25 09:39:43
* @Description:
-->
<template>
<el-row :gutter="10" style="background-color: #f2f4f9">
<!--部门数据-->
<el-col :span="4">
<div class="head-container">
<el-tabs v-model="activeName" stretch @tab-click="tabsClick">
<el-tab-pane label="部门" name="first"></el-tab-pane>
<el-tab-pane label="班组" name="second"></el-tab-pane>
</el-tabs>
<div v-if="activeName == 'first'">
<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>
<div v-else>
<el-input
v-model="groupName"
placeholder="请输入班组名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px" />
<el-tree
:data="groupOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree1"
default-expand-all
highlight-current
@node-click="handleNodeClick1" />
</div>
<div
class="app-container"
style="
background-color: #f2f4f9;
padding: 0;
display: grid;
grid-template-columns: 1fr 5fr;
gap: 10px;
">
<div class="head-container">
<el-tabs v-model="activeName" stretch @tab-click="tabsClick">
<el-tab-pane label="部门" name="first"></el-tab-pane>
<el-tab-pane label="班组" name="second"></el-tab-pane>
</el-tabs>
<div v-if="activeName == 'first'">
<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"
node-key="id"
default-expand-all
highlight-current
@node-click="handleNodeClick" />
</div>
</el-col>
<el-col :span="20">
<div class="groupTeamScheduling">
<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>
<span
style="color: #909399; font-size: 12px"
v-if="activeName == 'first'">
提示排班日历仅展示当前选中部门的节假日设置若休假日中仍有排班则表示下级组织已通过自定义假期进行排班
<div v-else>
<el-input
v-model="groupName"
placeholder="请输入班组名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px" />
<el-tree
:data="groupOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree1"
node-key="id"
default-expand-all
highlight-current
@node-click="handleNodeClick1" />
</div>
</div>
<div class="groupTeamScheduling">
<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>
<span
style="color: #909399; font-size: 12px"
v-if="activeName == 'first'">
提示排班日历仅展示当前选中部门的节假日设置若休假日中仍有排班则表示下级组织已通过自定义假期进行排班
</span>
<span v-else style="margin-left: 10px">
组长:
<span style="font-weight: 600; margin: 0 10px">
{{ showTeamName?.leaderName || '-' }}
</span>
<span v-else>
组长:{{ showTeamName?.leaderName || '-' }} 组长电话:{{
showTeamName?.leaderPhone || '-'
}}
组长电话:
<span style="font-weight: 600; margin: 0 10px">
{{ showTeamName?.leaderPhone || '-' }}
</span>
</el-form-item>
<el-form-item style="float: right">
<el-button
size="small"
type="primary"
@click="(startDay = new Date()), getHolidayPage()">
跳转到今天
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 日历区域 -->
<div class="calenderArea">
<el-calendar v-model="startDay">
<template slot="dateCell" slot-scope="{ data }">
<div v-if="data.type === 'current-month'">
<!-- 日期 -->
<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"
:style="{
backgroundColor: HolidayList[
Number(data.day.split('-')[2]) - 1
]?.isHoliday
? '#67C23A'
: '#409EFF',
}">
{{
HolidayList[Number(data.day.split('-')[2]) - 1]
?.isHoliday
? '假'
: '班'
}}
<!-- //变更节假日未更新排班计划的角标 -->
<div
class="subIcon"
v-if="
HolidayList[Number(data.day.split('-')[2]) - 1]
?.isUpdate
">
<el-tooltip
effect="dark"
content="本日节假日变更已影响当前排班计划,但尚未同步,请及时处理。"
placement="top">
<i
class="el-icon-warning"
style="color: #f56c6c"></i>
</el-tooltip>
</div>
</div>
</el-col>
<!-- 显示排班 -->
</span>
</el-form-item>
<el-form-item style="float: right">
<el-button
size="small"
type="primary"
@click="(startDay = new Date()), getHolidayPage()">
跳转到今天
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 日历区域 -->
<div class="calenderArea">
<el-calendar v-model="startDay">
<template slot="dateCell" slot-scope="{ data }">
<div v-if="data.type === 'current-month'">
<!-- 日期 -->
<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
v-if="
HolidayList[Number(data.day.split('-')[2]) - 1]?.det
.length > 0
">
<el-col
:span="24"
v-for="(item, index) in HolidayList[
class="work-tip"
:style="{
backgroundColor: HolidayList[
Number(data.day.split('-')[2]) - 1
]?.det.filter(
(item) =>
item.schedulingPlanId ==
HolidayList[Number(data.day.split('-')[2]) - 1]
?.schedulingPlanId
)"
:key="index">
]?.isHoliday
? '#67C23A'
: '#409EFF',
}">
{{
HolidayList[Number(data.day.split('-')[2]) - 1]
?.isHoliday
? '假'
: '班'
}}
<!-- //变更节假日未更新排班计划的角标 -->
<div
class="subIcon"
v-if="
HolidayList[Number(data.day.split('-')[2]) - 1]
?.isUpdate
">
<el-tooltip
effect="dark"
content="点击展示全部排班计划"
placement="top-start">
<div
class="holiday-div"
:style="{
backgroundColor:
holidayDivColor[item?.classesCode],
}"
@click="
holidayLog(
HolidayList[Number(data.day.split('-')[2]) - 1]
?.det,
HolidayList[Number(data.day.split('-')[2]) - 1]
?.date,
)
"
:title="
item.classesName +
' | ' +
item.teamName +
' | ' +
item.workTime
">
{{
item.classesName +
' | ' +
item.teamName +
' | ' +
item.workTime
}}
</div>
content="本日节假日变更已影响当前排班计划,但尚未同步,请及时处理。"
placement="top">
<i class="el-icon-warning" style="color: #f56c6c"></i>
</el-tooltip>
</el-col>
</div>
</div>
</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>
<!-- 显示排班 -->
<div
v-if="
HolidayList[Number(data.day.split('-')[2]) - 1]?.det
.length > 0
">
<el-col
:span="24"
v-for="(item, index) in HolidayList[
Number(data.day.split('-')[2]) - 1
]?.det.filter(
(item) =>
item.schedulingPlanId ==
HolidayList[Number(data.day.split('-')[2]) - 1]
?.schedulingPlanId
)"
:key="index">
<el-tooltip
effect="dark"
content="点击展示全部排班计划"
placement="top-start">
<div
class="holiday-div"
:style="{
backgroundColor: holidayDivColor[item?.classesCode],
}"
@click="
holidayLog(
HolidayList[Number(data.day.split('-')[2]) - 1]
?.det,
HolidayList[Number(data.day.split('-')[2]) - 1]
?.date,
HolidayList[Number(data.day.split('-')[2]) - 1]
?.detSort
)
"
:title="
item.classesName +
' | ' +
item.teamName +
' | ' +
item.workTime
">
{{
item.classesName +
' | ' +
item.teamName +
' | ' +
item.workTime
}}
</div>
</el-tooltip>
</el-col>
</div>
</el-row>
</div>
</template>
</el-calendar>
</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>
</el-col>
</div>
<base-dialog
:dialogTitle="dialogTitle"
:dialogVisible="logVisible"
@@ -228,7 +237,7 @@
</slot>
</template>
</base-dialog>
</el-row>
</div>
</template>
<script>
@@ -339,13 +348,43 @@ export default {
getEnableData().then((response) => {
// 处理 deptOptions 参数
this.deptOptions = [];
this.deptOptions.push(...this.handleTree(response.data, 'id'));
this.deptOptions.push(...this.handleTree1(response.data, 'id'));
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(this.deptId);
});
});
listByDeptId(this.topDept.id).then((response) => {
this.groupOptions = [];
this.groupOptions.push(...response.data);
});
},
//大整数精度丢失
handleTree1(data, fid) {
const id = fid || 'id';
const parentId = 'parentId';
const children = 'children';
const rootId =
data
.map((item) => item[parentId].toString())
.reduce((min, current) => {
if (current.length < min.length) return current;
if (current.length > min.length) return min;
return current < min ? current : min;
}) || 0;
//对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data));
//循环所有项
const treeData = cloneData.filter((father) => {
let branchArr = cloneData.filter((child) => {
//返回每一项的子级数组
return father[id] == child[parentId];
});
branchArr.length > 0 ? (father.children = branchArr) : '';
//返回第一层
return father[parentId] == rootId;
});
return treeData !== '' ? treeData : data;
},
// 筛选节点
filterNode(value, data) {
if (!value) return true;
@@ -358,8 +397,14 @@ export default {
leaderName: this.groupOptions[0].leaderName || '-',
leaderPhone: this.groupOptions[0].leaderPhone || '-',
};
this.$nextTick(() => {
this.$refs.tree1.setCurrentKey(this.teamId);
});
} else if (this.activeName == 'first') {
this.deptId = this.topDept.id;
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(this.deptId);
});
}
this.getHolidayPage();
},
@@ -441,11 +486,11 @@ export default {
return '';
}
},
holidayLog(det, date) {
holidayLog(det, date, detSort) {
this.dialogTitle = this.showDeptName + '-' + date + '-排班详情';
this.logVisible = true;
this.$nextTick(() => {
this.$refs.holidayLogRef.init(det);
this.$refs.holidayLogRef.init(det, detSort);
});
},
cancelLog() {
@@ -459,8 +504,8 @@ export default {
.head-container {
padding: 20px 10px 0;
background-color: #fff;
min-height: calc(100vh - 120px - 8px);
border-radius: 8px;
height: 100%;
}
.groupTeamScheduling {
.operationArea {

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2025-10-11 14:27:37
* @LastEditors: zwq
* @LastEditTime: 2025-11-10 16:20:13
* @LastEditTime: 2025-11-14 22:00:10
* @Description:
-->
<template>
@@ -104,7 +104,7 @@ export default {
// 获取数据列表
getDataList() {
this.urlOptions.getDataListURL(this.formInline).then((response) => {
this.tableData = response.data.list;
this.tableData = response.data.list.filter(item=>item.enabled===1);
this.tableData.forEach(item=>item.teamId= item.id)
this.$nextTick(() => {
if (this.selectedArr.length > 0) {

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2025-10-13 15:07:24
* @LastEditors: zwq
* @LastEditTime: 2025-11-11 15:32:04
* @LastEditTime: 2025-12-03 15:48:26
* @Description:
-->
<template>
@@ -138,7 +138,14 @@
<el-tag type="warning" style="color: black" v-if="!isClassTimePass">
<i class="el-icon-warning" style="color: #ffbd02"></i>
两班倒和三班倒的班次设置时间
<span style="color: red">不能重叠且必须连续覆盖24小时</span>
<span style="color: red">不能重叠</span>
在24小时
</el-tag>
<el-tag type="warning" style="color: black" v-if="!covers24Hours">
<i class="el-icon-warning" style="color: #ffbd02"></i>
两班倒和三班倒的班次设置时间,班次时间
<span style="color: red">必须连续</span>
在24小时
</el-tag>
</small-title>
<base-table
@@ -443,7 +450,7 @@ export default {
},
],
stepNum: 1, // 当前第几步
isEdit: false, //是否是编辑状态,编辑的时候,让前端上一步和取消都不用接口
isEdit: false, //是否是编辑状态,编辑的时候,让前端上一步和取消都不用接口
shiftTypeEdit: undefined, //该值为编辑时获取的shiftType到第二步判断dataForm.shiftType是否改变同时改变班次信息
//第一步参数
dataForm: {
@@ -531,7 +538,8 @@ export default {
btnName: '编辑',
},
].filter((v) => v),
isClassTimePass: true, //修改班次时间后,需要判断时间是否有覆盖,且是否覆盖24小时
isClassTimePass: true, //修改班次时间后,需要判断时间是否有重叠,且是否24小时内 true是没问题的
covers24Hours: true, //修改班次时间后需要判断时间是否覆盖24小时连续的时间 true是没问题的
tableData1: [],
tableProps2,
tableBtn2: [
@@ -621,9 +629,9 @@ export default {
};
},
methods: {
init(id,isEdit) {
init(id, isEdit) {
this.dataForm.id = id || undefined;
this.isEdit = isEdit || false
this.isEdit = isEdit || false;
this.$nextTick(() => {
this.$refs['dataForm'].resetFields();
if (this.dataForm.id) {
@@ -634,12 +642,18 @@ export default {
this.tableData1 = res.data?.stepTwo.groupPlanClassesBaseVOList;
this.tableData2 = res.data?.stepTwo.groupPlanTeamBaseVOList;
this.tableData2.forEach((item, index) => {
let lineName = '';
let lineName = [];
if (item.isProduction) {
lineName = this.setLineName(item.bindLineTree);
this.$set(
this.tableData2[index],
'lineName',
lineName.join(';')
);
}
this.$set(this.tableData2[index], 'lineName', lineName);
});
this.stepNum = 3;
this.getThreeGroup(7); // 第三步,进来加载预览排班,默认7天预览周期
});
} else {
getCode().then((res) => {
@@ -757,7 +771,7 @@ export default {
}
//班次验证
if (!this.isClassTimePass) {
if (!this.isClassTimePass || !this.covers24Hours) {
this.$message('班次内时间有误,请重新填写!');
return;
}
@@ -777,9 +791,10 @@ export default {
data.groupPlanClassesBaseVOList.push(obj);
});
data.groupPlanTeamBaseVOList = []; //排班-班组信息list
this.tableData2.forEach((item) => {
this.tableData2.forEach((item, index) => {
const obj = {
teamId: item.id,
sort: index + 1,
teamId: item.teamId || item.id,
name: item.name,
leaderName: item.leaderName,
leaderPhone: item.leaderPhone,
@@ -788,9 +803,13 @@ export default {
data.groupPlanTeamBaseVOList.push(obj);
});
createStepTwo(data).then((res) => {
this.stepNum += 1;
this.$emit('setSN', this.stepNum);
this.getThreeGroup(7); // 第三步,进来加载预览排班,默认7天预览周期
if (res.code === 200 || res.code === 0) {
this.stepNum += 1;
this.$emit('setSN', this.stepNum);
this.getThreeGroup(7); // 第三步,进来加载预览排班,默认7天预览周期
} else {
this.$message(res.msg);
}
});
return;
}
@@ -799,7 +818,7 @@ export default {
data.groupPlanTeamBaseVOList = []; //排班-班组信息list
this.tableData2.forEach((item) => {
const obj = {
teamId: item.id,
teamId: item.teamId || item.id,
name: item.name,
bindData: JSON.stringify(item.bindLineTree),
};
@@ -807,10 +826,14 @@ export default {
});
checkPlan(data).then((RES) => {
createStepFour(this.dataForm.id).then((res) => {
this.$modal.msgSuccess('创建计划成功');
this.stepNum = 1;
this.$emit('setSN', this.stepNum);
this.$emit('refreshDataList');
if (res.code === 200 || res.code === 0) {
this.$modal.msgSuccess('创建计划成功');
this.stepNum = 1;
this.$emit('setSN', this.stepNum);
this.$emit('refreshDataList');
} else {
this.$message(res.msg);
}
});
});
return;
@@ -943,6 +966,57 @@ export default {
return start1Min < end2Min && end1Min > start2Min;
}
},
/**
* 检查多个班次是否连续覆盖24小时
* @param {Array} shifts - 班次数组每个班次包含startTime和endTime
* @returns {boolean} 是否连续覆盖24小时
*/
isCover24Hours(shifts) {
if (!shifts || shifts.length === 0) return false;
const totalMinutes = 24 * 60;
// 将班次转换为时间段对象
const intervals = shifts.map((shift) => ({
start: this.timeToMinutes(shift.startTime),
end: this.timeToMinutes(shift.endTime),
}));
// 处理跨天情况如果结束时间小于开始时间加上24小时
const normalizedIntervals = intervals.map((interval) => {
let { start, end } = interval;
if (end < start) {
end += totalMinutes;
}
return { start, end, originalEnd: interval.end };
});
// 按开始时间排序
normalizedIntervals.sort((a, b) => a.start - b.start);
// 检查是否连续覆盖
let coveredUntil = normalizedIntervals[0].end;
let hasGap = false;
for (let i = 1; i < normalizedIntervals.length; i++) {
const current = normalizedIntervals[i];
// 如果当前班次的开始时间大于已覆盖的结束时间,说明有间隔
if (current.start > coveredUntil) {
hasGap = true;
break;
}
// 更新已覆盖的结束时间
coveredUntil = Math.max(coveredUntil, current.end);
}
// 检查是否覆盖完整的24小时
const firstStart = normalizedIntervals[0].start;
// 判断条件没有间隔且覆盖时间达到24小时
return !hasGap && coveredUntil - firstStart >= totalMinutes;
},
//编辑班次
handleClick1(val) {
this.addOrUpdateVisible1 = true;
@@ -1023,7 +1097,7 @@ export default {
this.tableData1.forEach((item, index) => {
item.sort = index + 1;
});
//修改班次时间后,需要判断时间是否有覆盖,且是否覆盖24小时,isTimeOverlap方法返回false才是校验通过
//修改班次时间后,需要判断时间是否有重叠,且是否24小时,isTimeOverlap方法返回false才是校验通过
if (this.dataForm.shiftType == 2) {
const twoClass = this.tableData1.map(({ startTime, endTime }) => ({
startTime,
@@ -1061,6 +1135,11 @@ export default {
);
this.isClassTimePass = !(result1 || result2 || result3);
}
//修改班次时间后两班倒和三班倒需要判断时间是否连续覆盖24小时内
if (this.dataForm.shiftType > 1) {
this.covers24Hours = this.isCover24Hours(this.tableData1);
console.log(1111, this.covers24Hours);
}
this.cancel1();
},
refreshTableData2(val) {
@@ -1069,39 +1148,45 @@ export default {
},
refreshTableData3(index, val) {
this.tableData2[index].bindLineTree = val;
let lineName = '';
lineName = this.setLineName(val);
let lineName = this.setLineName(val);
if (lineName) {
this.$set(this.tableData2[index], 'lineName', lineName);
this.$set(this.tableData2[index], 'lineName', lineName.join(';'));
}
this.cancel3();
},
//提取绑定的产线工段名展示出来
setLineName(val) {
if (!val || val.length === 0) return '';
setLineName(data) {
const paths = [];
const currentLevelNames = val.map((item) => item.name);
const processNode = (node, currentPath = []) => {
const newPath = [...currentPath, node.name];
// 处理当前层级显示
let currentDisplay = '';
if (currentLevelNames.length > 1) {
currentDisplay = `(${currentLevelNames.join(' 、 ')})`;
} else {
currentDisplay = currentLevelNames[0];
}
if (node.children && node.children.length > 0) {
// 检查子节点是否都是叶子节点
const allChildrenAreLeaves = node.children.every(
(child) => !child.children || child.children.length === 0
);
// 查找所有子节点(取第一个有子节点的元素)
const childNode = val.find(
(item) => item.children && item.children.length > 0
);
if (childNode) {
const childPath = this.setLineName(childNode.children);
if (childPath) {
return `${currentDisplay} / ${childPath}`;
if (allChildrenAreLeaves && node.children.length > 1) {
// 如果所有子节点都是叶子节点且多于1个用括号合并显示
const leafNames = node.children
.map((child) => child.name)
.join(' 、 ');
paths.push(`${newPath.join('/')}/(${leafNames})`);
} else if (allChildrenAreLeaves && node.children.length === 1) {
// 只有一个叶子子节点,用括号显示
paths.push(`${newPath.join('/')}/${node.children[0].name}`);
} else {
// 继续处理子节点
node.children.forEach((child) => processNode(child, newPath));
}
} else {
// 没有子节点的节点如原片产线1用括号显示
paths.push(`${currentPath.join('/')}/${node.name}`);
}
}
return currentDisplay;
};
data.forEach((node) => processNode(node));
return paths;
},
//第三步
// 进来加载预览排班

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: zwq
* @LastEditTime: 2025-11-10 16:07:01
* @LastEditTime: 2025-11-25 15:49:17
* @Description:
-->
<template>
@@ -62,9 +62,9 @@ export default {
},
processNode(node) {
// 创建唯一ID类型-原始ID
const uniqueId = node.id + node.type;
const uniqueId = node.type+'L'+node.id;
if (node.type > 0) {
node.fid = node.pid + node.type - 1;
node.fid = (node.type - 1)+'L'+node.pid;
}
return {

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2025-10-21 14:27:23
* @LastEditors: zwq
* @LastEditTime: 2025-11-10 17:07:54
* @LastEditTime: 2025-11-25 15:14:53
* @Description:
-->
<template>
@@ -73,7 +73,7 @@
:limit="10"
:table-data="tableData1"></base-table>
<base-table
v-else
v-else
:table-props="tableProps2"
:page="1"
:limit="10"
@@ -150,25 +150,45 @@ export default {
this.infoData = {};
getPlan(id).then((res) => {
this.infoData = res.data || {};
this.tableData1 = res.data?.groupPlanClassesBaseVOList.sort((a, b) => a.sort - b.sort);
this.tableData1 = res.data?.groupPlanClassesBaseVOList.sort(
(a, b) => a.sort - b.sort
);
this.tableData2 = res.data?.groupPlanTeamBaseVOList;
this.tableData2.forEach((item, index) => {
let lineName = '';
lineName = this.setLineName(item.bindLineTree, lineName);
this.$set(this.tableData2[index], 'lineName', lineName.slice(0, -1));
if (item.isProduction) {
lineName = this.setLineName(item.bindLineTree);
}
this.$set(this.tableData2[index], 'lineName', lineName);
});
});
},
//提取绑定的产线工段名展示出来
setLineName(val, lineName) {
val.forEach((item) => {
lineName += item.name + '';
if (item.children && item.children.length > 0) {
const childName = this.setLineName(item.children, lineName);
lineName = childName;
setLineName(val) {
if (!val || val.length === 0) return '';
const currentLevelNames = val.map((item) => item.name);
// 处理当前层级显示
let currentDisplay = '';
if (currentLevelNames.length > 1) {
currentDisplay = `(${currentLevelNames.join(' 、 ')})`;
} else {
currentDisplay = currentLevelNames[0];
}
// 查找所有子节点(取第一个有子节点的元素)
const childNode = val.find(
(item) => item.children && item.children.length > 0
);
if (childNode) {
const childPath = this.setLineName(childNode.children);
if (childPath) {
return `${currentDisplay} / ${childPath}`;
}
});
return lineName;
}
return currentDisplay;
},
},
};

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2025-10-13 15:07:24
* @LastEditors: zwq
* @LastEditTime: 2025-11-05 08:51:13
* @LastEditTime: 2025-11-28 09:56:09
* @Description:
-->
<template>
@@ -33,6 +33,7 @@
v-model="formInline.startDay"
size="small"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择日期时间"></el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="endDay">
@@ -40,6 +41,7 @@
v-model="formInline.endDay"
size="small"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择日期时间"></el-date-picker>
</el-form-item>
<el-form-item label="部门" prop="deptId">
@@ -396,12 +398,12 @@ export default {
},
],
formInline: {
code: '',
name: '',
startDay: '',
endDay: '',
deptId: '',
status: '',
code: null,
name: null,
startDay: null,
endDay: null,
deptId: null,
status: null,
},
stepNum: 1, // 新增编辑时当前第几步
detailVisible: false,
@@ -419,29 +421,29 @@ export default {
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) {
if (this.formInline.startDay > this.formInline.endDay) {
this.$message('开始时间不得晚于结束时间');
return;
}
this.listQuery = {
pageNo: 1,
pageSize: 10,
pageSize: 20,
total: 1,
...this.formInline,
};
this.getDataList();
break;
case 'reset':
this.$refs.searchBarForm.resetFields();
this.formInline.name = null;
this.formInline.code = null;
this.formInline.deptId = null;
this.$refs.deptSelect.clear();
this.formInline.status = null;
this.formInline.startDay = null;
this.formInline.endDay = null;
this.listQuery = {
pageSize: 10,
pageSize: 20,
pageNo: 1,
total: 1,
};
@@ -490,7 +492,8 @@ export default {
this.addOrUpdateVisible = true;
this.addOrEditTitle = '编辑';
this.$nextTick(() => {
this.$refs.addOrUpdate.init(val.data.id,true);
this.stepNum = 3;
this.$refs.addOrUpdate.init(val.data.id, true);
});
} else if (val.type === 'delete') {
this.deleteHandle(val.data.id, val.data.name, val.data._pageIndex);
@@ -505,13 +508,31 @@ export default {
this.getDataList();
});
} else if (val.type === 'sync') {
updateScheduleLater({
planId: val.data.id,
logId: val.data.updateLogId,
}).then((res) => {
this.$modal.msgSuccess('同步节假日成功');
this.getDataList();
});
this.$confirm(
'当前节假日包含尚未同步的节假日变更,是否立即将上述节假日同步更新到排班计划?',
'同步节假日',
{
confirmButtonText: '确认同步',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
updateScheduleLater({
planId: val.data.id,
logId: val.data.updateLogId,
}).then((res) => {
this.$modal.msgSuccess('同步节假日成功');
this.getDataList();
});
})
.catch((res) => {
this.getDataList();
this.$message({
type: 'info',
message: '已取消',
});
});
} else if (val.type === 'copy') {
copyPlan(val.data.id).then((res) => {
this.$modal.msgSuccess('复制成功');

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: zwq
* @LastEditTime: 2025-10-31 15:47:16
* @LastEditTime: 2025-11-27 14:49:34
* @Description:
-->
<template>
@@ -25,6 +25,7 @@
<el-col :span="8">
<el-form-item label="班组名称" prop="name">
<el-input
:disabled="nameDisable"
v-model="dataForm.name"
clearable
placeholder="请输入班组名称" />
@@ -78,6 +79,7 @@
<el-form-item label="生产班组" prop="isProduction">
<el-select
style="width: 100%"
@change="setIsProduction"
v-model="dataForm.isProduction"
placeholder="请选择是否为生产班组">
<el-option
@@ -109,6 +111,7 @@ import {
updateGroup,
getGroup,
getCode,
updateIsProduction,
} from '@/api/group/groupSetting';
export default {
@@ -139,6 +142,7 @@ export default {
remark: undefined,
},
setData: true,
nameDisable: false, //班组被使用时,班组名称不能修改
leaderArr: [],
options: [
{
@@ -157,9 +161,7 @@ export default {
name: [
{ required: true, message: '班组名称不能为空', trigger: 'blur' },
],
deptId: [
{ required: true, message: '部门不能为空', trigger: 'blur' },
],
deptId: [{ required: true, message: '部门不能为空', trigger: 'blur' }],
leaderId: [
{ required: true, message: '组长不能为空', trigger: 'change' },
],
@@ -191,6 +193,40 @@ export default {
},
setDataForm() {
this.$refs.deptSelect.setID(this.dataForm.deptId);
this.nameDisable = false;
updateIsProduction(this.dataForm.id).then(({ data }) => {
if (data) {
this.$notify({
title: '警告',
message: '当前班组仍参与排班计划或正在执行工单。班组名称不能修改',
type: 'warning',
});
this.nameDisable = true;
}
});
},
setIsProduction() {
// 修改的提交
if (this.dataForm.id && this.nameDisable) {
this.$confirm(
'当前班组仍参与排班计划或正在执行工单。<br/>修改<strong> 是否是生产班组 </strong>不会自动更新排班内容,可能导致排班信息与班组属性不一致,请注意核查!<br/>是否确认修改是否是生产班组?',
'修改是否是生产班组',
{
dangerouslyUseHTMLString: true,
confirmButtonText: '确认修改',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {})
.catch((res) => {
this.$message({
type: 'info',
message: '已取消',
});
this.dataForm.isProduction = !this.dataForm.isProduction;
});
}
},
setLeaderName(val) {
this.leaderArr.map((item) => {

View File

@@ -1,14 +1,21 @@
<!--
* @Author: zwq
* @Date: 2025-10-11 14:41:12
* @LastEditors: zwq
* @LastEditTime: 2025-11-27 14:19:39
* @Description:
-->
<template>
<el-switch
@change="changeStatus"
size="small"
v-model="list.enabled"
v-model="injectData.enabled"
:active-value="1"
:inactive-value="0"></el-switch>
</template>
<script>
import { updateGroup } from '@/api/group/groupSetting';
import { updateGroup, updateIsProduction } from '@/api/group/groupSetting';
export default {
props: {
injectData: {
@@ -17,17 +24,51 @@ export default {
},
},
data() {
return {
list: this.injectData,
};
return {};
},
created() {},
methods: {
changeStatus(val) {
const data = { ...this.injectData, enabled: val };
updateGroup(data).then((res) => {
this.$modal.msgSuccess('修改成功');
this.$emit('emitData');
const pdata = { ...this.injectData, enabled: val };
updateIsProduction(this.injectData.id).then(({ data }) => {
if (data) {
this.$confirm(
'当前班组仍参与排班计划或正在执行工单。<br/>修改<strong> 班组状态 </strong>不会自动更新排班内容,可能导致排班信息与班组属性不一致,请注意核查!<br/>是否确认修改班组状态?',
'更新班组状态',
{
dangerouslyUseHTMLString: true,
confirmButtonText: '确认修改',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
updateGroup(pdata).then((res) => {
if (res.code === 0 || res.code === 200) {
this.$modal.msgSuccess('修改成功');
this.$emit('emitData');
} else {
this.$modal.msg(res.msg);
}
});
})
.catch((res) => {
this.$emit('emitData');
this.$message({
type: 'info',
message: '已取消',
});
});
} else {
updateGroup(pdata).then((res) => {
if (res.code === 0 || res.code === 200) {
this.$modal.msgSuccess('修改成功');
this.$emit('emitData');
} else {
this.$modal.msg(res.msg);
}
});
}
});
},
},

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2025-10-11 14:27:37
* @LastEditors: zwq
* @LastEditTime: 2025-10-29 14:44:41
* @LastEditTime: 2025-11-14 21:50:40
* @Description:
-->
<template>
@@ -212,7 +212,7 @@ export default {
case 'search':
this.listQuery = {
pageNo: 1,
pageSize: 10,
pageSize: 20,
total: 1,
...this.formInline,
};
@@ -225,7 +225,7 @@ export default {
this.$refs.deptSelect.clear();
this.formInline.leaderName = null;
this.listQuery = {
pageSize: 10,
pageSize: 20,
pageNo: 1,
total: 1,
};

View File

@@ -2,7 +2,7 @@
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: zwq
* @LastEditTime: 2025-11-11 10:29:18
* @LastEditTime: 2025-12-11 10:47:50
* @Description:
-->
<template>
@@ -66,7 +66,7 @@
type="date"
placeholder="选择日期"
format="yyyy-MM-dd"
value-format="--MM-dd"
value-format="yyyy-MM-dd"
@blur="$forceUpdate()"
:picker-options="pickerOptions"></el-date-picker>
</el-form-item>
@@ -81,7 +81,7 @@
v-model="dataForm.dateDayArr"
type="daterange"
format="yyyy-MM-dd"
value-format="--MM-dd"
value-format="yyyy-MM-dd"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
@@ -213,6 +213,7 @@ export default {
deptId: undefined,
},
detail: false,
inherited: true,
options1: [
{
value: 1,
@@ -330,54 +331,80 @@ export default {
});
},
methods: {
init(deptId, val, detail) {
init(deptId, val, detail, inherited) {
this.dataForm.deptId = deptId || undefined;
this.dataForm.id = val.holidayId || undefined;
this.dataForm.inherited = val.isInherit || false;
this.detail = detail || false;
this.inherited = inherited; //编辑的时候该部门如果是自定义的但这个val数据是继承的修改之后则变成自定义的inherited
this.$nextTick(() => {
this.$refs['dataForm'].resetFields();
if (this.dataForm.id) {
getHoliday(this.dataForm.id).then((res) => {
//设置年份,不重复的直接用返回的年份,重复的直接用当年年份
let year = new Date().getFullYear();
if (res.data.calendarType == 1 && !res.data.repeat) {
year = res.data.noRepeatYear;
} else if (res.data.calendarType == 2 && !res.data.repeat) {
year = res.data.noRepeatYearChinese;
}
this.dataForm = res.data;
if (
this.dataForm.dateType == 1 &&
this.dataForm.calendarType == 1
) {
this.dataForm.dateDay = this.dataForm.oneDay;
this.dataForm.dateDay =
year + '-' + this.dataForm.oneDay.slice(-5);
} else if (
this.dataForm.dateType == 2 &&
this.dataForm.calendarType == 1
) {
let num = this.compareMonthDay(
this.dataForm.startDay.slice(-5),
this.dataForm.endDay.slice(-5)
);
this.dataForm.dateDayArr = [
this.dataForm.startDay,
this.dataForm.endDay,
year + '-' + this.dataForm.startDay.slice(-5),
(num > 0 ? year : year + 1) +
'-' +
this.dataForm.endDay.slice(-5),
];
} else if (
this.dataForm.dateType == 1 &&
this.dataForm.calendarType == 2
) {
this.dataForm.dateDayChinese =
'L' +
new Date().getFullYear() +
this.dataForm.oneDayChinese.slice(-6);
'L' + year + this.dataForm.oneDayChinese.slice(-6);
} else if (
this.dataForm.dateType == 2 &&
this.dataForm.calendarType == 2
) {
let num = this.compareMonthDay(
this.dataForm.startDayChinese.slice(-6),
this.dataForm.endDayChinese.slice(-6)
);
this.dataForm.dateDayArrChinese = [
'L' + year + this.dataForm.startDayChinese.slice(-6),
'L' +
new Date().getFullYear() +
this.dataForm.startDayChinese.slice(-6),
'L' +
new Date().getFullYear() +
this.dataForm.endDayChinese.slice(-6),
(num > 0 ? year : year + 1) +
+this.dataForm.endDayChinese.slice(-6),
];
}
});
}
});
},
//用于比较日期大小判断年份是否要加1
compareMonthDay(dateStr1, dateStr2) {
const [month1, day1] = dateStr1.split('-').map(Number);
const [month2, day2] = dateStr2.split('-').map(Number);
if (month1 !== month2) {
return month2 - month1;
}
return day2 - day1;
},
editHoliday() {
this.detail = false;
},
@@ -440,13 +467,16 @@ export default {
return false;
}
if (this.dataForm.dateType == 1 && this.dataForm.calendarType == 1) {
this.dataForm.oneDay = this.dataForm.dateDay;
const [year, month, day] = this.dataForm.dateDay.split('-');
this.dataForm.oneDay = `--${month}-${day}`;
} else if (
this.dataForm.dateType == 2 &&
this.dataForm.calendarType == 1
) {
this.dataForm.startDay = this.dataForm.dateDayArr[0];
this.dataForm.endDay = this.dataForm.dateDayArr[1];
const [year, month, day] = this.dataForm.dateDayArr[0].split('-');
this.dataForm.startDay = `--${month}-${day}`;
const [year1, month1, day1] = this.dataForm.dateDayArr[1].split('-');
this.dataForm.endDay = `--${month1}-${day1}`;
} else if (
this.dataForm.dateType == 1 &&
this.dataForm.calendarType == 2
@@ -462,16 +492,81 @@ export default {
this.dataForm.endDayChinese =
'--' + this.dataForm.dateDayArrChinese[1].slice(-5);
}
if (this.dataForm.calendarType == 1 && !this.dataForm.repeat) {
this.dataForm.noRepeatYear = new Date().getFullYear();
} else if (this.dataForm.calendarType == 2 && !this.dataForm.repeat) {
this.dataForm.noRepeatYearChinese = new Date().getFullYear();
//是否每年重复,不重复的话需要传 年份
if (
this.dataForm.dateType == 1 &&
this.dataForm.calendarType == 1 &&
!this.dataForm.repeat
) {
const [year, month, day] = this.dataForm.dateDay.split('-');
this.dataForm.noRepeatYear = year;
} else if (
this.dataForm.dateType == 2 &&
this.dataForm.calendarType == 1 &&
!this.dataForm.repeat
) {
const [year, month, day] = this.dataForm.dateDayArr[0].split('-');
this.dataForm.noRepeatYear = year;
} else if (
this.dataForm.dateType == 1 &&
this.dataForm.calendarType == 2 &&
!this.dataForm.repeat
) {
this.dataForm.noRepeatYearChinese =
this.dataForm.dateDayChinese.slice(1, 5);
} else if (
this.dataForm.dateType == 2 &&
this.dataForm.calendarType == 2 &&
!this.dataForm.repeat
) {
this.dataForm.noRepeatYearChinese =
this.dataForm.dateDayArrChinese[0].slice(1, 5);
}
// 修改的提交
if (this.dataForm.id) {
if (!this.inherited) {
this.dataForm.inherited = false;
}
updateHoliday(this.dataForm).then((res) => {
if (res.code === 0 || res.code === 200) {
if (!res.data.updateFlag) {
this.$modal.msgSuccess('修改成功');
this.$emit('refreshPage');
} else {
this.$confirm(
'新增节假日影响已有排班计划,是否立即更新?',
'更新排班',
{
confirmButtonText: '立即更新',
cancelButtonText: '暂不更新',
type: 'warning',
}
)
.then(() => {
updateSchedule({ logId: res.data.logId }).then((res1) => {
this.$modal.msgSuccess('更新成功');
});
this.$emit('refreshPage');
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消',
});
this.$emit('refreshPage');
});
}
} else {
this.$modal.msg(res.msg);
}
});
return;
}
// 添加的提交
createHoliday(this.dataForm).then((res) => {
if (res.code === 0 || res.code === 200) {
if (!res.data.updateFlag) {
this.$modal.msgSuccess('修改成功');
this.$modal.msgSuccess('新增成功');
this.$emit('refreshPage');
} else {
this.$confirm(
@@ -497,37 +592,8 @@ export default {
this.$emit('refreshPage');
});
}
});
return;
}
// 添加的提交
createHoliday(this.dataForm).then((res) => {
if (!res.data.updateFlag) {
this.$modal.msgSuccess('新增成功');
this.$emit('refreshPage');
} else {
this.$confirm(
'新增节假日影响已有排班计划,是否立即更新?',
'更新排班',
{
confirmButtonText: '立即更新',
cancelButtonText: '暂不更新',
type: 'warning',
}
)
.then(() => {
updateSchedule({ logId: res.data.logId }).then((res1) => {
this.$modal.msgSuccess('更新成功');
});
this.$emit('refreshPage');
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消',
});
this.$emit('refreshPage');
});
this.$modal.msg(res.msg);
}
});
});

View File

@@ -210,7 +210,7 @@
</template>
<script>
import moment from 'moment';
import JDatePickerScript from './jDatePicker.js'
import JDatePickerScript from './JDatePicker.js'
export default {
props: {

View File

@@ -2,283 +2,284 @@
* @Author: zwq
* @Date: 2024-07-01 14:54:06
* @LastEditors: zwq
* @LastEditTime: 2025-11-11 14:29:10
* @LastEditTime: 2025-11-24 16:11:20
* @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="6">
<div style="height: 40px; font-size: 16px">
<span style="color: #409eff; font-weight: 600">
{{ showDeptName }}
</span>
节假日设置
<span
:style="{
color: inherited ? '#67c23a' : '#b5b839',
fontSize: '12px',
fontWeight: 600,
}"
v-show="ishasParent">
{{ inherited ? '继承模式' : '自定义模式' }}
</span>
</div>
</el-col>
<el-col :span="18" v-if="!ishasParent">
<div style="float: right">
<el-button
size="small"
type="primary"
style="margin-right: 10px"
v-hasPermi="['base:group-holiday:create']"
@click="addHoliday">
新增节假日
</el-button>
<el-button
size="small"
v-hasPermi="['base:group-holiday-log:query']"
@click="holidayLog">
节假日变更记录
</el-button>
</div>
</el-col>
<el-col :span="18" v-else style="text-align: right">
<div
class="app-container"
style="
background-color: #f2f4f9;
padding: 0;
display: grid;
grid-template-columns: 1fr 5fr;
gap: 10px;
">
<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"
node-key="id"
default-expand-all
highlight-current
@node-click="handleNodeClick" />
</div>
<div class="groupTeamScheduling">
<div class="operationArea">
<el-row>
<el-col :span="6">
<div style="height: 40px; font-size: 16px">
<span style="color: #409eff; font-weight: 600">
{{ showDeptName }}
</span>
节假日设置
<span
:style="{
color: inherited ? '#67c23a' : '#b5b839',
fontSize: '12px',
fontWeight: 600,
}"
v-show="ishasParent">
{{ inherited ? '继承模式' : '自定义模式' }}
</span>
</div>
</el-col>
<el-col :span="18" v-if="!ishasParent">
<div style="float: right">
<el-button
size="small"
type="primary"
:disabled="inherited"
style="margin-right: 10px"
v-hasPermi="['base:group-holiday:create']"
@click="addHoliday">
新增节假日
</el-button>
<el-button
size="small"
type="success"
:disabled="inherited"
v-hasPermi="['base:group-holiday:update']"
@click="reExtends">
恢复继承
</el-button>
<el-button
size="small"
type="warning"
:disabled="!inherited"
v-hasPermi="['base:group-holiday:update']"
@click="disExtends">
解除继承
</el-button>
<el-button
size="small"
:disabled="inherited"
v-hasPermi="['base:group-holiday-log:query']"
@click="holidayLog">
节假日变更记录
</el-button>
</el-col>
</el-row>
</div>
</el-col>
<el-col :span="18" v-else style="text-align: right">
<el-button
size="small"
type="primary"
:disabled="inherited"
v-hasPermi="['base:group-holiday:create']"
@click="addHoliday">
新增节假日
</el-button>
<el-button
size="small"
type="success"
:disabled="inherited"
v-hasPermi="['base:group-holiday:update']"
@click="reExtends">
恢复继承
</el-button>
<el-button
size="small"
type="warning"
:disabled="!inherited"
v-hasPermi="['base:group-holiday:update']"
@click="disExtends">
解除继承
</el-button>
<el-button
size="small"
:disabled="inherited"
v-hasPermi="['base:group-holiday-log:query']"
@click="holidayLog">
节假日变更记录
</el-button>
</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 label="继承上一级的节假日" v-show="ishasParent">
<div
style="
width: 36px;
height: 36px;
background-color: #67c23a;
border-radius: 3px;
"></div>
</el-form-item>
<el-form-item label="本级自定义的节假日" v-show="ishasParent">
<div
style="
width: 36px;
height: 36px;
background-color: #b5b839;
border-radius: 3px;
"></div>
</el-form-item>
<el-form-item style="float: right">
<el-button
size="small"
type="primary"
@click="(startDay = new Date()), getHolidayPage()">
跳转到今天
</el-button>
</el-form-item>
</el-form>
<div v-if="!ishasParent">
<el-tag
type="warning"
closable
v-if="showTag1"
@close="handleClose(1)"
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="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 label="继承上一级的节假日" v-show="ishasParent">
<div
style="
width: 36px;
height: 36px;
background-color: #67c23a;
border-radius: 3px;
"></div>
</el-form-item>
<el-form-item label="本级自定义的节假日" v-show="ishasParent">
<div
style="
width: 36px;
height: 36px;
background-color: #b5b839;
border-radius: 3px;
"></div>
</el-form-item>
<el-form-item style="float: right">
<el-button
size="small"
type="primary"
@click="(startDay = new Date()), getHolidayPage()">
跳转到今天
</el-button>
</el-form-item>
</el-form>
<div v-if="!ishasParent">
<div v-else>
<div v-if="inherited">
<el-tag
type="warning"
closable
v-if="showTag1"
@close="handleClose(1)"
v-if="showTag2"
@close="handleClose(2)"
style="margin-bottom: 10px; color: black">
<i class="el-icon-warning" style="color: #ffbd02"></i>
当前节假日设置为组织架构中最高层级配置默认自动继承至下属部门子部门可选择
当前部门继承上级节假日设置默认不可修改可通过
<span style="color: red">解除继承</span>
设置后进行独立修改解除后不再继承上级设置
进行自定义解除后不再继承
</el-tag>
</div>
<div v-else>
<div v-if="inherited">
<el-tag
type="warning"
closable
v-if="showTag2"
@close="handleClose(2)"
style="margin-bottom: 10px; color: black">
<i class="el-icon-warning" style="color: #ffbd02"></i>
当前部门继承上级节假日设置默认不可修改可通过
<span style="color: red">解除继承</span>
进行自定义解除后将不再继承
</el-tag>
</div>
<div v-else>
<el-tag
type="warning"
closable
v-if="showTag3"
@close="handleClose(3)"
style="margin-bottom: 10px; color: black">
<i class="el-icon-warning" style="color: #ffbd02"></i>
当前部门已解除继承节假日可自由配置点击
<span style="color: red">恢复继承</span>
将清空当前设置并同步上级节假日
</el-tag>
</div>
<el-tag
type="warning"
closable
v-if="showTag3"
@close="handleClose(3)"
style="margin-bottom: 10px; color: black">
<i class="el-icon-warning" style="color: #ffbd02"></i>
当前部门已解除继承节假日可自由配置点击
<span style="color: red">恢复继承</span>
将清空当前设置并同步上级节假日
</el-tag>
</div>
</div>
<!-- 日历区域 -->
<div class="calenderArea">
<el-calendar v-model="startDay">
<template slot="dateCell" slot-scope="{ data }">
<div v-if="data.type === 'current-month'">
<!-- 日期 -->
<div
class="dateStyle"
@click="
new Date(data.day).getTime() >=
new Date().setHours(0, 0, 0, 0) &&
(!ishasParent || !inherited) &&
HolidayList[Number(data.day.split('-')[2]) - 1]
?.isHoliday &&
showDetail(
HolidayList[Number(data.day.split('-')[2]) - 1]
)
">
<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"
:style="{
backgroundColor: HolidayList[
Number(data.day.split('-')[2]) - 1
]?.isHoliday
? '#67C23A'
: '#409EFF',
}">
{{
HolidayList[Number(data.day.split('-')[2]) - 1]
?.isHoliday
? '假'
: '班'
}}
</div>
</el-col>
<!-- 显示假期名和操作 -->
<el-col
:span="24"
v-if="
</div>
<!-- 日历区域 -->
<div class="calenderArea">
<el-calendar v-model="startDay">
<template slot="dateCell" slot-scope="{ data }">
<div v-if="data.type === 'current-month'">
<!-- 日期 -->
<div
class="dateStyle"
@click="
new Date(data.day).getTime() >=
new Date().setHours(0, 0, 0, 0) &&
(!ishasParent || !inherited) &&
HolidayList[Number(data.day.split('-')[2]) - 1]
?.isHoliday &&
showDetail(HolidayList[Number(data.day.split('-')[2]) - 1])
">
<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"
:style="{
backgroundColor: HolidayList[
Number(data.day.split('-')[2]) - 1
]?.isHoliday
? '#67C23A'
: '#409EFF',
}">
{{
HolidayList[Number(data.day.split('-')[2]) - 1]
?.isHoliday
">
<div
class="holiday-div"
:style="{
backgroundColor: ishasParent
? HolidayList[Number(data.day.split('-')[2]) - 1]
.isInherit
? '#67c23a'
: '#b5b839'
: '',
}">
{{
HolidayList[Number(data.day.split('-')[2]) - 1]
?.holidayName
}}
<span
class="holiday-div-btn"
v-if="
(!ishasParent || !inherited) &&
new Date(data.day).getTime() >=
new Date().setHours(0, 0, 0, 0)
">
<i class="el-icon-more" style="color: #fff"></i>
</span>
</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>
? '假'
: '班'
}}
</div>
</el-col>
<!-- 显示假期名和操作 -->
<el-col
:span="24"
v-if="
HolidayList[Number(data.day.split('-')[2]) - 1]?.isHoliday
">
<div
class="holiday-div"
:style="{
backgroundColor: ishasParent
? HolidayList[Number(data.day.split('-')[2]) - 1]
.isInherit
? '#67c23a'
: '#b5b839'
: '',
}">
{{
HolidayList[Number(data.day.split('-')[2]) - 1]
?.holidayName
}}
<span
class="holiday-div-btn"
v-if="
(!ishasParent || !inherited) &&
new Date(data.day).getTime() >=
new Date().setHours(0, 0, 0, 0)
">
<i class="el-icon-more" style="color: #fff"></i>
</span>
</div>
</el-col>
</el-row>
</div>
</template>
</el-calendar>
</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>
</el-col>
</div>
<base-dialog
:dialogTitle="dialogTitle"
:dialogVisible="addOrUpdateVisible"
@@ -352,7 +353,7 @@
</slot>
</template>
</base-dialog>
</el-row>
</div>
</template>
<script>
@@ -453,13 +454,43 @@ export default {
// 遍历部门,判断当前帐号的部门是否有为最上级
const nowDept = response.data.filter((i) => i.id == this.topDeptId);
this.ishasParent = nowDept[0].hasParent;
this.parentId = nowDept.parentId;
this.parentId = nowDept[0].parentId;
// 处理 deptOptions 参数
this.deptOptions = [];
this.deptOptions.push(...this.handleTree(response.data, 'id'));
this.deptOptions.push(...this.handleTree1(response.data, 'id'));
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(this.deptId);
});
});
},
//大整数精度丢失
handleTree1(data, fid) {
const id = fid || 'id';
const parentId = 'parentId';
const children = 'children';
const rootId =
data
.map((item) => item[parentId].toString())
.reduce((min, current) => {
if (current.length < min.length) return current;
if (current.length > min.length) return min;
return current < min ? current : min;
}) || 0;
//对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data));
//循环所有项
const treeData = cloneData.filter((father) => {
let branchArr = cloneData.filter((child) => {
//返回每一项的子级数组
return father[id] == child[parentId];
});
branchArr.length > 0 ? (father.children = branchArr) : '';
//返回第一层
return father[parentId] == rootId;
});
return treeData !== '' ? treeData : data;
},
// 根据部门id查询该部门是否是继承
getSet() {
getSet({ id: this.deptId }).then((res) => {
@@ -643,6 +674,7 @@ export default {
type: 'info',
message: '已取消',
});
this.getSet();
});
}
});
@@ -676,7 +708,7 @@ export default {
this.detail = true;
this.dialogTitle = '节假日详情';
this.$nextTick(() => {
this.$refs.addOrUpdataRef.init(this.deptId, val, true);
this.$refs.addOrUpdataRef.init(this.deptId, val, true, this.inherited); //编辑的时候该部门如果是自定义的但这个val数据是继承的修改之后则变成自定义的inherited
});
},
editHoliday() {
@@ -709,7 +741,7 @@ export default {
.head-container {
padding: 20px 10px 0;
background-color: #fff;
min-height: calc(100vh - 120px - 8px);
height: 100%;
border-radius: 8px;
}
.groupTeamScheduling {