Compare commits

..

13 Commits

Author SHA1 Message Date
d859ba62c8 Merge pull request '更新班组' (#446) from projects/line-new-zwq into projects/line-new
Reviewed-on: #446
2025-10-24 11:43:01 +08:00
zwq
31bafae4aa 更新班组 2025-10-24 11:42:24 +08:00
67b6b88863 Merge pull request 'projects/line-new-zhp' (#445) from projects/line-new-zhp into projects/line-new
Reviewed-on: #445
2025-10-24 11:21:24 +08:00
‘937886381’
9f3cdcb1c4 Merge branch 'projects/line-new' into projects/line-new-zhp 2025-10-24 11:20:40 +08:00
‘937886381’
f11dfe04d5 生产管理 2025-10-24 11:19:34 +08:00
428a0752eb Merge pull request '更新班组' (#444) from projects/line-new-zwq into projects/line-new
Reviewed-on: #444
2025-10-19 00:40:20 +08:00
zwq
4e801873b9 更新班组 2025-10-19 00:38:48 +08:00
‘937886381’
463706663a xiugai 2025-08-21 08:44:24 +08:00
‘937886381’
e8638687b1 xiugai 2025-07-08 10:36:47 +08:00
6c46083d4a Merge pull request '首页样式调整' (#441) from projects/line-new-zjl into projects/line-new
Reviewed-on: #441
2025-07-07 15:49:44 +08:00
ab486dd71b 首页样式调整 2025-07-07 15:49:00 +08:00
4da1e6f0b1 Merge pull request '首页样式' (#440) from projects/line-new-zjl into projects/line-new
Reviewed-on: #440
2025-07-04 17:03:31 +08:00
0b689b5452 首页样式 2025-07-04 17:02:20 +08:00
93 changed files with 20992 additions and 12400 deletions

View File

@@ -13,7 +13,7 @@ VUE_APP_TITLE = 智能监控分析系统
# 芋道管理系统/开发环境
# VUE_APP_BASE_API = 'http://192.168.8.22:48080'
VUE_APP_BASE_API = 'http://172.16.32.79:48082'
VUE_APP_BASE_API = 'http://172.16.33.65:48082'
# 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true

BIN
dist.zip

Binary file not shown.

View File

@@ -6,7 +6,7 @@
"license": "MIT",
"scripts": {
"local": "vue-cli-service serve --mode local",
"dev": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --mode dev",
"dev": "vue-cli-service serve --mode dev",
"front": "vue-cli-service serve --mode front",
"build:prod": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build --mode prod",
"build:stage": "vue-cli-service build --mode stage",
@@ -42,16 +42,19 @@
},
"dependencies": {
"@babel/parser": "7.18.4",
"@jiaminghi/data-view": "^2.10.0",
"@riophae/vue-treeselect": "0.4.0",
"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",
"crypto-js": "^4.0.0",
"diagram-js": "^12.3.0",
"echarts": "5.4.0",
"el-tree-transfer": "^2.4.7",
"element-ui": "2.15.12",
"file-saver": "^2.0.5",
"fuse.js": "6.6.2",

View File

@@ -25,10 +25,10 @@ export function getCT(data) {
// 获取产线平衡分析数据new
export function getNewCTNow(data) {
return request({
url: '/analysis/production-analysis/getNewCTNow',
method: 'post',
data: data
})
url: '/analysis/production-analysis/getNewCTNow',
method: 'post',
data: data,
});
}
// 获取产线平衡分析数据趋势图new
export function getNewCTCharts(data) {
@@ -39,9 +39,10 @@ export function getNewCTCharts(data) {
})
}
// 获取产线平衡分析数据设备listnew
export function getNewCTDet(id) {
export function getNewCTDet(data) {
return request({
url: '/analysis/production-analysis/getNewCTDet?lineId='+id,
method: 'get',
})
url: '/analysis/production-analysis/getNewCTDet',
method: 'post',
data:data
});
}

View File

@@ -41,28 +41,55 @@ export function getProductAuto(data) {
})
}
export function getPdlAutoReportNewSearchNow(data) {
return request({
url: '/monitoring/production-monitor/getPdlAutoReportNewSearchNow',
method: 'post',
data: data,
timeout: 60000,
});
}
// 班组自动报表分页
export function getTeamReportPage(data) {
return request({
url: '/monitoring/group-off-record/page',
method: 'post',
data: data,
});
url: '/monitoring/team-auto-report/page',
method: 'post',
data: data
})
}
// 班组自动报表分页详细
export function getTeamReportPageDet(id) {
return request({
url: '/monitoring/group-off-record/get?id=' + id,
url: '/monitoring/team-auto-report/pageDet?id=' + id,
method: 'get',
})
}
export function exportGroupProductReportExcel(data) {
return request({
url: '/monitoring/group-off-record/export-det-excel',
method: 'get',
params: data,
responseType: 'blob',
// 获取产品当班数据
export function getProcessAutoReportGroup(data) {
return request({
url: '/monitoring/production-monitor/getProcessAutoReportGroup',
method: 'post',
data: data,
});
}
// 获取产品当天数据
export function getProcessAutoReportDay(data) {
return request({
url: '/monitoring/production-monitor/getProcessAutoReportDay',
method: 'post',
data: data,
});
}
// 获取产品历史数据
export function getProcessAutoReportNew(data) {
return request({
url: '/monitoring/production-monitor/getProcessAutoReportNew',
method: 'post',
data: data,
});
}

View File

@@ -14,3 +14,79 @@ export function getSectionDataSearch(data) {
data: data
})
}
// 获取下片日志分页数据
export function getDownLogPage(data) {
return request({
url: '/base/down-log/page',
method: 'get',
params: data,
});
}
// 获取下片日志历史数据
export function getDownLogHisData(data) {
return request({
url: '/base/down-log/pagehis',
method: 'get',
params: data,
});
}
// 导出下片日志Excel
export function exportDownLogData(query) {
return request({
url: '/base/down-log/export-excel',
method: 'get',
params: query,
responseType: 'blob',
});
}
// 获得所有工厂产线列表
export function getPdList() {
return request({
url: '/base/production-line/listAll',
method: 'get'
})
}
// 获得玻璃型号列表
export function getThick() {
return request({
url: '/base/down-log/thick',
method: 'get',
});
}
// 获得原片报表
export function getCostOriginRadioHisData(data) {
return request({
url: '/monitoring/cost-origin-ratio-his/page',
method: 'get',
params: data,
});
}
// 修改原片报表
export function editCostOriginRadioHisData(data) {
return request({
url: '/monitoring/cost-origin-ratio-his/update',
method: 'put',
data: data,
});
}
// 导出原片报表
export function exportCostOriginRadioHisData(data) {
return request({
url: '/monitoring/cost-origin-ratio-his/export-excel',
method: 'get',
params: data,
responseType: 'blob',
});
}

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

@@ -0,0 +1,143 @@
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 disablePlan(id) {
return request({
url: '/base/group-scheduling-plan/disablePlan?id='+id,
method: 'delete',
})
}
// 同步节假日
export function updateScheduleLater(data) {
return request({
url: '/base/group-holiday/updateScheduleLater',
method: 'post',
data:data
})
}
// 复制计划
export function copyPlan(id) {
return request({
url: '/base/group-scheduling-plan/copyPlan?id='+id,
method: 'get'
})
}
// 列表草稿编辑
export function draftEditing(id) {
return request({
url: '/base/group-scheduling-plan/draftEditing?id='+id,
method: 'get'
})
}
// 排班计划-详情
export function getPlan(id) {
return request({
url: '/base/group-scheduling-plan/get?id='+id,
method: 'get'
})
}
// 弹窗-取消
export function cancelStepThree(id) {
return request({
url: '/base/group-scheduling-plan/cancelStepThree?id='+id,
method: 'delete',
})
}
// 第三步确认并执行 检查计划时间是否冲突,如果有,返回冲突的计划列表
export function checkPlan(data) {
return request({
url: '/base/group-scheduling-plan/checkPlan',
method: 'post',
data:data
})
}
// 第三步确认并执行 执行
export function createStepFour(id) {
return request({
url: '/base/group-scheduling-plan/createStepFour',
method: 'post',
data:id
})
}
// 班组班次下一步至获取预览
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',
})
}
// 第三步获取预览
export function getPerView(data) {
return request({
url: '/base/group-scheduling-plan/getPerView',
method: 'post',
data:data
})
}

18
src/api/group/calendar.js Normal file
View File

@@ -0,0 +1,18 @@
import request from '@/utils/request'
// 获取部门及下级部门排班list
export function getDeptSchedulingList(data) {
return request({
url: '/base/group-team-scheduling/getDeptSchedulingList',
method: 'post',
data: data
})
}
// 某个班组的排班list
export function getClassSchedulingList(data) {
return request({
url: '/base/group-team-scheduling/getClassSchedulingList',
method: 'post',
data: data
})
}

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,92 @@
/*
* @Author: zwq
* @Date: 2025-10-18 21:24:37
* @LastEditors: zwq
* @LastEditTime: 2025-10-22 14:34:29
* @Description:
*/
import request from '@/utils/request'
// 获得节假日基础信息分页
export function deptHolidayPage(query) {
return request({
url: '/base/group-holiday/page',
method: 'get',
params: query
})
}
// 获得部门节假日信息
export function deptHolidayList(data) {
return request({
url: '/base/group-holiday/deptHolidayList',
method: 'post',
data: data
})
}
// 创建节假日基础信息
export function createHoliday(data) {
return request({
url: '/base/group-holiday/create',
method: 'post',
data: data
})
}
// 节假日操作后直接更新排班日历
export function updateSchedule(data) {
return request({
url: '/base/group-holiday/updateSchedule',
method: 'post',
data: data
})
}
// 更新节假日基础信息
export function updateHoliday(data) {
return request({
url: '/base/group-holiday/update',
method: 'put',
data: data
})
}
// 删除节假日基础信息前校验是否影响排班
export function checkDeleteHoliday(id) {
return request({
url: '/base/group-holiday/checkDelete?id=' + id,
method: 'delete'
})
}
// 删除
export function deleteHoliday(id) {
return request({
url: '/base/group-holiday/delete?id=' + id,
method: 'delete'
})
}
// 获得节假日基础信息
export function getHoliday(id) {
return request({
url: '/base/group-holiday/get?id=' + id,
method: 'get'
})
}
// 获得节假日变动日志分页
export function deptHolidayLogList(query) {
return request({
url: '/base/group-holiday-log/page',
method: 'get',
params: query
})
}
// 获得用户本人及以下的部门扁平化结构
export function getEnableData() {
return request({
url: '/base/group-team-scheduling/getEnableData',
method: 'get',
})
}

View File

@@ -1,18 +0,0 @@
import request from '@/utils/request'
// 获得工厂分页
export function getMaterialReceiptIssuePageData(data) {
return request({
url: '/base/material-warehouse-his/page',
method: 'post',
data: data,
});
}
export function createMaterialIssue(data) {
return request({
url: '/base/material-warehouse-his/create',
method: 'post',
data: data,
});
}

View File

@@ -1,45 +0,0 @@
import request from '@/utils/request'
export function getOrderPage(data) {
return request({
url: '/base/core-work-order/page',
method: 'post',
data: data,
});
}
export function updateOrderData(data) {
return request({
url: '/base/core-work-order/updatee',
method: 'put',
data: data,
});
}
export function createOrderData(data) {
return request({
url: '/base/core-work-order/create',
method: 'post',
data: data,
});
}
export function getOrderData(id) {
return request({
url: '/base/core-work-order/get?id=' + id,
method: 'get',
});
}
export function getOrderCode(data) {
return request({
url: '/base/core-work-order/getCode',
method: 'get',
data: data,
});
}
export function deleteOrder(id) {
return request({
url: '/base/core-work-order/delete?id=' + id,
method: 'delete'
})
}

View File

@@ -1 +1,18 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>全屏</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="首页" transform="translate(-1865.000000, -106.000000)" fill="#0B58FF">
<g id="全屏" transform="translate(1865.000000, 106.000000)">
<g>
<rect id="矩形" stroke="#0B58FF" opacity="0" x="0.5" y="0.5" width="31" height="31"></rect>
<path d="M27.3264942,0.842105263 L27.583814,0.850525866 C28.0103146,0.878577843 28.4244403,0.976634518 28.8180051,1.14344343 C29.2746777,1.33424853 29.6828981,1.60928508 30.0368065,1.96319349 C30.3881547,2.31454163 30.6645305,2.72554187 30.8562967,3.18137947 C31.0567273,3.65427262 31.1578947,4.15615812 31.1578947,4.6735058 L31.1578947,26.4843889 L31.1494741,26.7417088 C31.1214222,27.1682093 31.0233655,27.5823351 30.8565566,27.9758999 C30.6657515,28.4325724 30.3907149,28.8407928 30.0368065,29.1947013 C29.6854584,29.5460494 29.2744581,29.8224253 28.8186205,30.0141914 C28.3457274,30.214622 27.8438419,30.3157895 27.3264942,30.3157895 L5.51561106,30.3157895 L5.25829123,30.3073689 C4.83179065,30.2793169 4.41766492,30.1812602 4.02410014,30.0144513 C3.5674276,29.8236462 3.15920715,29.5486097 2.80529875,29.1947013 C2.45395061,28.8433531 2.17757472,28.4323529 1.98580856,27.9765153 C1.785378,27.5036221 1.68421053,27.0017366 1.68421053,26.4843889 L1.68421053,4.6735058 L1.69263113,4.41618596 C1.72068311,3.98968539 1.81873978,3.57555966 1.98554869,3.18199488 C2.17635379,2.72532234 2.45139035,2.31710189 2.80529875,1.96319349 C3.15664689,1.61184534 3.56764713,1.33546946 4.02348473,1.1437033 C4.49637788,0.943272735 4.99826338,0.842105263 5.51561106,0.842105263 L27.3264942,0.842105263 Z M27.3264942,2.75111508 L5.51561106,2.75111508 L5.36543907,2.75690192 C4.37431777,2.83358398 3.59322034,3.6628098 3.59322034,4.6735058 L3.59322034,26.4843889 L3.59900719,26.6345609 C3.67568924,27.6256822 4.50491506,28.4067797 5.51561106,28.4067797 L27.3264942,28.4067797 L27.4766662,28.4009928 C28.4677875,28.3243108 29.2488849,27.4950849 29.2488849,26.4843889 L29.2488849,4.6735058 L29.2430981,4.52333381 C29.166416,3.53221251 28.3371902,2.75111508 27.3264942,2.75111508 Z M6.93907808,16.8656959 L7.04823476,16.8742044 C7.51440654,16.9376019 7.87511151,17.3390325 7.87511151,17.8202498 L7.87511151,22.7419534 L12.5867439,18.0303211 L12.6763037,17.9513367 C13.0496528,17.6617272 13.593961,17.6880553 13.9362267,18.0303211 C14.3070145,18.4011089 14.3070145,19.009016 13.9362267,19.3798038 L9.18746648,24.1248885 L14.0860839,24.1248885 L14.2012238,24.1314959 C14.6923507,24.1882147 15.0747102,24.6029657 15.0673779,25.0991191 C15.056792,25.6176308 14.6323624,26.0338983 14.1128457,26.0338983 L6.94402319,26.0338983 L6.82978436,26.0273339 C6.34272021,25.9709676 5.96610169,25.5584576 5.96610169,25.0559768 L5.96610169,17.8436664 L5.97273726,17.7287345 C6.02968832,17.2384284 6.44595722,16.8559311 6.93907808,16.8656959 Z M25.9047725,5.12399643 L26.0180629,5.13054044 C26.501183,5.1867256 26.8760036,5.59778744 26.8760036,6.09522748 L26.8760036,13.3108831 L26.869368,13.425815 C26.8124169,13.9161211 26.396148,14.2986184 25.9030272,14.2888536 C25.3853261,14.278234 24.9669938,13.8525337 24.9669938,13.3342997 L24.9669938,8.4159413 L20.2553614,13.1275737 L20.1658016,13.2065581 C19.7924524,13.4961675 19.2481443,13.4698394 18.9058786,13.1275737 C18.5350907,12.7567858 18.5350907,12.1488788 18.9058786,11.7780909 L23.6509632,7.03300624 L18.7526762,7.03300624 L18.6375099,7.02640054 C18.1463449,6.96969821 17.7649507,6.5551 17.7747062,6.0600055 C17.7853132,5.54026395 18.2097429,5.12399643 18.7292596,5.12399643 L25.9047725,5.12399643 Z" id="形状结合" fill-rule="nonzero" opacity="0.79078311"></path>
</g>
<g>
<rect id="矩形" stroke="#0B58FF" opacity="0" x="0.5" y="0.5" width="31" height="31"></rect>
<path d="M27.3264942,0.842105263 L27.583814,0.850525866 C28.0103146,0.878577843 28.4244403,0.976634518 28.8180051,1.14344343 C29.2746777,1.33424853 29.6828981,1.60928508 30.0368065,1.96319349 C30.3881547,2.31454163 30.6645305,2.72554187 30.8562967,3.18137947 C31.0567273,3.65427262 31.1578947,4.15615812 31.1578947,4.6735058 L31.1578947,26.4843889 L31.1494741,26.7417088 C31.1214222,27.1682093 31.0233655,27.5823351 30.8565566,27.9758999 C30.6657515,28.4325724 30.3907149,28.8407928 30.0368065,29.1947013 C29.6854584,29.5460494 29.2744581,29.8224253 28.8186205,30.0141914 C28.3457274,30.214622 27.8438419,30.3157895 27.3264942,30.3157895 L5.51561106,30.3157895 L5.25829123,30.3073689 C4.83179065,30.2793169 4.41766492,30.1812602 4.02410014,30.0144513 C3.5674276,29.8236462 3.15920715,29.5486097 2.80529875,29.1947013 C2.45395061,28.8433531 2.17757472,28.4323529 1.98580856,27.9765153 C1.785378,27.5036221 1.68421053,27.0017366 1.68421053,26.4843889 L1.68421053,4.6735058 L1.69263113,4.41618596 C1.72068311,3.98968539 1.81873978,3.57555966 1.98554869,3.18199488 C2.17635379,2.72532234 2.45139035,2.31710189 2.80529875,1.96319349 C3.15664689,1.61184534 3.56764713,1.33546946 4.02348473,1.1437033 C4.49637788,0.943272735 4.99826338,0.842105263 5.51561106,0.842105263 L27.3264942,0.842105263 Z M27.3264942,2.75111508 L5.51561106,2.75111508 L5.36543907,2.75690192 C4.37431777,2.83358398 3.59322034,3.6628098 3.59322034,4.6735058 L3.59322034,26.4843889 L3.59900719,26.6345609 C3.67568924,27.6256822 4.50491506,28.4067797 5.51561106,28.4067797 L27.3264942,28.4067797 L27.4766662,28.4009928 C28.4677875,28.3243108 29.2488849,27.4950849 29.2488849,26.4843889 L29.2488849,4.6735058 L29.2430981,4.52333381 C29.166416,3.53221251 28.3371902,2.75111508 27.3264942,2.75111508 Z M6.93907808,16.8656959 L7.04823476,16.8742044 C7.51440654,16.9376019 7.87511151,17.3390325 7.87511151,17.8202498 L7.87511151,22.7419534 L12.5867439,18.0303211 L12.6763037,17.9513367 C13.0496528,17.6617272 13.593961,17.6880553 13.9362267,18.0303211 C14.3070145,18.4011089 14.3070145,19.009016 13.9362267,19.3798038 L9.18746648,24.1248885 L14.0860839,24.1248885 L14.2012238,24.1314959 C14.6923507,24.1882147 15.0747102,24.6029657 15.0673779,25.0991191 C15.056792,25.6176308 14.6323624,26.0338983 14.1128457,26.0338983 L6.94402319,26.0338983 L6.82978436,26.0273339 C6.34272021,25.9709676 5.96610169,25.5584576 5.96610169,25.0559768 L5.96610169,17.8436664 L5.97273726,17.7287345 C6.02968832,17.2384284 6.44595722,16.8559311 6.93907808,16.8656959 Z M25.9047725,5.12399643 L26.0180629,5.13054044 C26.501183,5.1867256 26.8760036,5.59778744 26.8760036,6.09522748 L26.8760036,13.3108831 L26.869368,13.425815 C26.8124169,13.9161211 26.396148,14.2986184 25.9030272,14.2888536 C25.3853261,14.278234 24.9669938,13.8525337 24.9669938,13.3342997 L24.9669938,8.4159413 L20.2553614,13.1275737 L20.1658016,13.2065581 C19.7924524,13.4961675 19.2481443,13.4698394 18.9058786,13.1275737 C18.5350907,12.7567858 18.5350907,12.1488788 18.9058786,11.7780909 L23.6509632,7.03300624 L18.7526762,7.03300624 L18.6375099,7.02640054 C18.1463449,6.96969821 17.7649507,6.5551 17.7747062,6.0600055 C17.7853132,5.54026395 18.2097429,5.12399643 18.7292596,5.12399643 L25.9047725,5.12399643 Z" id="形状结合" fill-rule="nonzero" opacity="0.79078311"></path>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>更新</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="首页" transform="translate(-474.000000, -116.000000)" fill="#0B58FF" fill-rule="nonzero">
<g id="更新" transform="translate(474.000000, 116.000000)">
<rect id="矩形" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M14.9793977,2.67910156 L13.9879914,3.45429687 C12.6362336,1.72636719 10.5338899,0.6171875 8.17314768,0.6171875 C4.09678049,0.6171875 0.797366431,3.91308594 0.792086694,7.99121094 C0.786819556,12.0728516 4.09326487,15.3828125 8.17314768,15.3828125 C11.3600617,15.3828125 14.0758821,13.3613281 15.1094758,10.5294922 C15.135843,10.4556641 15.0971711,10.3730469 15.023343,10.3484375 L14.0266633,10.0056641 C13.954593,9.98105469 13.8754914,10.0179688 13.8491242,10.0900391 C13.8174836,10.1779297 13.7823274,10.2658203 13.7454133,10.3519531 C13.4413117,11.0726562 13.0053742,11.7195312 12.4499055,12.275 C11.8944367,12.8304687 11.2475617,13.2664062 10.5286164,13.5722656 C9.78506174,13.8869141 8.99228831,14.046875 8.17666331,14.046875 C7.35928049,14.046875 6.56826487,13.8869141 5.82471018,13.5722656 C5.10576487,13.2681641 4.45888987,12.8322266 3.90342112,12.275 C3.34795237,11.7195312 2.91201487,11.0726562 2.60791331,10.3519531 C2.29326487,9.60664063 2.13330393,8.815625 2.13330393,7.99824219 C2.13330393,7.18085937 2.29326487,6.38984375 2.60791331,5.64453125 C2.91201487,4.92382812 3.34795237,4.27695312 3.90342112,3.72148438 C4.45888987,3.16601563 5.10576487,2.73007813 5.82471018,2.42421875 C6.56826487,2.10957031 7.36103831,1.94960938 8.17666331,1.94960938 C8.99404612,1.94960938 9.78506174,2.10957031 10.5286164,2.42421875 C11.2475617,2.72832031 11.8944367,3.16425781 12.4499055,3.72148438 C12.6239289,3.89550781 12.7874055,4.08007812 12.9385774,4.2734375 L11.8803742,5.09960938 C11.7872102,5.17167969 11.8188508,5.31933594 11.9331086,5.34746094 L15.0198274,6.10332031 C15.107718,6.12441406 15.1938508,6.05761719 15.1938508,5.96796875 L15.2079133,2.78808594 C15.2061555,2.67207031 15.0708039,2.60703125 14.9793977,2.67910156 L14.9793977,2.67910156 Z" id="路径" stroke="#0B58FF" stroke-width="0.5"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>推出全屏</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="首页" transform="translate(-1815.000000, -103.000000)" fill="#0B58FF">
<g id="推出全屏" transform="translate(1815.000000, 103.000000)">
<g id="全屏">
<rect id="矩形" stroke="#0B58FF" opacity="0" x="0.5" y="0.5" width="31" height="31"></rect>
<path d="M26.7638125,1.45454545 L27.0177905,1.4628567 C27.4387521,1.49054436 27.8474996,1.58732758 28.2359531,1.75197014 C28.6866948,1.94029725 29.0896137,2.2117619 29.4389259,2.56107409 C29.7857111,2.90785927 30.0584977,3.31352185 30.2477734,3.76343947 C30.4456009,4.23019115 30.5454545,4.72555866 30.5454545,5.23618754 L30.5454545,26.7638125 L30.5371433,27.0177905 C30.5094556,27.4387521 30.4126724,27.8474996 30.2480299,28.2359531 C30.0597028,28.6866948 29.7882381,29.0896137 29.4389259,29.4389259 C29.0921407,29.7857111 28.6864782,30.0584977 28.2365605,30.2477734 C27.7698088,30.4456009 27.2744413,30.5454545 26.7638125,30.5454545 L5.23618754,30.5454545 L4.98220952,30.5371433 C4.56124792,30.5094556 4.15250044,30.4126724 3.7640469,30.2480299 C3.31330516,30.0597028 2.91038628,29.7882381 2.56107409,29.4389259 C2.21428891,29.0921407 1.94150232,28.6864782 1.75222663,28.2365605 C1.55439906,27.7698088 1.45454545,27.2744413 1.45454545,26.7638125 L1.45454545,5.23618754 L1.4628567,4.98220952 C1.49054436,4.56124792 1.58732758,4.15250044 1.75197014,3.7640469 C1.94029725,3.31330516 2.2117619,2.91038628 2.56107409,2.56107409 C2.90785927,2.21428891 3.31352185,1.94150232 3.76343947,1.75222663 C4.23019115,1.55439906 4.72555866,1.45454545 5.23618754,1.45454545 L26.7638125,1.45454545 Z M26.7638125,3.33876293 L5.23618754,3.33876293 L5.08796584,3.34447462 C4.10971624,3.42016081 3.33876293,4.23861746 3.33876293,5.23618754 L3.33876293,26.7638125 L3.34447462,26.9120342 C3.42016081,27.8902838 4.23861746,28.6612371 5.23618754,28.6612371 L26.7638125,28.6612371 L26.9120342,28.6555254 C27.8902838,28.5798392 28.6612371,27.7613825 28.6612371,26.7638125 L28.6612371,5.23618754 L28.6555254,5.08796584 C28.5798392,4.10971624 27.7613825,3.33876293 26.7638125,3.33876293 Z M13.6987866,17.269857 L13.8115418,17.2763362 C14.2922804,17.3319705 14.6640078,17.7391232 14.6640078,18.2350783 L14.6640078,25.3537223 L14.6574584,25.4671616 C14.6012469,25.9511001 14.1903841,26.3286298 13.7036674,26.3189919 L13.5959284,26.3105939 C13.1358108,26.2480197 12.7797903,25.8518025 12.7797903,25.3768348 L12.7797903,20.5190494 L8.12934794,25.1694917 L8.04095127,25.2474504 C7.67245079,25.5332987 7.13521162,25.5073124 6.79739091,25.1694917 C6.43141847,24.8035193 6.43141847,24.2035071 6.79739091,23.8375347 L11.4844789,19.1540745 L6.64947993,19.1540745 L6.53583534,19.1475529 C6.05108664,19.0915708 5.67369292,18.6822061 5.68093001,18.1924962 C5.69137836,17.6807185 6.11029591,17.269857 6.62306566,17.269857 L13.6987866,17.269857 Z M18.2962333,5.68100813 C18.807211,5.69148979 19.2201105,6.11166149 19.2201105,6.62316519 L19.2201105,11.4776488 L23.8705528,6.82720649 L23.9589495,6.74924786 C24.32745,6.46339956 24.8646892,6.48938577 25.2025099,6.82720649 C25.5684823,7.19317893 25.5684823,7.79319108 25.2025099,8.15916352 L20.5190497,12.8426237 L25.3537226,12.8426237 L25.4673933,12.8491436 C25.9521795,12.9051095 26.3286206,13.3143234 26.3189917,13.8029881 C26.3085224,14.3159797 25.8896049,14.7268412 25.3768351,14.7268412 L18.2945107,14.7268412 L18.1826915,14.7203822 C17.7058457,14.6649267 17.335893,14.2592033 17.335893,13.7682235 L17.335893,6.64627767 L17.3424424,6.53283844 C17.3986538,6.04889994 17.8095166,5.67137018 18.2962333,5.68100813 Z" id="形状结合" fill-rule="nonzero" opacity="0.79078311"></path>
</g>
<g id="全屏">
<rect id="矩形" stroke="#0B58FF" opacity="0" x="0.5" y="0.5" width="31" height="31"></rect>
<path d="M26.7638125,1.45454545 L27.0177905,1.4628567 C27.4387521,1.49054436 27.8474996,1.58732758 28.2359531,1.75197014 C28.6866948,1.94029725 29.0896137,2.2117619 29.4389259,2.56107409 C29.7857111,2.90785927 30.0584977,3.31352185 30.2477734,3.76343947 C30.4456009,4.23019115 30.5454545,4.72555866 30.5454545,5.23618754 L30.5454545,26.7638125 L30.5371433,27.0177905 C30.5094556,27.4387521 30.4126724,27.8474996 30.2480299,28.2359531 C30.0597028,28.6866948 29.7882381,29.0896137 29.4389259,29.4389259 C29.0921407,29.7857111 28.6864782,30.0584977 28.2365605,30.2477734 C27.7698088,30.4456009 27.2744413,30.5454545 26.7638125,30.5454545 L5.23618754,30.5454545 L4.98220952,30.5371433 C4.56124792,30.5094556 4.15250044,30.4126724 3.7640469,30.2480299 C3.31330516,30.0597028 2.91038628,29.7882381 2.56107409,29.4389259 C2.21428891,29.0921407 1.94150232,28.6864782 1.75222663,28.2365605 C1.55439906,27.7698088 1.45454545,27.2744413 1.45454545,26.7638125 L1.45454545,5.23618754 L1.4628567,4.98220952 C1.49054436,4.56124792 1.58732758,4.15250044 1.75197014,3.7640469 C1.94029725,3.31330516 2.2117619,2.91038628 2.56107409,2.56107409 C2.90785927,2.21428891 3.31352185,1.94150232 3.76343947,1.75222663 C4.23019115,1.55439906 4.72555866,1.45454545 5.23618754,1.45454545 L26.7638125,1.45454545 Z M26.7638125,3.33876293 L5.23618754,3.33876293 L5.08796584,3.34447462 C4.10971624,3.42016081 3.33876293,4.23861746 3.33876293,5.23618754 L3.33876293,26.7638125 L3.34447462,26.9120342 C3.42016081,27.8902838 4.23861746,28.6612371 5.23618754,28.6612371 L26.7638125,28.6612371 L26.9120342,28.6555254 C27.8902838,28.5798392 28.6612371,27.7613825 28.6612371,26.7638125 L28.6612371,5.23618754 L28.6555254,5.08796584 C28.5798392,4.10971624 27.7613825,3.33876293 26.7638125,3.33876293 Z M13.6987866,17.269857 L13.8115418,17.2763362 C14.2922804,17.3319705 14.6640078,17.7391232 14.6640078,18.2350783 L14.6640078,25.3537223 L14.6574584,25.4671616 C14.6012469,25.9511001 14.1903841,26.3286298 13.7036674,26.3189919 L13.5959284,26.3105939 C13.1358108,26.2480197 12.7797903,25.8518025 12.7797903,25.3768348 L12.7797903,20.5190494 L8.12934794,25.1694917 L8.04095127,25.2474504 C7.67245079,25.5332987 7.13521162,25.5073124 6.79739091,25.1694917 C6.43141847,24.8035193 6.43141847,24.2035071 6.79739091,23.8375347 L11.4844789,19.1540745 L6.64947993,19.1540745 L6.53583534,19.1475529 C6.05108664,19.0915708 5.67369292,18.6822061 5.68093001,18.1924962 C5.69137836,17.6807185 6.11029591,17.269857 6.62306566,17.269857 L13.6987866,17.269857 Z M18.2962333,5.68100813 C18.807211,5.69148979 19.2201105,6.11166149 19.2201105,6.62316519 L19.2201105,11.4776488 L23.8705528,6.82720649 L23.9589495,6.74924786 C24.32745,6.46339956 24.8646892,6.48938577 25.2025099,6.82720649 C25.5684823,7.19317893 25.5684823,7.79319108 25.2025099,8.15916352 L20.5190497,12.8426237 L25.3537226,12.8426237 L25.4673933,12.8491436 C25.9521795,12.9051095 26.3286206,13.3143234 26.3189917,13.8029881 C26.3085224,14.3159797 25.8896049,14.7268412 25.3768351,14.7268412 L18.2945107,14.7268412 L18.1826915,14.7203822 C17.7058457,14.6649267 17.335893,14.2592033 17.335893,13.7682235 L17.335893,6.64627767 L17.3424424,6.53283844 C17.3986538,6.04889994 17.8095166,5.67137018 18.2962333,5.68100813 Z" id="形状结合" fill-rule="nonzero" opacity="0.79078311"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

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

@@ -1,16 +1,30 @@
<!--
filename: index.vue
author: liubin
date: 2024-04-02 09:49:36
description:
-->
<template>
<!-- 按钮切换 -->
<div v-if="buttonMode" class="button-nav">
<button v-for="m in menus" :key="m" @click="handleClick(m)"
<button
v-for="m in menus"
:key="m"
@click="currentMenu = m"
:data-text="m"
:class="[m === currentMenu ? 'active' : '']"
></button>
></button>
</div>
<!-- 标签切换 -->
<div v-else class="custom-tabs" style="height: 100%; width: 100%">
<el-tabs class="tag-nav" v-model="currentMenu" style="height: 100%" @tab-click="handleTabClick">
<el-tab-pane v-for="(m, idx) in menus" :key="m" :label="idx == 0 ? `\u2002${m}\u2002` : `\u3000${m}\u3000`"
:name="m">
<el-tabs class="tag-nav" v-model="currentMenu" style="height: 100%">
<el-tab-pane
v-for="(m, idx) in menus"
:key="m"
:label="idx == 0 ? `\u2002${m}\u2002` : `\u3000${m}\u3000`"
:name="m"
>
<slot :name="`tab${idx + 1}`"></slot>
</el-tab-pane>
</el-tabs>
@@ -25,17 +39,14 @@ export default {
type: Array,
required: true,
default: () => [],
validator: (val) => val.length > 0,
validator: (val) => {
return val.length > 0;
},
},
buttonMode: {
type: Boolean,
default: true,
},
// 新增支持外部传入选中值v-model 绑定)
value: {
type: String,
default: "",
},
},
data() {
return {
@@ -43,34 +54,16 @@ export default {
};
},
created() {
// 优先使用外部传入的 value否则默认第一个
this.currentMenu = this.value || this.menus[0];
this.currentMenu = this.menus[0];
},
watch: {
// 监听外部 value 变化,同步到内部 currentMenu
value(newVal) {
this.currentMenu = newVal;
},
// 监听内部 currentMenu 变化,通知外部
currentMenu(val) {
this.$emit("input", val); // 触发 v-model 同步
this.$emit("change", val); // 保留原 change 事件
},
},
methods: {
// 按钮点击事件
handleClick(m) {
this.currentMenu = m;
},
// 标签点击事件el-tabs 自带)
handleTabClick(tab) {
this.currentMenu = tab.name;
this.$emit("change", val);
},
},
};
</script>
<!-- 样式不变 -->
<style scoped lang="scss">
.button-nav {
width: 100%;
@@ -88,7 +81,7 @@ export default {
border: none;
background: #fff;
border-radius: 8px;
padding: 15px;
padding: 16px;
color: #888;
letter-spacing: 2px;
flex: 1;
@@ -115,24 +108,22 @@ export default {
</style>
<style scoped>
.custom-tabs>>>.el-tabs__header {
.custom-tabs >>> .el-tabs__header {
margin-bottom: 8px;
display: inline-block;
/* transform: translateY(-12px); */
}
.custom-tabs>>>.el-tabs__item {
.custom-tabs >>> .el-tabs__item {
padding-left: 0px !important;
padding-right: 0px !important;
line-height: 36px !important;
height: 36px;
}
.custom-tabs>>>.el-tabs__content {
.custom-tabs >>> .el-tabs__content {
height: calc(100% - 42px);
}
.custom-tabs>>>.el-tab-pane {
.custom-tabs >>> .el-tab-pane {
box-sizing: border-box;
height: 100%;
padding: 20px;

View File

@@ -9,6 +9,7 @@ import store from './store';
import router from './router';
import directive from './directive'; // directive
import plugins from './plugins'; // plugins
import dataV from '@jiaminghi/data-view'
import './assets/icons'; // icon
import './permission'; // permission control
@@ -77,6 +78,7 @@ Vue.use(directive);
Vue.use(plugins);
Vue.use(VueMeta);
// Vue.use(hljs.vuePlugin);
Vue.use(dataV);
// bpmnProcessDesigner 需要引入
import MyPD from '@/components/bpmnProcessDesigner/package/index.js';

View File

@@ -76,7 +76,7 @@ export const constantRoutes = [
children: [
{
path: "index",
component: (resolve) => require(["@/views/core/base/factory/index"], resolve),
component: (resolve) => require(["@/views/home/index"], resolve),
name: "首页",
meta: { title: "首页", icon: "dashboard", affix: true },
hidden: true

View File

@@ -80,8 +80,18 @@ export default {
immediate: true,
},
},
methods: {
initChart() {
methods: {
getUniqueTimes() {
const { edgeCt, temperCt, downCt } = this.barData;
// 合并所有包含时间的数组
const allTimeEntries = [...(edgeCt || []), ...(temperCt || []), ...(downCt || [])];
// 提取时间戳并去重(使用 Set
const uniqueTimes = [...new Set(allTimeEntries.map(item => item.recordTime))];
// 按时间戳排序(确保时间顺序正确)
return uniqueTimes.sort((a, b) => a - b);
},
initChart() {
const uniqueTimes = this.getUniqueTimes();
const _this = this;
this.chart.setOption({
title: {
@@ -124,9 +134,7 @@ export default {
},
xAxis: {
type: 'category',
data: this.barData.edgeCt.map((item) => {
return parseTime(item.recordTime, '{m}-{d} {h}:{i}');
}),
data: uniqueTimes.map(time => parseTime(time, '{m}-{d} {h}:{i}')),
axisPointer: {
type: 'shadow',
},
@@ -152,44 +160,50 @@ export default {
end: 100,
},
],
series: [
{
name: '磨边节拍',
type: 'line',
tooltip: {
valueFormatter: function (value) {
return value + 'pcs/min';
},
},
data: this.barData.edgeCt.map((item) => {
return item.ct;
}),
},
{
name: '钢化节拍',
type: 'line',
tooltip: {
valueFormatter: function (value) {
return value + 'pcs/min';
},
},
data: this.barData.temperCt.map((item) => {
return item.ct;
}),
},
{
name: '下片节拍',
type: 'line',
tooltip: {
valueFormatter: function (value) {
return value + 'pcs/min';
},
},
data: this.barData.downCt.map((item) => {
return item.ct;
}),
},
],
series: [
{
name: '磨边节拍',
type: 'line',
tooltip: {
valueFormatter: function (value) {
return value + 'pcs/min';
},
},
data: uniqueTimes.map(time => {
// 查找当前时间对应的 ct 值,没有则补 null图表中会显示为断点
const match = this.barData.edgeCt.find(item => item.recordTime === time);
return match ? match.ct : 0;
})
},
// 钢化节拍
{
name: '钢化节拍',
type: 'line',
tooltip: {
valueFormatter: function (value) {
return value + 'pcs/min';
},
},
data: uniqueTimes.map(time => {
const match = this.barData.temperCt.find(item => item.recordTime === time);
return match ? match.ct : 0;
})
},
// 下片节拍
{
name: '下片节拍',
type: 'line',
tooltip: {
valueFormatter: function (value) {
return value + 'pcs/min';
},
},
data: uniqueTimes.map(time => {
const match = this.barData.downCt.find(item => item.recordTime === time);
return match ? match.ct : 0;
})
}
]
});
},
},

View File

@@ -119,12 +119,12 @@ export default {
},
methods: {
// 获取数据列表
init(lineId, startTime, endTime) {
init(lId, startTime, endTime) {
this.eqChartData = [];
this.time.startTime = startTime;
this.time.endTime = endTime;
this.dataListLoading = true;
getNewCTDet(lineId).then((response) => {
getNewCTDet({ lineId: [lId], startTime, endTime }).then((response) => {
this.tableData = response.data;
this.dataListLoading = false;
});

View File

@@ -183,7 +183,7 @@ export default {
// 获取当前时间
const now = new Date();
// 获取前一天的同一时间
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
const yesterday = new Date(now.getTime());
// 设置为00:00:00
yesterday.setHours(0, 0, 0, 0);
// 设置为23:59:59

View File

@@ -80,7 +80,7 @@ export default {
defaultSelect: [],
multiple: true,
filterable: true,
width: 400,
width: 200,
},
{
type: 'datePicker',

View File

@@ -1,157 +1,104 @@
<template>
<el-drawer
:visible.sync="visible"
:show-close="false"
:wrapper-closable="false"
class="drawer"
size="60%">
<small-title slot="title" :no-padding="true">
{{ isdetail ? '详情' : !dataForm.id ? '新增' : '编辑' }}
</small-title>
<el-drawer :visible.sync="visible" :show-close="false" :wrapper-closable="false" class="drawer" size="60%">
<small-title slot="title" :no-padding="true">
{{ isdetail ? '详情' : !dataForm.id ? '新增' : '编辑' }}
</small-title>
<div class="content">
<div class="visual-part">
<el-form
ref="dataForm"
:model="dataForm"
:rules="dataRule"
label-width="100px"
label-position="top"
@keyup.enter.native="dataFormSubmit">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产品编码" prop="code">
<el-input
v-model="dataForm.code"
clearable
:disabled="isdetail"
placeholder="请输入产品编码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品名称" prop="name">
<el-input
v-model="dataForm.name"
clearable
:disabled="isdetail"
placeholder="请输入产品名称" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产品类型" prop="typeDictValue">
<el-select
v-model="dataForm.typeDictValue"
style="width: 100%"
:disabled="isdetail"
placeholder="请选择产品类型">
<el-option
v-for="dict in getDictDatas(DICT_TYPE.PRODUCT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位" prop="unitDictValue">
<el-select
v-model="dataForm.unitDictValue"
style="width: 100%"
:disabled="isdetail"
placeholder="请选择单位">
<el-option
v-for="dict in getDictDatas(DICT_TYPE.UNIT_DICT)"
:key="dict.value"
:label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="原片规格" prop="originalSpecifications">
<el-input
:disabled="isdetail"
v-model="dataForm.originalSpecifications"
placeholder="请输入原片规格" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="原片单位平方数" prop="originalArea">
<el-input
:disabled="isdetail"
v-model="dataForm.originalArea"
placeholder="请输入原片单位平方数" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="深加工规格" prop="specifications">
<el-input
:disabled="isdetail"
v-model="dataForm.specifications"
placeholder="请输入深加工规格" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="深加工单位平方数" prop="area">
<el-input
:disabled="isdetail"
v-model="dataForm.area"
placeholder="请输入深加工单位平方数" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="完成单位产品用时" prop="processTime">
<el-input
:disabled="isdetail"
v-model="dataForm.processTime"
placeholder="请输入完成单位产品用时" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="content">
<div class="visual-part">
<el-form ref="dataForm" :model="dataForm" :rules="dataRule" label-width="100px" label-position="top"
@keyup.enter.native="dataFormSubmit">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产品编码" prop="code">
<el-input v-model="dataForm.code" clearable :disabled="isdetail" placeholder="请输入产品编码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品名称" prop="name">
<el-input v-model="dataForm.name" clearable :disabled="isdetail" placeholder="请输入产品名称" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产品类型" prop="typeDictValue">
<el-select v-model="dataForm.typeDictValue" style="width: 100%" :disabled="isdetail"
placeholder="请选择产品类型">
<el-option v-for="dict in getDictDatas(DICT_TYPE.PRODUCT_TYPE)" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位" prop="unitDictValue">
<el-select v-model="dataForm.unitDictValue" style="width: 100%" :disabled="isdetail"
placeholder="请选择单位">
<el-option v-for="dict in getDictDatas(DICT_TYPE.UNIT_DICT)" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="原片规格" prop="originalSpecifications">
<el-input :disabled="isdetail" v-model="dataForm.originalSpecifications" placeholder="请输入原片规格" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="原片单位平方数" prop="originalArea">
<el-input :disabled="isdetail" v-model="dataForm.originalArea" placeholder="请输入原片单位平方数" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="深加工规格" prop="specifications">
<el-input :disabled="isdetail" v-model="dataForm.specifications" placeholder="请输入深加工规格" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="深加工单位平方数" prop="area">
<el-input :disabled="isdetail" v-model="dataForm.area" placeholder="请输入深加工单位平方数" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="完成单位产品用时" prop="processTime">
<el-input :disabled="isdetail" v-model="dataForm.processTime" placeholder="请输入完成单位产品用时" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label=" 加工属性" prop="processType">
<el-select v-model="dataForm.processType" clearable style="width: 100%" :disabled="isdetail"
placeholder="请选择加工属性">
<el-option v-for="dict in processTypeList" :key="dict.id" :label="dict.label"
:value="dict.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
<small-title
style="margin: 16px 0; padding-left: 8px"
:no-padding="true">
产品属性列表
</small-title>
<small-title style="margin: 16px 0; padding-left: 8px" :no-padding="true">
产品属性列表
</small-title>
<div class="attr-list">
<base-table
:table-props="tableProps"
:page="listQuery.pageNo"
:limit="listQuery.pageSize"
:add-button-show="isdetail ? null : '添加属性'"
@emitButtonClick="addNew()"
:table-data="productAttributeList">
<method-btn
v-if="!isdetail"
slot="handleBtn"
:width="120"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination
v-show="listQuery.total > 0"
:total="listQuery.total"
:page.sync="listQuery.pageNo"
:limit.sync="listQuery.pageSize"
:page-sizes="[5, 10, 15]"
@pagination="getList" />
</div>
</div>
</div>
<div class="attr-list">
<base-table :table-props="tableProps" :page="listQuery.pageNo" :limit="listQuery.pageSize"
:add-button-show="isdetail ? null : '添加属性'" @emitButtonClick="addNew()" :table-data="productAttributeList">
<method-btn v-if="!isdetail" slot="handleBtn" :width="120" label="操作" :method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<pagination v-show="listQuery.total > 0" :total="listQuery.total" :page.sync="listQuery.pageNo"
:limit.sync="listQuery.pageSize" :page-sizes="[5, 10, 15]" @pagination="getList" />
</div>
</div>
</div>
<!-- <div style="position: absolute; bottom: 24px; right: 24px">
<!-- <div style="position: absolute; bottom: 24px; right: 24px">
<el-button style="margin-right: 10px" @click="goback()">返回</el-button>
<el-button v-if="isdetail" type="primary" @click="goEdit()">
编辑
@@ -167,22 +114,19 @@
</span>
</div> -->
<div class="drawer-body__footer">
<el-button style="" @click="goback()">取消</el-button>
<el-button v-if="isdetail" type="primary" @click="goEdit()">
编辑
</el-button>
<el-button v-else type="primary" @click="dataFormSubmit()">
确定
</el-button>
</div>
<div class="drawer-body__footer">
<el-button style="" @click="goback()">取消</el-button>
<el-button v-if="isdetail" type="primary" @click="goEdit()">
编辑
</el-button>
<el-button v-else type="primary" @click="dataFormSubmit()">
确定
</el-button>
</div>
<product-attr-add
v-if="addOrUpdateVisible"
ref="addOrUpdate"
:product-id="dataForm.id"
@refreshDataList="getList" />
</el-drawer>
<product-attr-add v-if="addOrUpdateVisible" ref="addOrUpdate" :product-id="dataForm.id"
@refreshDataList="getList" />
</el-drawer>
</template>
<script>
@@ -246,13 +190,31 @@ export default {
specifications: '', // 深加工规格
unitDictValue: '', // 单位id
originalSpecifications: '', // 原片规格
originalArea: 0, // 原片单位平方数
originalArea: 0, // 原片单位平方数
processType:undefined,
},
listQuery: {
pageSize: 10,
pageNo: 1,
total: 0,
},
},
processTypeList: [
{
id: '0',
label:'压花丝印'
},
{
id: '1',
label: '无印打孔'
},
{
id: '2',
label: '单层镀膜'
}, {
id: '3',
label: '双层镀膜'
}
],
dataRule: {
code: [
{

View File

@@ -6,31 +6,17 @@
* @Description:
-->
<template>
<div class="app-container">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick" />
<base-table
v-if="showData.length"
class="right-aside"
v-loading="dataListLoading"
:table-props="tableProps"
:page="listQuery.pageNo"
:limit="listQuery.pageSize"
:selectWidth="55"
:table-data="showData"
@selection-change="selectChange"
>
</base-table>
<div v-else class="no-data-bg"></div>
<pagination
:limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo"
:total="listQuery.total"
@pagination="getDataList"
/>
<!-- <div v-show="false" ref="pdf">
<div class="app-container">
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<base-table v-if="showData.length" class="right-aside" v-loading="dataListLoading" :table-props="tableProps"
:page="listQuery.pageNo" :limit="listQuery.pageSize" :selectWidth="55" :table-data="showData"
@selection-change="selectChange">
</base-table>
<div v-else class="no-data-bg"></div>
<pagination :limit.sync="listQuery.pageSize" :page.sync="listQuery.pageNo" :total="listQuery.total"
@pagination="getDataList" />
<!-- <div v-show="false" ref="pdf">
<base-table
v-loading="dataListLoading"
:table-props="tableProps"
@@ -39,19 +25,15 @@
:table-data="selectedList"
/>
</div> -->
<el-dialog
title="提示"
:visible.sync="dialogVisible"
width="30%"
:before-close="handleClose">
<el-button type="primary" @click="exportXlsx">xlsx</el-button>
<el-button type="success" @click="exportPdf">pdf</el-button>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="dialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
<el-dialog title="提示" :visible.sync="dialogVisible" width="30%" :before-close="handleClose">
<el-button type="primary" @click="exportXlsx">xlsx</el-button>
<el-button type="success" @click="exportPdf">pdf</el-button>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="dialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
@@ -203,7 +185,8 @@ export default {
}
],
};
},
},
created() {
this.getDataList()
this.getPdLineList()

View File

@@ -0,0 +1,132 @@
<!--
* @Author: zhp
* @Date: 2024-10-21 08:43:35
* @LastEditTime: 2024-10-21 09:10:09
* @LastEditors: zhp
* @Description: Vue2版本的标签切换组件
-->
<template>
<!-- 按钮切换 -->
<div v-if="buttonMode" class="button-nav">
<button v-for="m in menus" :key="m" :class="{ active: m === currentMenu }" :data-text="m"
@click="handleMenuChange(m)"></button>
</div>
<!-- 标签切换 -->
<div v-else class="custom-tabs" style="height: 100%; width: 100%">
<el-tabs v-model="currentMenu" class="tag-nav" style="height: 100%">
<el-tab-pane v-for="(m, idx) in menus" :key="m" :label="idx == 0 ? `\u2002${m}\u2002` : `\u3000${m}\u3000`"
:name="m">
<slot :name="`tab${idx + 1}`"></slot>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
export default {
props: {
menus: {
type: Array,
required: true,
default: () => [],
validator(val) {
return val.length > 0;
}
},
buttonMode: {
type: Boolean,
default: true
}
},
data() {
return {
currentMenu: this.menus[0] || ''
};
},
methods: {
handleMenuChange(menu) {
this.currentMenu = menu;
this.$emit('change', menu);
}
},
watch: {
currentMenu(val) {
this.$emit('change', val);
}
},
mounted() {
if (this.menus.length > 0) {
this.currentMenu = this.menus[0];
}
}
};
</script>
<style scoped>
.button-nav {
width: 100%;
padding: 12px 0;
display: flex;
gap: 12px;
}
.button-nav * {
user-select: none;
}
.button-nav button {
cursor: pointer;
appearance: none;
outline: none;
border: none;
background: #fff;
border-radius: 8px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
padding: 20px;
color: #888;
letter-spacing: 2px;
flex: 1;
box-sizing: padding-box;
position: relative;
}
.button-nav button::after {
content: attr(data-text);
position: absolute;
top: 10px;
left: 50%;
font-size: 18px;
font-weight: 500;
transform: translate(-50%);
}
.button-nav button.active {
color: #111;
border-bottom: 4px solid #0b58ff;
}
.custom-tabs /deep/ .el-tabs__header {
margin-bottom: 8px;
display: inline-block;
}
.custom-tabs /deep/ .el-tabs__item {
padding-left: 0px !important;
padding-right: 0px !important;
line-height: 36px !important;
height: 36px;
}
.custom-tabs /deep/ .el-tabs__content {
height: calc(100% - 42px);
}
.custom-tabs /deep/ .el-tab-pane {
box-sizing: border-box;
height: 100%;
padding: 20px;
border: 10px solid #f002;
}
</style>

View File

@@ -1,47 +1,61 @@
<template>
<div>
<div style="background: #f2f4f9; height: 40px; width: 100%">
<ButtonNav :menus="['当前', '历史']" @change="currentMenu" v-model="activeName">
<!-- <template v-slot:tab1>
<div>当前</div>
</template>
<template v-slot:tab2>
<div>历史</div>
</template> -->
</ButtonNav>
</div>
<div class="app-container energyOverlimitLog">
<div v-if="activeName === '历史' && routeType !== 'order'">
<!-- 搜索工作栏 -->
<search-bar :formConfigs="formConfig" ref="searchBarForm" @select-changed="handleSearchBarChanged"
@headBtnClick="buttonClick" />
</div>
<h2 v-else>
订单名称:{{ name }}
订单编码:{{ code }}
<el-button v-hasPermi="['base:core-work-order:export']" type="primary" @click="handleExport">导出</el-button>
</h2>
<div v-show="activeName === '当前'"></div>
<!-- 列表 -->
<div v-if="activeName === '历史'">
<base-table-s :page="1" :limit="100" :table-props="tableProps" :table-data="tableData"
:max-height="tableH / 2"></base-table-s>
<div v-if="listQuery.lineId.length !== 1 && tableData.length > 0">
<barChart style="margin-top: 50px" ref="barChart" height="600px" :bar-data="tableData" />
</div>
</div>
<div v-if="activeName === '当前'">
<span class="blue-block"></span>
<span class="tip">当班数据</span>
<base-table-s :page="1" :limit="100" :table-props="tableProps" :table-data="tableData2"
:max-height="tableH / 2" />
<span class="blue-block"></span>
<span class="tip">当天数据</span>
<base-table-s :page="1" :limit="100" :table-props="tableProps" :table-data="tableData3"
:max-height="tableH / 2" />
</div>
</div>
</div>
<div>
<div style="background: #f2f4f9; height: 40px; width: 100%">
<ButtonNav :menus="['当前', '历史']" @change="currentMenu">
<template v-slot:tab1>
<div>当前</div>
</template>
<template v-slot:tab2>
<div>历史</div>
</template>
</ButtonNav>
</div>
<div class="app-container energyOverlimitLog">
<div v-show="activeName === 'his'">
<!-- 搜索工作栏 -->
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@select-changed="handleSearchBarChanged"
@headBtnClick="buttonClick" />
</div>
<div v-show="activeName === 'now'"></div>
<!-- 列表 -->
<div v-if="activeName === 'his'">
<base-table-s
:page="1"
:limit="100"
:table-props="tableProps"
:table-data="tableData"
:max-height="tableH / 2"></base-table-s>
<div v-if="listQuery.lineId.length !== 1 && tableData.length > 0">
<barChart
style="margin-top: 50px"
ref="barChart"
height="600px"
:bar-data="tableData" />
</div>
</div>
<div v-if="activeName === 'now'">
<span class="blue-block"></span>
<span class="tip">当班数据</span>
<base-table-s
:page="1"
:limit="100"
:table-props="tableProps"
:table-data="tableData2"
:max-height="tableH / 2" />
<span class="blue-block"></span>
<span class="tip">当天数据</span>
<base-table-s
:page="1"
:limit="100"
:table-props="tableProps"
:table-data="tableData3"
:max-height="tableH / 2" />
</div>
</div>
</div>
</template>
<script>
@@ -81,7 +95,7 @@ const tableProps = [
prop: 'inputN',
label: '投入',
align: 'center',
children: [
children: [
{
prop: 'inputNum',
label: '投入数量/片',
@@ -266,10 +280,7 @@ export default {
return {
urlOptions: {
getDataListURL: getPdlAutoReportNewSearch,
},
routeType: 'line',
code: '',
name:'',
},
formConfig: [
{
type: 'select',
@@ -283,8 +294,7 @@ export default {
label: '产线',
selectOptions: [],
param: 'lineId',
multiple: true,
defaultSelect:[]
multiple: true,
},
{
type: 'datePicker',
@@ -295,8 +305,7 @@ export default {
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'timeVal',
defaultSelect:[],
param: 'timeVal',
width: 350,
},
{
@@ -309,8 +318,8 @@ export default {
type: 'separate',
},
{
type: this.$auth.hasPermi('|base:core-work-order:export') ? 'button' : '',
// type: 'button',
// type: this.$auth.hasPermi('base:factory:export') ? 'button' : '',
type: 'button',
btnName: '导出',
name: 'export',
color: 'warning',
@@ -319,7 +328,7 @@ export default {
listQuery: {
lineId: [],
},
activeName: '当前',
activeName: 'now',
tableProps,
tableData: [],
tableData2: [],
@@ -331,32 +340,10 @@ export default {
baseTableS,
barChart,
},
mounted() {
this.getPdLineList()
this.$nextTick(() => {
const tab = this.$route.query.tab; // 获取路由参数
console.log(this.$route.query);
this.routeType = this.$route.query.buttonType === 'order' ? 'order' : 'line'
this.code = this.$route.query.code
this.name = this.$route.query.name
if (tab === 'his') {
// 路由参数为 his 时,切换到“历史”标签
this.activeName = '历史'; // 注意:这里直接传标签名('历史'),不是'his'
this.formConfig[1].defaultSelect = this.$route.query.lineId
this.formConfig[2].defaultSelect[0] = this.$route.query.startProduceTime
this.formConfig[2].defaultSelect[1] = this.$route.query.finishProduceTime
// this.buttonClick()
this.listQuery.lineId = this.$route.query.lineId
} else {
this.activeName = '当前'; // 默认“当前”标签
}
})
// ... 其他方法 ...
},
methods: {
created() {
this.getPdLineList();
},
methods: {
getPdLineList() {
getPdList().then((res) => {
this.formConfig[1].selectOptions = res.data || [];
@@ -376,7 +363,7 @@ export default {
this.listQuery.lineId = val.lineId || [];
this.listQuery.startTime = val.timeVal ? val.timeVal[0] : undefined;
this.listQuery.endTime = val.timeVal ? val.timeVal[1] : undefined;
if (this.activeName === '历史') {
if (this.activeName === 'his') {
this.getDataList();
} else {
this.getDataList2();
@@ -388,19 +375,13 @@ export default {
default:
console.log(val);
}
},
currentMenu(val) {
this.activeName = val; // 同步标签名到 activeName
let actualName = val === '历史' ? 'his' : 'now'; // 转换为内部状态
// 后续逻辑不变(根据 activeName 加载对应数据)
if (actualName === 'his') {
// this.$refs.searchBarForm.resetForm();
this.listQuery.factoryId = undefined;
if (!this.$route.query.lineId) {
this.listQuery.lineId = [];
}
},
currentMenu(val) {
this.activeName = val === '历史' ? 'his' : 'now';
if (this.activeName === 'his') {
this.$refs.searchBarForm.resetForm();
this.listQuery.factoryId = undefined;
this.listQuery.lineId = [];
// 获取当前时间
const now = new Date();
// 获取前一天的同一时间
@@ -423,98 +404,39 @@ export default {
this.getDataList2();
}
},
getDataList() {
if (this.listQuery.startTime) {
this.dataListLoading = true;
// 判断是否为order类型且lineId有值
if (this.routeType === 'order' && this.listQuery.lineId?.length) {
// 存储所有接口请求的Promise
const requests = this.listQuery.lineId.map(lineId => {
// 复制原有查询参数替换当前lineId
const params = { ...this.listQuery, lineId: [lineId] };
return this.urlOptions.getDataListURL(params);
});
// 并行执行所有请求
Promise.all(requests)
.then(responses => {
// 合并所有接口返回的数据
this.tableData = responses.flatMap(response => {
return response.data.map(item => {
// 处理嵌套属性
item.originalLossNum = item.original?.lossNum;
item.originalLossArea = item.original?.lossArea;
item.edgeLossNum = item.edge?.lossNum;
item.edgeLossArea = item.edge?.lossArea;
item.drillLossNum = item.drill?.lossNum;
item.drillLossArea = item.drill?.lossArea;
item.coatingLossNum = item.coating?.lossNum;
item.coatingLossArea = item.coating?.lossArea;
item.silkLossNum = item.silk?.lossNum;
item.silkLossArea = item.silk?.lossArea;
item.temperingLossNum = item.tempering?.lossNum;
item.temperingLossArea = item.tempering?.lossArea;
item.packingLossNum = item.packing?.lossNum;
item.packingLossArea = item.packing?.lossArea;
return item;
});
});
this.$nextTick(() => {
if (this.tableData.length > 0) {
this.$refs.barChart.initChart();
}
});
})
.catch(error => {
console.error('接口请求失败:', error);
this.$message.error('数据加载失败,请重试');
})
.finally(() => {
this.dataListLoading = false;
});
} else {
// 非order类型或无lineId时正常单次请求
this.urlOptions.getDataListURL(this.listQuery)
.then(response => {
this.tableData = response.data.map((item, index) => {
item.originalLossNum = item.original?.lossNum;
item.originalLossArea = item.original?.lossArea;
item.edgeLossNum = item.edge?.lossNum;
item.edgeLossArea = item.edge?.lossArea;
item.drillLossNum = item.drill?.lossNum;
item.drillLossArea = item.drill?.lossArea;
item.coatingLossNum = item.coating?.lossNum;
item.coatingLossArea = item.coating?.lossArea;
item.silkLossNum = item.silk?.lossNum;
item.silkLossArea = item.silk?.lossArea;
item.temperingLossNum = item.tempering?.lossNum;
item.temperingLossArea = item.tempering?.lossArea;
item.packingLossNum = item.packing?.lossNum;
item.packingLossArea = item.packing?.lossArea;
return item;
});
this.$nextTick(() => {
if (this.tableData.length > 0) {
this.$refs.barChart.initChart();
}
});
})
.catch(error => {
console.error('接口请求失败:', error);
this.$message.error('数据加载失败,请重试');
})
.finally(() => {
this.dataListLoading = false;
});
}
} else {
this.$message.warning('请选择时间范围');
}
},
// 获取数据列表
getDataList() {
if (this.listQuery.startTime) {
this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then((response) => {
this.tableData = response.data.map((item, index) => {
item.originalLossNum = item.original?.lossNum;
item.originalLossArea = item.original?.lossArea;
item.edgeLossNum = item.edge?.lossNum;
item.edgeLossArea = item.edge?.lossArea;
item.drillLossNum = item.drill?.lossNum;
item.drillLossArea = item.drill?.lossArea;
item.coatingLossNum = item.coating?.lossNum;
item.coatingLossArea = item.coating?.lossArea;
item.silkLossNum = item.silk?.lossNum;
item.silkLossArea = item.silk?.lossArea;
item.temperingLossNum = item.tempering?.lossNum;
item.temperingLossArea = item.tempering?.lossArea;
item.packingLossNum = item.packing?.lossNum;
item.packingLossArea = item.packing?.lossArea;
return item;
});
this.$nextTick(() => {
if(this.tableData.length>0){
this.$refs.barChart.initChart();
}
});
this.dataListLoading = false;
});
} else {
this.$message.warning('请选择时间范围');
}
},
// 获取数据2列表
getDataList2() {
getPdlAutoReportNewSearchNow().then((response) => {

View File

@@ -0,0 +1,272 @@
<template>
<div :class="className" :style="{ height: height, width: width, marginLeft: '10px' }" />
</template>
<script>
import * as echarts from 'echarts';
require('echarts/theme/macarons'); // 引入主题
import resize from '@/utils/chartMixins/resize';
const animationDuration = 1000;
export default {
mixins: [resize], // 混入 resize 逻辑(自适应窗口)
props: {
className: {
type: String,
default: 'chart',
},
title: {
type: String,
default: '',
},
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '300px',
},
barData: {
type: Array,
default: () => [],
},
},
data() {
return {
chart: null, // 图表实例
};
},
watch: {
// 监听 barData 变化(深度监听数组内部元素)
barData: {
deep: true,
handler: 'handleBarDataChange', // 调用处理方法
},
},
mounted() {
// 组件挂载后初始化图表(确保 DOM 已就绪)
this.$nextTick(() => {
this.initChart();
});
},
beforeDestroy() {
// 组件销毁前清理图表实例
if (this.chart) {
this.chart.dispose();
this.chart = null;
}
},
methods: {
// barData 变化时的处理方法
handleBarDataChange() {
// 确保 DOM 存在再更新图表
this.$nextTick(() => {
// 如果图表未初始化,先初始化;否则直接更新数据
if (!this.chart) {
this.initChart();
} else {
this.updateChart();
}
});
},
// 初始化图表
initChart() {
// 避免重复初始化(先销毁旧实例)
if (this.chart) {
this.chart.dispose();
}
// 确保 DOM 元素存在
if (!this.$el) {
console.error('图表容器 DOM 元素不存在');
return;
}
// 初始化图表实例
this.chart = echarts.init(this.$el, 'macarons');
// 设置图表配置
this.setChartOption();
},
// 更新图表数据(复用配置逻辑)
updateChart() {
if (!this.chart) return;
this.setChartOption();
},
// 图表配置项(抽离为单独方法,方便初始化和更新复用)
setChartOption() {
const dataValues = this.barData.flatMap(item => [item.inputNum || 0, item.outputNum || 0]);
const maxData = Math.max(...dataValues, 0); // 加 0 确保无数据时 maxData 为 0
// 2. 计算 Y 轴最大值(留 10% 余量,避免数据顶到顶部)
let yMax = 0;
if (maxData > 0) {
yMax = Math.ceil(maxData * 1.1); // 向上取整,确保刻度为整数
} else {
yMax = 100; // 无数据时默认最大值为 100避免 Y 轴消失
}
// 3. 计算 interval5 个刻度对应 4 个间隔,向上取整确保间隔为整数)
const yInterval = Math.ceil((yMax - 0) / 4); // min 固定为 0直接减 0
this.chart.setOption({
title: {
text: this.title
? '{space|}{tip|}{space|}{value|' + this.title + '}'
: '',
textStyle: {
rich: {
tip: {
width: 6,
height: 6,
borderRadius: 50,
backgroundColor: '#288AFF',
},
space: {
width: 8,
},
value: {
fontSize: 14,
color: 'black',
},
},
},
},
color: ['#288AFF', '#8EF0AB', '#FFDC94'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999',
},
},
},
legend: {
data: ['投入', '产出', '加工成品率'],
},
grid: {
left: 20,
right: 30,
top:40,
bottom: 10,
containLabel: true,
splitArea: {
show: false // 关键:关闭网格背景分区(去掉间隔色块)
}
},
xAxis: {
type: 'category',
data: this.barData.map((item) => item.lineName),
axisLine: {
lineStyle: {
color: 'rgba(0, 0, 0, 0.45)',
width: 1 // 轴线宽度(可选,默认 1可按需调整
}
},
// 2. 控制 X 轴刻度线颜色(与轴线颜色保持一致,视觉统一)
axisTick: {
lineStyle: {
color: 'rgba(0, 0, 0, 0.45)', // 刻度线颜色,需与轴线颜色匹配
width: 1 // 刻度线宽度(可选)
},
alignWithLabel: true // 可选:让刻度线与文字对齐(避免文字偏移时刻度线错位)
},
// 3. 控制 X 轴文字颜色(如:深灰色 rgba(0, 0, 0, 0.45)
axisLabel: {
color: 'rgba(0, 0, 0, 0.45)6', // 文字颜色,可自定义
fontSize: 12, // 可选:调整文字大小(默认 12按需修改
// 可选:文字过长时换行/省略(避免文字重叠,按需开启)
formatter: (value) => {
// 示例:文字超过 6 个字符时换行(可根据需求调整字符数)
if (value.length > 6) {
return value.slice(0, 6) + '\n' + value.slice(6);
}
return value;
}
},
// 原有配置(若需保留可解开注释)
// axisPointer: {
// type: 'shadow',
// }
},
yAxis: [
{
type: 'value',
name: '投入/产出 片',
min: 0, // 最小值固定为 0
max: yMax,
interval: yInterval,
splitArea: {
show: false
},
axisLabel: {
formatter: '{value}',
color: 'rgba(0, 0, 0, 0.45)'
},
// 可选:修改 Y 轴名称颜色(与文字颜色保持一致)
nameTextStyle: {
color: 'rgba(0, 0, 0, 0.45)'
}
},
{
type: 'value',
name: '加工成品率',
min: 0,
max: 100, // 成品率固定 0-100%
interval: 25, // 100 / 4 = 25刚好 5 个刻度0、25、50、75、100
axisLabel: {
formatter: '{value} %',
color: 'rgba(0, 0, 0, 0.45)'
},
splitArea: {
show: false
},
// 可选:修改 Y 轴名称颜色
nameTextStyle: {
color: 'rgba(0, 0, 0, 0.45)'
}
},
],
series: [
{
name: '投入',
type: 'bar',
barWidth: '20',
data: this.barData.map((item) => item.inputNum),
tooltip: {
valueFormatter: (value) => `${value}`,
},
animationDuration,
},
{
name: '产出',
type: 'bar',
barWidth: '20',
data: this.barData.map((item) => item.outputNum),
tooltip: {
valueFormatter: (value) => `${value}`,
},
animationDuration,
},
{
name: '加工成品率',
type: 'line',
yAxisIndex: 1,
tooltip: {
valueFormatter: (value) => `${value} %`,
},
data: this.barData.map((item) => item.processingRatio),
},
],
});
},
},
};
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,361 @@
<!--
* @Author: Do not edit
* @Date: 2023-08-29 14:59:29
* @LastEditTime: 2024-12-02 13:44:47
* @LastEditors: zwq
* @Description:
-->
<template>
<div class="app-container">
<!-- :isFold="true" 控制展开 -->
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick" />
<base-table
v-if="showData.length"
class="right-aside"
v-loading="dataListLoading"
:table-props="tableProps"
:page="listQuery.pageNo"
:limit="listQuery.pageSize"
:table-data="showData"
>
<!-- <method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="120"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick" /> -->
</base-table>
<div v-else class="no-data-bg"></div>
<pagination
:limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo"
:total="listQuery.total"
@pagination="getDataList" />
<!-- <el-dialog
title="提示"
:visible.sync="dialogVisible"
width="30%"
:before-close="handleClose">
<el-button type="primary" @click="exportXlsx">xlsx</el-button>
<el-button type="success" @click="exportPdf">pdf</el-button>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="dialogVisible = false"> </el-button>
</span>
</el-dialog> -->
</div>
</template>
<script>
import { parseTime } from '../../mixins/code-filter';
import { getDownLogHisData, getPdList, getThick } from '@/api/core/monitoring/index'
import * as XLSX from 'xlsx'
import FileSaver from 'file-saver'
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
const tableProps = [
{
prop: 'productionLineName',
label: '产线'
},
{
prop: 'eqName',
label: '下片机械手编号'
},
{
prop: 'pos',
label: '工位编号'
},
{
prop: 'pallet',
label: '托数/托'
},
{
prop: 'palletNum',
label: '下片托数'
},
{
prop: 'startTime',
label: '开始时间',
filter: parseTime,
width: 160
},
{
prop: 'endTime',
label: '结束时间',
filter: parseTime,
width: 160
},
{
prop: 'outputNum',
label: '玻璃长度/mm'
},
{
prop: 'width',
label: '玻璃宽度/mm',
},
{
prop: 'thick',
label: '玻璃长度/mm'
},
];
export default {
data() {
return {
urlOptions: {
getDataListURL: getDownLogHisData
},
tableData: [],
listQuery: {
pageSize: 10,
pageNo: 1,
total: 1,
eqName: undefined,
productionLineId: undefined,
thick:undefined
},
exportLoading: false,
dataListLoading: false,
selectedList: [],
dialogVisible: false,
addOrEditTitle: '',
addOrUpdateVisible: false,
tableProps,
tableBtn: [
{
type: 'his',
btnName: '历史',
},
].filter((v) => v),
tableData: [],
showData: [],
fileName: '',
formConfig: [
{
type: 'select',
label: '产线',
selectOptions: [],
param: 'productionLineId'
},
{
type: 'select',
label: '玻璃型号',
selectOptions: [],
param: 'thick'
},
{
type: 'datePicker',
label: '统计开始时间',
dateType: 'daterange',
format: 'yyyy-MM-dd',
valueFormat: "yyyy-MM-dd HH:mm:ss",
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'timeVal',
defaultTime: ['00:00:00', '23:59:59'],
defaultSelect: []
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary',
},
{
type: 'separate',
},
{
// type: this.$auth.hasPermi('base:factory:export') ? 'button' : '',
type: 'button',
btnName: '导出',
name: 'export',
color: 'warning',
}
],
};
},
mounted() {
this.$refs.searchBarForm.formInline.productionLineId = this.$route.query.productionLineId
this.$refs.searchBarForm.formInline.thick = this.$route.query.thick
this.listQuery.productionLineId = this.$route.query.productionLineId
this.listQuery.thick = this.$route.query.thick
this.getDataList()
this.getPdLineList()
console.log('this.$route.query', this.$route.query);
},
methods: {
handleClick(val) {
this.addOrUpdateVisible = true;
this.addOrEditTitle =
val.data?.factoryName + '-' + val.data?.lineName + ' 详情';
this.$nextTick(() => {
this.$refs.eqDetail.init(
val.data.lineId,
this.listQuery.startTime,
this.listQuery.endTime
);
});
},
test() {
var target = document.getElementsByClassName("right-aside")[0]
target.style.background = '#FFFFFF'
var that = this
setTimeout(() => {
html2canvas(target).then(function(canvas) {
var contentWidth = canvas.width
var contentHeight = canvas.height
// 一页pdf显示html页面生成的canvas高度
var pageHeight = contentHeight / 592.28 * 841.89
// 未生成pdf的html页面高度
var leftHeight = contentHeight
// 页面偏移
var position = 0
// a4纸的尺寸[595.28,841.89]html页面生成的canvas在pdf中图片的高度
var imgWidth = 595.28
var imgHeight = 592.28 / contentWidth * contentHeight
var pageData = canvas.toDataURL('image/jpeg', 1.0)
console.log('nihc URL', leftHeight, pageHeight)
var pdf = new jsPDF('', 'pt', 'a4')
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 0, 20, imgWidth, imgHeight)
} else {
while(leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight
position -= 841.89
// 避免空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
}
pdf.save(that.fileName + '工段统计.pdf')
})
}, 300)
},
exportECL() {
let tables = document.querySelector('.el-table').cloneNode(true)
const fix = tables.querySelector('.el-table__fixed')
const fixRight = tables.querySelector('.el-table__fixed-right')
if (fix) {
tables.removeChild(tables.querySelector('.el-table__fixed'))
}
if (fixRight) {
tables.removeChild(tables.querySelector('.el-table__fixed-right'))
}
let exportTable = XLSX.utils.table_to_book(tables)
var exportTableOut = XLSX.write(exportTable, {
bookType: 'xlsx', bookSST: true, type: 'array'
})
// sheetjs.xlsx为导出表格的标题名称
try {
FileSaver.saveAs(new Blob([exportTableOut], {
type: 'application/octet-stream'
}), this.fileName + '工段统计.xlsx')
} catch (e) {
if (typeof console !== 'undefined') console.log(e, exportTableOut)
}
return exportTableOut
},
exportPdf() {
this.test()
setTimeout(() =>{
this.dialogVisible = false
this.showData = this.tableData
}, 600)
},
exportXlsx() {
this.exportECL()
this.dialogVisible = false
this.showData = this.tableData
},
handleClose(done) {
this.$confirm('确认关闭?')
.then(_ => {
done();
})
.catch(_ => {});
},
getPdLineList() {
getPdList().then((res) => {
this.formConfig[0].selectOptions = res.data || []
})
getThick().then((res) => {
this.formConfig[1].selectOptions = res.data.map((item) => {
return {
id: item.thick + 'mm',
name: item.thick + 'mm'
}
})
})
},
selectChange(val) {
console.log(val)
this.selectedList = val
},
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.listQuery.pageNo = 1;
this.listQuery.pageSize = 10;
this.listQuery.productId = val.productId ? val.productId : undefined;
this.listQuery.startTime = val.timeVal ? val.timeVal[0]: undefined;
this.listQuery.endTime = val.timeVal ? val.timeVal[1]: undefined;
//this.listQuery.reportEndTime = val.timeVal ? [new Date(val.timeVal[1]).getTime()] : undefined;
this.getDataList();
break;
case 'export':
this.handleExport();
break;
default:
console.log(val);
}
},
// 获取数据列表
getDataList() {
this.listQuery.eqName = this.$route.query.eqName
this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then(response => {
this.tableData = response.data.list
this.showData = this.tableData
this.listQuery.total = response.data.total;
this.dataListLoading = false;
});
},
// 每页数
sizeChangeHandle(val) {
this.listQuery.pageSize = val;
this.listQuery.pageNo = 1;
this.getDataList();
},
// 当前页
currentChangeHandle(val) {
this.listQuery.pageNo = val;
this.getDataList();
},
handleExport() {
if (this.selectedList.length > 0) {
this.showData = this.selectedList
}
this.dialogVisible = true
}
},
};
</script>

View File

@@ -0,0 +1,373 @@
<!--
* @Author: Do not edit
* @Date: 2023-08-29 14:59:29
* @LastEditTime: 2024-12-02 13:44:47
* @LastEditors: zwq
* @Description:
-->
<template>
<div class="app-container">
<!-- :isFold="true" 控制展开 -->
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick" />
<base-table
v-if="showData.length"
class="right-aside"
v-loading="dataListLoading"
:table-props="tableProps"
:page="listQuery.pageNo"
:limit="listQuery.pageSize"
:table-data="showData"
>
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="120"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<div v-else class="no-data-bg"></div>
<pagination
:limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo"
:total="listQuery.total"
@pagination="getDataList" />
<!-- <el-dialog
title="提示"
:visible.sync="dialogVisible"
width="30%"
:before-close="handleClose">
<el-button type="primary" @click="exportXlsx">xlsx</el-button>
<el-button type="success" @click="exportPdf">pdf</el-button>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="dialogVisible = false"> </el-button>
</span>
</el-dialog> -->
</div>
</template>
<script>
import { parseTime } from '../../mixins/code-filter';
import { getDownLogPage, getPdList, getThick, exportDownLogData } from '@/api/core/monitoring/index'
import * as XLSX from 'xlsx'
import FileSaver from 'file-saver'
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
const tableProps = [
{
prop: 'productionLineName',
label: '产线'
},
{
prop: 'eqName',
label: '下片机械手编号'
},
{
prop: 'pos',
label: '工位编号'
},
{
prop: 'pallet',
label: '托数/托'
},
{
prop: 'palletNum',
label: '下片托数'
},
{
prop: 'startTime',
label: '开始时间',
filter: parseTime,
width: 160
},
{
prop: 'endTime',
label: '结束时间',
filter: parseTime,
width: 160
},
{
prop: 'outputNum',
label: '玻璃长度/mm'
},
{
prop: 'width',
label: '玻璃宽度/mm',
},
{
prop: 'thick',
label: '玻璃厚度/mm'
},
];
export default {
data() {
return {
urlOptions: {
getDataListURL: getDownLogPage
},
tableData: [],
listQuery: {
pageSize: 10,
pageNo: 1,
total: 1,
productionLineId: undefined,
thick: undefined,
},
exportLoading: false,
dataListLoading: false,
selectedList: [],
dialogVisible: false,
addOrEditTitle: '',
addOrUpdateVisible: false,
tableProps,
tableBtn: [
{
type: 'his',
btnName: '历史',
},
].filter((v) => v),
tableData: [],
showData: [],
fileName: '',
formConfig: [
{
type: 'select',
label: '产线',
selectOptions: [],
param: 'productionLineId'
},
{
type: 'select',
label: '玻璃型号',
selectOptions: [],
param: 'thick'
},
{
type: 'datePicker',
label: '统计开始时间',
dateType: 'daterange',
format: 'yyyy-MM-dd',
valueFormat: "yyyy-MM-dd HH:mm:ss",
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'timeVal',
defaultTime: ['00:00:00', '23:59:59'],
defaultSelect: []
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary',
},
{
type: 'separate',
},
{
// type: this.$auth.hasPermi('base:factory:export') ? 'button' : '',
type: 'button',
btnName: '导出',
name: 'export',
color: 'warning',
}
],
};
},
created() {
this.getDataList()
this.getPdLineList()
},
methods: {
handleClick(val) {
console.log(val);
if (val.type === 'his') {
this.$router.push({
path: 'nextClipHis',
query: {
eqName: val.data.eqName,
productionLineId: this.listQuery.productionLineId,
thick: this.listQuery.thick,
},
});
}
// this.addOrUpdateVisible = true;
// this.addOrEditTitle =
// val.data?.factoryName + '-' + val.data?.lineName + ' 详情';
// this.$nextTick(() => {
// this.$refs.eqDetail.init(
// val.data.lineId,
// this.listQuery.startTime,
// this.listQuery.endTime
// );
// });
},
test() {
var target = document.getElementsByClassName("right-aside")[0]
target.style.background = '#FFFFFF'
var that = this
setTimeout(() => {
html2canvas(target).then(function(canvas) {
var contentWidth = canvas.width
var contentHeight = canvas.height
// 一页pdf显示html页面生成的canvas高度
var pageHeight = contentHeight / 592.28 * 841.89
// 未生成pdf的html页面高度
var leftHeight = contentHeight
// 页面偏移
var position = 0
// a4纸的尺寸[595.28,841.89]html页面生成的canvas在pdf中图片的高度
var imgWidth = 595.28
var imgHeight = 592.28 / contentWidth * contentHeight
var pageData = canvas.toDataURL('image/jpeg', 1.0)
console.log('nihc URL', leftHeight, pageHeight)
var pdf = new jsPDF('', 'pt', 'a4')
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 0, 20, imgWidth, imgHeight)
} else {
while(leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight
position -= 841.89
// 避免空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
}
pdf.save(that.fileName + '工段统计.pdf')
})
}, 300)
},
exportECL() {
let tables = document.querySelector('.el-table').cloneNode(true)
const fix = tables.querySelector('.el-table__fixed')
const fixRight = tables.querySelector('.el-table__fixed-right')
if (fix) {
tables.removeChild(tables.querySelector('.el-table__fixed'))
}
if (fixRight) {
tables.removeChild(tables.querySelector('.el-table__fixed-right'))
}
let exportTable = XLSX.utils.table_to_book(tables)
var exportTableOut = XLSX.write(exportTable, {
bookType: 'xlsx', bookSST: true, type: 'array'
})
// sheetjs.xlsx为导出表格的标题名称
try {
FileSaver.saveAs(new Blob([exportTableOut], {
type: 'application/octet-stream'
}), this.fileName + '工段统计.xlsx')
} catch (e) {
if (typeof console !== 'undefined') console.log(e, exportTableOut)
}
return exportTableOut
},
exportPdf() {
this.test()
setTimeout(() =>{
this.dialogVisible = false
this.showData = this.tableData
}, 600)
},
exportXlsx() {
this.exportECL()
this.dialogVisible = false
this.showData = this.tableData
},
handleClose(done) {
this.$confirm('确认关闭?')
.then(_ => {
done();
})
.catch(_ => {});
},
getPdLineList() {
getPdList().then((res) => {
this.formConfig[0].selectOptions = res.data || []
})
getThick().then((res) => {
this.formConfig[1].selectOptions = res.data.map((item) => {
return {
id: item.thick + 'mm',
name: item.thick + 'mm'
}
})
})
},
selectChange(val) {
console.log(val)
this.selectedList = val
},
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.listQuery.pageNo = 1;
this.listQuery.pageSize = 10;
this.listQuery.productionLineId = val.productionLineId ? val.productionLineId : undefined;
this.listQuery.thick = val.thick ? val.thick : undefined;
this.listQuery.startTime = val.timeVal ? val.timeVal[0]: undefined;
this.listQuery.endTime = val.timeVal ? val.timeVal[1]: undefined;
//this.listQuery.reportEndTime = val.timeVal ? [new Date(val.timeVal[1]).getTime()] : undefined;
this.getDataList();
break;
case 'export':
this.handleExport();
break;
default:
console.log(val);
}
},
// 获取数据列表
getDataList() {
this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then(response => {
this.tableData = response.data.list
this.showData = this.tableData
this.listQuery.total = response.data.total;
this.dataListLoading = false;
});
},
// 每页数
sizeChangeHandle(val) {
this.listQuery.pageSize = val;
this.listQuery.pageNo = 1;
this.getDataList();
},
// 当前页
currentChangeHandle(val) {
this.listQuery.pageNo = val;
this.getDataList();
},
handleExport() {
// 处理查询参数
let params = { ...this.listQuery };
params.pageNo = undefined;
params.pageSize = undefined;
this.$modal.confirm('是否确认导出下片日志?').then(() => {
this.exportLoading = true;
return exportDownLogData(params);
}).then(response => {
this.$download.excel(response, '下片日志.xls');
this.exportLoading = false;
}).catch(() => { });
}
},
};
</script>

View File

@@ -0,0 +1,272 @@
<template>
<div :class="className" :style="{ height: height, width: width, marginLeft: '10px' }" />
</template>
<script>
import * as echarts from 'echarts';
require('echarts/theme/macarons'); // 引入主题
import resize from '@/utils/chartMixins/resize';
const animationDuration = 1000;
export default {
mixins: [resize], // 混入 resize 逻辑(自适应窗口)
props: {
className: {
type: String,
default: 'chart',
},
title: {
type: String,
default: '',
},
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '300px',
},
barData: {
type: Array,
default: () => [],
},
},
data() {
return {
chart: null, // 图表实例
};
},
watch: {
// 监听 barData 变化(深度监听数组内部元素)
barData: {
deep: true,
handler: 'handleBarDataChange', // 调用处理方法
},
},
mounted() {
// 组件挂载后初始化图表(确保 DOM 已就绪)
this.$nextTick(() => {
this.initChart();
});
},
beforeDestroy() {
// 组件销毁前清理图表实例
if (this.chart) {
this.chart.dispose();
this.chart = null;
}
},
methods: {
// barData 变化时的处理方法
handleBarDataChange() {
// 确保 DOM 存在再更新图表
this.$nextTick(() => {
// 如果图表未初始化,先初始化;否则直接更新数据
if (!this.chart) {
this.initChart();
} else {
this.updateChart();
}
});
},
// 初始化图表
initChart() {
// 避免重复初始化(先销毁旧实例)
if (this.chart) {
this.chart.dispose();
}
// 确保 DOM 元素存在
if (!this.$el) {
console.error('图表容器 DOM 元素不存在');
return;
}
// 初始化图表实例
this.chart = echarts.init(this.$el, 'macarons');
// 设置图表配置
this.setChartOption();
},
// 更新图表数据(复用配置逻辑)
updateChart() {
if (!this.chart) return;
this.setChartOption();
},
// 图表配置项(抽离为单独方法,方便初始化和更新复用)
setChartOption() {
const dataValues = this.barData.flatMap(item => [item.inputNum || 0, item.outputNum || 0]);
const maxData = Math.max(...dataValues, 0); // 加 0 确保无数据时 maxData 为 0
// 2. 计算 Y 轴最大值(留 10% 余量,避免数据顶到顶部)
let yMax = 0;
if (maxData > 0) {
yMax = Math.ceil(maxData * 1.1); // 向上取整,确保刻度为整数
} else {
yMax = 100; // 无数据时默认最大值为 100避免 Y 轴消失
}
// 3. 计算 interval5 个刻度对应 4 个间隔,向上取整确保间隔为整数)
const yInterval = Math.ceil((yMax - 0) / 4); // min 固定为 0直接减 0
this.chart.setOption({
title: {
text: this.title
? '{space|}{tip|}{space|}{value|' + this.title + '}'
: '',
textStyle: {
rich: {
tip: {
width: 6,
height: 6,
borderRadius: 50,
backgroundColor: '#288AFF',
},
space: {
width: 8,
},
value: {
fontSize: 14,
color: 'black',
},
},
},
},
color: ['#288AFF', '#8EF0AB', '#FFDC94'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999',
},
},
},
legend: {
data: ['投入', '产出', '加工成品率'],
},
grid: {
left: 20,
right: 30,
top:40,
bottom: 10,
containLabel: true,
splitArea: {
show: false // 关键:关闭网格背景分区(去掉间隔色块)
}
},
xAxis: {
type: 'category',
data: this.barData.map((item) => item.lineName),
axisLine: {
lineStyle: {
color: 'rgba(0, 0, 0, 0.45)',
width: 1 // 轴线宽度(可选,默认 1可按需调整
}
},
// 2. 控制 X 轴刻度线颜色(与轴线颜色保持一致,视觉统一)
axisTick: {
lineStyle: {
color: 'rgba(0, 0, 0, 0.45)', // 刻度线颜色,需与轴线颜色匹配
width: 1 // 刻度线宽度(可选)
},
alignWithLabel: true // 可选:让刻度线与文字对齐(避免文字偏移时刻度线错位)
},
// 3. 控制 X 轴文字颜色(如:深灰色 rgba(0, 0, 0, 0.45)
axisLabel: {
color: 'rgba(0, 0, 0, 0.45)6', // 文字颜色,可自定义
fontSize: 12, // 可选:调整文字大小(默认 12按需修改
// 可选:文字过长时换行/省略(避免文字重叠,按需开启)
formatter: (value) => {
// 示例:文字超过 6 个字符时换行(可根据需求调整字符数)
if (value.length > 6) {
return value.slice(0, 6) + '\n' + value.slice(6);
}
return value;
}
},
// 原有配置(若需保留可解开注释)
// axisPointer: {
// type: 'shadow',
// }
},
yAxis: [
{
type: 'value',
name: '投入/产出 片',
min: 0, // 最小值固定为 0
max: yMax,
interval: yInterval,
splitArea: {
show: false
},
axisLabel: {
formatter: '{value}',
color: 'rgba(0, 0, 0, 0.45)'
},
// 可选:修改 Y 轴名称颜色(与文字颜色保持一致)
nameTextStyle: {
color: 'rgba(0, 0, 0, 0.45)'
}
},
{
type: 'value',
name: '加工成品率',
min: 0,
max: 100, // 成品率固定 0-100%
interval: 25, // 100 / 4 = 25刚好 5 个刻度0、25、50、75、100
axisLabel: {
formatter: '{value} %',
color: 'rgba(0, 0, 0, 0.45)'
},
splitArea: {
show: false
},
// 可选:修改 Y 轴名称颜色
nameTextStyle: {
color: 'rgba(0, 0, 0, 0.45)'
}
},
],
series: [
{
name: '投入',
type: 'bar',
barWidth: '20',
data: this.barData.map((item) => item.inputNum),
tooltip: {
valueFormatter: (value) => `${value}`,
},
animationDuration,
},
{
name: '产出',
type: 'bar',
barWidth: '20',
data: this.barData.map((item) => item.outputNum),
tooltip: {
valueFormatter: (value) => `${value}`,
},
animationDuration,
},
{
name: '加工成品率',
type: 'line',
yAxisIndex: 1,
tooltip: {
valueFormatter: (value) => `${value} %`,
},
data: this.barData.map((item) => item.processingRatio),
},
],
});
},
},
};
</script>

View File

@@ -0,0 +1,273 @@
<template>
<div class="baseTable">
<el-table
:ref="id"
:data="renderData"
v-bind="$attrs"
:border="cancelBorder ? false : true"
@current-change="currentChange"
@selection-change="handleSelectionChange"
style="width: 100%"
:header-cell-style="{
background: '#F2F4F9',
color: '#606266',
}">
<!-- 多选 -->
<el-table-column
v-if="selectWidth"
type="selection"
:width="selectWidth" />
<!-- 序号 -->
<el-table-column
v-if="page && limit"
prop="_pageIndex"
:width="pageWidth"
align="center"
:fixed="cancelPageFixed ? false : true">
<template slot="header">
<el-popover placement="bottom-start" width="300" trigger="click">
<div
class="setting-box"
style="max-height: 400px; overflow-y: auto">
<el-checkbox
v-for="(item, index) in tableProps"
:key="'cb' + index"
v-model="selectedBox[index]"
:label="item.label" />
</div>
<i slot="reference" class="el-icon-s-tools" />
</el-popover>
</template>
</el-table-column>
<el-table-column
v-for="item in renderTableHeadList"
:key="item.prop"
v-bind="item"
:label="item.label"
:prop="item.prop"
:fixed="item.fixed || false"
:show-overflow-tooltip="item.showOverflowtooltip || false"
:sortable="item.sortable || false">
<template slot="header">
<span>{{ item.label }}</span>
</template>
<!-- 多表头 -->
<template v-if="item.children">
<el-table-column
v-for="sub in item.children"
:prop="sub.prop"
:key="sub.prop"
v-bind="sub"
:label="sub.label">
<template v-if="sub.children">
<el-table-column
v-for="ssub in sub.children"
:prop="ssub.prop"
:key="ssub.prop"
v-bind="ssub"
:label="ssub.label">
<template slot-scope="sscopeInner">
<component
:is="ssub.subcomponent"
v-if="ssub.subcomponent"
:key="sscopeInner.row.id"
:inject-data="{ ...sscopeInner.row, ...ssub }"
@emitData="emitData" />
<span v-else>
{{ sscopeInner.row[ssub.prop] | commonFilter(ssub.filter) }}
</span>
</template>
</el-table-column>
</template>
<template slot-scope="scopeInner">
<component
:is="sub.subcomponent"
v-if="sub.subcomponent"
:key="scopeInner.row.id"
:inject-data="{ ...scopeInner.row, ...sub }"
@emitData="emitData" />
<span v-else>
{{ scopeInner.row[sub.prop] | commonFilter(sub.filter) }}
</span>
</template>
</el-table-column>
</template>
<template slot-scope="scope">
<component
:is="item.subcomponent"
v-if="item.subcomponent"
:key="scope.row.id"
:itemProp="item.prop"
:inject-data="{ ...scope.row, ...item }"
@emitData="emitData" />
<span v-else>
{{ scope.row[item.prop] | commonFilter(item.filter) }}
</span>
</template>
</el-table-column>
<slot name="handleBtn" />
</el-table>
<!-- 表格底部加号 -->
<el-button
v-if="addButtonShow"
class="addButton"
icon="el-icon-plus"
@click="emitButtonClick">
{{ addButtonShow }}
</el-button>
</div>
</template>
<script>
export default {
name: 'BaseTable',
filters: {
commonFilter: (source, filterType = (a) => a) => {
return filterType(source);
},
},
props: {
cancelBorder: {
type: Boolean,
default: false,
},
cancelPageFixed: {
type: Boolean,
default: false,
},
tableData: {
type: Array,
required: true,
default: () => {
return [];
},
},
tableProps: {
type: Array,
default: () => {
return [];
},
},
id: {
type: String,
required: false,
default: '',
},
page: {
type: Number,
required: false,
default: 0,
},
pageWidth: {
type: Number,
required: false,
default: 70,
},
limit: {
type: Number,
required: false,
default: 0,
},
selectWidth: {
type: Number,
required: false,
default: 0,
},
addButtonShow: {
type: String,
required: false,
default: '',
},
},
data() {
return {
selectedBox: new Array(100).fill(true),
};
},
computed: {
renderTableHeadList() {
return this.tableProps.filter((item, index) => {
return this.selectedBox[index];
});
},
renderData() {
return this.tableData.map((item, index) => {
return {
...item,
_pageIndex: (this.page - 1) * this.limit + index + 1,
};
});
},
},
beforeMount() {
this.selectedBox = new Array(100).fill(true);
},
methods: {
currentChange(newVal, oldVal) {
this.$emit('current-change', { newVal, oldVal });
},
handleSelectionChange(val) {
this.$emit('selection-change', val);
},
emitData(val) {
this.$emit('emitFun', val);
},
emitButtonClick() {
this.$emit('emitButtonClick');
},
setCurrent(name, index) {
let _this = this;
let obj = _this.$refs[name].data[index];
_this.$refs[name].setCurrentRow(obj);
},
doLayout(name) {
this.$refs[name].doLayout();
},
},
};
</script>
<style scoped>
.baseTable .show-col-btn {
margin-right: 5px;
line-height: inherit;
cursor: pointer;
}
.baseTable .el-icon-refresh {
cursor: pointer;
}
</style>
<style>
.baseTable .el-table__body tr.current-row > td.el-table__cell {
background-color: #eaf1fc;
}
.baseTable .el-table .el-table__cell {
padding: 0;
height: 35px;
}
.baseTable .addButton {
width: 100%;
height: 35px;
border-top: none;
color: #0b58ff;
border-color: #ebeef5;
border-radius: 0;
}
.baseTable .addButton:hover {
color: #0b58ff;
border-color: #ebeef5;
background-color: #fff;
}
.baseTable .addButton:focus {
border-color: #ebeef5;
background-color: #fff;
}
.el-tooltip__popper.is-dark {
background: rgba(0, 0, 0, 0.6) !important;
}
.el-tooltip__popper .popper__arrow,
.el-tooltip__popper .popper__arrow::after {
border-top-color: rgba(0, 0, 0, 0.4) !important;
}
</style>

File diff suppressed because it is too large Load Diff

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: 18px;
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,221 @@
<template>
<el-dialog :visible.sync="visible" width="40%">
<small-title slot="title" :no-padding="true">
{{ !dataForm.id ? '新增' : '编辑' }}
</small-title>
<div class="content">
<div class="visual-part">
<el-form ref="dataForm" :model="dataForm" :rules="dataRule" label-width="100px"
@keyup.enter.native="dataFormSubmit">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="维度" prop="statisticType">
<el-select v-model="dataForm.statisticType" style="width: 100%" placeholder="请选择维度">
<el-option v-for="item in statisticTypeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="厚度" prop="modifyThick">
<el-input v-model="dataForm.modifyThick" placeholder="请输入厚度" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="在线速度" prop="modifySpeed">
<el-input v-model="dataForm.modifySpeed" placeholder="请输入在线速度" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="宽度" prop="modifyWidth">
<el-input v-model="dataForm.modifyWidth" placeholder="请输入宽度" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="拉引量" prop="modifyInArea">
<el-input v-model="dataForm.modifyInArea" placeholder="请输入拉引量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="下片面积" prop="modifyOutArea">
<el-input v-model="dataForm.modifyOutArea" placeholder="请输入下片面积" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="良品率" prop="modifyRatio">
<el-input v-model="dataForm.modifyRatio" placeholder="请输入良品率" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button style="" @click="goback()">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">
确定
</el-button>
</div>
</el-dialog>
</template>
<script>
import { editCostOriginRadioHisData } from '@/api/core/monitoring/index'
import { parseTime } from '../../mixins/code-filter';
import SmallTitle from './SmallTitle';
export default {
components: { SmallTitle },
data() {
return {
visible: false,
addOrUpdateVisible: false,
statisticTypeList: [
{
id: '0',
name: '班组'
},
{
id: '1',
name: '日'
},
{
id: '2',
name: '周'
},
{
id: '3',
name: '月'
},
{
id: '4',
name: '年'
}
],
dataForm: {
id: null,
statisticType:undefined,
modifyThick: undefined,
modifySpeed: undefined,
modifyWidth: undefined,
modifyInArea: undefined,
modifyOutArea: undefined,
modifyRatio: undefined,
},
dataRule: {
statisticType: [
{
required: true,
message: '维度不能为空',
trigger: 'blur',
},
// {
// type: 'number',
// message: '产品编码为数字类型',
// trigger: 'blur',
// transfom: 'val => Number(val)',
// },
],
},
};
},
methods: {
init(data) {
console.log(data,'data');
this.dataForm.id = data.id || null;
this.visible = true;
this.$nextTick(() => {
this.$refs['dataForm'].resetFields();
this.dataForm = {
id: data.id || null,
statisticType: data.statisticType || undefined,
modifyThick: data.thick || undefined, // 厚度对应
modifySpeed: data.speed || undefined, // 在线速度对应
modifyWidth: data.width || undefined, // 掰边宽度对应
modifyInArea: data.inArea || undefined, // 拉引量对应
modifyOutArea: data.outArea || undefined, // 下片面积对应
modifyRatio: data.ratio || undefined, // 良品率对应
};
});
},
// 表单提交
dataFormSubmit() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
// 修改的提交
if (this.dataForm.id) {
editCostOriginRadioHisData(this.dataForm).then((response) => {
this.$modal.msgSuccess('修改成功');
this.visible = false;
this.$emit('refreshDataList');
});
return;
}
}
});
},
goback() {
this.$emit('refreshDataList');
this.visible = false;
this.initData();
},
},
};
</script>
<style scoped>
.drawer >>> .el-drawer {
border-radius: 8px 0 0 8px;
display: flex;
flex-direction: column;
}
.drawer >>> .el-form-item__label {
padding: 0;
}
.drawer >>> .el-drawer__header {
margin: 0;
padding: 32px 32px 24px;
border-bottom: 1px solid #dcdfe6;
}
.drawer >>> .el-drawer__body {
flex: 1;
height: 1px;
display: flex;
flex-direction: column;
}
.drawer >>> .content {
padding: 30px 24px;
flex: 1;
display: flex;
flex-direction: column;
/* height: 100%; */
}
.drawer >>> .visual-part {
flex: 1 auto;
max-height: 76vh;
overflow: hidden;
overflow-y: scroll;
padding-right: 10px; /* 调整滚动条样式 */
}
.drawer >>> .el-form,
.drawer >>> .attr-list {
padding: 0 16px;
}
.drawer-body__footer {
display: flex;
justify-content: flex-end;
padding: 18px;
}
</style>

View File

@@ -0,0 +1,281 @@
<!--
* @Author: Do not edit
* @Date: 2023-08-29 14:59:29
* @LastEditTime: 2024-12-02 13:44:47
* @LastEditors: zwq
* @Description:
-->
<template>
<div class="app-container">
<!-- :isFold="true" 控制展开 -->
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<base-table v-if="tableData.length" class="right-aside" v-loading="dataListLoading" :table-props="tableProps"
:page="listQuery.pageNo" :limit="listQuery.pageSize" :table-data="tableData">
<method-btn v-if="tableBtn.length" slot="handleBtn" :width="120" label="操作" :method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<div v-else class="no-data-bg"></div>
<pagination :limit.sync="listQuery.pageSize" :page.sync="listQuery.pageNo" :total="listQuery.total"
@pagination="getDataList" />
<!-- <el-dialog
title="提示"
:visible.sync="dialogVisible"
width="30%"
:before-close="handleClose">
<el-button type="primary" @click="exportXlsx">xlsx</el-button>
<el-button type="success" @click="exportPdf">pdf</el-button>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="dialogVisible = false"> </el-button>
</span>
</el-dialog> -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList" />
</div>
</template>
<script>
import AddOrUpdate from './add-or-updata';
import { parseTime } from '../../mixins/code-filter';
import { getCostOriginRadioHisData, getPdList } from '@/api/core/monitoring/index'
import * as XLSX from 'xlsx'
import FileSaver from 'file-saver'
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
import { exportCostOriginRadioHisData } from '../../../../api/core/monitoring';
const tableProps = [
{
prop: 'reportType',
label: '报表类型'
},
{
prop: 'time',
label: '日期',
// filter: parseTime,
width: 160
},
{
prop: 'bindObjectName',
label: '产线'
},
{
prop: 'thick',
label: '厚度'
},
{
prop: 'speed',
label: '在线速度'
},
{
prop: 'width',
label: '掰边宽度'
},
{
prop: 'inArea',
label: '拉引量/㎡'
},
{
prop: 'outArea',
label: '下片面积/㎡'
},
{
prop: 'ratio',
label: '良品率'
},
];
export default {
components: {
AddOrUpdate
},
data() {
return {
urlOptions: {
getDataListURL: getCostOriginRadioHisData
},
tableData: [],
listQuery: {
pageSize: 10,
pageNo: 1,
total: 1,
bindObjectId: undefined,
statisticType: undefined,
},
pdLineList:[],
exportLoading: false,
dataListLoading: false,
selectedList: [],
dialogVisible: false,
addOrEditTitle: '',
addOrUpdateVisible: false,
tableProps,
tableBtn: [
{
type: 'edit',
btnName: '编辑',
},
].filter((v) => v),
tableData: [],
fileName: '',
formConfig: [
{
type: 'select',
label: '维度',
selectOptions: [
{
id: '0',
name:'班组'
},
{
id: '1',
name: '日'
},
{
id: '2',
name: '周'
},
{
id: '3',
name: '月'
},
{
id: '4',
name: '年'
}
],
param: 'statisticType'
},
{
type: 'select',
label: '产线',
selectOptions: [],
param: 'bindObjectId'
},
{
type: 'datePicker',
label: '统计开始时间',
dateType: 'daterange',
format: 'yyyy-MM-dd',
valueFormat: "yyyy-MM-dd HH:mm:ss",
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'timeVal',
defaultTime: ['00:00:00', '23:59:59'],
defaultSelect: []
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary',
},
{
type: 'separate',
},
{
// type: this.$auth.hasPermi('base:factory:export') ? 'button' : '',
type: 'button',
btnName: '导出',
name: 'export',
color: 'warning',
}
],
};
},
mounted() {
this.$refs.searchBarForm.formInline.statisticType = '1';
this.listQuery.statisticType = '1';
this.getDataList()
this.getPdLineList()
},
methods: {
handleClick(val) {
console.log(val);
if (val.type === 'edit') {
this.addOrUpdateVisible= true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(val.data);
});
}
},
getPdLineList() {
getPdList().then((res) => {
this.formConfig[1].selectOptions = res.data || []
this.pdLineList = res.data || []; // 保存产线数据
})
},
selectChange(val) {
console.log(val)
this.selectedList = val
},
buttonClick(val) {
console.log('val', val);
switch (val.btnName) {
case 'search':
this.listQuery.pageNo = 1;
this.listQuery.pageSize = 10;
this.listQuery.bindObjectId = val.bindObjectId ? val.bindObjectId : undefined;
this.listQuery.statisticType = val.statisticType ? val.statisticType : undefined;
this.listQuery.startTime = val.timeVal ? val.timeVal[0]: undefined;
this.listQuery.endTime = val.timeVal ? val.timeVal[1]: undefined;
//this.listQuery.reportEndTime = val.timeVal ? [new Date(val.timeVal[1]).getTime()] : undefined;
this.getDataList();
break;
case 'export':
this.handleExport();
break;
default:
console.log(val);
}
},
// 获取数据列表
getDataList() {
this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then(response => {
const arr = ['班组','日', '周', '月', '年'];
this.tableData = response.data?.list?.map((item) => {
item.reportType = arr[this.listQuery.statisticType];
item.statisticType = this.listQuery.statisticType
// 匹配 bindObjectName
const targetLine = this.pdLineList.find(line => line.id === item.bindObjectId);
item.bindObjectName = targetLine ? targetLine.name : ''; // 赋值名称,无匹配则为空
return item;
});
this.listQuery.total = response.data.total;
this.dataListLoading = false;
});
},
// 每页数
sizeChangeHandle(val) {
this.listQuery.pageSize = val;
this.listQuery.pageNo = 1;
this.getDataList();
},
// 当前页
currentChangeHandle(val) {
this.listQuery.pageNo = val;
this.getDataList();
},
handleExport() {
// 处理查询参数
let params = { ...this.listQuery };
params.pageNo = undefined;
params.pageSize = undefined;
this.$modal.confirm('是否确认导出原片报表?').then(() => {
this.exportLoading = true;
return exportCostOriginRadioHisData(params);
}).then(response => {
this.$download.excel(response, '原片报表.xls');
this.exportLoading = false;
}).catch(() => { });
}
},
};
</script>

View File

@@ -0,0 +1,83 @@
<!--
* @Author: zwq
* @Date: 2025-10-23 13:43:55
* @LastEditors: zwq
* @LastEditTime: 2025-10-23 16:50:43
* @Description:
-->
<template>
<div class="app-container">
<div v-for="item in groupClassArr" :key="item.planId">
<small-title style="margin: 16px 0" size="sm" :no-padding="true">
{{item.planName}}
</small-title>
<base-table
:table-props="tableProps"
:table-data="
tableData.filter((titem) => titem.schedulingPlanId == item.planId)
"></base-table>
</div>
</div>
</template>
<script>
import SmallTitle from '../Schedule/SmallTitle';
const tableProps = [
{
prop: 'classesName',
label: '班次名称',
},
{
prop: 'workTime',
label: '班次时间',
},
{
prop: 'teamName',
label: '班组名称',
},
{
prop: 'teamLeader',
label: '组长',
},
{
prop: 'teamLeaderPhone',
label: '组长电话',
},
];
export default {
components: {
SmallTitle,
},
data() {
return {
tableProps,
tableData: [],
groupClassArr: [],
};
},
created() {},
methods: {
init(det) {
this.tableData = det;
//返回计划名和id
const arr = det.map((item) => {
const obj = {
planName: item.schedulingPlanName,
planId: item.schedulingPlanId,
};
return obj;
});
//去重
const map = new Map();
arr.forEach((item) => {
if (!map.has(item.planId)) {
map.set(item.planId, item);
}
});
this.groupClassArr = Array.from(map.values());
},
},
};
</script>

View File

@@ -0,0 +1,567 @@
<!--
* @Author: zwq
* @Date: 2025-10-23 13:43:55
* @LastEditors: zwq
* @LastEditTime: 2025-10-24 11:14:55
* @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>
</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'">
提示排班日历仅展示当前选中部门的节假日设置若休假日中仍有排班则表示下级组织已通过自定义假期进行排班
</span>
<span v-else>
组长:{{ showTeamName?.leaderName || '-' }} 组长电话:{{
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>
<!-- 显示排班 -->
<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
)
"
:title="
item.classesName +
' | ' +
item.teamName +
' | ' +
item.workTime
">
{{
item.classesName +
' | ' +
item.teamName +
' | ' +
item.workTime
}}
</div>
</el-tooltip>
</el-col>
</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>
</el-row>
</div>
</template>
</el-calendar>
</div>
</div>
</el-col>
<base-dialog
:dialogTitle="dialogTitle"
:dialogVisible="logVisible"
@cancel="cancelLog"
:before-close="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 { getUserProfile } from '@/api/system/user';
import { listByDeptId } from '@/api/group/Schedule';
import { getEnableData } from '@/api/group/holidaySetting';
import {
getDeptSchedulingList,
getClassSchedulingList,
} from '@/api/group/calendar';
import holidayLog from './holidayLog';
export default {
name: '',
components: {
holidayLog,
},
data() {
return {
activeName: 'first',
startDay: '', // 查询参数
HolidayList: [],
// 选择的部门名称
showDeptName: undefined,
showTeamName: {},
// 部门树选项
deptOptions: undefined,
groupOptions: undefined,
// 查询的部门名称
deptName: undefined,
groupName: undefined,
topDept: {}, // 保存当前用户的部门id为最高级部门id
deptId: undefined,
teamId: undefined,
defaultProps: {
children: 'children',
label: 'name',
},
logVisible: false,
dialogTitle: '',
holidayDivColor: ['#67c23a', '#69d983', '#f5c931', '#4fa6f0'],
};
},
watch: {
// 根据名称筛选部门树
deptName(val) {
this.$refs.tree.filter(val);
},
groupName(val) {
this.$refs.tree1.filter(val);
},
},
created() {
this.startDay = new Date();
// 查询用户个人信息
getUserProfile().then((response) => {
this.showDeptName = response.data.dept.name || '';
this.topDept = {
name: response.data.dept.name || '',
id: response.data.dept.id || '',
}; // 保存当前用户的部门为最高级部门id
this.deptId = response.data.dept.id || '';
this.getHolidayPage();
this.getTreeselect();
});
},
methods: {
getHolidayPage() {
const now = new Date(this.startDay);
const year = now.getFullYear();
const month = now.getMonth();
const startTime = `${year}-${String(month + 1).padStart(2, '0')}-01`;
const lastDate = new Date(year, month + 1, 0).getDate();
const endTime = `${year}-${String(month + 1).padStart(2, '0')}-${String(
lastDate
).padStart(2, '0')}`;
this.HolidayList = [];
if (this.activeName == 'first') {
getDeptSchedulingList({
deptId: this.deptId,
startTime: Date.parse(startTime),
endTime: Date.parse(endTime),
}).then((res) => {
this.HolidayList = res.data;
});
} else {
getClassSchedulingList({
teamId: this.teamId,
startTime: Date.parse(startTime),
endTime: Date.parse(endTime),
}).then((res) => {
this.HolidayList = res.data;
});
}
},
// 切换月份
selectMonth() {
this.getHolidayPage();
},
/** 查询部门下拉树结构 */
getTreeselect() {
getEnableData().then((response) => {
// 处理 deptOptions 参数
this.deptOptions = [];
this.deptOptions.push(...this.handleTree(response.data, 'id'));
});
listByDeptId(this.topDept.id).then((response) => {
this.groupOptions = [];
this.groupOptions.push(...response.data);
});
},
// 筛选节点
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
tabsClick() {
if (this.activeName == 'second' && this.groupOptions.length > 0) {
this.teamId = this.groupOptions[0].id;
this.showTeamName = {
leaderName: this.groupOptions[0].leaderName || '-',
leaderPhone: this.groupOptions[0].leaderPhone || '-',
};
} else if (this.activeName == 'first') {
this.deptId = this.topDept.id;
}
this.getHolidayPage();
},
// 节点单击事件
handleNodeClick(data) {
this.deptId = data.id;
this.showDeptName = data.name;
this.getHolidayPage();
},
handleNodeClick1(data) {
this.teamId = data.id;
this.showTeamName = {
leaderName: data.leaderName || '-',
leaderPhone: data.leaderPhone || '-',
};
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 '';
}
},
holidayLog(det, date) {
this.dialogTitle = this.showDeptName + '-' + date + '-排班详情';
this.logVisible = true;
this.$nextTick(() => {
this.$refs.holidayLogRef.init(det);
});
},
cancelLog() {
this.dialogTitle = '';
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;
overflow: hidden;
.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;
position: relative;
.subIcon {
position: absolute;
top: -18px;
right: -10px;
font-size: 10px;
}
}
.holiday-div {
background-color: #67c23a;
border-radius: 3px;
height: 25px;
text-align: center;
font-size: 12px;
line-height: 24px;
color: #fff;
margin-bottom: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
}
}
</style>

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>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
<!--
* @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 {
title: ['待选', '已选'],
mode: 'transfer',
fromData: [], //左边内容
toData: [], //右边已选内容
};
},
methods: {
init(val) {
this._pageIndex = val._pageIndex-1;
this.fromData = [];
this.toData = [];
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._pageIndex,this.toData);
},
},
};
</script>

View File

@@ -0,0 +1,187 @@
<!--
* @Author: zwq
* @Date: 2025-10-21 14:27:23
* @LastEditors: zwq
* @LastEditTime: 2025-10-21 15:02:27
* @Description:
-->
<template>
<div>
<div class="info-div">
<div>
<span class="title">计划编号:</span>
{{ infoData.code }}
</div>
<div>
<span class="title">计划状态:</span>
{{ ['', '草稿', '已确认', '已作废'][infoData.status] }}
</div>
<div>
<span class="title">计划名称:</span>
{{ infoData.name }}
</div>
<div>
<span class="title">部门:</span>
{{ infoData.deptName }}
</div>
<div>
<span class="title">开始时间:</span>
{{ parseTime(infoData.startDay) }}
</div>
<div>
<span class="title">结束时间:</span>
{{ parseTime(infoData.endDay) }}
</div>
<div>
<span class="title">周末休假方式:</span>
{{ ['', '双休', '周六休', '周日休', '不休'][infoData.weekType] }}
</div>
<div>
<span class="title">倒班方式:</span>
{{ ['', '长白班', '两班倒', '三班倒'][infoData.shiftType] }}
</div>
<div>
<span class="title">同班次连排:</span>
{{ infoData.shiftSustainedNum }}
</div>
<div>
<span class="title">创建人:</span>
{{ infoData.creator }}
</div>
<div>
<span class="title">创建时间:</span>
{{ parseTime(infoData.createTime) }}
</div>
<div v-if="infoData.status == 3">
<span class="title">作废时间:</span>
{{ parseTime(infoData.disableTime) }}
</div>
<div>
<span class="title">备注:</span>
{{ infoData.remark }}
</div>
</div>
<div>
<el-tabs v-model="activeName" stretch>
<el-tab-pane label="班次" name="first"></el-tab-pane>
<el-tab-pane label="班组" name="second"></el-tab-pane>
</el-tabs>
<base-table
v-if="activeName == 'first'"
:table-props="tableProps1"
:page="1"
:limit="10"
:table-data="tableData1"></base-table>
<base-table
v-else
:table-props="tableProps2"
:page="1"
:limit="10"
:table-data="tableData2"></base-table>
</div>
</div>
</template>
<script>
import { parseTime } from '@/filter/code-filter';
import { getPlan } from '@/api/group/Schedule';
const tableProps1 = [
{
prop: 'name',
label: '班次名称',
},
{
prop: 'startTime',
label: '开始时间',
filter: (val) => {
return val ? val.slice(0, -3) : '-';
},
},
{
prop: 'endTime',
label: '结束时间',
filter: (val) => {
return val ? val.slice(0, -3) : '-';
},
},
{
prop: 'remark',
label: '备注',
},
];
const tableProps2 = [
{
prop: 'code',
label: '班组编号',
width: 140,
},
{
prop: 'name',
label: '班组名称',
},
{
prop: 'isProduction',
label: '是否生产班组',
filter: (val) => {
return val ? '是' : '否';
},
width: 110,
},
{
prop: 'lineName',
label: '产线及工段',
},
];
export default {
data() {
return {
infoData: {},
activeName: 'first',
tableProps1,
tableData1: [],
tableProps2,
tableData2: [],
};
},
methods: {
init(id) {
this.infoData = {};
getPlan(id).then((res) => {
this.infoData = res.data || {};
console.log(res);
this.tableData1 = res.data?.groupPlanClassesBaseVOList;
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));
});
});
},
//提取绑定的产线工段名展示出来
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;
}
});
return lineName;
},
},
};
</script>
<style scoped>
.info-div {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 10px;
}
.title {
font-weight: bold;
}
</style>

View File

@@ -0,0 +1,114 @@
<!--
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: zwq
* @LastEditTime: 2025-10-20 10:23:52
* @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="startTime">
<el-time-picker
style="width: 100%"
format="H:mm"
value-format="HH:mm:ss"
v-model="dataForm.startTime"
placeholder="选择日期时间"></el-time-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束时间" prop="endTime">
<el-time-picker
style="width: 100%"
format="H:mm"
value-format="HH:mm:ss"
v-model="dataForm.endTime"
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,
startTime: undefined,
endTime: undefined,
remark: undefined,
},
_pageIndex: 1,
dataRule: {
index: [{ required: true, message: '序号不能为空', trigger: 'blur' }],
name: [
{ required: true, message: '班次名称不能为空', trigger: 'blur' },
],
startTime: [
{ required: true, message: '开始时间不能为空', trigger: 'change' },
],
endTime: [
{ 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.startTime = val.startTime || undefined;
this.dataForm.endTime = val.endTime || 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,542 @@
<!--
* @Author: zwq
* @Date: 2025-10-11 14:27:37
* @LastEditors: zwq
* @LastEditTime: 2025-10-23 16:31:41
* @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="270"
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="successSubmit">
保存草稿
</el-button>
<el-button
type="primary"
class="btnTextStyle"
size="small"
@click="handleConfirm">
{{ stepNum < 3 ? '下一步' : '确认并执行' }}
</el-button>
</el-col>
</el-row>
</slot>
</template>
</base-dialog>
<base-dialog
:dialogTitle="'排班计划详情'"
:dialogVisible="detailVisible"
@cancel="detailCancel"
:before-close="detailCancel"
:destroy-on-close="true"
append-to-body
width="50%">
<detail ref="detailRef"></detail>
<template #footer>
<slot name="footer">
<el-row slot="footer" type="flex" justify="end">
<el-col :span="24">
<el-button
size="small"
class="btnTextStyle"
@click="detailCancel">
取消
</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 detail from './detail.vue';
import { parseTime } from '@/filter/code-filter';
import {
getGroupPlanPage,
deleteGroupPlan,
copyPlan,
disablePlan,
updateScheduleLater,
} 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: 'deptName',
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, // 新增编辑时当前第几步
detailVisible: false,
};
},
components: {
AddOrUpdate,
deptSelect,
detail
},
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;
},
successSubmit() {
this.addOrUpdateVisible = false;
this.addOrEditTitle = '';
this.stepNum = 1;
this.getDataList();
},
// dialog取消
handleCancel() {
this.$refs.addOrUpdate.cancelStep();
},
handleConfirm(val) {
if (val == 'up') {
this.$refs.addOrUpdate.upSubmit();
} else {
this.$refs.addOrUpdate.nextSubmit();
}
},
//tableBtn点击
handleClick(val) {
if (val.type === 'edit') {
this.addOrUpdateVisible = true;
this.addOrEditTitle = '编辑';
this.$nextTick(() => {
this.$refs.addOrUpdate.init(val.data.id);
});
} else if (val.type === 'delete') {
this.deleteHandle(val.data.id, val.data.name, val.data._pageIndex);
} else if (val.type === 'detail') {
this.detailVisible = true;
this.$nextTick(() => {
this.$refs.detailRef.init(val.data.id);
});
} else if (val.type === 'cancel') {
disablePlan(val.data.id).then((res) => {
this.$modal.msgSuccess('作废成功');
this.getDataList()
});
} else if (val.type === 'sync') {
updateScheduleLater({ planId: val.data.id }).then((res) => {
this.$modal.msgSuccess('同步节假日成功');
this.getDataList()
});
} else if (val.type === 'copy') {
copyPlan(val.data.id).then((res) => {
this.$modal.msgSuccess('复制成功');
this.getDataList()
});
}
},
detailCancel() {
this.detailVisible = false;
},
},
};
</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,36 @@
<!--
* @Author: zwq
* @Date: 2025-10-13 16:18:41
* @LastEditors: zwq
* @LastEditTime: 2025-10-21 13:54:52
* @Description:
-->
<template>
<span>
<el-tag
v-if="injectData.work"
size="small"
:type="['', 'success', 'warning'][injectData.sort]">
{{ injectData[injectData.prop] }}
</el-tag>
<el-tag v-else effect="dark" size="small" type="info">
{{ injectData[injectData.prop] }}
</el-tag>
</span>
</template>
<script>
export default {
props: {
injectData: {
type: Object,
default: () => ({}),
},
},
data() {
return {};
},
created() {},
methods: {},
};
</script>

View File

@@ -0,0 +1,33 @@
<!--
* @Author: zwq
* @Date: 2025-10-13 16:18:41
* @LastEditors: zwq
* @LastEditTime: 2025-10-20 10:13:26
* @Description:
-->
<template>
<div>
<div>
{{ injectData.name.split('/')[0] }}
</div>
<div>
{{ injectData.name.split('/')[1] }}
</div>
</div>
</template>
<script>
export default {
props: {
injectData: {
type: Object,
default: () => ({}),
},
},
data() {
return {};
},
created() {},
methods: {},
};
</script>

View File

@@ -0,0 +1,33 @@
<!--
* @Author: zwq
* @Date: 2025-10-13 16:18:41
* @LastEditors: zwq
* @LastEditTime: 2025-10-20 10:13:26
* @Description:
-->
<template>
<span>
{{
injectData.shiftSustainedNum +
(injectData.shiftSustainedType
? ['', '日', '周', '月', '季'][injectData.shiftSustainedType]
: '')
}}
</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

@@ -6,224 +6,208 @@
* @Description:
-->
<template>
<div>
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<el-table id="detail" :data="tableData" :header-cell-style="{
<div>
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick" />
<el-table
id="detail"
:data="tableData"
:header-cell-style="{
background: '#F2F4F9',
color: '#606266',
}" border v-loading="dataListLoading" style="width: 100%" ref="dataList">
<el-table-column prop="lineName" label="产线" align="center" />
<!-- <el-table-column prop="sizes" width="105" showOverflowtooltip align="center" label="规格" /> -->
<!-- <el-table-column prop="process" label="产品工艺" align="center" /> -->
<el-table-column prop="inputN" label="磨边" align="center">
<el-table-column prop="edgeNum" label="投入数量/片" />
<el-table-column prop="edgeTime" label="数据上报时间">
<template v-slot="scope">
<span>
{{
scope.row.reportType === 0
? parseTime(scope.row.edgeTime)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="outputN" label="打孔/丝印" align="center">
<el-table-column prop="drillCoating" label="投入数量">
<template v-slot="scope">
<span>
{{
(scope.row.drillNum ?? '-')
+ '/'
+ (scope.row.coatingNum ?? '-')
}}
</span>
</template>
</el-table-column>
<el-table-column prop="coatingTime" label="数据上报时间">
<template v-slot="scope">
<span>
{{
scope.row.reportType === 0
? parseTime(scope.row.coatingTime)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="lossN" label="镀膜" align="center">
<el-table-column prop="silkNum" label="投入数量" />
<el-table-column prop="silkTime" label="数据上报时间">
<template v-slot="scope">
<span>
{{
scope.row.reportType === 0
? parseTime(scope.row.silkTime)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="lossN" label="钢化" align="center">
<el-table-column prop="temperingNum" label="投入数量" />
<el-table-column prop="temperingTime" label="数据上报时间">
<template v-slot="scope">
<span>
{{
scope.row.reportType === 0
? parseTime(scope.row.temperingTime)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="lossN" label="包装" align="center">
<el-table-column prop="packingNum" label="投入数量" />
<el-table-column prop="packingTime" label="数据上报时间">
<template v-slot="scope">
<span>
{{
scope.row.reportType === 0
? parseTime(scope.row.packingTime)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<!-- <el-table-column prop="lossRatio" label="不良率/%">
<template v-slot="scope">
<span>
{{
scope.row.lossRatio != null ? scope.row.lossRatio.toFixed(2) : '-'
}}
</span>
</template>
</el-table-column>
<el-table-column prop="outputRatio" label="投入产出率/%">
<template v-slot="scope">
<span>
{{
scope.row.outputRatio != null
? scope.row.outputRatio.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
<el-table-column prop="processingRatio" label="加工成品率/%">
<template v-slot="scope">
<span>
{{
scope.row.processingRatio != null
? scope.row.processingRatio.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
<el-table-column prop="lossD" label="不良详情" align="center">
<el-table-column prop="original" label="原片" align="center">
<el-table-column prop="originalLossNum" label="原片不良/片" />
<el-table-column prop="originalLossArea" label="原片不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.originalLossArea != null
? scope.row.originalLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="edge" label="磨边" align="center">
<el-table-column prop="edgeLossNum" label="磨边不良/片" />
<el-table-column prop="edgeLossArea" label="磨边不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.edgeLossArea != null
? scope.row.edgeLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="drill" label="打孔" align="center">
<el-table-column prop="drillLossNum" label="打孔不良/片" />
<el-table-column prop="drillLossArea" label="打孔不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.drillLossArea != null
? scope.row.drillLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="coating" label="镀膜" align="center">
<el-table-column prop="coatingLossNum" label="镀膜不良/片" />
<el-table-column prop="coatingLossArea" label="镀膜不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.coatingLossArea != null
? scope.row.coatingLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="silk" label="丝印" align="center">
<el-table-column prop="silkLossNum" label="丝印不良/片" />
<el-table-column prop="silkLossArea" label="丝印不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.silkLossArea != null
? scope.row.silkLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="tempering" label="钢化" align="center">
<el-table-column prop="temperingLossNum" label="钢化不良/片" />
<el-table-column prop="temperingLossArea" label="钢化不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.temperingLossArea != null
? scope.row.temperingLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column> -->
<el-table-column prop="down" label="下片" align="center">
<el-table-column prop="downNum" label="成品数量" />
<el-table-column prop="scrapNum" label="废片数量" />
<el-table-column prop="inputOutputRate" label="投入产出率" />
<el-table-column prop="yieldRate" label="加工成品率" />
</el-table-column>
</el-table-column>
</el-table>
</div>
}"
border
:span-method="arraySpanMethod"
v-loading="dataListLoading"
style="width: 100%"
ref="dataList">
<el-table-column prop="lineName" label="产线" align="center" />
<el-table-column
prop="sizes"
width="105"
showOverflowtooltip
align="center"
label="规格" />
<el-table-column prop="process" label="产品工艺" align="center" />
<el-table-column prop="inputN" label="投入" align="center">
<el-table-column prop="inputNum" label="投入数量/片" />
<el-table-column prop="inputArea" label="投入面积/m²">
<template v-slot="scope">
<span>
{{
scope.row.inputArea != null
? scope.row.inputArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="outputN" label="产出" align="center">
<el-table-column prop="outputNum" label="产出数量/片" />
<el-table-column prop="outputArea" label="产出面积/m²">
<template v-slot="scope">
<span>
{{
scope.row.outputArea != null
? scope.row.outputArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="lossN" label="不良" align="center">
<el-table-column prop="lossNum" label="不良数量/片" />
<el-table-column prop="lossArea" label="不良面积/m²">
<template v-slot="scope">
<span>
{{
scope.row.lossArea != null ? scope.row.lossArea.toFixed(2) : '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="lossRatio" label="不良率/%">
<template v-slot="scope">
<span>
{{
scope.row.lossRatio != null ? scope.row.lossRatio.toFixed(2) : '-'
}}
</span>
</template>
</el-table-column>
<el-table-column prop="outputRatio" label="投入产出率/%">
<template v-slot="scope">
<span>
{{
scope.row.outputRatio != null
? scope.row.outputRatio.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
<el-table-column prop="processingRatio" label="加工成品率/%">
<template v-slot="scope">
<span>
{{
scope.row.processingRatio != null
? scope.row.processingRatio.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
<el-table-column prop="lossD" label="不良详情" align="center">
<el-table-column prop="original" label="原片" align="center">
<el-table-column prop="originalLossNum" label="原片不良/片" />
<el-table-column prop="originalLossArea" label="原片不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.originalLossArea != null
? scope.row.originalLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="edge" label="磨边" align="center">
<el-table-column prop="edgeLossNum" label="磨边不良/片" />
<el-table-column prop="edgeLossArea" label="磨边不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.edgeLossArea != null
? scope.row.edgeLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="drill" label="打孔" align="center">
<el-table-column prop="drillLossNum" label="打孔不良/片" />
<el-table-column prop="drillLossArea" label="打孔不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.drillLossArea != null
? scope.row.drillLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="coating" label="镀膜" align="center">
<el-table-column prop="coatingLossNum" label="镀膜不良/片" />
<el-table-column prop="coatingLossArea" label="镀膜不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.coatingLossArea != null
? scope.row.coatingLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="silk" label="丝印" align="center">
<el-table-column prop="silkLossNum" label="丝印不良/片" />
<el-table-column prop="silkLossArea" label="丝印不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.silkLossArea != null
? scope.row.silkLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="tempering" label="钢化" align="center">
<el-table-column prop="temperingLossNum" label="钢化不良/片" />
<el-table-column prop="temperingLossArea" label="钢化不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.temperingLossArea != null
? scope.row.temperingLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column prop="packing" label="包装" align="center">
<el-table-column prop="packingLossNum" label="包装不良/片" />
<el-table-column prop="packingLossArea" label="包装不良/m²">
<template v-slot="scope">
<span>
{{
scope.row.packingLossArea != null
? scope.row.packingLossArea.toFixed(2)
: '-'
}}
</span>
</template>
</el-table-column>
</el-table-column>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getTeamReportPageDet, exportGroupProductReportExcel } from '@/api/core/monitoring/auto';
import { getTeamReportPageDet } from '@/api/core/monitoring/auto';
import * as XLSX from 'xlsx';
import FileSaver from 'file-saver';
@@ -231,8 +215,7 @@ export default {
components: {},
data() {
return {
tableData: [],
id:null,
tableData: [],
dataListLoading: false,
formConfig: [
{
@@ -250,8 +233,7 @@ export default {
mounted() {},
methods: {
// 获取数据列表
init(id) {
this.id = id
init(id) {
this.dataListLoading = true;
getTeamReportPageDet(id).then((response) => {
this.tableData = response.data?.map((item, index) => {
@@ -277,17 +259,17 @@ export default {
this.dataListLoading = false;
});
},
// arraySpanMethod({ row, column, rowIndex, columnIndex }) {
// if (row.isSummaryReport) {
// if (columnIndex === 0) {
// return [1, 3];
// } else if (columnIndex === 1) {
// return [0, 0];
// } else if (columnIndex === 2) {
// return [0, 0];
// }
// }
// },
arraySpanMethod({ row, column, rowIndex, columnIndex }) {
if (row.isSummaryReport) {
if (columnIndex === 0) {
return [1, 3];
} else if (columnIndex === 1) {
return [0, 0];
} else if (columnIndex === 2) {
return [0, 0];
}
}
},
buttonClick(val) {
switch (val.btnName) {
@@ -298,33 +280,27 @@ export default {
console.log(val);
}
},
handleExport() {
this.$modal.confirm('是否确认导出').then(() => {
return exportGroupProductReportExcel({id:this.id});
}).then(response => {
console.log(response)
this.$download.excel(response, '班组生产报表-详情.xls');
}).catch(() => {})
// let tables = document.querySelector('#detail').cloneNode(true);
// let exportTable = XLSX.utils.table_to_book(tables);
handleExport() {
let tables = document.querySelector('#detail').cloneNode(true);
let exportTable = XLSX.utils.table_to_book(tables);
// var exportTableOut = XLSX.write(exportTable, {
// bookType: 'xlsx',
// bookSST: true,
// type: 'array',
// });
// // sheetjs.xlsx为导出表格的标题名称
// try {
// FileSaver.saveAs(
// new Blob([exportTableOut], {
// type: 'application/octet-stream',
// }),
// '班组生产报表-详情.xlsx'
// );
// } catch (e) {
// if (typeof console !== 'undefined') console.log(e, exportTableOut);
// }
// return exportTableOut;
var exportTableOut = XLSX.write(exportTable, {
bookType: 'xlsx',
bookSST: true,
type: 'array',
});
// sheetjs.xlsx为导出表格的标题名称
try {
FileSaver.saveAs(
new Blob([exportTableOut], {
type: 'application/octet-stream',
}),
'班组生产报表-详情.xlsx'
);
} catch (e) {
if (typeof console !== 'undefined') console.log(e, exportTableOut);
}
return exportTableOut;
},
},
};

View File

@@ -74,7 +74,7 @@ const tableProps = [
fixed: true
},
{
prop: 'reportDate',
prop: 'reportName',
label: '日期',
width: 180,
fixed: true
@@ -89,241 +89,211 @@ const tableProps = [
label: '班组',
fixed: true
},
{
prop: 'edgeNum',
label: '磨边',
fixed: true
}, {
prop: 'drillOrCoating',
label: '打孔/丝印',
fixed: true
}, {
prop: 'silkNum',
label: '镀膜',
fixed: true
}, {
prop: 'temperingNum',
label: '钢化',
fixed: true
}, {
prop: 'packingNum',
label: '包装',
fixed: true
},
{
prop: 'inputN',
label: '下片',
label: '投入',
align: 'center',
children: [
{
prop: 'downNum',
label: '成品数量',
prop: 'inputNum',
label: '投入数量/片',
width:100
},
{
prop: 'scrapNum',
label: '废片数量',
prop: 'inputArea',
label: '投入面积/m²',
width:100
},
{
prop: 'inputOutputRate',
label: '投入产出率',
width: 100
},
{
prop: 'yieldRate',
label: '加工成品率',
width: 100
},
},
],
},
{
prop: 'outputN',
label: '产出',
align: 'center',
children: [
{
prop: 'outputNum',
label: '产出数量/片',
width:100
},
{
prop: 'outputArea',
label: '产出面积/㎡',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
],
},
{
prop: 'lossN',
label: '不良',
align: 'center',
children: [
{
prop: 'lossNum',
label: '不良数量/片',
width:100
},
{
prop: 'lossArea',
label: '不良面积/㎡',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
],
},
{
prop: 'lossRatio',
label: '不良率/%',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
{
prop: 'outputRatio',
label: '投入产出率/%',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:110
},
{
prop: 'processingRatio',
label: '加工成品率/%',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:110
},
{
prop: 'lossD',
label: '不良详情',
align: 'center',
children: [
{
prop: 'original',
label: '原片',
align: 'center',
children: [
{
prop: 'originalLossNum',
label: '原片不良/片',
width:100
},
{
prop: 'originalLossArea',
label: '原片不良/㎡',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
],
},
{
prop: 'edge',
label: '磨边',
align: 'center',
children: [
{
prop: 'edgeLossNum',
label: '磨边不良/片',
width:100
},
{
prop: 'edgeLossArea',
label: '磨边不良/㎡',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
],
},
{
prop: 'drill',
label: '打孔',
align: 'center',
children: [
{
prop: 'drillLossNum',
label: '打孔不良/片',
width:100
},
{
prop: 'drillLossArea',
label: '打孔不良/㎡',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
],
},
{
prop: 'coating',
label: '镀膜',
align: 'center',
children: [
{
prop: 'coatingLossNum',
label: '镀膜不良/片',
width:100
},
{
prop: 'coatingLossArea',
label: '镀膜不良/㎡',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
],
},
{
prop: 'silk',
label: '丝印',
align: 'center',
children: [
{
prop: 'silkLossNum',
label: '丝印不良/片',
width:100
},
{
prop: 'silkLossArea',
label: '丝印不良/㎡',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
],
},
{
prop: 'tempering',
label: '钢化',
align: 'center',
children: [
{
prop: 'temperingLossNum',
label: '钢化不良/片',
width:100
},
{
prop: 'temperingLossArea',
label: '钢化不良/㎡',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
],
},
{
prop: 'packing',
label: '包装',
align: 'center',
children: [
{
prop: 'packingLossNum',
label: '包装不良/片',
width:100
},
{
prop: 'packingLossArea',
label: '包装不良/㎡',
filter: (val) => (val != null ? val.toFixed(2) : '-'),
width:100
},
],
},
],
},
// {
// prop: 'outputN',
// label: '产出',
// align: 'center',
// children: [
// {
// prop: 'outputNum',
// label: '产出数量/片',
// width:100
// },
// {
// prop: 'outputArea',
// label: '产出面积/㎡',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// ],
// },
// {
// prop: 'lossN',
// label: '不良',
// align: 'center',
// children: [
// {
// prop: 'lossNum',
// label: '不良数量/片',
// width:100
// },
// {
// prop: 'lossArea',
// label: '不良面积/㎡',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// ],
// },
// {
// prop: 'lossRatio',
// label: '不良率/%',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// {
// prop: 'outputRatio',
// label: '投入产出率/%',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:110
// },
// {
// prop: 'processingRatio',
// label: '加工成品率/%',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:110
// },
// {
// prop: 'lossD',
// label: '不良详情',
// align: 'center',
// children: [
// {
// prop: 'original',
// label: '原片',
// align: 'center',
// children: [
// {
// prop: 'originalLossNum',
// label: '原片不良/片',
// width:100
// },
// {
// prop: 'originalLossArea',
// label: '原片不良/㎡',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// ],
// },
// {
// prop: 'edge',
// label: '磨边',
// align: 'center',
// children: [
// {
// prop: 'edgeLossNum',
// label: '磨边不良/片',
// width:100
// },
// {
// prop: 'edgeLossArea',
// label: '磨边不良/㎡',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// ],
// },
// {
// prop: 'drill',
// label: '打孔',
// align: 'center',
// children: [
// {
// prop: 'drillLossNum',
// label: '打孔不良/片',
// width:100
// },
// {
// prop: 'drillLossArea',
// label: '打孔不良/㎡',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// ],
// },
// {
// prop: 'coating',
// label: '镀膜',
// align: 'center',
// children: [
// {
// prop: 'coatingLossNum',
// label: '镀膜不良/片',
// width:100
// },
// {
// prop: 'coatingLossArea',
// label: '镀膜不良/㎡',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// ],
// },
// {
// prop: 'silk',
// label: '丝印',
// align: 'center',
// children: [
// {
// prop: 'silkLossNum',
// label: '丝印不良/片',
// width:100
// },
// {
// prop: 'silkLossArea',
// label: '丝印不良/㎡',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// ],
// },
// {
// prop: 'tempering',
// label: '钢化',
// align: 'center',
// children: [
// {
// prop: 'temperingLossNum',
// label: '钢化不良/片',
// width:100
// },
// {
// prop: 'temperingLossArea',
// label: '钢化不良/㎡',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// ],
// },
// {
// prop: 'packing',
// label: '包装',
// align: 'center',
// children: [
// {
// prop: 'packingLossNum',
// label: '包装不良/片',
// width:100
// },
// {
// prop: 'packingLossArea',
// label: '包装不良/㎡',
// filter: (val) => (val != null ? val.toFixed(2) : '-'),
// width:100
// },
// ],
// },
// ],
// },
];
export default {
@@ -339,22 +309,17 @@ export default {
listQuery: {
reportType: 1,
pageSize: 10,
pageNo: 1,
startTime: undefined,
endTime: undefined,
pageNo: 1,
total: 1,
},
fileName: '',
dataListLoading: false,
tableProps,
tableBtn: [
this.$auth.hasPermi(`monitoring:group-off:update`)
? {
type: 'eq',
btnName: '详情',
}
: undefined,
tableBtn: [
{
type: 'eq',
btnName: '详情',
},
].filter((v) => v),
showData: [],
tableData: [],
@@ -374,19 +339,15 @@ export default {
{
type: 'select',
label: '报表类型',
selectOptions: [
{
id: 0,
name: '班',
},
selectOptions: [
{
id: 1,
name: '日',
},
// {
// id: 2,
// name: '周',
// },
{
id: 2,
name: '周',
},
{
id: 3,
name: '月',
@@ -403,16 +364,15 @@ export default {
type: 'datePicker',
label: '时间范围',
dateType: 'daterange',
format: 'yyyy-MM-dd',
valueFormat: 'timestamp',
format: 'yyyy-MM-dd',
valueFormat: 'timestamp',
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'timeVal',
},
{
type: this.$auth.hasPermi('monitoring:group-off:query') ? 'button' : '',
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary',
@@ -421,8 +381,8 @@ export default {
type: 'separate',
},
{
type: this.$auth.hasPermi('monitoring:group-off:export') ? 'button' : '',
// type: 'button',
// type: this.$auth.hasPermi('base:factory:export') ? 'button' : '',
type: 'button',
btnName: '导出',
name: 'export',
color: 'warning',
@@ -442,9 +402,7 @@ export default {
// 设置为23:59:59
const end = new Date(yesterday.getTime());
end.setHours(23, 59, 59, 59);
this.listQuery.startTime = yesterday.getTime(), [end.getTime()];
this.listQuery.endTime = end.getTime()
this.listQuery.reportStartTime = [yesterday.getTime(),end.getTime()];
this.$nextTick(() => {
this.$refs.searchBarForm.formInline.timeVal = [yesterday.getTime(),end.getTime()];
});
@@ -503,12 +461,9 @@ export default {
this.listQuery.factoryId = val.factoryId || undefined;
this.listQuery.teamId = val.teamId || undefined;
this.listQuery.reportType = val.reportType || undefined;
this.listQuery.startTime = val.timeVal
? val.timeVal[0]
: undefined;
this.listQuery.endTime = val.timeVal
? val.timeVal[1]
: undefined;
this.listQuery.reportStartTime = val.timeVal
? val.timeVal
: undefined;
this.getDataList();
break;
case 'export':
@@ -520,10 +475,8 @@ export default {
},
// 获取数据列表
getDataList() {
this.dataListLoading = true;
console.log(this.listQuery);
const arr = ['班', '日', '', '月', '年']; // 索引0对应班1对应日3对应月4对应年索引2留空
this.dataListLoading = true;
const arr = ['日', '周', '月', '年'];
this.urlOptions.getDataListURL(this.listQuery).then((response) => {
if (!response.data.list) {
this.showData = [];
@@ -531,8 +484,7 @@ export default {
return;
}
this.tableData = response.data?.list.map((item, index) => {
item.reportType = arr[item.reportType] || item.reportType;
item.drillOrCoating = item.drillNum + "/" + item.coatingNum
item.reportType = arr[item.reportType - 1];
item.originalLossNum = item.original?.lossNum;
item.originalLossArea = item.original?.lossArea;
item.edgeLossNum = item.edge?.lossNum;
@@ -556,7 +508,7 @@ export default {
},
handleClick(val) {
this.addOrUpdateVisible = true;
const time = val.data.timeVal ? parseTime(val.data.timeVal[0]) + '-' + parseTime(val.data.timeVal[1]) : '- '
const time = val.data.reportName?val.data.reportName:'- '
const teamName = val.data.teamName?val.data.teamName:'- '
const teamLeader = val.data.teamLeader?val.data.teamLeader:'- '
this.addOrEditTitle =

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-24 09:11:11
* @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: 'deptName',
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,382 @@
<!--
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: zwq
* @LastEditTime: 2025-10-23 13:29:58
* @Description:
-->
<template>
<div>
<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
v-if="dataForm.dateType == 1"
label="日期"
prop="dateDay">
<el-date-picker
:disabled="detail"
style="width: 100%"
key="date"
v-model="dataForm.dateDay"
type="date"
placeholder="选择日期"
format="yyyy-MM-dd"
value-format="--MM-dd"
:picker-options="pickerOptions"></el-date-picker>
</el-form-item>
<el-form-item v-else label="日期" prop="dateDayArr">
<el-date-picker
:disabled="detail"
style="width: 100%"
key="daterange"
v-model="dataForm.dateDayArr"
type="daterange"
format="yyyy-MM-dd"
value-format="--MM-dd"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"></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-col :span="12" v-if="detail">
<el-form-item label="来源" prop="inherited">
<el-radio-group disabled v-model="dataForm.inherited">
<el-radio :label="true">继承</el-radio>
<el-radio :label="false">自定义</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-dialog
title="删除确认"
:visible.sync="dialogVisible"
width="30%"
append-to-body>
<span>
确认删除该节假日吗
<br />
该节假日已被排班计划使用建议确认是否同步更新排班计划
<br />
此操作不可恢复请确认是否继续
</span>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button @click="onlyDelete">仅删除</el-button>
<el-button type="primary" @click="deleteAndUpdate">
删除并更新
</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {
createHoliday,
updateHoliday,
checkDeleteHoliday,
deleteHoliday,
updateSchedule,
getHoliday,
} from '@/api/group/holidaySetting';
export default {
components: {},
data() {
return {
dataForm: {
id: undefined,
name: undefined,
calendarType: 1,
dateType: 1,
dateDay: undefined,
dateDayArr: [],
repeat: true,
inherited: true, // 是否为继承
deptId: undefined,
},
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',
},
],
},
pickerOptions: {
disabledDate(time) {
return time.getTime() < Date.now();
},
},
dialogVisible: false,
};
},
methods: {
init(deptId, id, detail) {
this.dataForm.deptId = deptId || undefined;
this.dataForm.id = id || undefined;
this.detail = detail || false;
this.$nextTick(() => {
this.$refs['dataForm'].resetFields();
if (this.dataForm.id) {
getHoliday(this.dataForm.id).then((res) => {
this.dataForm = res.data;
if (
this.dataForm.dateType == 1 &&
this.dataForm.calendarType == 1
) {
this.dataForm.dateDay = this.dataForm.oneDay;
} else if (
this.dataForm.dateType == 2 &&
this.dataForm.calendarType == 1
) {
this.dataForm.dateDayArr = [
this.dataForm.startDay,
this.dataForm.endDay,
];
} else if (
this.dataForm.dateType == 1 &&
this.dataForm.calendarType == 2
) {
this.dataForm.dateDay = this.dataForm.oneDayChinese;
} else if (
this.dataForm.dateType == 2 &&
this.dataForm.calendarType == 2
) {
this.dataForm.dateDayArr = [
this.dataForm.startDayChinese,
this.dataForm.endDayChinese,
];
}
});
}
});
},
editHoliday() {
this.detail = false;
},
deleteHoliday() {
//删除节假日基础信息前校验是否影响排班
checkDeleteHoliday(this.dataForm.id).then((res) => {
if (res.data) {
this.dialogVisible = true;
} else {
this.$confirm(
'确认删除该节假日吗?<br/>此操作不可恢复,请确认是否继续',
'删除确认',
{
dangerouslyUseHTMLString: true,
confirmButtonText: '确认删除',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
deleteHoliday(this.dataForm.id).then((res) => {
console.log(res);
this.$modal.msgSuccess('删除成功');
this.$emit('refreshPage');
});
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消',
});
});
}
});
},
onlyDelete() {
deleteHoliday(this.dataForm.id).then((res) => {
this.dialogVisible = false;
this.$modal.msgSuccess('删除成功');
this.$emit('refreshPage');
});
},
deleteAndUpdate() {
deleteHoliday(this.dataForm.id).then((res) => {
this.dialogVisible = false;
this.$modal.msgSuccess('删除成功');
updateSchedule({ logId: res.data.logId }).then((res1) => {
this.$modal.msgSuccess('更新成功');
this.$emit('refreshPage');
});
});
},
// 表单提交
dataFormSubmit() {
this.$refs['dataForm'].validate((valid) => {
if (!valid) {
return false;
}
if (this.dataForm.dateType == 1 && this.dataForm.calendarType == 1) {
this.dataForm.oneDay = this.dataForm.dateDay;
} else if (
this.dataForm.dateType == 2 &&
this.dataForm.calendarType == 1
) {
this.dataForm.startDay = this.dataForm.dateDayArr[0];
this.dataForm.endDay = this.dataForm.dateDayArr[1];
} else if (
this.dataForm.dateType == 1 &&
this.dataForm.calendarType == 2
) {
this.dataForm.oneDayChinese = this.dataForm.dateDay;
} else if (
this.dataForm.dateType == 2 &&
this.dataForm.calendarType == 2
) {
this.dataForm.startDayChinese = this.dataForm.dateDayArr[0];
this.dataForm.endDayChinese = this.dataForm.dateDayArr[1];
}
// 修改的提交
if (this.dataForm.id) {
updateHoliday(this.dataForm).then((response) => {
this.$modal.msgSuccess('修改成功');
this.$emit('refreshPage');
});
return;
}
// 添加的提交
createHoliday(this.dataForm).then((res) => {
updateSchedule;
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: '已取消',
});
});
}
});
});
},
},
};
</script>

View File

@@ -0,0 +1,182 @@
<template>
<div class="app-container">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
:isFold="true"
@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,558 @@
<!--
* @Author: zwq
* @Date: 2024-07-01 14:54:06
* @LastEditors: zwq
* @LastEditTime: 2025-10-23 13:52:52
* @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(),getHolidayPage()">
跳转到今天
</el-button>
</el-form-item>
</el-form>
<el-tag
v-if="topDeptId == deptId"
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'">
<!-- 日期 -->
<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>
</el-col>
<!-- 显示假期名和操作 -->
<el-col
:span="24"
v-if="
HolidayList[Number(data.day.split('-')[2]) - 1]
?.isHoliday
">
<div
class="holiday-div"
@click="
new Date(data.day).getTime() > Date.now() &&
showDetail(
HolidayList[Number(data.day.split('-')[2]) - 1]
.holidayId
)
">
{{
HolidayList[Number(data.day.split('-')[2]) - 1]
?.holidayName
}}
<span
class="holiday-div-btn"
v-if="new Date(data.day).getTime() > Date.now()">
<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>
</el-col>
</el-row>
</div>
</template>
</el-calendar>
</div>
</div>
</el-col>
<base-dialog
:dialogTitle="dialogTitle"
:dialogVisible="addOrUpdateVisible"
@cancel="cancel"
@confirm="handleConfirm"
:before-close="cancel"
:destroy-on-close="true"
width="40%">
<add-or-updata
ref="addOrUpdataRef"
@refreshPage="successSubmit"></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"
:before-close="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 { getUserProfile } from '@/api/system/user';
import { deptHolidayList, getEnableData } from '@/api/group/holidaySetting';
import addOrUpdata from './add-or-updata.vue';
import holidayLog from './holidayLog';
export default {
name: '',
components: {
addOrUpdata,
holidayLog,
},
data() {
return {
startDay: '', // 查询参数
HolidayList: [],
// 部门树选项
deptOptions: undefined,
// 查询的部门名称
deptName: undefined,
// 选择的部门名称
showDeptName: undefined,
topDeptId: undefined, // 保存当前用户的部门id为最高级部门id
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.topDeptId = response.data.dept.id || ''; // 保存当前用户的部门id为最高级部门id
this.deptId = response.data.dept.id || '';
this.getHolidayPage();
});
this.getTreeselect();
},
methods: {
getHolidayPage() {
const now = new Date(this.startDay);
const year = now.getFullYear();
const month = now.getMonth();
const startTime = `${year}-${String(month + 1).padStart(2, '0')}-01`;
const lastDate = new Date(year, month + 1, 0).getDate();
const endTime = `${year}-${String(month + 1).padStart(2, '0')}-${String(
lastDate
).padStart(2, '0')}`;
this.HolidayList = [];
deptHolidayList({
deptId: this.deptId,
startTime: Date.parse(startTime),
endTime: Date.parse(endTime),
}).then((res) => {
this.HolidayList = res.data;
});
},
// 切换月份
selectMonth() {
this.getHolidayPage();
},
/** 查询部门下拉树结构 */
getTreeselect() {
getEnableData().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(this.deptId);
});
},
holidayLog() {
this.logVisible = true;
this.$nextTick(() => {
this.$refs.holidayLogRef.getDataList();
});
},
cancel() {
this.getHolidayPage();
this.addOrUpdateVisible = false;
},
successSubmit() {
this.cancel();
this.getHolidayPage();
},
handleConfirm() {
this.$refs.addOrUpdataRef.dataFormSubmit();
},
showDetail(id) {
this.addOrUpdateVisible = true;
this.detail = true;
this.dialogTitle = '节假日详情';
this.$nextTick(() => {
this.$refs.addOrUpdataRef.init(this.deptId, 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;
}
.holiday-div {
background-color: #67c23a;
border-radius: 3px;
height: 25px;
text-align: center;
line-height: 24px;
color: #fff;
.holiday-div-btn {
float: right;
transform: rotate(90deg);
font-size: 14px;
pointer-events: none;
}
}
}
}
}
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -0,0 +1,152 @@
<template>
<ModelBox name='1-3'>
<div class='count-box'>
<div class='count-tip-box'>
<div class='item'>
<p class='num'> {{ totalDownLoadNumClass ? totalDownLoadNumClass : 0}}</p>
<p class='title'>本班合计</p>
</div>
<div class='split'></div>
<div class='item'>
<p class='num'>{{ totalDownLoadNumDay ? totalDownLoadNumDay : 0 }} </p>
<p class='title'>本日合计</p>
</div>
</div>
<dv-scroll-board ref="locationScrollBoardTable" :config="config" style="width:580px;height:180px;margin-top: 10px;"
class='count-table' />
<span class='split-line' style='left: 70px;'></span>
<span class='split-line' style='left: 195px;'></span>
<span class='split-line' style='left: 320px;'></span>
<span class='split-line' style='left: 445px;'></span>
</div>
</ModelBox>
</template>
<script>
import ModelBox from './ModelBox.vue';
export default {
name: 'Count',
components: {
ModelBox
},
props: {
dataObj: {
type: Array,
default: () => []
}
},
watch: {
dataObj: {
handler(newVal, oldVal) {
console.log(newVal, 'newVal');
if (newVal) {
this.totalGlassNum = 0;
this.tableData = [];
// 累加所有项目的downLoadNumDay
this.totalDownLoadNumDay = 0;
this.totalDownLoadNumClass = 0
// 遍历每条产线数据
newVal.forEach(item => {
// 计算当前项目的glassNum总和
const glassArray = JSON.parse(item.glassNum);
const itemGlassSum = glassArray.reduce((sum, num) => sum + num, 0);
this.tableData.push({
lineName: item.lineName,
glassSum: itemGlassSum,
downLoadNumDay:item.downLoadNumDay,
downLoadNumClass: item.downLoadNumClass
});
// 累加downLoadNumDay
this.totalDownLoadNumDay += item.downLoadNumDay;
this.totalDownLoadNumClass += item.downLoadNumClass;
});
let eqArr = this.tableData.map((item, index) => [
`<span style="color:#000;" >${index+1 || ''}
</span>`,
`<span style="color:#000;" >${item.lineName || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:#000;" >${item.downLoadNumClass || ''}
</span>`,
`<span style="color:#000;">${item.downLoadNumDay || ''}</span>`,
`<span style="color:#000;">${item.glassSum || ''}</span>`,
])
this.config.data = eqArr
this.$refs['locationScrollBoardTable'].updateRows(eqArr)
}
// this.getChart(this.todayClass)
},
deep: true // 深度监听
}
},
data() {
return {
totalDownLoadNumDay: 0,
totalDownLoadNumClass:0,
config:{
header: ['<span style="color:#000;">序号</span>', '<span style="color:#000;">产线</span>', '<span style="color:#000;">本班</span>', '<span style="color:#000;">本日</span>', '<span style="color:#000;">一托玻璃数量</span>'],
headerHeight: 30,
headerBGC: '#E8EDF8',
oddRowBGC:'#E8EDF8',
evenRowBGC:'#fff',
rowNum: 5,
columnWidth: [70, 125, 125, 125, 130],
align : ['center', 'center', 'center', 'center', 'center'],
data: [
]
}
}
},
methods: {
init() {
}
}
}
</script>
<style lang='scss' scoped>
.count-box {
position: relative;
.count-tip-box {
margin-bottom: 20px;
.split {
width: 2px;
height: 40px;
background: #CDD3DF;
display: inline-block;
}
.item {
display: inline-block;
width: 290px;
text-align: center;
p{
margin: 0;
}
.num {
font-weight: 500;
font-size: 28px;
color: #155EFF;
margin-bottom: 5px;
}
.title {
font-size: 14px;
color: rgba(0,0,0,0.5);
line-height: 14px;
}
}
}
.count-table {
box-shadow: 0px 2px 4px 0px #D5DAE6;
}
.split-line {
position: absolute;
top: 77px;
width: 1px;
height: 180px;
background: #CDD3DF;
}
}
</style>

View File

@@ -0,0 +1,102 @@
<template>
<ModelBox name='1-2'>
<div class='eq-alarm'>
<dv-scroll-board ref='locationScrollBoard' :config="config" style="width:580px;height:250px;margin-top: 10px;" />
<span class='split-line' style='left: 66px;'></span>
<span class='split-line' style='left: 226px;'></span>
<span class='split-line' style='left: 366px;'></span>
</div>
</ModelBox>
</template>
<script>
import ModelBox from './ModelBox.vue';
export default {
name: 'EqAlarm',
components: {
ModelBox
},
props: {
dataObj: {
type: Array,
default: () => []
}
},
watch: {
dataObj: {
handler(newVal, oldVal) {
console.log(newVal, 'newVal');
if (newVal) {
this.tableData = newVal
let eqArr = this.tableData.map((item, index) => [
`<span style="color:#000;" >${index + 1 || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:#000;" >${this.formatDate(item.time) || ''}
</span>`,
`<span style="color:#000;">${item.equipmentName || ''}</span>`,
`<span style="color:#000;">${item.content || ''}</span>`,
])
this.config.data = eqArr
this.$refs['locationScrollBoard'].updateRows(eqArr)
}
// this.getChart(this.todayClass)
},
deep: true // 深度监听
}
},
data() {
return {
tableData:[],
config:{
header: ['<span style="color:#000;">序号</span>', '<span style="color:#000;">报警时间</span>', '<span style="color:#000;">报警设备</span>', '<span style="color:#000;">报警内容</span>'],
headerHeight: 25,
headerBGC: '#E8EDF8',
oddRowBGC:'#E8EDF8',
evenRowBGC:'#fff',
rowNum: 8,
columnWidth: [66, 160, 140, 214],
align : ['center', 'center', 'center', 'center'],
data: [
]
}
}
},
methods: {
init() {
},
formatDate(time) {
if (!time) return '';
// 如果是时间戳格式
if (typeof time === 'number' || !isNaN(time)) {
const date = new Date(parseInt(time));
return `${date.getFullYear()}/${(date.getMonth() + 1).toString().padStart(2, '0')}/${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
}
// 如果是ISO字符串格式
if (typeof time === 'string') {
const date = new Date(time);
if (!isNaN(date.getTime())) {
return `${date.getFullYear()}/${(date.getMonth() + 1).toString().padStart(2, '0')}/${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
}
}
return time;
}
}
}
</script>
<style lang='scss' scoped>
.eq-alarm {
position: relative;
.split-line {
position: absolute;
top: 0;
width: 1px;
height: 250px;
background: #CDD3DF;
}
}
</style>

View File

@@ -0,0 +1,60 @@
<template>
<div class='header'>
<span class='title'>生产驾驶舱</span>
<span class='time'>刷新时间{{time}}</span>
<svg-icon icon-class="refresh" class='refresh-btn' @click='refreshData'/>
<el-button type="text" class="screen-btn" @click="changeFullScreen">
<svg-icon v-if="isFullScreen" icon-class="unFullscreen" />
<svg-icon v-else icon-class="fullscreen" />
</el-button>
</div>
</template>
<script>
import moment from 'moment/moment';
export default {
props: {
isFullScreen: false
},
data() {
return {
time: moment().format('HH:mm:ss YYYY.MM.DD')
}
},
methods: {
changeFullScreen() {
this.$emit('screenfullChange');
},
refreshData() {
this.time = moment().format('HH:mm:ss YYYY.MM.DD')
}
}
}
</script>
<style lang='scss' scoped>
.header {
position: relative;
.title {
font-size: 28px;
color:'#000';
letter-spacing: 2px;
margin-right: 10px;
}
.time {
font-size: 14px;
color:'#000';
margin-right: 8px;
}
.refresh-btn {
font-size: 16px;
cursor: pointer;
}
.screen-btn {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
font-size: 32px;
}
}
</style>

View File

@@ -0,0 +1,260 @@
<template>
<ModelBox name='2-2'>
<div class='line-container'>
<div class='table-box'>
<div class='title'>本班次</div>
<dv-scroll-board ref="classToday" :config="classConfig" style="width:600px;height:242px;margin-top: 10px;" />
<span class='split-line' style='left: 66px;'></span>
<span class='split-line' style='left: 186px;'></span>
<span class='split-line' style='left: 311px;'></span>
<span class='split-line' style='left: 437px;'></span>
</div>
<div class='table-box'>
<div class='title'>本日</div>
<dv-scroll-board ref="day" :config="dayConfig" style="width:600px;height:242px;margin-top: 10px;" />
<span class='split-line' style='left: 66px;'></span>
<span class='split-line' style='left: 186px;'></span>
<span class='split-line' style='left: 311px;'></span>
<span class='split-line' style='left: 437px;'></span>
</div>
<div class='table-box'>
<div class='title'>本周</div>
<dv-scroll-board ref="week" :config="weekConfig" style="width:600px;height:242px;margin-top: 10px;" />
<span class='split-line' style='left: 66px;'></span>
<span class='split-line' style='left: 186px;'></span>
<span class='split-line' style='left: 311px;'></span>
<span class='split-line' style='left: 437px;'></span>
</div>
<div class='table-box'>
<div class='title'>本月</div>
<dv-scroll-board ref="month" :config="monthConfig" style="width:600px;height:242px;margin-top: 10px;" />
<span class='split-line' style='left: 66px;'></span>
<span class='split-line' style='left: 186px;'></span>
<span class='split-line' style='left: 311px;'></span>
<span class='split-line' style='left: 437px;'></span>
</div>
</div>
</ModelBox>
</template>
<script>
import ModelBox from './ModelBox.vue';
export default {
name: 'LineInpurAndOutput',
components: {
ModelBox
},
props: {
dataObj: {
type: Object,
default: () => {}
}
},
watch: {
dataObj: {
handler(newVal, oldVal) {
console.log(newVal, 'newVal');
if (newVal) {
this.nowMonth = newVal.todayClass ? newVal.nowMonth.map((item, index) => [
`<span style="color:#000;" >${index + 1 || ''}
</span>`,
`<span style="color:#000;" >${item.name || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:#000;" >${item.inputNum || ''}
</span>`,
`<span style="color:#000;">${item.outputNum || ''}</span>`,
`<div style="width:136px; margin:0px;">
<div style="display:inline-block;height:8px; background:#D8DEEB; border-radius:4px; overflow:hidden;text-align:left;width:90px">
<div style="width:${item.inputOutputRate}%; height:100%; background:#0B58FF;border-radius:4px;"></div>
</div>
<span style="display:inline-block;margin-left:10px; color:#000; font-size:14px;">${item.inputOutputRate}%</span>
</div>`,
]) : []
this.monthConfig.data = this.nowMonth
this.$refs['month'].updateRows(this.nowMonth)
this.nowWeek = newVal.nowWeek ? newVal.nowWeek.map((item, index) => [
`<span style="color:#000;" >${index + 1 || ''}
</span>`,
`<span style="color:#000;" >${item.name || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:#000;" >${item.inputNum || ''}
</span>`,
`<span style="color:#000;">${item.outputNum || ''}</span>`,
`<div style="width:136px; margin:0px;">
<div style="display:inline-block;height:8px; background:#D8DEEB; border-radius:4px; overflow:hidden;text-align:left;width:90px">
<div style="width:${item.inputOutputRate}%; height:100%; background:#0B58FF;border-radius:4px;"></div>
</div>
<span style="display:inline-block;margin-left:10px; color:#000; font-size:14px;">${item.inputOutputRate}%</span>
</div>`,
]) : []
this.weekConfig.data = this.nowWeek
this.$refs['week'].updateRows(this.nowWeek)
this.oneDay = newVal.oneDay ? newVal.oneDay.map((item, index) => [
`<span style="color:#000;" >${index + 1 || ''}
</span>`,
`<span style="color:#000;" >${item.name || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:#000;" >${item.inputNum || ''}
</span>`,
`<span style="color:#000;">${item.outputNum || ''}</span>`,
`<div style="width:136px; margin:0px;">
<div style="display:inline-block;height:8px; background:#D8DEEB; border-radius:4px; overflow:hidden;text-align:left;width:90px">
<div style="width:${item.inputOutputRate}%; height:100%; background:#0B58FF;border-radius:4px;"></div>
</div>
<span style="display:inline-block;margin-left:10px; color:#000; font-size:14px;">${item.inputOutputRate}%</span>
</div>`,
]) : []
this.dayConfig.data = this.oneDay
this.$refs['day'].updateRows(this.oneDay)
this.todayClass = newVal.todayClass ? newVal.todayClass.map((item, index) => [
`<span style="color:#000;" >${index + 1 || ''}
</span>`,
`<span style="color:#000;" >${item.name || ''}
</span>`,
// formatDate(item.planStartTime) || '',
`
<span style="color:#000;" >${item.inputNum || ''}
</span>`,
`<span style="color:#000;">${item.outputNum || ''}</span>`,
`<div style="width:136px; margin:0px;">
<div style="display:inline-block;height:8px; background:#D8DEEB; border-radius:4px; overflow:hidden;text-align:left;width:90px">
<div style="width:${item.inputOutputRate}%; height:100%; background:#0B58FF;border-radius:4px;"></div>
</div>
<span style="display:inline-block;margin-left:10px; color:#000; font-size:14px;">${item.inputOutputRate}%</span>
</div>`,
]) : []
this.classConfig.data = this.todayClass
this.$refs['classToday'].updateRows(this.todayClass)
}
},
deep: true // 深度监听
}
},
data() {
return {
nowMonth: [],
nowWeek: [],
oneDay: [],
todayClass: [],
classConfig:{
header: ['<span style="color:#000;">序号</span>', '<span style="color:#000;">工段</span>', '<span style="color:#000;">投入数量/片</span>', '<span style="color:#000;">产出数量/片</span>','<span style="color:#000;">产出率/%</span>'],
headerHeight: 25,
headerBGC: '#E8EDF8',
oddRowBGC:'#E8EDF8',
evenRowBGC:'#fff',
rowNum: 8,
columnWidth: [66, 120, 125, 126, 174],
align : ['center', 'center', 'center', 'center', 'center'],
data: []
},
monthConfig: {
header: ['<span style="color:#000;">序号</span>', '<span style="color:#000;">工段</span>', '<span style="color:#000;">投入数量/片</span>', '<span style="color:#000;">产出数量/片</span>', '<span style="color:#000;">产出率/%</span>'],
headerHeight: 25,
headerBGC: '#E8EDF8',
oddRowBGC: '#E8EDF8',
evenRowBGC: '#fff',
rowNum: 8,
columnWidth: [66, 120, 125, 126, 174],
align: ['center', 'center', 'center', 'center', 'center'],
data: []
},
dayConfig: {
header: ['<span style="color:#000;">序号</span>', '<span style="color:#000;">工段</span>', '<span style="color:#000;">投入数量/片</span>', '<span style="color:#000;">产出数量/片</span>', '<span style="color:#000;">产出率/%</span>'],
headerHeight: 25,
headerBGC: '#E8EDF8',
oddRowBGC: '#E8EDF8',
evenRowBGC: '#fff',
rowNum: 8,
columnWidth: [66, 120, 125, 126, 174],
align: ['center', 'center', 'center', 'center', 'center'],
data: []
},
weekConfig: {
header: ['<span style="color:#000;">序号</span>', '<span style="color:#000;">工段</span>', '<span style="color:#000;">投入数量/片</span>', '<span style="color:#000;">产出数量/片</span>', '<span style="color:#000;">产出率/%</span>'],
headerHeight: 25,
headerBGC: '#E8EDF8',
oddRowBGC: '#E8EDF8',
evenRowBGC: '#fff',
rowNum: 8,
columnWidth: [66, 120, 125, 126, 174],
align: ['center', 'center', 'center', 'center', 'center'],
data: []
}
}
},
mounted() {
this.getData()
},
methods: {
getData() {
// let arr1 = []
// let arr2 = []
// let data = [
// [1,'A1-磨边',234344,234333,100],
// [2,'A1-磨边',234344,234333,10],
// [3,'A1-磨边',234344,234333,96],
// [4,'A1-磨边',234344,234333,20],
// [5,'A1-磨边',234344,234333,40],
// [6,'A1-磨边',234344,234333,90],
// [7,'A1-磨边',234344,234333,80],
// [8,'A1-磨边',234344,234333,80],
// ]
// for (let i =0; i < data.length; i++) {
// arr1.push(`<span style="color:#000;">${data[i][0]}</span>`)
// arr1.push(`<span style="color:#000;">${data[i][1]}</span>`)
// arr1.push(`<span style="color:#000;">${data[i][2]}</span>`)
// arr1.push(`<span style="color:#000;">${data[i][3]}</span>`)
// arr1.push(`<div style="width:136px; margin:0px;">
// <div style="display:inline-block;height:8px; background:#D8DEEB; border-radius:4px; overflow:hidden;text-align:left;width:90px">
// <div style="width:${data[i][4]}%; height:100%; background:#0B58FF;border-radius:4px;"></div>
// </div>
// <span style="display:inline-block;margin-left:10px; color:#000; font-size:14px;">${data[i][4]}%</span>
// </div>`)
// arr2.push(arr1)
// arr1 = []
// }
// this.config.data = arr2
}
}
}
</script>
<style lang='scss' scoped>
.line-container {
display: flex;
height: 600px;
justify-content: space-between;
align-content: space-between;
flex-wrap: wrap;
.table-box {
width: 601px;
height: 300px;
position: relative;
padding-top: 16px;
.title {
width: 271px;
height: 27px;
line-height: 27px;
font-size: 20px;
color: #000000;
letter-spacing: 4px;
text-align: center;
background-image: url(../assets/img/title-bg.png);
background-size: 100% 100%;
margin: 0 auto;
}
.split-line {
position: absolute;
top: 53px;
width: 1px;
height: 242px;
background: #CDD3DF;
}
}
}
</style>

View File

@@ -0,0 +1,210 @@
<template>
<ModelBox name='3-1'>
<div class='line-rate'>
<div class='legend-box'>
<div class='legend-item' v-for='(item, index) in legendArr' :key='index'>
<span class='dot' :style='{backgroundColor: item.color,"--dot-color": item.color}'></span>
<span class='name'>{{item.name}}</span>
</div>
</div>
<div id='lineRate' style='width: 584px; height: 270px;'></div>
</div>
</ModelBox>
</template>
<script>
import * as echarts from 'echarts';
import ModelBox from './ModelBox.vue';
export default {
name: 'LineRate',
components: {
ModelBox
},
data() {
return {
legendArr:[
]
}
},
watch: {
dataObj: {
handler(newVal, oldVal) {
console.log(newVal, 'newVal');
if (newVal) {
let xData = []
newVal.forEach(ele => {
xData.push(this.formatDate(ele.time))
});
// console.log(, 'xData');
const series = this.convertToSeries(newVal)
this.getChart([...new Set(xData)], series)
// this.nowMonth = newVal.nowMonth ? newVal.nowMonth : []
// this.nowWeek = newVal.nowWeek ? newVal.nowWeek : []
// this.oneDay = newVal.oneDay ? newVal.oneDay : []
// this.todayClass = newVal.todayClass ? newVal.todayClass : []
}
// this.getChart(this.todayClass)
},
deep: true, // 深度监听
immediate: true
},
},
props: {
dataObj: {
type: Array,
default: () => []
}
},
mounted() {
this.getChart()
},
methods: {
init() {
},
formatDate(time) {
if (!time) return '';
// 如果是时间戳格式
if (typeof time === 'number' || !isNaN(time)) {
const date = new Date(parseInt(time));
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
}
// 如果是ISO字符串格式
if (typeof time === 'string') {
const date = new Date(time);
if (!isNaN(date.getTime())) {
return `$${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
}
}
return time;
},
convertToSeries(data) {
this.legendArr = []
// 1. 按产线ID分组
const colorArr = ['#FFCE6A', '#21CECD', '#77B4FD', '#29BDFA', '#5A7DDA', '#FFB49D', '#C5A6FC', '#7164FF', '#98B0C7', '#5D7092']
const grouped = {};
data.forEach(item => {
if (!grouped[item.id]) {
grouped[item.id] = {
id: item.id,
name: item.name,
data: []
};
}
// 将数据存入对应产线,并保留时间信息用于排序
grouped[item.id].data.push({
value: item.inputOutputRate,
time: item.time
});
});
// 2. 转换为series格式并按时间排序数据点
// let legendArr= []
return Object.values(grouped)
.sort((a, b) => a.id - b.id) // 按产线ID排序
.map((line,index) => {
// 按时间排序每个产线的数据点
line.data.sort((a, b) => a.time - b.time);
this.legendArr.push({
name: line.name, // 产线名称与series.name一致
color: colorArr[index] // 匹配colorArr中对应索引的颜色
});
// 提取value作为数据保持ECharts series格式
return {
name: line.name,
type: 'line',
symbol: 'circle',
symbolSize: 6,
data: line.data.map(item => item.value)
};
});
},
getChart(xData, series) {
var chartDom = document.getElementById('lineRate');
var myChart = echarts.init(chartDom);
var option;
option = {
color: ['#FFCE6A', '#21CECD', '#77B4FD', '#29BDFA', '#5A7DDA', '#FFB49D', '#C5A6FC', '#7164FF', '#98B0C7', '#5D7092'],
tooltip: {
trigger: 'axis'
},
grid: {
top: 32,
left: 10,
right: 15,
bottom: 5,
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
axisLine: {
show:true,
lineStyle:{
color: 'rgba(0,0,0,0.45)'
}
},
data: xData ? xData: [],
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value}%'
},
axisLine: {
show:false,
lineStyle:{
color: 'rgba(0,0,0,0.45)'
}
}
},
series: series ? series: []
};
option && myChart.setOption(option);
}
}
}
</script>
<style lang='scss' scoped>
.line-rate {
position: relative;
.legend-box {
width: 340px;
height: 35px;
position: absolute;
right: 0;
top:-20px;
display: flex;
flex-flow: row wrap;
.legend-item {
width: 100px;
.dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
position: relative;
}
.dot::before {
content: '';
display: inline-block;
width: 14px;
height: 2px;
background-color: var(--dot-color);
position: absolute;
top:3px;
left: -3px;
}
.name{
font-size: 14px;
color: #8C8C8C;
}
}
}
}
</style>

View File

@@ -0,0 +1,223 @@
<template>
<ModelBox name='1-1'>
<div class='loss-sum'>
<SwitchBtn class='switch-btn' @chooseBtn='chooseBtn'/>
<div id='lossSum' style='width: 584px; height: 270px;'></div>
</div>
</ModelBox>
</template>
<script>
import * as echarts from 'echarts';
import ModelBox from './ModelBox.vue';
import SwitchBtn from './SwitchBtn.vue';
export default {
name: 'LossSum',
components: {
ModelBox,
SwitchBtn
},
data() {
return {
nowMonth:[],
nowWeek: [],
oneDay: [],
todayClass: []
}
},
props: {
dataObj: {
type: Object,
default: () => {}
}
},
mounted() {
// this.getChart()
},
watch: {
dataObj: {
handler(newVal, oldVal) {
console.log(newVal,'newVal');
if (newVal) {
this.nowMonth = newVal.nowMonth ? newVal.nowMonth : []
this.nowWeek = newVal.nowWeek ? newVal.nowWeek : []
this.oneDay = newVal.oneDay ? newVal.oneDay :[]
this.todayClass = newVal.todayClass ? newVal.todayClass : []
}
this.getChart(this.todayClass)
},
deep: true // 深度监听
}
},
methods: {
getChart(val) {
console.log(val, 'val');
let series = []
const lineNames = [...new Set(val.map(item => item.lineName))].sort();
// const types = [...new Set(val.map(item => item.type))].sort();
// lineNames.forEach(lineName => {
// series.push({
// name: lineName,
// type: 'bar',
// barGap: '30%',
// barWidth: 15,
// data: []
// });
// });
const types = ['上片', '磨边', '打孔', '丝印', '镀膜', '钢化', '包装']
const typeMap = { 1: '上片', 2: '磨边', 3: '打孔',4:'丝印', 5: '镀膜', 6: '钢化', 7: '包装' };
types.forEach(typeName => {
series.push({
name: typeName,
type: 'bar',
barGap: '30%',
barWidth: 15,
data: []
});
});
types.forEach((type, index) => {
const typeValue = parseInt(Object.keys(typeMap).find(key => typeMap[key] === type));
console.log(typeValue,'typeValue');
const itemSeries = series.find(s => s.name === type);
lineNames.forEach(lineName => {
const item = val.find(d => d.lineName === lineName && d.type === typeValue);
if (item) {
itemSeries.data.push(item.lossNum);
} else {
// 如果没有数据添加0
itemSeries.data.push(0);
}
});
// lineSeries.data.push(lineSeries.lossNum)
// types.forEach(type => {
// // 查找对应产线和工序类型的数据
// const item = oneDay.find(d => d.lineName === lineName && d.type === type);
// if (item) {
// // 将lossNum添加到对应的系列中
// lineSeries.data.push(item.lossNum);
// } else {
// // 如果没有数据添加0
// lineSeries.data.push(0);
// }
// });
});
console.log('series', series);
// const lineNames = [...new Set(val.map(item => item.lineName))].sort();
// // let xData = []
// lineNames.forEach(line => {
// // sectionNames.forEach(section => {
// // 查找对应产线和工段的数据
// const item = val.find(d => d.lineName === line);
// if (item) {
// // 将lossNum添加到对应的系列中
// this.series[item.type - 1].data.push(item.lossNum);
// } else {
// // 如果没有数据添加0
// this.series.forEach(s => s.data.push(0));
// }
// // });
// });
// val.forEach((ele) => {
// console.log(ele.lineName,ele.type);
// if (!xData.includes(ele.lineName)) {
// xData.push(ele.lineName)
// }
// })
// console.log('xData', xData);
var chartDom = document.getElementById('lossSum');
var myChart = echarts.init(chartDom);
var option;
option = {
color: ['#FFCE6A', '#96F0B1', '#63BDFF', '#288AFF', '#AC8BFF', '#7164FF', '#3B2BFD'],
legend: {
top:18,
right:5,
itemWidth:8,
itemHeight: 8,
// data: [ '上片', '磨边','打孔','镀膜','钢化','包装'],
textStyle:{
color: '#8C8C8C',
fontSize: '14px'
}
},
grid: {
top: 50,
left: 10,
right: 5,
bottom: 5,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function(params) {
let result = `<span>${params[0].name}</span><br/>`;
params.forEach(item => {
result += `<div style="display:flex;align-items:center;">
<span style="display:inline-block;width:8px;height:8px;background:${item.color};margin-right:5px;"></span>
<span style="display:inline-block;margin-right:20px;">${item.seriesName}</span>
<span style="font-weight:bold;">${item.value}</span><br/>
</div>`;
});
return result;
}
},
xAxis: {
type: 'category',
axisLine: {
show:true,
lineStyle:{
color: 'rgba(0,0,0,0.45)'
}
},
axisTick: {
show:false
},
data: lineNames
},
yAxis: {
name:'单位/片',
nameTextStyle:{
color: 'rgba(0,0,0,0.45)',
fontSize: '14px'
},
axisLine: {
show:true,
lineStyle:{
color: 'rgba(0,0,0,0.45)'
}
}
},
series: series
};
option && myChart.setOption(option);
},
chooseBtn(val) {
if (val ===3) {
this.getChart(this.nowMonth)
} else if (val === 2) {
this.getChart(this.nowWeek)
} else if (val === 1) {
this.getChart(this.oneDay)
} else if (val === 0) {
this.getChart(this.todayClass)
}
}
}
}
</script>
<style lang='scss' scoped>
.loss-sum {
position: relative;
.switch-btn {
position: absolute;
right: -3px;
top: -27px;
}
}
</style>

View File

@@ -0,0 +1,85 @@
<template>
<div class='model-box' :style='{width: width + "px", height: height + "px"}'>
<div>
<img :src="icon" alt="" width='24' height='24' class='icon'>
<span class='model-title'>{{title}}</span>
</div>
<slot></slot>
</div>
</template>
<script>
import img1 from './../assets/img/1-1.png';
import img2 from './../assets/img/1-2.png';
import img3 from './../assets/img/1-3.png';
import img4 from './../assets/img/2-1.png';
import img5 from './../assets/img/2-2.png';
import img6 from './../assets/img/3-1.png';
export default {
name: 'ModelBox',
props: {
name: {
type: String,
default: ''
}
},
data () {
return {
title: '',
icon:'',
width: 618,
height: 322
}
},
mounted () {
this.getTitle()
},
methods: {
getTitle () {
switch (this.name) {
case '1-1':
this.title = '工段损耗汇总'
this.icon = img1
break;
case '1-2':
this.title = '设备报警'
this.icon = img2
break;
case '1-3':
this.title = '托数统计'
this.icon = img3
break;
case '2-1':
this.title = '工段投入和产出'
this.icon = img4
break;
case '3-1':
this.title = '产线加工成品率'
this.icon = img6
break;
default:
this.title = '产线投入和产出'
this.icon = img5
this.width = 1253
this.height = 660
}
}
}
}
</script>
<style lang='scss' scoped>
.model-box {
background: #FFFFFF;
border-radius: 8px;
border: 1px solid #FFFFFF;
padding: 14px 16px;
.icon {
vertical-align: bottom;
margin-right: 5px;
}
.model-title{
font-size: 20px;
color: #000000;
line-height: 24px;
}
}
</style>

View File

@@ -0,0 +1,231 @@
<template>
<ModelBox name='2-1'>
<div class='section-content'>
<SwitchBtn class='switch-btn' @chooseBtn='chooseBtn' />
<div class='section-list'>
<div v-for='item in sectionArr' :key='item.id' class='section-item'
:class='{active: sectionActive === item.id}' @click='sectionActive = item.id'>{{item.name}}</div>
</div>
<dv-scroll-board ref="showTable" :config="config" style="width:583px;height:220px;" class='section-table' />
<span class='split-line' style='left: 66px;'></span>
<span class='split-line' style='left: 186px;'></span>
<span class='split-line' style='left: 306px;'></span>
<span class='split-line' style='left: 426px;'></span>
</div>
</ModelBox>
</template>
<script>
import ModelBox from './ModelBox.vue';
import SwitchBtn from './SwitchBtn.vue';
import arrowGreen from '../assets/img/arrow-green.png';
import arrowRed from '../assets/img/arrow-red.png';
export default {
name: 'SectionInputAndOutput',
components: {
ModelBox,
SwitchBtn
},
watch: {
dataObj: {
handler(newVal, oldVal) {
console.log(newVal, 'newVal');
if (newVal) {
this.nowMonth = newVal.nowMonth ? newVal.nowMonth : []
this.nowWeek = newVal.nowWeek ? newVal.nowWeek : []
this.oneDay = newVal.oneDay ? newVal.oneDay : []
this.todayClass = newVal.todayClass ? newVal.todayClass : []
}
// this.getChart(this.todayClass)
},
deep: true, // 深度监听
immediate: true
},
sectionActive: {
handler(newVal) {
if (newVal) {
console.log(newVal, 'sectionActive');
this.updateChartData();
}
// this.updateChartData();
},
deep: true,
immediate: true
},
time: {
handler(newVal) {
if (newVal) {
console.log(newVal, 'sectionActive');
this.updateChartData();
}
// this.updateChartData();
},
deep: true,
immediate: true
},
},
props: {
dataObj: {
type: Object,
default: () => {}
}
},
data() {
return {
sectionArr:[
{id:1, name:'上片'},
{id:2, name:'磨边'},
{id:3, name:'打孔'},
{id:4, name:'丝印'},
{id:5, name:'镀膜'},
{id:6, name:'钢化'},
{id:7, name:'包装'}
],
nowMonth: [],
nowWeek: [],
oneDay: [],
todayClass: [],
time: 0,
showData: [],
sectionActive: '1',
config:{
header: ['<span style="color:#000;">序号</span>', '<span style="color:#000;">工段</span>', '<span style="color:#000;">投入数量/片</span>', '<span style="color:#000;">产出数量/片</span>','<span style="color:#000;">产出率/%</span>'],
headerHeight: 25,
headerBGC: '#E8EDF8',
oddRowBGC:'#E8EDF8',
evenRowBGC:'#fff',
rowNum: 7,
columnWidth: [60, 120, 120, 120, 163],
align : ['center', 'center', 'center', 'center', 'center'],
data:[]
}
}
},
mounted() {
this.updateChartData()
},
methods: {
updateChartData() {
const data = this.getDataByTimeAndType();
this.getData(data);
},
getDataByTimeAndType() {
// console.log(this.time,'time');
console.log(this.sectionActive, this.time, 'sectionActive');
// 确定当前时间范围的数据源
let dataSource;
switch (this.time) {
case 3: // 本月
dataSource = this.nowMonth;
break;
case 2: // 本周
dataSource = this.nowWeek;
break;
case 1: // 今日
dataSource = this.oneDay;
break;
case 0: // 今日班次
default:
dataSource = this.todayClass;
break;
}
console.log(dataSource,'dataSource');
// 如果没有数据,返回空数组
if (!dataSource || dataSource.length === 0) {
return [];
}
// 根据sectionActive对应type筛选数据
const filteredData = dataSource.filter(item => item.type === parseInt(this.sectionActive));
// 按产出率排序
return filteredData
},
getData(data) {
console.log(data,'ryf');
this.showData = data ? data.map((item, index) => [
// `<span style="color:#000;" >${index + 1 || ''}
// </span>`,
`<span style="color:#000;">${index + 1 }</span>`,
// `<span style="color:#000;" >${item.sectionName || ''}
// </span>`,
`<span style="color:#000;">${item.sectionName}</span>`,
// formatDate(item.planStartTime) || '',
`<span style="color:#000;" >${item.inputNum || ''}
</span>`,
// `<span style="color:#000;">${data[i][3]}</span>`
`<span style="color:#000;">${item.outputNum || ''}</span>`,
`<div style="width:136px; margin:0px;">
<div style="display:inline-block;height:8px; background:#D8DEEB; border-radius:4px; overflow:hidden;text-align:left;width:90px">
<div style="width:${item.inputOutputRate}%; height:100%; background:#0B58FF;border-radius:4px;"></div>
</div>
<span style="display:inline-block;margin-left:10px; color:#000; font-size:14px;">${item.inputOutputRate }%</span>
</div>`,
]) : []
this.config = {
...this.config,
data: this.showData
};
console.log(this.showData, 'arr', this.config.data);
// console.log(this.$refs['showTable'])
this.$nextTick(() => {
if (this.$refs.showTable) {
this.$refs.showTable.updateRows(this.showData);
}
});
// this.$forceUpdate()
// this.config.data = arr2
},
chooseBtn(val) {
this.time = val
console.log(this.time,' this.time');
this.updateChartData()
}
}
}
</script>
<style lang='scss' scoped>
.section-content {
position: relative;
.switch-btn {
position: absolute;
right: -3px;
top: -27px;
}
.section-list {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
padding-top: 20px;
cursor: pointer;
.section-item {
height: 24px;
line-height: 24px;
width: 81px;
text-align: center;
letter-spacing: 4px;
background-color: #E8EDF8;
}
.active {
background-color: #0B58FF;
color: #fff;
}
}
.section-table {
margin-top: 2px;
box-shadow: 0px 2px 4px 0px #D5DAE6;
}
.split-line {
position: absolute;
top: 46px;
width: 1px;
height: 221px;
background: #CDD3DF;
}
}
</style>

View File

@@ -0,0 +1,57 @@
<template>
<div class='button-box'>
<span v-for='(item, index) in btnArr' :key='index' style='display: inline-block;'>
<span class='split' v-if='index > 0' :class='{ "split-active": activeIndex === index || activeIndex === index - 1}'>|</span>
<span class='button-item' @click='chooseBtn(index)' :class='{ "item-active": activeIndex === index}'>{{item}}</span>
</span>
</div>
</template>
<script>
export default {
name: 'SwitchBtn',
data() {
return {
activeIndex: 1,
btnArr: ['本班次', '本日', '本周', '本月']
}
},
methods: {
chooseBtn(index) {
this.activeIndex = index
this.$emit('chooseBtn', index)
}
}
}
</script>
<style lang='scss' scoped>
.button-box {
display: inline-block;
color: #000;
background: #F2F4F9;
border-radius: 40px;
cursor: pointer;
.button-item {
display: inline-block;
font-size: 16px;
height: 32px;
line-height: 32px;
padding: 0 12px;
}
.item-active {
color: #fff;
background: #0B58FF;
border-radius: 40px;
}
.split {
display: inline-block;
font-size: 14px;
height: 32px;
line-height: 32px;
vertical-align: top;
color: #686868;
}
.split-active {
color: #F2F4F9;
}
}
</style>

249
src/views/home/index.vue Normal file
View File

@@ -0,0 +1,249 @@
<template>
<div class='home-box'>
<div id="homeComtainerB" ref="homeComtainerB">
<div class='home-comtainer' id="homeComtainer" style="width: 1920px; height: 1080px"
:style="{ transform: 'scale(' + scaleNum + ')' }">
<HomeHeader :isFullScreen="isFullScreen" @screenfullChange="screenfullChange" />
<div class='line-one'>
<LossSum style='margin-right: 16px;' :dataObj="dataObj.workShopData" />
<EqAlarm style='margin-right: 16px;' :dataObj="dataObj.alarmData" />
<Count :dataObj="dataObj.downLoadNum" />
</div>
<div class='line-two'>
<div>
<SectionInputAndOutput style='margin-bottom: 16px;' :dataObj="dataObj.workShopData" />
<LineRate :dataObj="dataObj.lineHours"/>
</div>
<LineInpurAndOutput :dataObj="dataObj.productionLineData" style='margin-left: 16px;' />
</div>
</div>
</div>
</div>
</template>
<script>
import HomeHeader from './components/HomeHeader.vue';
import LossSum from './components/LossSum.vue';
import EqAlarm from './components/EqAlarm.vue';
import Count from './components/Count.vue';
import SectionInputAndOutput from './components/SectionInputAndOutput.vue';
import LineRate from './components/LineRate.vue';
import LineInpurAndOutput from './components/LineInpurAndOutput.vue';
import { debounce } from '@/utils/debounce';
import screenfull from 'screenfull';
import { getAccessToken } from '@/utils/auth';
import store from "@/store";
export default {
name: 'Home',
components: {
HomeHeader,
LossSum,
EqAlarm,
Count,
SectionInputAndOutput,
LineRate,
LineInpurAndOutput
},
data() {
return {
isFullScreen: false,
scaleNum: 1,
dataObj: {},
sseReader: null, // 保存流读取器
abortController: null, // 用于中止 fetch 请求
retryCount: 0, // 当前重试次数
isDestroyed: false // 标记组件是否已销毁
};
},
created() {
this.init();
this.getData()
},
mounted() {
this.boxReset();
window.addEventListener('resize', this.boxReset);
// this.boxReset = debounce(() => {
// this.resetSize()
// }, 300)
// this.boxReset()
// window.addEventListener('resize', () => {
// this.boxReset()
// })
this.getData()
},
destroyed() {
window.removeEventListener('resize', this.boxReset);
},
methods: {
boxReset() {
debounce(() => {
this.resetSize();
}, 300)();
},
async getData() {
let _this = this;
if (_this.isDestroyed) return;
const url = process.env.VUE_APP_BASE_API + '/admin-api/monitoring/message/subscribe/' + store.getters.userId + '-' + Date.now();
const token = getAccessToken()
const headers = new Headers({
'Authorization': `Bearer ${token}`,
'Content-Type': 'text/event-stream'
});
try {
// 创建中止控制器
this.abortController = new AbortController();
// 发起 fetch 请求(替换为你的接口地址)
const response = await fetch(url, {
method: 'GET',
headers: headers,
signal: _this.abortController.signal // 绑定中止信号
});
// 获取流读取器
_this.sseReader = response.body.getReader();
const decoder = new TextDecoder();
// 持续读取流数据
while (true) {
const { done, value } = await _this.sseReader.read();
if (done) {
console.log('SSE 连接正常关闭');
_this.handleReconnect(); // 触发重连
break;
}
// 处理 SSE 事件数据
const data = decoder.decode(value);
console.log('收到消息:', data);
if (_this.isValidData(data)) {
_this.upDateMsg(data);
}
}
} catch (error) {
// 主动中止的请求不报错
if (error.name === 'AbortError') return;
console.error('SSE 连接异常:', error);
_this.handleReconnect(); // 触发重连
}
},
closeSSE() {
this.isDestroyed = true; // 标记销毁
if (this.abortController) {
this.abortController.abort(); // 中止 fetch 请求
}
if (this.sseReader) {
this.sseReader.cancel(); // 关闭流读取器
this.sseReader = null;
}
console.log('SSE 连接已强制关闭');
},
handleReconnect() {
if (this.isDestroyed) return;
// 指数退避策略(最大重试 5 次)
const maxRetries = 5;
if (this.retryCount < maxRetries) {
const delay = Math.pow(2, this.retryCount) * 1000;
setTimeout(() => {
this.retryCount++;
this.initSSE();
}, delay);
} else {
console.error('SSE 重连次数已达上限');
}
},
isValidData(data) {
return data.trim().startsWith('data:{') && !data.includes('heartbeat');
},
upDateMsg(data) {
const jsonStr = data.replace(/^data:/, '').trim();
console.log('jsonStr', jsonStr);
try {
const dataObj = JSON.parse(jsonStr);
this.dataObj = dataObj
console.log('dataObj', this.dataObj)
} catch (e) {
console.error('JSON 解析失败:', e);
}
},
change() {
this.isFullScreen = screenfull.isFullscreen;
},
init() {
if (screenfull.isEnabled) {
screenfull.on('change', this.change);
}
},
destroy() {
if (screenfull.isEnabled) {
screenfull.off('change', this.change);
}
},
// 全屏
screenfullChange() {
if (!screenfull.isEnabled) {
this.$message({
message: 'you browser can not work',
type: 'warning',
});
return false;
}
screenfull.toggle(this.$refs.homeComtainerB);
},
resetSize() {
let coldContainerBox = document.getElementById('homeComtainer');
let rw = parseFloat(window.innerWidth);
let rh = parseFloat(window.innerHeight);
let bw = parseFloat(coldContainerBox.style.width);
let bh = parseFloat(coldContainerBox.style.height);
let wx = 0;
let hx = 0;
if (screenfull.isFullscreen) {
wx = rw / bw;
hx = rh / bh;
} else {
if (this.$store.state.app.sidebar.opened) {
wx = (rw - 280) / bw;
hx = (rh - 116) / bh;
} else {
wx = (rw - 85) / bw;
hx = (rh - 116) / bh;
}
}
this.scaleNum = wx;
},
},
watch: {
'$store.state.app.sidebar.opened': {
handler(newVal, oldVal) {
this.boxReset();
},
immediate: true
}
}
}
</script>
<style lang='scss' scoped>
.home-box {
min-height: calc(100vh - 56px - 72px);
min-width: calc(100vh - 280px);
background-color: #F2F4F9;
}
.home-comtainer{
background-color: #F2F4F9;
position: absolute;
transform-origin: 16px 8px;
top: 0px;
left: 0px;
padding-left: 16px;
padding-top: 10px;
overflow: hidden;
.line-one {
display: flex;
margin-top: 16px;
}
.line-two {
display: flex;
margin-top: 16px;
}
}
</style>

View File

@@ -1,123 +0,0 @@
<!--
* @Author: zwq
* @Date: 2021-11-18 14:16:25
* @LastEditors: DY
* @LastEditTime: 2023-10-16 11:16:48
* @Description:
-->
<template>
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="物料名称" prop="materialName">
<el-input v-model="dataForm.materialName" clearable placeholder="请输入物料名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="物料规格" prop="materialSize">
<el-input v-model="dataForm.materialSize" clearable placeholder="请输入物料规格" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="物料批次" prop="materialDate">
<el-input v-model="dataForm.materialDate" clearable placeholder="请输入物料批次" />
</el-form-item>
</el-col>
<!-- <el-form-item label="启用状态" prop="enabled">
<el-select
v-model="dataForm.enabled"
placeholder="请选择启用状态">
<el-option
v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value"
:label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item> -->
<el-col :span="12">
<el-form-item label="入库数量" prop="materialNum">
<el-input v-model="dataForm.materialNum" clearable placeholder="请输入入库数量" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
import basicAdd from './../mixins/basic-add';
import {
createMaterialIssue,
} from '@/api/material/issue';
export default {
mixins: [basicAdd],
data() {
return {
urlOptions: {
isGetCode: false,
// codeURL: getCode,
createURL: createMaterialIssue,
// updateURL: updateFactory,
// infoURL: getFactory,
},
dataForm: {
id: undefined,
materialName: undefined,
materialNum: undefined,
materialDate: undefined,
materialSize: undefined,
operateStatus:undefined
},
dataRule: {
materialName: [{ required: true, message: "物料名称不能为空", trigger: "blur" }],
materialNum: [{ required: true, message: "数量不能为空", trigger: "blur" }],
}
};
},
methods: {
init(operateStatus) {
this.dataForm.operateStatus = operateStatus;
this.visible = true;
this.$nextTick(() => {
this.$refs["dataForm"].resetFields();
// if (this.dataForm.id) {
// this.urlOptions.infoURL(id).then(response => {
// this.dataForm = response.data;
// if (this.setData) {
// this.setDataForm()
// }
// });
// } else {
// if (this.urlOptions.isGetCode) {
// this.getCode()
// }
// }
});
},
dataFormSubmit() {
this.$refs["dataForm"].validate((valid) => {
if (!valid) {
return false;
}
// 修改的提交
// 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

@@ -1,243 +0,0 @@
<template>
<div class="app-container">
<search-bar
:formConfigs="formConfig"
ref="searchBarForm"
@headBtnClick="buttonClick" />
<base-table
v-loading="dataListLoading"
:table-props="tableProps"
:page="listQuery.pageNo"
:limit="listQuery.pageSize"
:table-data="tableData">
<!-- <method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="120"
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"
width="30%">
<add-or-update
ref="addOrUpdate"
@refreshDataList="successSubmit"></add-or-update>
</base-dialog>
</div>
</template>
<script>
import AddOrUpdate from './add-or-updata';
import basicPage from './../mixins/basic-page';
import { parseTime } from './../mixins/code-filter';
import {
getMaterialReceiptIssuePageData,
} from '@/api/material/issue';
const tableProps = [
{
prop: 'materialName',
label: '物料名称'
},
{
prop: 'materialSize',
label: '物料规格'
},
{
prop: 'materialDate',
label: '物料批次'
},
{
prop: 'operateStatus',
label: '操作状态',
filter: (val) => ['', '入库', '出库'][val]
},
{
prop: 'materialNum',
label: '出入库数量',
// filter: (val) => ['', '入库', '出库', '移库'][val]
},
{
prop: 'createTime',
label: '操作时间',
filter: parseTime
},
{
prop: 'creator',
label: '操作人',
// filter: parseTime
},
];
export default {
mixins: [basicPage],
data() {
return {
urlOptions: {
getDataListURL: getMaterialReceiptIssuePageData,
// deleteURL: deleteFactory,
// exportURL: exportFactoryExcel,
},
operateStatus:undefined,
tableProps,
// tableBtn: [
// this.$auth.hasPermi(`base:factory:update`)
// ? {
// type: 'edit',
// btnName: '编辑',
// }
// : undefined,
// this.$auth.hasPermi(`base:factory:delete`)
// ? {
// type: 'delete',
// btnName: '删除',
// }
// : undefined,
// ].filter((v)=>v),
tableData: [],
formConfig: [
{
type: 'input',
label: '物料名称',
placeholder: '物料名称',
param: 'materialName',
},
{
type: 'input',
label: '操作人',
placeholder: '操作人',
param: 'operateName',
},
{
type: 'select',
label: '操作状态',
placeholder: '操作状态',
selectOptions: [{
id: 1,
name:'入库'
},
{
id: 2,
name: '出库'
}],
param: 'operateStatus',
},
{
type: 'datePicker',
label: '操作时间',
dateType: 'daterange',
format: 'yyyy-MM-dd',
valueFormat: 'timestamp',
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'timeVal',
},
{
type: this.$auth.hasPermi('base:material-warehouse-his:query') ? 'button' : '',
btnName: '搜索',
name: 'search',
color: 'primary',
},
// {
// type: 'separate',
// },
// {
// type: 'button',
// btnName: '重置',
// name: 'reset',
// },
{
type: 'separate',
},
{
type: this.$auth.hasPermi('base:material-warehouse-his:create') ? 'button' : '',
btnName: '入库',
name: 'receipt',
color: 'success',
plain: true,
},
{
type: this.$auth.hasPermi('base:material-warehouse-his:create') ? 'button' : '',
btnName: '出库',
name: 'issue',
color: 'success',
plain: true,
},
// {
// type: this.$auth.hasPermi('base:factory:create') ? 'separate' : '',
// },
// {
// type: this.$auth.hasPermi('base:factory:export') ? 'button' : '',
// btnName: '导出',
// name: 'export',
// color: 'warning',
// },
],
};
},
components: {
AddOrUpdate,
},
created() {},
methods: {
addOrUpdateHandle() {
this.addOrUpdateVisible = true;
this.$nextTick(() => {
this.$refs.addOrUpdate.init(this.operateStatus);
});
},
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.listQuery.pageNo = 1;
this.listQuery.pageSize = 10;
this.listQuery.operateName = val.operateName ? val.operateName :undefined;
this.listQuery.operateTimeStart = val.timeVal? val.timeVal[0] :undefined;
this.listQuery.operateTimeEnd = val.timeVal ? val.timeVal[1] : undefined;
this.listQuery.materialName = val.materialName ? val.materialName : undefined;
this.listQuery.operateStatus = val.operateStatus ? val.operateStatus : undefined;
this.getDataList();
break;
case 'reset':
this.$refs.searchBarForm.resetForm();
this.listQuery = {
pageSize: 10,
pageNo: 1,
total: 1,
};
this.getDataList();
break;
case 'receipt':
this.addOrEditTitle = '入库';
this.addOrUpdateVisible = true;
this.operateStatus = 1
this.addOrUpdateHandle();
break;
case 'issue':
this.addOrEditTitle = '出库';
this.addOrUpdateVisible = true;
this.operateStatus = 2
this.addOrUpdateHandle();
break;
case 'export':
this.handleExport();
break;
default:
console.log(val);
}
},
},
};
</script>

View File

@@ -1,100 +0,0 @@
/*
* @Author: zwq
* @Date: 2022-08-24 11:19:43
* @LastEditors: zwq
* @LastEditTime: 2023-08-03 14:21:04
* @Description:
*/
export default {
data() {
/* eslint-disable */
return {
urlOptions: {
createURL: '',
updateURL: '',
infoURL: '',
codeURL: '',
getOption: false,
isGetCode: false,
optionArrUrl: [],
optionArr: {}
},
visible: false,
setData: false
}
},
created() {
},
activated() {
},
methods: {
init(id) {
this.dataForm.id = id || "";
this.visible = true;
if (this.urlOptions.getOption) {
this.getArr()
}
this.$nextTick(() => {
this.$refs["dataForm"].resetFields();
if (this.dataForm.id) {
this.urlOptions.infoURL(id).then(response => {
this.dataForm = response.data;
if (this.setData) {
this.setDataForm()
}
});
} else {
if (this.urlOptions.isGetCode) {
this.getCode()
}
}
});
},
getCode() {
this.urlOptions.codeURL()
.then(({ data: res }) => {
this.dataForm.code = res;
})
.catch(() => {});
},
getArr() {
const params = {
pageSize: 100,
pageNo: 1,
}
this.urlOptions.optionArrUrl.forEach((item, index) => {
item(params).then(({ data: res }) => {
this.$set(this.urlOptions.optionArr, `arr${index}`, res.list)
})
.catch(() => {
});
});
},
// 表单提交
dataFormSubmit() {
this.$refs["dataForm"].validate((valid) => {
if (!valid) {
return false;
}
// 修改的提交
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");
});
});
},
formClear() {
this.$refs.dataForm.resetFields()
}
}
}

View File

@@ -1,168 +0,0 @@
/*
* @Author: zwq
* @Date: 2022-08-24 11:19:43
* @LastEditors: DY
* @LastEditTime: 2023-09-21 16:02:07
* @Description:
*/
export default {
data() {
/* eslint-disable */
return {
urlOptions: {
getDataListURL: '',
deleteURL: '',
statusUrl: '',
exportURL: ''
},
tableData: [],
listQuery: {
pageSize: 10,
pageNo: 1,
total: 1,
},
exportLoading: false,
dataListLoading: false,
addOrEditTitle: '',
addOrUpdateVisible: false,
}
},
created() {
},
mounted() {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList() {
this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then(response => {
this.tableData = response.data.list;
this.listQuery.total = response.data.total;
this.dataListLoading = false;
});
},
// 每页数
sizeChangeHandle(val) {
this.listQuery.pageSize = val;
this.listQuery.pageNo = 1;
this.getDataList();
},
// 当前页
currentChangeHandle(val) {
this.listQuery.pageNo = val;
this.getDataList();
},
// 新增 / 修改
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true;
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id);
});
},
cancel(id) {
this.$refs["popover-" + id].showPopper = false;
},
//改变状态
changeStatus(id) {
this.$http
.post(this.urlOptions.statusUrl, { id })
.then(({ data: res }) => {
if (res.code !== 0) {
return this.$message.warning(res.msg);
}
this.$refs["popover-" + id].showPopper = false;
this.$message({
message: this.$t("prompt.success"),
type: "success",
duration: 500,
onClose: () => {
this.getDataList();
},
});
})
.catch(() => { });
},
//tableBtn点击
handleClick(val) {
if (val.type === "edit") {
this.addOrUpdateVisible = true;
this.addOrEditTitle = "编辑";
this.$nextTick(() => {
this.$refs.addOrUpdate.init(val.data.id);
});
} else if (val.type === "delete") {
this.deleteHandle(val.data.id, val.data.name, val.data._pageIndex)
} else if (val.type === "change") {
this.changeStatus(val.data.id)
} else {
this.otherMethods(val)
}
},
// 删除
deleteHandle(id, name, index) {
this.$confirm(`确定对${name ? '[名称=' + name + ']' : '[序号=' + index + ']'}进行删除操作?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
this.urlOptions.deleteURL(id).then(({ data }) => {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.getDataList();
},
});
});
})
.catch(() => { });
},
//search-bar点击
buttonClick(val) {
switch (val.btnName) {
case "search":
this.listQuery.xm1 = val.xm1;
this.listQuery.xm2 = val.xm2;
this.listQuery.pageNo = 1;
this.getDataList();
break;
case "add":
this.addOrEditTitle = '新增'
this.addOrUpdateVisible = true;
this.addOrUpdateHandle()
break;
default:
console.log(val)
}
},
handleCancel() {
this.$refs.addOrUpdate.formClear()
this.addOrUpdateVisible = false
this.addOrEditTitle = ''
},
handleConfirm() {
this.$refs.addOrUpdate.dataFormSubmit()
},
successSubmit() {
this.handleCancel()
this.getDataList()
},
/** 导出按钮操作 */
handleExport() {
// 处理查询参数
let params = { ...this.queryParams };
params.pageNo = undefined;
params.pageSize = undefined;
this.$modal.confirm('是否确认导出所有数据项?').then(() => {
this.exportLoading = true;
return this.urlOptions.exportURL(params);
}).then(response => {
this.$download.excel(response, '工厂.xls');
this.exportLoading = false;
}).catch(() => { });
}
}
}

View File

@@ -1,73 +0,0 @@
/*
* @Date: 2020-12-29 16:49:28
* @LastEditors: DY
* @LastEditTime: 2023-09-12 11:13:34
* @FilePath: \basic-admin\src\filters\basicData\index.js
* @Description:
*/
const table = {
lineStatus: {
1: '生产中',
2: '停止',
3: '未知',
},
reportType: {
1: '日',
2: '周',
3: '月'
},
pdType: {
1: '原片',
0: '深加工',
},
}
// 日期格式化
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) {
return null
}
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.\d{3}/gm),'');
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') {
return ['日', '一', '二', '三', '四', '五', '六'][value]
}
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
export default function (dictTable) {
return function (val) {
return table?.[dictTable]?.[val]
}
}

View File

@@ -1,172 +0,0 @@
<!--
* @Author: zwq
* @Date: 2023-08-01 13:52:10
* @LastEditors: DY
* @LastEditTime: 2023-10-16 13:40:00
* @Description:
-->
<template>
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()"
label-width="130px">
<el-row :gutter="20">
<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="code">
<el-input v-model="dataForm.code" clearable placeholder="请输入订单编码" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="客户" prop="customerName">
<el-input v-model="dataForm.customerName" clearable placeholder="请输入客户" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计划加工量(片)" prop="planProductNum">
<el-input v-model="dataForm.planProductNum" clearable placeholder="请输入计划加工量" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产品工艺" prop="processName">
<el-input v-model="dataForm.processName" clearable placeholder="请输入产品工艺" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品规格" prop="planProductSize">
<el-input v-model="dataForm.planProductSize" clearable placeholder="请输入产品规格" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="交货日期" prop="deliveryTime">
<el-date-picker v-model="dataForm.deliveryTime" type="date" placeholder="选择交货日期" value-format="timestamp">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="包装规格(片/箱)" prop="packSpec">
<el-input v-model="dataForm.packSpec" clearable placeholder="请输入包装规格" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<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>
<span>产线信息</span>
<el-button type="text" icon="el-icon-plus" @click="handleAdd" style="color: #409EFF;float: right;">
</el-button>
<el-divider></el-divider>
<el-row :gutter="24" v-for="(item, index) in dataForm.bindLineList" :key="index">
<el-col :span="8">
<el-form-item label="产线" :prop="'bindLineList.' + index + '.lineId'">
<el-select v-model="item.lineId" filterable clearable placeholder="请选择产线">
<el-option v-for="line in urlOptions.optionArr.arr0" :key="line.id" :label="line.name"
:value="line.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="实际生产时间段" :prop="'bindLineList[' + index + '].dateRange'">
<el-date-picker v-model="item.dateRange" type="datetimerange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期" value-format="timestamp" @change="(va1) => handleChange(va1, index)" />
</el-form-item>
</el-col>
<el-col :span="2">
<!-- 删除按钮至少保留一项 -->
<el-button type="text" icon="el-icon-delete" @click="handleDelete(index)"
:disabled="dataForm.bindLineList.length <= 1" style="color: #f56c6c;"></el-button>
</el-col>
</el-row>
<!-- <el-form-item label="备注" prop="remark">
<el-input v-model="dataForm.remark" clearable placeholder="请输入备注" />
</el-form-item> -->
</el-form>
</template>
<script>
import basicAdd from '../../mixins/basic-add';
import {
updateOrderData,
createOrderData,
getOrderData,
getOrderCode
// exportWorkshopSectionExcel
} from "@/api/order/index";
import {
getProductionLinePage,
} from '@/api/core/base/productionLine';
export default {
mixins: [basicAdd],
data() {
return {
urlOptions: {
getOption: true,
isGetCode: true,
codeURL: getOrderCode,
createURL: createOrderData,
updateURL: updateOrderData,
infoURL: getOrderData,
optionArrUrl: [getProductionLinePage],
},
dataForm: {
id: undefined,
code: undefined,
bindLineList: [
],
deliveryTime:undefined,
name: undefined,
remark: undefined,
planProductSize: undefined,
planProductNum: undefined,
customerName: undefined,
processName: undefined,
packSpec: undefined,
},
dataRule: {
code: [
{ required: true, message: '订单编码不能为空', trigger: 'blur' },
],
name: [
{ required: true, message: '订单名称不能为空', trigger: 'blur' },
],
},
};
},
methods: {
handleAdd() {
const newItem = {
productionLineId: '',
dateRange: []
};
// 使用 this.$set 向数组添加元素(确保响应式)
this.$set(
this.dataForm.bindLineList,
this.dataForm.bindLineList.length, // 新元素的索引(数组长度即最后一个位置)
newItem // 要添加的新元素
);
},
handleChange(val, index) {
console.log(val);
this.dataForm.bindLineList[index].startProduceTime = val[0]
this.dataForm.bindLineList[index].finishProduceTime = val[1]
},
handleDelete(index) {
this.dataForm.bindLineList.splice(index, 1);
Message.info('已删除该配置');
},
},
};
</script>

View File

@@ -1,358 +0,0 @@
<!--
* @Author: zhp
* @Date: 2021-12-04 16:27:09
* @LastEditTime: 2022-08-09 09:47:17
* @LastEditors: zhp
* @Description:
-->
<template>
<div>
<el-drawer title="详情" :visible.sync="drawer" :before-close="handleClose" size="60%">
<div class="detailBox">
<h2 style="margin-left: 32px;">
订单编号: {{ dataForm.code}}
</h2>
<el-row>
<el-col :span="8">
<p class="title">订单名称</p>
<p class="text">{{ dataForm.name }}</p>
</el-col>
<el-col :span="8">
<p class="title">客户</p>
<p class="text">{{ dataForm.customerName }}</p>
</el-col>
<el-col :span="8">
<p class="title">计划加工量</p>
<p class="text">{{ dataForm?.planProductNum }}</p>
</el-col>
</el-row>
<!-- <el-divider /> -->
<div>
<el-row>
<el-col :span="8">
<p class="title">产品工艺</p>
<p class="text">{{ dataForm?.processName }}</p>
</el-col>
<el-col :span="8">
<p class="title">产品规格</p>
<p class="text">{{ dataForm?.planProductSize }}</p>
</el-col>
<el-col :span="8">
<p class="title">交货日期</p>
<p class="text">{{ parseTime(dataForm?.deliveryTime) }}</p>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<p class="title">包装规格/</p>
<p class="text">{{ dataForm?.packSpec }}</p>
</el-col>
<el-col :span="8">
<p class="title">实际生产时间</p>
<p class="text">{{ parseTime(dataForm?.startProduceTime) + '-' + parseTime(dataForm?.finishProduceTime) }}</p>
</el-col>
<el-col :span="8">
<p class="title">实际投入数量</p>
<p class="text">{{ dataForm?.assignQuantity }}</p>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<p class="title">实际产出数量</p>
<p class="text">{{ dataForm?.actualQuantity }}</p>
</el-col>
<el-col :span="8">
<p class="title">备注</p>
<p class="text">{{ dataForm?.remark }}</p>
</el-col>
</el-row>
</div>
<!-- <div v-if="glass === 4"> -->
<!-- 铜铟镓硒待完成 -->
<!-- </div> -->
</div>
<!-- <head-form :form-config="headFormConfig" @headBtnClick="btnClick" /> -->
<h2 style="margin-left: 32px;">
产线信息
</h2>
<el-divider />
<el-button @click="handleRoute" type="text" style="float: right;margin-right: 20px;">订单生产详情</el-button>
<base-table :table-props="tableProps" :table-data="tableData">
<method-btn v-if="tableBtn.length" slot="handleBtn" :width="120" label="操作" :method-list="tableBtn"
@clickBtn="handleClick" />
</base-table>
<!-- <pagination :limit.sync="listQuery.pageSize" :page.sync="listQuery.pageNo" :total="listQuery.total"
@pagination="getDataList" /> -->
</el-drawer>
</div>
</template>
<script>
const tableProps = [{
prop: 'lineName',
label: '产线',
align: 'center'
},
{
prop: 'processName',
label: '产品工艺',
align: 'center'
}, {
prop: 'planProductSize',
label: '产品规格',
align: 'center'
}, {
prop: 'startProduceTime',
label: '开始时间',
filter: parseTime,
align: 'center'
}, {
prop: 'finishProduceTime',
label: '结束时间',
filter: parseTime,
align: 'center'
}, {
prop: 'assignQuantity',
label: '投入数量',
align: 'center'
}, {
prop: 'actualQuantity',
label: '产出数量',
align: 'center'
},
]
// import { getPageWareHouseSubstrateHistory } from '@/api/equipment/trans'
// import BaseTable from '@/components/BaseTable'
// // import MethodBtn from '@/components/BaseTable/subcomponents/MethodBtn'
// import Pagination from '@/components/Pagination'
// edit here
// import i18n from '@/lang'
// import HeadForm from '@/components/basicData/HeadForm'
import {
getOrderData,
// exportWorkshopSectionExcel
} from "@/api/order/index";
import { parseTime } from '../../mixins/code-filter';
export default {
components: {
// BaseTable,
// Pagination,
// HeadForm
// MethodBtn
// MethodBtn
},
data() {
return {
tableProps,
drawer: false,
list: [],
tableBtn: [{
type: 'detail',
btnName: '产线生产详情',
}
// this.$auth.hasPermi(`base:workshop-section:delete`)
// ? {
// type: 'delete',
// btnName: '删除',
// }
// : undefined,
].filter((v) => v),
tableData:[],
listLoading: false,
total: 0,
edit: false,
// dataForm: {
// id: null,
// io: null,
// status: null
// },
lineIdList:[],
dataForm: {
id: null,
name: '', // 订单名称
customerName: '', // 客户
planProductNum: '', // 计划加工量
processName: '', // 产品工艺
planProductSize: '', // 产品规格
deliveryTime: '', // 交货日期
packSpec: '', // 包装规格(片/箱)
startProduceTime: '', // 实际生产时间
assignQuantity: '', // 实际投入数量
actualQuantity: '', // 实际产出数量
remark: '' // 备注
},
listQuery: {
},
// headFormConfig: [
// {
// label: this.$t('module.equipmentManager.trans.inEqTime'),
// type: 'datePickRange',
// dateType: 'datetimerange',
// format: 'yyyy-MM-dd HH:mm:ss',
// valueFormat: 'yyyy-MM-ddTHH:mm:ss',
// param: 'inTime'
// },
// {
// label: this.$t('module.equipmentManager.trans.outTime'),
// type: 'datePickRange',
// dateType: 'datetimerange',
// format: 'yyyy-MM-dd HH:mm:ss',
// valueFormat: 'yyyy-MM-ddTHH:mm:ss',
// param: 'outTime'
// },
// {
// type: 'button',
// btnName: 'btn.search',
// name: 'search',
// color: 'primary'
// }
// // {
// // type: 'button',
// // btnName: 'btn.exportBtn',
// // name: 'exportBtn',
// // color: 'primary'
// // }
// ],
rules: {
// slotNo: [
// { required: true, message: this.$i18nForm(['placeholder.input', this.$t('module.equipmentManager.trans.slotNo')]), trigger: 'blur' }
// ],
// substrateId: [
// { required: true, message: this.$i18nForm(['placeholder.input', this.$t('module.equipmentManager.trans.substrateId')]), trigger: 'blur' }
// ]
}
}
},
methods: {
handleClose() {
this.drawer = false
},
handleRoute() {
console.log(this.lineIdList);
this.$router.push({
path: '/core/monitoring/production-line-data',
query: {
lineId: this.lineIdList,
startProduceTime: this.dataForm.startProduceTime,
finishProduceTime: this.dataForm.finishProduceTime,
tab: 'his',
name: this.dataForm.name,
code: this.dataForm.code,
buttonType:'order'
}
})
},
handleClick(val) {
console.log(val);
this.$router.push({
path: '/core/monitoring/production-line-data',
query: {
lineId: [val.data.lineId],
startProduceTime: val.data.startProduceTime,
finishProduceTime: val.data.finishProduceTime,
tab:'his'
}
})
},
async init(id) {
// if (id !== undefined) {
this.dataForm.id = id || ''
// this.edit = true
// }
// console.log(this.edit)
this.drawer = true
this.$nextTick(() => {
// this.$refs['dataForm'].resetFields()
getOrderData(id).then((res) => {
this.dataForm = res.data
this.tableData = res.data.bindLines
this.tableData.forEach(ele => {
this.lineIdList.push(
ele.lineId
)
});
})
// console.log();
// if (this.listQuery.id) {
// this.getList()
// }
})
},
getList() {
getPageWareHouseSubstrateHistory(this.listQuery).then(res => {
console.log(res)
this.list = res.data.records
this.total = res.data.total
})
}
// dataSubmit() {
// this.$refs['dataForm'].validate((valid) => {
// if (valid) {
// if (this.dataForm.id) {
// const data = {
// id: this.dataForm.id,
// io: this.dataForm.io,
// status: this.dataForm.status
// }
// addOrUpdateLineSideWareHouseDetailData(data).then(res => {
// this.$message({
// message: this.$t('module.basicData.visual.success'),
// type: 'success',
// duration: 1500,
// onClose: () => {
// this.drawer = false
// this.$emit('refreshDataList')
// }
// })
// })
// }
// }
// })
// }
}
}
</script>
<style lang="scss" scoped>
.detailBox p {
margin: 0;
padding: 0 32px;
}
.detailBox.title {
/* width: 56px; */
/* height: 14px; */
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.85);
line-height: 16px;
text-align: left;
font-style: normal;
text-transform: none;
}
.detailBox.text {
font-size: 14px;
font-weight: 400;
color: rgba(102, 102, 102, 0.75);
padding-bottom: 20px;
}
.detailBox {
width: 98%;
padding: 30px 0;
}
</style>

View File

@@ -1,362 +0,0 @@
<template>
<div class="app-container">
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<el-table v-loading="dataListLoading" :data="tableData" row-key="id" border style="width: 100%">
<el-table-column label="序号" type="index" width="50" />
<!-- 订单名称/产线名称列 -->
<el-table-column prop="name" label="订单名称/产线名称" align="center" />
<!-- 订单编号列 -->
<el-table-column prop="code" label="订单编号" align="center" />
<!-- 客户列 -->
<el-table-column prop="customerName" label="客户" align="center" />
<!-- 计划加工量列 -->
<el-table-column prop="planProductNum" label="计划加工量" align="center" />
<!-- 实际加工量列 -->
<el-table-column prop="actualQuantity" label="实际加工量" align="center" />
<!-- 产品工艺列 -->
<el-table-column prop="processName" label="产品工艺" align="center" />
<!-- 产品规格列 -->
<el-table-column prop="planProductSize" label="产品规格" align="center" />
<!-- 交货时间列带时间格式化 -->
<el-table-column prop="deliveryTime" label="交货时间" align="center">
<template #default="scope">
{{ parseTime(scope.row.deliveryTime) }}
</template>
</el-table-column>
<!-- 开始时间列带时间格式化 -->
<el-table-column prop="startProduceTime" label="开始时间" align="center">
<template #default="scope">
{{ parseTime(scope.row.startProduceTime) }}
</template>
</el-table-column>
<!-- 结束时间列带时间格式化 -->
<el-table-column prop="finishProduceTime" label="结束时间" align="center">
<template #default="scope">
{{ parseTime(scope.row.finishProduceTime) }}
</template>
</el-table-column>
<!-- 产线数量列 -->
<el-table-column prop="bindLineNum" label="产线数量" align="center" />
<!-- 操作列对应原handleBtn插槽 -->
<el-table-column fixed="right" label="操作" width="120">
<template #default="scope">
<!-- 只在 level 0 时显示操作按钮 -->
<div v-if="scope.row.level === 0">
<!-- 详情按钮 -->
<span v-hasPermi="['base:core-work-order:update']" class="iconfont icon-detail primary-color"
@click="handleDetail(scope.row)" style="cursor: pointer; margin-right: 10px;"></span>
<!-- 编辑按钮 -->
<span v-hasPermi="['base:core-work-order:update']" class="iconfont icon-edit primary-color"
@click="handleEdit(scope.row)" style="cursor: pointer; margin-right: 10px;"></span>
<!-- 删除按钮 -->
<span v-hasPermi="['base:core-work-order:delete']" class="iconfont icon-delete delete-color"
@click="deleteHandle(scope.row.id,scope.row.name,scope.row.index)" style="cursor: pointer;"></span>
</div>
</template>
</el-table-column>
</el-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" width="50%">
<add-or-update ref="addOrUpdate" @refreshDataList="successSubmit"></add-or-update>
</base-dialog>
<detail v-if="dialogVisibleDetail" ref="detailDialog" @refreshDataList="getDataList" />
</div>
</template>
<script>
import AddOrUpdate from './add-or-updata';
import detail from './detail';
import basicPage from '../../mixins/basic-page';
import { parseTime } from '../../mixins/code-filter';
import {
getOrderPage,
deleteOrder
// getWorkshopSectionPage,
// exportWorkshopSectionExcel
} from "@/api/order/index";
import { getPdList } from '@/api/core/monitoring/auto';
import { getFactoryPage } from '@/api/core/base/factory';
const tableProps = [
{
prop: 'name',
label: '订单名称/产线名称'
},
{
prop: 'code',
label: '订单编号'
},
{
prop: 'customerName',
label: '客户'
},
{
prop: 'planProductNum',
label: '计划加工量'
},
{
prop: 'productNum',
label: '实际加工量'
},
{
prop: 'processName',
label: '产品工艺'
},
{
prop: 'planProductSize',
label: '产品规格'
},
{
prop: 'deliveryTime',
label: '交货时间',
filter: parseTime
},
{
prop: 'startProduceTime',
label: '开始时间',
filter: parseTime
},
{
prop: 'finishProduceTime',
label: '结束时间',
filter: parseTime
},
{
prop: 'bindLineNum',
label: '产线数量',
},
];
export default {
mixins: [basicPage],
data() {
return {
urlOptions: {
getDataListURL: getOrderPage,
deleteURL: deleteOrder,
// exportURL: exportWorkshopSectionExcel,
},
tableProps,
dialogVisibleDetail:false,
tableBtn: [
this.$auth.hasPermi(`base:core-work-order:update`)
? {
type: 'edit',
btnName: '编辑',
}
: undefined,
this.$auth.hasPermi(`base:core-work-order:delete`)
? {
type: 'delete',
btnName: '删除',
}
: undefined,
].filter((v)=>v),
listQuery: {
lineId: null,
startTime: undefined,
endTime:undefined
},
tableData: [],
formConfig: [
{
type: 'input',
label: '订单名称',
placeholder: '工段名称',
param: 'name',
},
// {
// type: 'select',
// label: '工厂',
// selectOptions: [],
// param: 'factoryId',
// onchange: true,
// },
{
type: 'select',
label: '产线',
selectOptions: [],
param: 'lineId',
// multiple: true,
},
{
type: 'datePicker',
label: '时间范围',
dateType: 'daterange',
format: 'yyyy-MM-dd',
valueFormat: 'timestamp',
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'timeVal',
},
{
type: this.$auth.hasPermi('base:core-work-order:query') ? 'button' : '',
btnName: '搜索',
name: 'search',
color: 'primary',
},
// {
// type: 'separate',
// },
// {
// type: 'button',
// btnName: '重置',
// name: 'reset',
// },
{
type: 'separate',
},
{
type: this.$auth.hasPermi('base:core-work-order:create') ? 'button' : '',
btnName: '新增',
name: 'add',
color: 'success',
plain: true,
},
// {
// type: this.$auth.hasPermi('base:workshop-section:create') ? 'separate' : '',
// },
// {
// type: this.$auth.hasPermi('base:workshop-section:export') ? 'button' : '',
// btnName: '导出',
// name: 'export',
// color: 'warning',
// },
],
};
},
components: {
AddOrUpdate,
detail
},
created() {
this.getPdLineList();
},
methods: {
getPdLineList() {
getPdList().then((res) => {
this.formConfig[1].selectOptions = res.data || [];
});
const params = {
pageSize: 100,
pageNo: 1,
};
getFactoryPage(params).then((res) => {
this.formConfig[0].selectOptions = res.data.list || [];
});
},
// handleSearchBarChanged({ param, value }) {
// this.listQuery.lineId = null;
// this.$refs.searchBarForm.formInline.lineId = undefined;
// getPdList(value).then((res) => {
// this.formConfig[1].selectOptions = res.data || [];
// });
// },
getDataList() {
this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then(response => {
this.tableData = response.data.list.map((ele) => {
ele.children = ele.bindLines.map((item) => {
item.name = item.lineName;
item.level = 1
return item
})
ele.level = 0
return ele;
})
console.log(this.tableData);
this.listQuery.total = response.data.total;
this.dataListLoading = false;
});
},
handleEdit(val) {
console.log(val);
// if (val.type === "edit") {
this.addOrUpdateVisible = true;
this.addOrEditTitle = "编辑";
this.$nextTick(() => {
this.$refs.addOrUpdate.init(val.id);
});
// }
},
handleDetail(val) {
console.log(val);
// if (val.type === "edit") {
this.dialogVisibleDetail = true;
// this.addOrEditTitle = "编辑";
this.$nextTick(() => {
this.$refs.detailDialog.init(val.id);
});
// }
},
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true;
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id);
});
},
buttonClick(val) {
console.log(val);
switch (val.btnName) {
case 'search':
this.listQuery.pageNo = 1;
this.listQuery.pageSize = 10;
this.listQuery.name = val.name;
this.listQuery.factoryId = val.factoryId || undefined;
this.listQuery.lineId = val.lineId || null;
this.listQuery.startTime = val.timeVal ? val.timeVal[0] : undefined
this.listQuery.endTime = val.timeVal ? val.timeVal[1] : undefined
this.getDataList();
break;
case 'reset':
this.$refs.searchBarForm.resetForm();
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);
}
},
},
};
</script>
<style>
.delete-color {
color: #ff5454;
}
.primary-color {
color: #0b58ff;
}
</style>

View File

@@ -1,109 +0,0 @@
/*
* @Author: zwq
* @Date: 2022-08-24 11:19:43
* @LastEditors: zwq
* @LastEditTime: 2023-08-03 14:21:04
* @Description:
*/
export default {
data() {
/* eslint-disable */
return {
urlOptions: {
createURL: '',
updateURL: '',
infoURL: '',
codeURL: '',
getOption: false,
isGetCode: false,
optionArrUrl: [],
optionArr: {}
},
dataForm:{},
visible: false,
setData: false
}
},
created() {
},
activated() {
},
methods: {
init(id) {
this.dataForm.id = id || "";
this.visible = true;
if (this.urlOptions.getOption) {
this.getArr()
}
this.$nextTick(() => {
this.$refs["dataForm"].resetFields();
this.dataForm.bindLineList = []
if (this.dataForm.id) {
this.urlOptions.infoURL(id).then(response => {
this.dataForm = response.data;
this.dataForm.bindLineList = response.data.bindLines.map((ele) => {
ele.lineId = ele.lineId;
ele.dateRange = [ele.startProduceTime, ele.finishProduceTime];
return ele
})
if (this.setData) {
this.setDataForm()
}
});
} else {
if (this.urlOptions.isGetCode) {
this.getCode()
}
}
});
},
getCode() {
this.urlOptions.codeURL()
.then(({ data: res }) => {
this.dataForm.code = res;
})
.catch(() => {});
},
getArr() {
const params = {
pageSize: 100,
pageNo: 1,
}
this.urlOptions.optionArrUrl.forEach((item, index) => {
item(params).then(({ data: res }) => {
this.$set(this.urlOptions.optionArr, `arr${index}`, res.list)
})
.catch(() => {
});
});
},
// 表单提交
dataFormSubmit() {
this.$refs["dataForm"].validate((valid) => {
if (!valid) {
return false;
}
// 修改的提交
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");
});
});
},
formClear() {
this.$refs.dataForm.resetFields()
}
}
}

View File

@@ -1,168 +0,0 @@
/*
* @Author: zwq
* @Date: 2022-08-24 11:19:43
* @LastEditors: DY
* @LastEditTime: 2023-09-21 16:02:07
* @Description:
*/
export default {
data() {
/* eslint-disable */
return {
urlOptions: {
getDataListURL: '',
deleteURL: '',
statusUrl: '',
exportURL: ''
},
tableData: [],
listQuery: {
pageSize: 10,
pageNo: 1,
total: 1,
},
exportLoading: false,
dataListLoading: false,
addOrEditTitle: '',
addOrUpdateVisible: false,
}
},
created() {
},
mounted() {
this.getDataList()
},
methods: {
// 获取数据列表
getDataList() {
this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then(response => {
this.tableData = response.data.list;
this.listQuery.total = response.data.total;
this.dataListLoading = false;
});
},
// 每页数
sizeChangeHandle(val) {
this.listQuery.pageSize = val;
this.listQuery.pageNo = 1;
this.getDataList();
},
// 当前页
currentChangeHandle(val) {
this.listQuery.pageNo = val;
this.getDataList();
},
// 新增 / 修改
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true;
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id);
});
},
cancel(id) {
this.$refs["popover-" + id].showPopper = false;
},
//改变状态
changeStatus(id) {
this.$http
.post(this.urlOptions.statusUrl, { id })
.then(({ data: res }) => {
if (res.code !== 0) {
return this.$message.warning(res.msg);
}
this.$refs["popover-" + id].showPopper = false;
this.$message({
message: this.$t("prompt.success"),
type: "success",
duration: 500,
onClose: () => {
this.getDataList();
},
});
})
.catch(() => { });
},
//tableBtn点击
handleClick(val) {
if (val.type === "edit") {
this.addOrUpdateVisible = true;
this.addOrEditTitle = "编辑";
this.$nextTick(() => {
this.$refs.addOrUpdate.init(val.data.id);
});
} else if (val.type === "delete") {
this.deleteHandle(val.data.id, val.data.name, val.data._pageIndex)
} else if (val.type === "change") {
this.changeStatus(val.data.id)
} else {
this.otherMethods(val)
}
},
// 删除
deleteHandle(id, name, index) {
this.$confirm(`确定对${name ? '[名称=' + name + ']' : '[序号=' + index + ']'}进行删除操作?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
this.urlOptions.deleteURL(id).then(({ data }) => {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.getDataList();
},
});
});
})
.catch(() => { });
},
//search-bar点击
buttonClick(val) {
switch (val.btnName) {
case "search":
this.listQuery.xm1 = val.xm1;
this.listQuery.xm2 = val.xm2;
this.listQuery.pageNo = 1;
this.getDataList();
break;
case "add":
this.addOrEditTitle = '新增'
this.addOrUpdateVisible = true;
this.addOrUpdateHandle()
break;
default:
console.log(val)
}
},
handleCancel() {
this.$refs.addOrUpdate.formClear()
this.addOrUpdateVisible = false
this.addOrEditTitle = ''
},
handleConfirm() {
this.$refs.addOrUpdate.dataFormSubmit()
},
successSubmit() {
this.handleCancel()
this.getDataList()
},
/** 导出按钮操作 */
handleExport() {
// 处理查询参数
let params = { ...this.queryParams };
params.pageNo = undefined;
params.pageSize = undefined;
this.$modal.confirm('是否确认导出所有数据项?').then(() => {
this.exportLoading = true;
return this.urlOptions.exportURL(params);
}).then(response => {
this.$download.excel(response, '工厂.xls');
this.exportLoading = false;
}).catch(() => { });
}
}
}

View File

@@ -1,73 +0,0 @@
/*
* @Date: 2020-12-29 16:49:28
* @LastEditors: DY
* @LastEditTime: 2023-09-12 11:13:34
* @FilePath: \basic-admin\src\filters\basicData\index.js
* @Description:
*/
const table = {
lineStatus: {
1: '生产中',
2: '停止',
3: '未知',
},
reportType: {
1: '日',
2: '周',
3: '月'
},
pdType: {
1: '原片',
0: '深加工',
},
}
// 日期格式化
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) {
return null
}
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.\d{3}/gm),'');
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') {
return ['日', '一', '二', '三', '四', '五', '六'][value]
}
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
export default function (dictTable) {
return function (val) {
return table?.[dictTable]?.[val]
}
}

17161
yarn.lock

File diff suppressed because it is too large Load Diff