Compare commits

...

74 Commits

Author SHA1 Message Date
9be57ad750 Merge pull request 'test' (#47) from test into master
Reviewed-on: #47
2023-10-17 08:53:53 +08:00
f7313c5911 Merge pull request 'lb' (#46) from lb into test
Reviewed-on: #46
2023-10-17 08:38:26 +08:00
lb
a7e81ad8fb Merge branch 'test' into lb 2023-10-16 17:04:34 +08:00
lb
32cbf9076f update 更新设备类型上传 2023-10-16 17:03:29 +08:00
9a411cc663 Merge pull request 'dy' (#45) from dy into test
Reviewed-on: #45
2023-10-16 15:50:05 +08:00
helloDy
445a88e540 Merge branch 'test' into dy 2023-10-16 15:47:50 +08:00
helloDy
2b355aaf8f ui 2023-10-16 15:47:31 +08:00
lb
f60a46ed1d update 设备信息上传 2023-10-16 15:36:26 +08:00
2688896660 Merge pull request 'dy' (#44) from dy into test
Reviewed-on: #44
2023-10-13 17:12:02 +08:00
helloDy
4a67e226e1 merge 2023-10-13 17:09:07 +08:00
helloDy
11ee0d6541 Merge branch 'test' into dy 2023-10-13 17:06:35 +08:00
98b3237c9a Merge pull request 'lb' (#43) from lb into test
Reviewed-on: #43
2023-10-13 17:06:02 +08:00
helloDy
46aaa47e07 bug 2023-10-13 17:05:01 +08:00
lb
ca0f62b2eb Merge branch 'test' into lb 2023-10-13 17:04:18 +08:00
lb
5534104e87 update equipment edit 2023-10-13 17:01:49 +08:00
lb
b72fe1bfed update equipment edit 2023-10-13 16:08:31 +08:00
f4493bde1c Merge pull request 'dy' (#42) from dy into test
Reviewed-on: #42
2023-10-12 17:06:04 +08:00
helloDy
d682ecc91c Merge branch 'test' into dy 2023-10-12 17:05:03 +08:00
helloDy
2b76ef7d23 bug 2023-10-12 17:04:03 +08:00
lb
9ec13b35b8 update equipment 2023-10-12 17:03:07 +08:00
lb
7acccd3de0 bugfix zentao 2023-10-12 15:40:05 +08:00
e40f45a79a Merge pull request 'zjl' (#41) from zjl into test
Reviewed-on: #41
2023-10-12 14:43:24 +08:00
77083a84a1 merge test 2023-10-12 14:41:59 +08:00
9f53ea6981 10.12 2023-10-12 14:39:59 +08:00
lb
8f634d012c bugfix zentao 2023-10-12 11:08:51 +08:00
03c573d5b2 Merge pull request 'zjl' (#40) from zjl into test
Reviewed-on: #40
2023-10-11 14:34:26 +08:00
348714edaf merge test 2023-10-11 14:33:37 +08:00
814fe4605a UI样式修改能源&班组 2023-10-11 14:30:18 +08:00
bb6bfc5ab6 Merge pull request 'lb' (#39) from lb into test
Reviewed-on: #39
2023-10-11 10:33:08 +08:00
lb
0162f338ba Merge branch 'test' into lb 2023-10-11 10:30:25 +08:00
lb
8f99915720 add 设备看板 2023-10-11 10:29:26 +08:00
744e5d80be Merge pull request 'dy' (#38) from dy into test
Reviewed-on: #38
2023-10-10 17:08:40 +08:00
helloDy
d3b119ad9b Merge branch 'test' into dy 2023-10-10 17:07:02 +08:00
helloDy
69bdab75d4 bug 2023-10-10 17:06:39 +08:00
lb
af5e0333ed update ui 2023-10-10 17:01:57 +08:00
lb
b2ee61b8cb update 改为抽屉展示 2023-10-10 14:22:00 +08:00
lb
723d83cc02 update 设备分组 2023-10-10 10:04:23 +08:00
lb
769830c448 update 设备分组 2023-10-09 17:01:19 +08:00
lb
907db90e93 update 设备分组的抽屉 2023-10-09 16:27:44 +08:00
lb
6e390bf12a update 2023-10-09 13:32:33 +08:00
lb
b39f0625a1 update 2023-10-09 11:37:19 +08:00
lb
e985c73e00 update gantt 2023-10-08 17:14:18 +08:00
180fcf5ad8 Merge pull request 'dy' (#37) from dy into test
Reviewed-on: #37
2023-10-08 17:03:04 +08:00
helloDy
b1c0d21888 Merge branch 'test' into dy 2023-10-08 16:59:31 +08:00
helloDy
96d52fafee pdf 2023-10-08 16:59:09 +08:00
lb
4f7889ff60 done time sequence demo 2023-10-08 15:53:38 +08:00
lb
bc2c5a4787 update time sequence status 2023-10-08 15:00:38 +08:00
lb
ab8e9cb2a5 update 2023-10-08 14:30:50 +08:00
lb
57960ff8bf update print page 2023-10-08 10:18:08 +08:00
lb
b9e39dacf1 bugfix zentao 2023-10-08 09:31:02 +08:00
lb
80ad95be6e add print module 2023-10-07 14:29:43 +08:00
7dfaae02a8 Merge pull request 'lb' (#36) from lb into test
Reviewed-on: #36
2023-09-27 17:00:53 +08:00
lb
cf2d7af94b delete echarts 2023-09-27 17:00:33 +08:00
lb
a48b31dcbe Merge branch 'test' into lb 2023-09-27 16:58:28 +08:00
lb
96b0a9e503 update 产品质量分析 2023-09-27 16:57:57 +08:00
lb
9c257aa24a update demo 2023-09-27 16:19:55 +08:00
lb
7ab5f1b812 update demo 2023-09-27 14:42:38 +08:00
lb
5c796cd44f add custom echarts demo 2023-09-27 14:01:57 +08:00
lb
fe0b5538a8 update 设备产量时序图 2023-09-27 13:22:09 +08:00
lb
90695acefd update 检测信息记录 2023-09-27 10:14:24 +08:00
a90c5cca0c Merge pull request 'zjl' (#35) from zjl into test
Reviewed-on: #35
2023-09-27 09:38:02 +08:00
4d7e3c0e04 merge test 2023-09-27 09:36:20 +08:00
ee40707d2c 修改bug及能源分析样式 2023-09-27 09:33:28 +08:00
lb
20808c0975 update 产量时序图 2023-09-26 17:00:46 +08:00
lb
87f56dd9ac update responsejson 2023-09-26 15:12:41 +08:00
lb
24ffea330d update 设备产量分析 2023-09-26 14:56:00 +08:00
lb
a4594426c3 bugfix 2023-09-26 14:38:30 +08:00
lb
c899bb37e3 update quality inspection record 2023-09-26 11:03:34 +08:00
47e91d821b Merge pull request 'lb' (#34) from lb into test
Reviewed-on: #34
2023-09-25 17:14:23 +08:00
lb
d907d2f341 Merge branch 'test' into lb 2023-09-25 17:13:47 +08:00
lb
1116531aff update 2023-09-25 16:58:35 +08:00
db4ecc91d6 Merge pull request 'dy' (#33) from dy into test
Reviewed-on: #33
2023-09-22 16:59:55 +08:00
helloDy
699810f40a Merge branch 'test' into dy 2023-09-22 16:54:40 +08:00
helloDy
d454f20bd6 bug 2023-09-22 16:54:16 +08:00
107 changed files with 8817 additions and 2026 deletions

View File

@@ -1,7 +1,7 @@
### ###
# @Author: Do not edit # @Author: Do not edit
# @Date: 2023-08-29 09:40:39 # @Date: 2023-08-29 09:40:39
# @LastEditTime: 2023-09-18 10:44:07 # @LastEditTime: 2023-10-16 09:22:52
# @LastEditors: DY # @LastEditors: DY
# @Description: # @Description:
### ###

1
.gitignore vendored
View File

@@ -20,3 +20,4 @@ selenium-debug.log
*.local *.local
package-lock.json package-lock.json
echarts.js

View File

@@ -50,6 +50,7 @@
"code-brick-zj": "^1.0.2", "code-brick-zj": "^1.0.2",
"core-js": "^3.26.0", "core-js": "^3.26.0",
"crypto-js": "^4.0.0", "crypto-js": "^4.0.0",
"diagram-js": "^12.3.0",
"echarts": "5.4.0", "echarts": "5.4.0",
"element-ui": "2.15.12", "element-ui": "2.15.12",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
@@ -64,21 +65,25 @@
"moment": "^2.29.4", "moment": "^2.29.4",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"qrcode.vue": "^1.7.0", "qrcode.vue": "^1.7.0",
"qs": "^6.11.2",
"quill": "1.3.7", "quill": "1.3.7",
"screenfull": "5.0.2", "screenfull": "5.0.2",
"sortablejs": "1.10.2", "sortablejs": "1.10.2",
"throttle-debounce": "2.1.0", "throttle-debounce": "2.1.0",
"video.js": "^8.5.2",
"vue": "2.7.14", "vue": "2.7.14",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-cropper": "0.5.8", "vue-cropper": "0.5.8",
"vue-meta": "^2.4.0", "vue-meta": "^2.4.0",
"vue-plugin-hiprint": "0.0.54-fix",
"vue-quill-editor": "^3.0.6", "vue-quill-editor": "^3.0.6",
"vue-router": "3.4.9", "vue-router": "3.4.9",
"vue-video-player": "^5.0.2", "vue-video-player": "^5.0.2",
"vuedraggable": "2.24.3", "vuedraggable": "2.24.3",
"vuex": "3.6.2", "vuex": "3.6.2",
"xlsx": "^0.18.5", "xlsx": "^0.18.5",
"xml-js": "1.6.11" "xml-js": "1.6.11",
"yorkie": "^2.0.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "4.5.18", "@vue/cli-plugin-babel": "4.5.18",
@@ -92,7 +97,7 @@
"chalk": "4.1.0", "chalk": "4.1.0",
"compression-webpack-plugin": "5.0.2", "compression-webpack-plugin": "5.0.2",
"connect": "3.6.6", "connect": "3.6.6",
"eslint": "7.15.0", "eslint": "6.8.0",
"eslint-config-airbnb-base": "^14.0.0", "eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^3.1.0", "eslint-plugin-prettier": "^3.1.0",

View File

@@ -1,215 +1,218 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="utf-8" /> <head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta charset="utf-8" />
<meta name="renderer" content="webkit" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta <meta name="renderer" content="webkit" />
name="viewport" <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> <link rel="stylesheet" type="text/css" media="print" href="<%= BASE_URL %>print-lock.css">
<link rel="icon" href="<%= BASE_URL %>favicon.ico" /> <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title><%= webpackConfig.name %></title> <title>
<!--[if lt IE 11]> <%= webpackConfig.name %>
</title>
<!--[if lt IE 11]>
<script> <script>
window.location.href = 'html/ie.html'; window.location.href = 'html/ie.html';
</script> </script>
<![endif]--> <![endif]-->
<style> <style>
html, html,
body, body,
#app { #app {
height: 100%; height: 100%;
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
}
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
#loader-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999999;
}
#loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #fff;
-webkit-animation: spin 2s linear infinite;
-ms-animation: spin 2s linear infinite;
-moz-animation: spin 2s linear infinite;
-o-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
z-index: 1001;
}
#loader:before {
content: '';
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #fff;
-webkit-animation: spin 3s linear infinite;
-moz-animation: spin 3s linear infinite;
-o-animation: spin 3s linear infinite;
-ms-animation: spin 3s linear infinite;
animation: spin 3s linear infinite;
}
#loader:after {
content: '';
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #fff;
-moz-animation: spin 1.5s linear infinite;
-o-animation: spin 1.5s linear infinite;
-ms-animation: spin 1.5s linear infinite;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
} }
.chromeframe { 100% {
margin: 0.2em 0; -webkit-transform: rotate(360deg);
background: #ccc; -ms-transform: rotate(360deg);
color: #000; transform: rotate(360deg);
padding: 0.2em 0; }
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
} }
#loader-wrapper { 100% {
position: fixed; -webkit-transform: rotate(360deg);
top: 0; -ms-transform: rotate(360deg);
left: 0; transform: rotate(360deg);
width: 100%;
height: 100%;
z-index: 999999;
} }
}
#loader { #loader-wrapper .loader-section {
display: block; position: fixed;
position: relative; top: 0;
left: 50%; width: 51%;
top: 50%; height: 100%;
width: 150px; background: #7171c6;
height: 150px; z-index: 1000;
margin: -75px 0 0 -75px; -webkit-transform: translateX(0);
border-radius: 50%; -ms-transform: translateX(0);
border: 3px solid transparent; transform: translateX(0);
border-top-color: #fff; }
-webkit-animation: spin 2s linear infinite;
-ms-animation: spin 2s linear infinite;
-moz-animation: spin 2s linear infinite;
-o-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
z-index: 1001;
}
#loader:before { #loader-wrapper .loader-section.section-left {
content: ''; left: 0;
position: absolute; }
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #fff;
-webkit-animation: spin 3s linear infinite;
-moz-animation: spin 3s linear infinite;
-o-animation: spin 3s linear infinite;
-ms-animation: spin 3s linear infinite;
animation: spin 3s linear infinite;
}
#loader:after { #loader-wrapper .loader-section.section-right {
content: ''; right: 0;
position: absolute; }
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #fff;
-moz-animation: spin 1.5s linear infinite;
-o-animation: spin 1.5s linear infinite;
-ms-animation: spin 1.5s linear infinite;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
@-webkit-keyframes spin { .loaded #loader-wrapper .loader-section.section-left {
0% { -webkit-transform: translateX(-100%);
-webkit-transform: rotate(0deg); -ms-transform: translateX(-100%);
-ms-transform: rotate(0deg); transform: translateX(-100%);
transform: rotate(0deg); -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
} transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
100% { .loaded #loader-wrapper .loader-section.section-right {
-webkit-transform: rotate(360deg); -webkit-transform: translateX(100%);
-ms-transform: rotate(360deg); -ms-transform: translateX(100%);
transform: rotate(360deg); transform: translateX(100%);
} -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
} transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
@keyframes spin { .loaded #loader {
0% { opacity: 0;
-webkit-transform: rotate(0deg); -webkit-transition: all 0.3s ease-out;
-ms-transform: rotate(0deg); transition: all 0.3s ease-out;
transform: rotate(0deg); }
}
100% { .loaded #loader-wrapper {
-webkit-transform: rotate(360deg); visibility: hidden;
-ms-transform: rotate(360deg); -webkit-transform: translateY(-100%);
transform: rotate(360deg); -ms-transform: translateY(-100%);
} transform: translateY(-100%);
} -webkit-transition: all 0.3s 1s ease-out;
transition: all 0.3s 1s ease-out;
}
#loader-wrapper .loader-section { .no-js #loader-wrapper {
position: fixed; display: none;
top: 0; }
width: 51%;
height: 100%;
background: #7171c6;
z-index: 1000;
-webkit-transform: translateX(0);
-ms-transform: translateX(0);
transform: translateX(0);
}
#loader-wrapper .loader-section.section-left { .no-js h1 {
left: 0; color: #222222;
} }
#loader-wrapper .loader-section.section-right { #loader-wrapper .load_title {
right: 0; font-family: 'Open Sans';
} color: #fff;
font-size: 19px;
width: 100%;
text-align: center;
z-index: 9999999999999;
position: absolute;
top: 60%;
opacity: 1;
line-height: 30px;
}
.loaded #loader-wrapper .loader-section.section-left { #loader-wrapper .load_title span {
-webkit-transform: translateX(-100%); font-weight: normal;
-ms-transform: translateX(-100%); font-style: italic;
transform: translateX(-100%); font-size: 13px;
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); color: #fff;
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); opacity: 0.5;
} }
</style>
</head>
.loaded #loader-wrapper .loader-section.section-right { <body>
-webkit-transform: translateX(100%); <div id="app">
-ms-transform: translateX(100%); <div id="loader-wrapper">
transform: translateX(100%); <div id="loader"></div>
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); <div class="loader-section section-left"></div>
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); <div class="loader-section section-right"></div>
} <div class="load_title">正在加载系统资源,请耐心等待</div>
.loaded #loader {
opacity: 0;
-webkit-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
.loaded #loader-wrapper {
visibility: hidden;
-webkit-transform: translateY(-100%);
-ms-transform: translateY(-100%);
transform: translateY(-100%);
-webkit-transition: all 0.3s 1s ease-out;
transition: all 0.3s 1s ease-out;
}
.no-js #loader-wrapper {
display: none;
}
.no-js h1 {
color: #222222;
}
#loader-wrapper .load_title {
font-family: 'Open Sans';
color: #fff;
font-size: 19px;
width: 100%;
text-align: center;
z-index: 9999999999999;
position: absolute;
top: 60%;
opacity: 1;
line-height: 30px;
}
#loader-wrapper .load_title span {
font-weight: normal;
font-style: italic;
font-size: 13px;
color: #fff;
opacity: 0.5;
}
</style>
</head>
<body>
<div id="app">
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<div class="load_title">正在加载系统资源,请耐心等待</div>
</div>
</div> </div>
</body> </div>
</html> </body>
</html>

339
public/print-lock.css Normal file
View File

@@ -0,0 +1,339 @@
@media print {
body {
margin: 0px;
padding: 0px;
}
}
@page {
margin: 0;
}
.hiprint-printPaper * {
box-sizing: border-box;
-moz-box-sizing: border-box; /* Firefox */
-webkit-box-sizing: border-box; /* Safari */
}
.hiprint-printPaper *:focus {
outline: -webkit-focus-ring-color auto 0px;
}
.hiprint-printPaper {
position: relative;
padding: 0 0 0 0;
page-break-after: always;
-webkit-user-select: none; /* Chrome/Safari/Opera */
-moz-user-select: none; /* Firefox */
user-select: none;
overflow-x: hidden;
overflow: hidden;
}
.hiprint-printPaper .hiprint-printPaper-content {
position: relative;
}
/* 火狐浏览器打印 第一页过后 重叠问题 */
@-moz-document url-prefix() {
.hiprint-printPaper .hiprint-printPaper-content {
position: relative;
margin-top: 20px;
top: -20px
}
}
.hiprint-printPaper.design {
overflow: visible;
}
.hiprint-printTemplate .hiprint-printPanel {
page-break-after: always;
}
.hiprint-printPaper, hiprint-printPanel {
box-sizing: border-box;
border: 0px;
}
.hiprint-printPanel .hiprint-printPaper:last-child {
page-break-after: avoid;
}
.hiprint-printTemplate .hiprint-printPanel:last-child {
page-break-after: avoid;
}
.hiprint-printPaper .hideheaderLinetarget {
border-top: 0px dashed rgb(201, 190, 190) !important;
}
.hiprint-printPaper .hidefooterLinetarget {
border-top: 0px dashed rgb(201, 190, 190) !important;
}
.hiprint-printPaper.design {
border: 1px dashed rgba(170, 170, 170, 0.7);
}
.design .hiprint-printElement-table-content, .design .hiprint-printElement-longText-content {
overflow: hidden;
box-sizing: border-box;
}
.design .resize-panel {
box-sizing: border-box;
border: 1px dotted;
}
.hiprint-printElement-text {
background-color: transparent;
background-repeat: repeat;
padding: 0 0 0 0;
border: 0.75pt none rgb(0, 0, 0);
direction: ltr;
font-family: 'SimSun';
font-size: 9pt;
font-style: normal;
font-weight: normal;
padding-bottom: 0pt;
padding-left: 0pt;
padding-right: 0pt;
padding-top: 0pt;
text-align: left;
text-decoration: none;
line-height: 9.75pt;
box-sizing: border-box;
word-wrap: break-word;
word-break: break-all;
}
.design .hiprint-printElement-text-content {
border: 1px dashed rgb(206, 188, 188);
box-sizing: border-box;
}
.hiprint-printElement-longText {
background-color: transparent;
background-repeat: repeat;
border: 0.75pt none rgb(0, 0, 0);
direction: ltr;
font-family: 'SimSun';
font-size: 9pt;
font-style: normal;
font-weight: normal;
padding-bottom: 0pt;
padding-left: 0pt;
padding-right: 0pt;
padding-top: 0pt;
text-align: left;
text-decoration: none;
line-height: 9.75pt;
box-sizing: border-box;
word-wrap: break-word;
word-break: break-all;
/*white-space: pre-wrap*/
}
.hiprint-printElement-table {
background-color: transparent;
background-repeat: repeat;
color: rgb(0, 0, 0);
border-color: rgb(0, 0, 0);
border-style: none;
direction: ltr;
font-family: 'SimSun';
font-size: 9pt;
font-style: normal;
font-weight: normal;
padding-bottom: 0pt;
padding-left: 0pt;
padding-right: 0pt;
padding-top: 0pt;
text-align: left;
text-decoration: none;
padding: 0 0 0 0;
box-sizing: border-box;
line-height: 9.75pt;
}
.hiprint-printElement-table thead {
background: #e8e8e8;
font-weight: 700;
}
.hiprint-printElement-tableTarget, .hiprint-printElement-tableTarget tr, .hiprint-printElement-tableTarget td {
border-color: rgb(0, 0, 0);
/*border-style: none;*/
/*border: 1px solid rgb(0, 0, 0);*/
font-weight: normal;
direction: ltr;
padding-bottom: 0pt;
padding-left: 4pt;
padding-right: 4pt;
padding-top: 0pt;
text-decoration: none;
vertical-align: middle;
box-sizing: border-box;
word-wrap: break-word;
word-break: break-all;
/*line-height: 9.75pt;
font-size: 9pt;*/
}
.hiprint-printElement-tableTarget-border-all {
border: 1px solid;
}
.hiprint-printElement-tableTarget-border-none {
border: 0px solid;
}
.hiprint-printElement-tableTarget-border-lr {
border-left: 1px solid;
border-right: 1px solid;
}
.hiprint-printElement-tableTarget-border-left {
border-left: 1px solid;
}
.hiprint-printElement-tableTarget-border-right {
border-right: 1px solid;
}
.hiprint-printElement-tableTarget-border-tb {
border-top: 1px solid;
border-bottom: 1px solid;
}
.hiprint-printElement-tableTarget-border-top {
border-top: 1px solid;
}
.hiprint-printElement-tableTarget-border-bottom {
border-bottom: 1px solid;
}
.hiprint-printElement-tableTarget-border-td-none td {
border: 0px solid;
}
.hiprint-printElement-tableTarget-border-td-all td:not(:last-child) {
border-right: 1px solid;
}
/*.hiprint-printElement-tableTarget tr,*/
.hiprint-printElement-tableTarget td {
height: 18pt;
}
.hiprint-printPaper .hiprint-paperNumber {
font-size: 9pt;
}
.design .hiprint-printElement-table-handle {
position: absolute;
height: 21pt;
width: 21pt;
background: red;
z-index: 1;
}
.hiprint-printPaper .hiprint-paperNumber-disabled {
float: right !important;
right: 0 !important;
color: gainsboro !important;
}
.hiprint-printElement-vline, .hiprint-printElement-hline {
border: 0px none rgb(0, 0, 0);
}
.hiprint-printElement-vline {
border-left: 0.75pt solid #000;
border-right: 0px none rgb(0, 0, 0) !important;
border-bottom: 0px none rgb(0, 0, 0) !important;
border-top: 0px none rgb(0, 0, 0) !important;
}
.hiprint-printElement-hline {
border-top: 0.75pt solid #000;
border-right: 0px none rgb(0, 0, 0) !important;
border-bottom: 0px none rgb(0, 0, 0) !important;
border-left: 0px none rgb(0, 0, 0) !important;
}
.hiprint-printElement-oval, .hiprint-printElement-rect {
border: 0.75pt solid #000;
}
.hiprint-text-content-middle {
}
.hiprint-text-content-middle > div {
display: grid;
align-items: center;
}
.hiprint-text-content-bottom {
}
.hiprint-text-content-bottom > div {
display: grid;
align-items: flex-end;
}
.hiprint-text-content-wrap {
}
.hiprint-text-content-wrap .hiprint-text-content-wrap-nowrap {
white-space: nowrap;
}
.hiprint-text-content-wrap .hiprint-text-content-wrap-clip {
white-space: nowrap;
overflow: hidden;
text-overflow: clip;
}
.hiprint-text-content-wrap .hiprint-text-content-wrap-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/*hi-grid-row */
.hi-grid-row {
position: relative;
height: auto;
margin-right: 0;
margin-left: 0;
zoom: 1;
display: block;
box-sizing: border-box;
}
.hi-grid-row::after, .hi-grid-row::before {
display: table;
content: '';
box-sizing: border-box;
}
.hi-grid-col {
display: block;
box-sizing: border-box;
position: relative;
float: left;
flex: 0 0 auto;
}
.table-grid-row {
margin-left: -0pt;
margin-right: -0pt;
}
.tableGridColumnsGutterRow {
padding-left: 0pt;
padding-right: 0pt;
}
.hiprint-gridColumnsFooter {
text-align: left;
clear: both;
}

View File

@@ -1,3 +1,10 @@
/*
* @Author: Do not edit
* @Date: 2023-08-28 15:30:53
* @LastEditTime: 2023-10-13 17:08:33
* @LastEditors: DY
* @Description:
*/
import request from '@/utils/request' import request from '@/utils/request'
// 创建产线目前生产产品表 主要为更新 // 创建产线目前生产产品表 主要为更新
@@ -35,11 +42,11 @@ export function getLineBindProductLog(id) {
} }
// 获得产线目前生产产品表 主要为更新分页 // 获得产线目前生产产品表 主要为更新分页
export function getLineBindProductLogPage(query) { export function getLineBindProductLogPage(data) {
return request({ return request({
url: '/base/line-bind-product-log/page', url: '/base/line-bind-product-log/page',
method: 'get', method: 'post',
params: query data: data
}) })
} }

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="设备管理" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="设备监控-设备加工数量" transform="translate(-496.000000, -187.000000)" fill-rule="nonzero">
<g id="选择" transform="translate(496.000000, 187.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M13.8735116,4.65950813 C13.6725171,4.47101195 13.3555167,4.48001613 13.1670206,4.6815213 L6.49899208,11.7809972 L2.85649778,8.19199033 C2.65950812,7.99798414 2.34299153,8 2.14898534,8.19698966 C1.95497916,8.3934955 1.95747883,8.71049591 2.15449536,8.90399141 L6.16199434,12.8519823 C6.16349952,12.8534874 6.1654885,12.8539712 6.16699367,12.8554764 C6.16849884,12.8569816 6.16898266,12.8589706 6.17048783,12.8604757 C6.20198897,12.8904717 6.23999461,12.9074856 6.27649508,12.9274829 C6.29450341,12.9379922 6.3099852,12.953474 6.32898802,12.960973 C6.38798547,12.9839807 6.45047706,12.9959683 6.51248486,12.9959683 C6.57798681,12.9959683 6.64348875,12.9829593 6.70447519,12.9569682 C6.72498319,12.9484747 6.74097566,12.930977 6.75997848,12.919957 C6.79747344,12.8979438 6.83647358,12.8794517 6.86848541,12.8469561 C6.86999058,12.8454509 6.87047439,12.8429512 6.87197956,12.841446 C6.87297405,12.8399409 6.87498991,12.8394571 6.8764682,12.8379519 L13.8949604,5.36594544 C14.0849886,5.16548852 14.0745061,4.8489988 13.8735116,4.65950813 Z" id="路径" fill="#0B58FF"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,14 @@
<?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="设备管理" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="设备监控-设备加工数量" transform="translate(-284.000000, -164.000000)" fill-rule="nonzero">
<g id="编组-7" transform="translate(284.000000, 162.000000)">
<g id="菜单" transform="translate(0.000000, 2.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M3.00057813,4.49926562 L13.0025156,4.49926562 C13.2786563,4.49926562 13.5025156,4.27540625 13.5025156,3.99926563 C13.5025156,3.723125 13.2786563,3.49926563 13.0025156,3.49926563 L3.00057813,3.49926563 C2.7244375,3.49926563 2.50057813,3.723125 2.50057813,3.99926563 C2.50057813,4.27540625 2.7244375,4.49926562 3.00057813,4.49926562 Z M3.0004375,8.48703125 L12.9786875,8.48703125 C13.2548281,8.48703125 13.4786875,8.26317187 13.4786875,7.98703125 C13.4786875,7.71089062 13.2548281,7.48703125 12.9786875,7.48703125 L3.0004375,7.48703125 C2.72429687,7.48703125 2.5004375,7.71089062 2.5004375,7.98703125 C2.5004375,8.26317187 2.72429687,8.48703125 3.0004375,8.48703125 Z M13.0025156,11.4969063 L3.00057813,11.4969063 C2.7244375,11.4969063 2.50057813,11.7207656 2.50057813,11.9969063 C2.50057813,12.2730469 2.7244375,12.4969063 3.00057813,12.4969063 L13.0025156,12.4969063 C13.2786563,12.4969063 13.5025156,12.2730469 13.5025156,11.9969063 C13.5025156,11.7207656 13.2786563,11.4969063 13.0025156,11.4969063 L13.0025156,11.4969063 Z" id="形状" fill="#373738"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,15 @@
<?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>状态切换备份 3</title>
<g id="页面" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon和插图" transform="translate(-877.000000, -246.000000)">
<g id="状态切换备份-3" transform="translate(885.000000, 254.000000) rotate(-270.000000) translate(-885.000000, -254.000000) translate(877.000000, 246.000000)">
<rect id="矩形" stroke="#979797" fill="#D8D8D8" opacity="0" x="0.5" y="0.5" width="15" height="15"></rect>
<g id="错误" transform="translate(0.000000, 0.000000)" fill-rule="nonzero">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="6.4293957e-15" width="16" height="16"></rect>
<path d="M8,1 C11.85,1 15,4.15 15,8 C15,11.85 11.85,15 8,15 C4.15,15 1,11.85 1,8 C1,4.15 4.15,1 8,1 Z M8,2.19926499 C4.80249503,2.19926499 2.18635461,4.80959575 2.18635461,8 C2.18635461,11.1904043 4.80249503,13.800735 8,13.800735 C11.197505,13.800735 13.8136454,11.1904043 13.8136454,8 C13.8136454,4.80959575 11.197505,2.19926499 8,2.19926499 Z M9.18342887,4.39602962 C9.42080763,4.39602962 9.61773566,4.56921978 9.65474747,4.7961346 L9.66099805,4.87359881 L9.66052938,9.97276858 L10.1621026,9.47231604 C10.3071657,9.32725295 10.5223305,9.29501672 10.6985498,9.37560733 L10.7713701,9.41705393 L10.837514,9.47231606 C11.0033004,9.63810246 11.0217211,9.89545015 10.8927761,10.0815835 L10.837514,10.1477274 L9.52115984,11.4640816 C9.38457467,11.600698 9.17913638,11.6415719 9.00066062,11.5676398 C8.84450229,11.5029526 8.73591407,11.3615296 8.71120699,11.1977853 L8.70585968,11.1264012 L8.70585968,4.87359881 C8.70585968,4.60984463 8.91967469,4.39602962 9.18342887,4.39602962 Z M6.99933939,4.43236016 C7.15549771,4.49704738 7.26408594,4.63847045 7.28879302,4.80221473 L7.29414032,4.87359881 L7.29414032,11.1264012 C7.29414032,11.2530604 7.24382515,11.374532 7.15426356,11.4640936 C7.06470196,11.5536552 6.94323037,11.6039704 6.81657114,11.6039704 C6.68991191,11.6039704 6.56844032,11.5536552 6.47887872,11.4640936 C6.40722945,11.3924443 6.36069788,11.3003726 6.34495123,11.2015604 L6.33900196,11.1264012 L6.33858601,6.02634681 L5.83789738,6.52768395 C5.69283427,6.67274705 5.47766955,6.70498329 5.30145018,6.62439268 L5.22862992,6.58294608 L5.162486,6.52768394 C4.9966996,6.36189754 4.97827889,6.10454984 5.10722387,5.91841648 L5.162486,5.85227257 L6.47884017,4.53591839 C6.61542534,4.39930202 6.82086362,4.35842813 6.99933939,4.43236016 Z" id="形状结合" fill="#0B58FF"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>收起箭头小备份 3</title>
<g id="能源管理" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="能源监控-同比分析" transform="translate(-848.000000, -392.000000)" fill-rule="nonzero">
<g id="收起箭头小备份-3" transform="translate(855.000000, 399.000000) scale(1, -1) translate(-855.000000, -399.000000) translate(848.000000, 392.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="14" height="14"></rect>
<path d="M6.60391666,1.57197282 L7.01633334,1.15897282 L12.70325,6.8441395 C12.9278333,7.06872283 12.93075,7.4408895 12.70325,7.6683895 C12.5941655,7.77825546 12.4457769,7.84009206 12.2909546,7.84020155 C12.1361323,7.84031104 11.9876564,7.77868438 11.8784167,7.66897282 L7.0175,2.8103895 L2.16066666,7.66955616 C2.05160439,7.77930978 1.90326864,7.84102718 1.74854166,7.84102718 C1.59381468,7.84102718 1.44547894,7.77930978 1.33641666,7.66955616 C1.10964988,7.4415567 1.1091291,7.07336285 1.33525,6.84472282 L6.58116666,1.59647282 L6.6045,1.57255616 L6.60391666,1.57197282 Z" id="路径" fill="#FFBD02"></path>
<path d="M6.60391666,6.57197282 L7.01633334,6.15897282 L12.70325,11.8441395 C12.9278333,12.0687228 12.93075,12.4408895 12.70325,12.6683895 C12.5941655,12.7782555 12.4457769,12.8400921 12.2909546,12.8402015 C12.1361323,12.840311 11.9876564,12.7786844 11.8784167,12.6689728 L7.0175,7.8103895 L2.16066666,12.6695562 C2.05160439,12.7793098 1.90326864,12.8410272 1.74854166,12.8410272 C1.59381468,12.8410272 1.44547894,12.7793098 1.33641666,12.6695562 C1.10964988,12.4415567 1.1091291,12.0733628 1.33525,11.8447228 L6.58116666,6.59647282 L6.6045,6.57255616 L6.60391666,6.57197282 Z" id="路径备份-2" fill="#FFBD02"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>收起箭头小备份 11</title>
<g id="能源管理" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="能源监控-同比分析" transform="translate(-848.000000, -269.000000)" fill-rule="nonzero">
<g id="收起箭头小备份-11" transform="translate(848.000000, 269.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="14" height="14"></rect>
<path d="M6.60391666,1.57197282 L7.01633334,1.15897282 L12.70325,6.8441395 C12.9278333,7.06872283 12.93075,7.4408895 12.70325,7.6683895 C12.5941655,7.77825546 12.4457769,7.84009206 12.2909546,7.84020155 C12.1361323,7.84031104 11.9876564,7.77868438 11.8784167,7.66897282 L7.0175,2.8103895 L2.16066666,7.66955616 C2.05160439,7.77930978 1.90326864,7.84102718 1.74854166,7.84102718 C1.59381468,7.84102718 1.44547894,7.77930978 1.33641666,7.66955616 C1.10964988,7.4415567 1.1091291,7.07336285 1.33525,6.84472282 L6.58116666,1.59647282 L6.6045,1.57255616 L6.60391666,1.57197282 Z" id="路径" fill="#37D97F"></path>
<path d="M6.60391666,6.57197282 L7.01633334,6.15897282 L12.70325,11.8441395 C12.9278333,12.0687228 12.93075,12.4408895 12.70325,12.6683895 C12.5941655,12.7782555 12.4457769,12.8400921 12.2909546,12.8402015 C12.1361323,12.840311 11.9876564,12.7786844 11.8784167,12.6689728 L7.0175,7.8103895 L2.16066666,12.6695562 C2.05160439,12.7793098 1.90326864,12.8410272 1.74854166,12.8410272 C1.59381468,12.8410272 1.44547894,12.7793098 1.33641666,12.6695562 C1.10964988,12.4415567 1.1091291,12.0733628 1.33525,11.8447228 L6.58116666,6.59647282 L6.6045,6.57255616 L6.60391666,6.57197282 Z" id="路径备份-2" fill="#37D97F"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 B

View File

@@ -10,6 +10,8 @@
ref="form" ref="form"
:model="form" :model="form"
:label-width="`${labelWidth}px`" :label-width="`${labelWidth}px`"
:size="size"
:label-position="labelPosition"
v-loading="formLoading"> v-loading="formLoading">
<el-row :gutter="20" v-for="(row, rindex) in rows" :key="rindex"> <el-row :gutter="20" v-for="(row, rindex) in rows" :key="rindex">
<el-col v-for="col in row" :key="col.label" :span="24 / row.length"> <el-col v-for="col in row" :key="col.label" :span="24 / row.length">
@@ -46,23 +48,6 @@
:placeholder="`请选择${col.label}`" :placeholder="`请选择${col.label}`"
value-format="timestamp" value-format="timestamp"
v-bind="col.bind"></el-date-picker> v-bind="col.bind"></el-date-picker>
<el-upload
class="upload-in-dialog"
v-if="col.upload"
:file-list="uploadedFileList"
:action="col.url"
:on-success="handleUploadSuccess"
v-bind="col.bind">
<el-button
size="small"
type="primary"
:disabled="col.bind?.disabled || false">
点击上传
</el-button>
<div class="el-upload__tip" slot="tip" v-if="col.uploadTips">
{{ col.uploadTips || '只能上传jpg/png文件大小不超过2MB' }}
</div>
</el-upload>
<el-switch <el-switch
v-if="col.switch" v-if="col.switch"
v-model="form[col.prop]" v-model="form[col.prop]"
@@ -74,6 +59,43 @@
:key="col.key" :key="col.key"
:is="col.subcomponent" :is="col.subcomponent"
:inlineStyle="col.style"></component> :inlineStyle="col.style"></component>
<div
class="upload-area"
:class="uploadOpen ? '' : 'height-48'"
ref="uploadArea"
v-if="col.upload">
<span class="close-icon" :class="uploadOpen ? 'open' : ''">
<el-button
type="text"
icon="el-icon-arrow-right"
@click="handleFilesOpen" />
</span>
<!-- :file-list="uploadedFileList" -->
<el-upload
class="upload-in-dialog"
v-if="col.upload"
:action="uploadUrl"
:headers="uploadHeaders"
:show-file-list="false"
icon="el-icon-upload2"
:before-upload="beforeUpload"
:on-success="handleUploadSuccess"
v-bind="col.bind">
<el-button size="mini" :disabled="col.bind?.disabled || false">
上传文件
</el-button>
<div class="el-upload__tip" slot="tip" v-if="col.uploadTips">
{{ col.uploadTips || '只能上传jpg/png文件, 大小不超过2MB' }}
</div>
</el-upload>
<uploadedFile
class="file"
v-for="file in form[col.prop] || []"
:file="file"
@delete="handleDeleteFile(file)" />
</div>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -81,6 +103,9 @@
</template> </template>
<script> <script>
import { getAccessToken } from '@/utils/auth';
import tupleImg from '@/assets/images/tuple.png';
/** /**
* 找到最长的label * 找到最长的label
* @param {*} options * @param {*} options
@@ -89,6 +114,8 @@ function findMaxLabelWidth(rows) {
let max = 0; let max = 0;
rows.forEach((row) => { rows.forEach((row) => {
row.forEach((opt) => { row.forEach((opt) => {
// debugger;
if (!opt.label) return 0;
if (opt.label.length > max) { if (opt.label.length > max) {
max = opt.label.length; max = opt.label.length;
} }
@@ -97,6 +124,48 @@ function findMaxLabelWidth(rows) {
return max; return max;
} }
const uploadedFile = {
name: 'UploadedFile',
props: ['file'],
data() {
return {};
},
methods: {
handleDelete() {
console.log('emit delete event')
this.$emit('delete', this.file);
},
},
mounted() {},
render: function (h) {
return (
<div
title={this.file.fileName}
style={{
background: `url(${tupleImg}) no-repeat`,
backgroundSize: '14px',
backgroundPosition: '0 55%',
paddingLeft: '20px',
paddingRight: '24px',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflow: 'hidden',
cursor: 'pointer',
display: 'inline-block',
}}>
{this.file.fileName}
<el-button
type="text"
icon="el-icon-close"
style="float: right; position: relative; top: 2px; left: 8px; z-index: 100"
class="dialog__upload_component__close"
onClick={this.handleDelete}
/>
</div>
);
},
};
export default { export default {
name: 'DialogForm', name: 'DialogForm',
model: { model: {
@@ -104,7 +173,7 @@ export default {
event: 'update', event: 'update',
}, },
emits: ['update'], emits: ['update'],
components: {}, components: { uploadedFile },
props: { props: {
rows: { rows: {
type: Array, type: Array,
@@ -114,13 +183,29 @@ export default {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
disabled: {
type: Boolean,
default: false,
},
labelPosition: {
type: String,
default: 'right',
},
size: {
type: String,
default: '',
},
}, },
data() { data() {
return { return {
uploadOpen: false,
form: {},
formLoading: true, formLoading: true,
optionListOf: {}, optionListOf: {},
uploadedFileList: [], uploadedFileList: [],
dataLoaded: false, dataLoaded: false,
uploadHeaders: { Authorization: 'Bearer ' + getAccessToken() },
uploadUrl: process.env.VUE_APP_BASE_API + '/admin-api/infra/file/upload', // 上传有关的headersurl都是固定的
}; };
}, },
computed: { computed: {
@@ -130,16 +215,6 @@ export default {
return max * 20; return max * 20;
// return max * 20 + 'px'; // return max * 20 + 'px';
}, },
form: {
get() {
// if (this.dataLoaded) return this.dataForm;
// else return {}
return this.dataForm;
},
set(val) {
console.log('set form', val);
},
},
}, },
watch: { watch: {
rows: { rows: {
@@ -152,6 +227,13 @@ export default {
deep: true, deep: true,
immediate: false, immediate: false,
}, },
dataForm: {
handler(val) {
this.form = JSON.parse(JSON.stringify(val));
},
deep: true,
immediate: true,
},
}, },
mounted() { mounted() {
// 处理 options // 处理 options
@@ -267,23 +349,27 @@ export default {
beforeUpload() {}, beforeUpload() {},
// 上传前的验证规则可通过 bind 属性传入 // 上传前的验证规则可通过 bind 属性传入
handleUploadSuccess(response, file, fileList) { handleUploadSuccess(response, file, fileList) {
console.log( this.form.files.push({
'[dialogForm:handleUploadSuccess]', fileName: file.name,
response, fileUrl: response.data,
file, fileType: 2,
fileList, });
this.form
);
// 保存原始文件名
if ('fileNames' in this.form) this.form.fileNames.push(file.name);
// 保存完整地址
if ('fileUrls' in this.form) this.form.fileUrls.push(response.data);
this.$modal.msgSuccess('上传成功'); this.$modal.msgSuccess('上传成功');
this.$emit('update', this.form);
}, },
getFileName(fileUrl) { getFileName(fileUrl) {
return fileUrl.split('/').pop(); return fileUrl.split('/').pop();
}, },
handleFilesOpen() {
this.uploadOpen = !this.uploadOpen;
},
handleDeleteFile(file) {
this.form.files = this.form.files.filter(item => item.fileUrl != file.fileUrl);
this.$emit('update', this.form);
},
}, },
}; };
</script> </script>
@@ -293,4 +379,52 @@ export default {
.el-select { .el-select {
width: 100%; width: 100%;
} }
.upload-area {
// background: #ccc;
// display: grid;
// grid-auto-rows: 34px;
// grid-template-columns: repeat(6, minmax(32px, max-content));
// gap: 8px;
// align-items: center;
position: relative;
overflow: hidden;
transition: height 0.3s ease-out;
}
.upload-in-dialog {
// display: inline-block;
margin-right: 24px;
// background: #ccc;
position: relative;
// top: -13px;
float: left;
}
.close-icon {
// background: #ccc;
position: absolute;
top: 0;
right: 12px;
z-index: 100;
transition: transform 0.3s ease-out;
}
.close-icon.open {
transform: rotateZ(90deg);
}
</style>
<style>
.dialog__upload_component__close {
color: #ccc;
}
.dialog__upload_component__close:hover {
/* color: #777; */
color: red;
}
.height-48 {
height: 35px !important;
}
</style> </style>

View File

@@ -32,7 +32,7 @@ export default {
min-height: calc(100vh - 56px); min-height: calc(100vh - 56px);
min-width: calc(100vh - 280px); min-width: calc(100vh - 280px);
position: relative; position: relative;
overflow: hidden; overflow: visible;
margin: 8px 14px 0px 16px; margin: 8px 14px 0px 16px;
border-radius: 8px; border-radius: 8px;
background-color: #fff; background-color: #fff;

View File

@@ -53,6 +53,9 @@ export default {
case 'delete': case 'delete':
this.handleDelete(data); this.handleDelete(data);
break; break;
case 'detail':
this.handleDetail(data);
break;
} }
}, },
// 处理搜索栏按钮 // 处理搜索栏按钮

View File

@@ -64,6 +64,11 @@ export const constantRoutes = [
component: (resolve) => require(['@/views/error/401'], resolve), component: (resolve) => require(['@/views/error/401'], resolve),
hidden: true hidden: true
}, },
{
path: '/print-design',
component: (resolve) => require(['@/views/print/design'], resolve),
hidden: true
},
{ {
path: '/', path: '/',
component: Layout, component: Layout,

View File

@@ -0,0 +1,74 @@
<!--
filename: index.vue
author: liubin
date: 2023-10-11 09:32:04
description: 设备看板
-->
<template>
<div ref="dataBoard" class=""></div>
</template>
<script>
export default {
name: 'DataBoard',
components: {},
props: {},
data() {
return {
appMain: null, // dom
parentStyle: {
margin: '8px 14px 0px 16px',
minHeight: 'calc(100vh - 120px - 8px)',
}, // object
mainFooter: null, // dom
};
},
mounted() {
this.$nextTick(() => {
this.modify();
});
},
activated() {
this.modify();
},
deactivated() {
this.recover();
},
beforeDestroy() {
this.recover();
},
methods: {
modify() {
// 在这个页面临时修改下父类的margin结束时需还原
this.appMain = document.querySelector('.app-main');
// this.appMain.style.minHeight = 'calc(100vh - 90px)';
this.appMain.style.margin = 0;
// 在这个页面临时删除 main-footer 元素,结束时需还原
// this.mainFooter = document.querySelector('.main-footer').cloneNode(true);
// document.querySelector('.main-footer').remove();
this.$refs.dataBoard.classList.add('data-board');
},
recover() {
this.$refs.dataBoard.classList.remove('data-board');
this.$nextTick(() => {
this.appMain.style.margin = this.parentStyle.margin;
// this.appMain.style.minHeight = this.parentStyle.minHeight;
// this.appMain.insertAdjacentElement('afterend', this.mainFooter);
});
},
},
};
</script>
<style scoped lang="scss">
.data-board {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('../../../assets/images/DataBoard.png') 100% 100% / contain
no-repeat;
}
</style>

View File

@@ -0,0 +1,482 @@
<!--
filename: EquipmentDrawer.vue
author: liubin
date: 2023-08-22 14:38:56
description:
-->
<template>
<el-drawer
:visible="visible"
:show-close="false"
:wrapper-closable="false"
class="drawer"
custom-class="mes-drawer"
size="60%"
@closed="$emit('destroy')">
<SmallTitle slot="title">
{{
mode.includes('detail')
? '详情'
: mode.includes('edit')
? '编辑'
: '新增'
}}
</SmallTitle>
<div class="drawer-body flex">
<div class="drawer-body__content">
<section v-for="(section, index) in sections" :key="section.key">
<SmallTitle v-if="index != 0">{{ section.name }}</SmallTitle>
<div class="form-part" v-if="section.key == 'base'">
<el-skeleton v-if="!showForm" animated />
<BaseInfoForm
key="drawer-dialog-form"
v-if="showForm"
ref="form"
:disabled="true"
:dataForm="form"
:rows="formRows" />
</div>
<div v-if="section.key == 'attrs'" style="margin-top: 12px">
<base-table
v-loading="attrListLoading"
:table-props="section.props"
:page="attrQuery?.params.pageNo || 1"
:limit="attrQuery?.params.pageSize || 10"
:table-data="list"
:add-button-show="mode.includes('detail') ? null : '添加属性'"
@emitButtonClick="handleAddAttr"
@emitFun="handleEmitFun">
<method-btn
v-if="section.tableBtn"
slot="handleBtn"
label="操作"
:method-list="tableBtn"
@clickBtn="handleTableBtnClick" />
</base-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="attrQuery.params.pageNo"
:limit.sync="attrQuery.params.pageSize"
@pagination="getAttrList" />
</div>
</section>
</div>
<div class="drawer-body__footer">
<el-button style="" @click="handleCancel">取消</el-button>
<el-button v-if="mode == 'detail'" type="primary" @click="toggleEdit">
编辑
</el-button>
<el-button v-else type="primary" @click="handleCancel">确定</el-button>
<!-- sections的第二项必须是 属性列表 -->
<!-- <el-button
v-if="sections[1].allowAdd"
type="primary"
@click="handleAddAttr">
添加属性
</el-button> -->
</div>
</div>
<!-- 属性对话框 -->
<base-dialog
v-if="sections[1].allowAdd"
:dialogTitle="attrTitle"
:dialogVisible="attrFormVisible"
width="45%"
:append-to-body="true"
custom-class="baseDialog"
@close="closeAttrForm"
@cancel="closeAttrForm"
@confirm="submitAttrForm">
<DialogForm
v-if="attrFormVisible"
ref="attrForm"
:disabled="mode.includes('detail')"
v-model="attrForm"
:rows="attrRows" />
</base-dialog>
</el-drawer>
</template>
<script>
import BaseInfoForm from '@/components/DialogForm';
import DialogForm from './dialogForm';
const SmallTitle = {
name: 'SmallTitle',
props: ['size'],
data() {
return {};
},
methods: {},
render: function (h) {
return h(
'span',
{
class: 'small-title',
style: {
fontSize: '18px',
lineHeight:
this.size == 'lg' ? '24px' : this.size == 'sm' ? '18px' : '20px',
fontWeight: 500,
fontFamily: '微软雅黑, Microsoft YaHei, Arial, Helvetica, sans-serif',
},
},
this.$slots.default
);
},
};
export default {
components: { SmallTitle, DialogForm, BaseInfoForm },
props: ['sections', 'defaultMode', 'dataId'], // dataId 作为一个通用的存放id的字段
data() {
return {
mode: '',
visible: false,
showForm: false,
total: 0,
form: {},
list: [],
attrTitle: '',
attrForm: {
id: null,
equipmentGroupId: '',
code: '',
type: '',
grade: '',
alarmCode: '',
alarmContent: '',
plcParamName: '',
},
attrFormVisible: false,
attrRows: [
[
{
input: true,
label: '报警编码', // 自动生成
prop: 'code',
url: '/base/equipment-group-alarm/getCode',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
{
select: true,
label: '报警类型', // 固定选项
prop: 'type',
options: [
{ label: '布尔型', value: 2 },
{ label: '字符型', value: 1 },
],
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
],
[
{
select: true,
label: '报警级别', // 字典
prop: 'grade',
options: this.getDictDatas(this.DICT_TYPE.EQU_ALARM_LEVEL),
},
{
input: true,
label: '设备报警编码', // 对应到设备实际的报警编码
prop: 'alarmCode',
},
],
[
{
input: true,
label: '参数列名', // 在实时数据库的列名
prop: 'plcParamName',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
{
input: true,
label: '报警内容',
prop: 'alarmContent',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
],
],
attrQuery: {
params: {
pageNo: 1,
pageSize: 10,
},
}, // 属性列表的请求
infoQuery: null, // 基本信息的请求
attrFormSubmitting: false,
attrListLoading: false,
};
},
computed: {
formRows() {
return this.sections[0].rows.map((row) => {
return row.map((col) => {
return {
...col,
bind: {
// 详情 模式下,禁用各种输入
// disabled: this.mode == 'detail',
disabled: true,
},
};
});
});
},
tableBtn() {
return this.mode == 'detail' ? [] : this.sections[1].tableBtn;
},
},
mounted() {
this.mode = this.defaultMode || 'detail';
for (const section of this.sections) {
// 请求具体信息
if ('url' in section) {
const query = {
url: section.url,
method: section.method || 'get',
params: section.queryParams || null,
data: section.data || null,
};
// debugger;
this.$axios(query).then(({ data }) => {
if (section.key == 'base') {
this.form = data;
this.showForm = true;
this.infoQuery = query;
} else if (section.key == 'attrs') {
this.attrQuery = query;
this.list = data.list;
this.total = data.total;
}
});
}
}
},
methods: {
handleTableBtnClick({ type, data }) {
switch (type) {
case 'edit':
this.handleEditAttr(data.id);
break;
case 'delete':
this.handleDeleteAttr(data.id);
break;
}
},
handleEmitFun(val) {
console.log('handleEmitFun', val);
},
init() {
this.visible = true;
},
async getAttrList() {
this.attrListLoading = true;
const res = await this.$axios(this.attrQuery);
if (res.code == 0) {
this.list = res.data.list;
this.total = res.data.total;
}
this.attrListLoading = false;
},
// 保存表单
handleSave() {
this.$refs['form'][0].validate(async (valid) => {
if (valid) {
const isEdit = this.mode == 'edit';
await this.$axios({
url: this.sections[0][isEdit ? 'urlUpdate' : 'urlCreate'],
method: isEdit ? 'put' : 'post',
data: this.form,
});
this.$modal.msgSuccess(`${isEdit ? '更新' : '创建'}成功`);
this.visible = false;
this.$emit('refreshDataList');
}
});
},
handleCancel() {
this.visible = false;
},
// 开启编辑
toggleEdit() {
this.mode = 'edit';
},
// 新增属性
handleAddAttr() {
if (!this.dataId) return this.$message.error('请先创建设备分组信息');
this.attrForm = {
id: null,
equipmentGroupId: this.dataId,
code: '',
type: '',
grade: '',
alarmCode: '',
alarmContent: '',
plcParamName: '',
};
this.attrTitle = '添加设备分组报警';
this.attrFormVisible = true;
},
// 编辑属性
async handleEditAttr(attrId) {
const res = await this.$axios({
url: this.sections[1].urlDetail,
method: 'get',
params: { id: attrId },
});
if (res.code == 0) {
this.attrForm = res.data;
this.attrTitle = '编辑设备分组报警';
this.attrFormVisible = true;
}
},
// 删除属性
handleDeleteAttr(attrId) {
this.$confirm('确定删除该分组报警?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
const res = await this.$axios({
url: this.sections[1].urlDelete,
method: 'delete',
params: { id: attrId },
});
if (res.code == 0) {
this.$message({
message: '删除成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getAttrList();
},
});
}
})
.catch(() => {});
},
// 提交属性表
async submitAttrForm() {
this.$refs['attrForm'].validate((valid) => {
if (!valid) {
return;
}
});
console.log('this.attrform', this.attrForm);
const isEdit = this.attrForm.id != null;
this.attrFormSubmitting = true;
const res = await this.$axios({
url: isEdit ? this.sections[1].urlUpdate : this.sections[1].urlCreate,
method: isEdit ? 'put' : 'post',
data: this.attrForm,
});
if (res.code == 0) {
this.closeAttrForm();
this.$message({
message: `${isEdit ? '更新' : '创建'}成功`,
type: 'success',
duration: 1500,
onClose: () => {
this.getAttrList();
},
});
}
this.attrFormSubmitting = false;
},
closeAttrForm() {
this.attrFormVisible = false;
},
handleClick(raw) {
if (raw.type === 'delete') {
this.$confirm(`确定删除该报警?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteProductAttr(raw.data.id).then(({ data }) => {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getList();
},
});
});
})
.catch(() => {});
} else {
this.addNew(raw.data.id);
}
},
},
};
</script>
<style scoped>
.drawer >>> .el-drawer {
border-radius: 8px 0 0 8px;
}
.drawer >>> .el-drawer__header {
margin: 0;
padding: 32px 32px 24px;
border-bottom: 1px solid #dcdfe6;
margin-bottom: 0px;
}
.small-title::before {
content: '';
display: inline-block;
vertical-align: top;
width: 4px;
height: 22px;
border-radius: 1px;
margin-right: 8px;
background-color: #0b58ff;
}
.drawer-body {
display: flex;
flex-direction: column;
height: 100%;
}
.drawer-body__content {
flex: 1;
/* background: #eee; */
padding: 20px 30px;
overflow-y: auto;
}
.drawer-body__footer {
display: flex;
justify-content: flex-end;
padding: 18px;
}
</style>

View File

@@ -0,0 +1,186 @@
<!--
filename: dialogForm.vue
author: liubin
date: 2023-09-11 15:55:13
description: DialogForm for equipmentBindSection only
-->
<template>
<el-form
ref="form"
:model="dataForm"
label-width="100px"
v-loading="formLoading">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="报警编码"
prop="code"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input
:disabled="disabled"
v-model="dataForm.code"
@change="$emit('update', dataForm)"
placeholder="请输入工段排序" />
</el-form-item>
<!--
<el-form-item
label="报警编码"
prop="code"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select
v-model="dataForm.code"
placeholder="请选择产线"
@change="handleProductlineChange">
<el-option
v-for="opt in productionLineList"
:key="opt.value"
:label="opt.label"
:value="opt.value" />
</el-select>
</el-form-item> -->
</el-col>
<el-col :span="12">
<el-form-item
label="报警类型"
prop="type"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select
:disabled="disabled"
v-model="dataForm.type"
placeholder="请选择报警类型"
@change="handleTypeChange">
<el-option
v-for="opt in [
{ label: '布尔型', value: 2 },
{ label: '字符型', value: 1 },
]"
:key="opt.value"
:label="opt.label"
:value="opt.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="报警级别"
prop="grade"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select
:disabled="disabled"
v-model="dataForm.grade"
placeholder="请选择报警级别"
@change="$emit('update', dataForm)">
<el-option
v-for="opt in getDictDatas(DICT_TYPE.EQU_ALARM_LEVEL)"
:key="opt.value"
:label="opt.label"
:value="opt.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
v-if="+dataForm.type == 1"
label="设备报警编码"
prop="alarmCode">
<el-input
:disabled="disabled"
v-model="dataForm.alarmCode"
@change="$emit('update', dataForm)"
placeholder="请输入设备报警编码" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="参数列名"
prop="plcParamName"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input
:disabled="disabled"
v-model="dataForm.plcParamName"
placeholder="请输入参数列名"
@change="$emit('update', dataForm)"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="报警内容"
prop="alarmContent"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input
:disabled="disabled"
v-model="dataForm.alarmContent"
placeholder="请输入报警内容"
@change="$emit('update', dataForm)"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
export default {
name: 'DialogForm',
model: {
prop: 'dataForm',
event: 'update',
},
emits: ['update'],
components: {},
props: {
dataForm: {
type: Object,
default: () => ({}),
},
disabled: {
type: Boolean,
default: false,
},
},
data() {
return {
formLoading: true,
};
},
mounted() {
this.getCode('/base/equipment-group-alarm/getCode').then((code) => {
this.formLoading = false;
this.$emit('update', {
...this.dataForm,
code,
});
});
},
methods: {
/** 模拟透传 ref */
validate(cb) {
return this.$refs.form.validate(cb);
},
resetFields(args) {
return this.$refs.form.resetFields(args);
},
async handleTypeChange(id) {
this.dataForm.alarmCode = '';
this.$emit('update', this.dataForm);
},
async getCode(url) {
const response = await this.$axios(url);
return response.data;
},
},
};
</script>
<style scoped lang="scss">
.el-date-editor,
.el-select {
width: 100%;
}
</style>

View File

@@ -40,6 +40,57 @@
@confirm="submitForm"> @confirm="submitForm">
<DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" /> <DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" />
</base-dialog> </base-dialog>
<!-- 抽屉 详情 -->
<BasicDrawer
v-if="editVisible"
ref="drawer"
:default-mode="editMode"
:data-id="alarmForm.id"
:sections="[
{
name: '基本信息',
key: 'base',
rows: drawerBaseInfoRows,
url: '/base/equipment-group/get',
urlUpdate: '/base/equipment-group/update',
urlCreate: '/base/equipment-group/create',
queryParams: { id: alarmForm.id },
},
{
name: '属性列表',
key: 'attrs',
props: drawerListProps,
url: '/base/equipment-group-alarm/page',
urlCreate: '/base/equipment-group-alarm/create',
urlUpdate: '/base/equipment-group-alarm/update',
urlDelete: '/base/equipment-group-alarm/delete',
urlDetail: '/base/equipment-group-alarm/get',
queryParams: {
equipmentGroupId: alarmForm.id,
pageNo: 1,
pageSize: 10,
},
tableBtn: [
this.$auth.hasPermi('base:equipment-group:update')
? {
type: 'edit',
btnName: '修改',
}
: undefined,
this.$auth.hasPermi('base:equipment-group:delete')
? {
type: 'delete',
btnName: '删除',
}
: undefined,
].filter((v) => v),
allowAdd: true,
},
]"
@refreshDataList="getList"
@cancel="editVisible = false"
@destroy="editVisible = false" />
</div> </div>
</template> </template>
@@ -55,16 +106,24 @@ import {
import { getEquipmentGroupPage } from '@/api/base/equipmentGroup'; import { getEquipmentGroupPage } from '@/api/base/equipmentGroup';
import moment from 'moment'; import moment from 'moment';
import { publicFormatter } from '@/utils/dict';
import basicPageMixin from '@/mixins/lb/basicPageMixin'; import basicPageMixin from '@/mixins/lb/basicPageMixin';
import BasicDrawer from './components/BasicDrawer.vue';
export default { export default {
name: 'EquipmentBindGroup', name: 'EquipmentBindGroup',
components: {}, components: { BasicDrawer },
mixins: [basicPageMixin], mixins: [basicPageMixin],
data() { data() {
return { return {
searchBarKeys: ['groupId', 'equipmentName'], searchBarKeys: ['groupId', 'equipmentName'],
tableBtn: [ tableBtn: [
this.$auth.hasPermi('base:equipment-bind-group:update')
? {
type: 'detail',
btnName: '查看报警',
}
: undefined,
this.$auth.hasPermi('base:equipment-bind-group:update') this.$auth.hasPermi('base:equipment-bind-group:update')
? { ? {
type: 'edit', type: 'edit',
@@ -88,32 +147,32 @@ export default {
}, },
{ prop: 'equipmentName', label: '设备' }, { prop: 'equipmentName', label: '设备' },
{ prop: 'groupName', label: '分组' }, { prop: 'groupName', label: '分组' },
{ // {
_action: 'equipment-bind-group-show-alert', // _action: 'equipment-bind-group-show-alert',
label: '分组报警', // label: '分组报警',
subcomponent: { // subcomponent: {
props: ['injectData'], // props: ['injectData'],
render: function (h) { // render: function (h) {
const _this = this; // const _this = this;
return h( // return h(
'el-button', // 'el-button',
{ // {
props: { type: 'text' }, // props: { type: 'text' },
on: { // on: {
click: function () { // click: function () {
console.log('inejctdata', _this.injectData); // console.log('inejctdata', _this.injectData);
_this.$emit('emitData', { // _this.$emit('emitData', {
action: _this.injectData._action, // action: _this.injectData._action,
value: _this.injectData, // value: _this.injectData,
}); // });
}, // },
}, // },
}, // },
'查看报警' // '查看报警'
); // );
}, // },
}, // },
}, // },
], ],
searchBarFormConfig: [ searchBarFormConfig: [
{ {
@@ -196,6 +255,59 @@ export default {
form: {}, form: {},
// 表单校验 // 表单校验
rules: {}, rules: {},
//
alarmForm: {
id: undefined,
equipmentGroupCode: undefined,
equipmentGroupName: undefined,
},
editVisible: false,
editMode: '',
drawerBaseInfoRows: [
[
{
input: true,
label: '设备分组名称',
prop: 'name',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
// bind: {
// disabled: this.editMode == 'detail', // some condition, like detail mode...
// }
},
{
input: true,
label: '设备分组编码',
prop: 'code',
// url: '/base/equipment/getCode',
},
],
],
drawerListProps: [
{
prop: 'createTime',
label: '添加时间',
fixed: true,
width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
},
{ width: 240, prop: 'code', label: '报警编码' },
{
width: 100,
prop: 'type',
label: '报警类型',
filter: (val) =>
val != null ? ['-', '字符型', '布尔型', '-'][val] : '-',
},
{
width: 90,
prop: 'grade',
label: '报警级别',
filter: publicFormatter(this.DICT_TYPE.EQU_ALARM_LEVEL),
},
{ width: 180, prop: 'alarmCode', label: '设备报警编码' },
{ width: 128, prop: 'plcParamName', label: '参数列名' },
{ width: 128, prop: 'alarmContent', label: '报警内容' },
],
}; };
}, },
created() { created() {
@@ -229,7 +341,6 @@ export default {
/** 查询列表 */ /** 查询列表 */
getList() { getList() {
this.loading = true; this.loading = true;
_; // 执行查询
getEquipmentBindGroupPage(this.queryParams).then((response) => { getEquipmentBindGroupPage(this.queryParams).then((response) => {
this.list = response.data.list; this.list = response.data.list;
this.total = response.data.total; this.total = response.data.total;
@@ -299,11 +410,25 @@ export default {
}); });
}); });
}, },
// 查看报警
handleDetail(row) {
const { equipmentId, equipmentName, groupCode, groupId, groupName, id } =
row;
// 打开抽屉
this.editMode = 'detail';
this.alarmForm.id = groupId;
this.alarmForm.equipmentGroupCode = groupCode;
this.alarmForm.equipmentGroupName = groupName;
this.editVisible = true;
this.$nextTick(() => {
this.$refs['drawer'].init();
});
},
/** 删除按钮操作 */ /** 删除按钮操作 */
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm('是否确认删除设备与分组绑定编号为"' + id + '"的数据项?') .confirm('是否确认删除分组绑定?')
.then(function () { .then(function () {
return deleteEquipmentBindGroup(id); return deleteEquipmentBindGroup(id);
}) })

View File

@@ -20,6 +20,7 @@
<el-select <el-select
v-model="dataForm.productionLineId" v-model="dataForm.productionLineId"
placeholder="请选择产线" placeholder="请选择产线"
filterable
@change="handleProductlineChange"> @change="handleProductlineChange">
<el-option <el-option
v-for="opt in productionLineList" v-for="opt in productionLineList"
@@ -37,6 +38,7 @@
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> :rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select <el-select
v-model="dataForm.workshopSectionId" v-model="dataForm.workshopSectionId"
filterable
placeholder="请选择工段" placeholder="请选择工段"
@change="$emit('update', dataForm)"> @change="$emit('update', dataForm)">
<el-option <el-option
@@ -56,6 +58,7 @@
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> :rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select <el-select
v-model="dataForm.equipmentId" v-model="dataForm.equipmentId"
filterable
placeholder="请选择设备" placeholder="请选择设备"
@change="$emit('update', dataForm)"> @change="$emit('update', dataForm)">
<el-option <el-option
@@ -69,8 +72,11 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="工段排序" prop="sort"> <el-form-item label="工段排序" prop="sort">
<el-input <el-input-number
v-model="dataForm.sort" v-model="dataForm.sort"
filterable
min="0"
max="100"
@change="$emit('update', dataForm)" @change="$emit('update', dataForm)"
placeholder="请输入工段排序" /> placeholder="请输入工段排序" />
</el-form-item> </el-form-item>

View File

@@ -134,6 +134,8 @@ export default {
label: '工段', label: '工段',
placeholder: '请选择工段', placeholder: '请选择工段',
param: 'workshopSectionId', param: 'workshopSectionId',
selectOptions: [],
filterable: true
}, },
{ {
type: 'input', type: 'input',
@@ -239,8 +241,24 @@ export default {
}, },
created() { created() {
this.getList(); this.getList();
this.initWorksection();
}, },
methods: { methods: {
/** 准备工段数据 */
async initWorksection() {
const { code, data } = await this.$axios({
url: '/base/workshop-section/listAll',
method: 'get',
});
if (code == 0) {
this.searchBarFormConfig[0].selectOptions = data.map((item) => {
return {
name: item.name,
id: item.id,
};
});
}
},
/** 查询列表 */ /** 查询列表 */
getList() { getList() {
this.loading = true; this.loading = true;
@@ -323,7 +341,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm('是否确认删除工段设备绑定编号为"' + id + '"的数据项?') .confirm('是否确认删除工段设备绑定设备名称为"' + row.equipment + '"的数据项?')
.then(function () { .then(function () {
return deleteEquipmentBindSection(id); return deleteEquipmentBindSection(id);
}) })

View File

@@ -0,0 +1,481 @@
<!--
filename: EquipmentDrawer.vue
author: liubin
date: 2023-08-22 14:38:56
description:
-->
<template>
<el-drawer
:visible="visible"
:show-close="false"
:wrapper-closable="false"
class="drawer"
custom-class="mes-drawer"
size="60%"
@closed="$emit('destroy')">
<SmallTitle slot="title">
{{
mode.includes('detail')
? '详情'
: mode.includes('edit')
? '编辑'
: '新增'
}}
</SmallTitle>
<div class="drawer-body flex">
<div class="drawer-body__content">
<section v-for="(section, index) in sections" :key="section.key">
<SmallTitle v-if="index != 0">{{ section.name }}</SmallTitle>
<div class="form-part" v-if="section.key == 'base'">
<el-skeleton v-if="!showForm" animated />
<BaseInfoForm
key="drawer-dialog-form"
v-if="showForm"
ref="form"
:disabled="mode.includes('detail')"
:dataForm="form"
:rows="formRows" />
</div>
<div v-if="section.key == 'attrs'" style="margin-top: 12px">
<base-table
v-loading="attrListLoading"
:table-props="section.props"
:page="attrQuery?.params.pageNo || 1"
:limit="attrQuery?.params.pageSize || 10"
:table-data="list"
:add-button-show="mode.includes('detail') ? null : '添加属性'"
@emitButtonClick="handleAddAttr"
@emitFun="handleEmitFun">
<method-btn
v-if="section.tableBtn"
slot="handleBtn"
label="操作"
:method-list="tableBtn"
@clickBtn="handleTableBtnClick" />
</base-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="attrQuery.params.pageNo"
:limit.sync="attrQuery.params.pageSize"
@pagination="getAttrList" />
</div>
</section>
</div>
<div class="drawer-body__footer">
<el-button style="" @click="handleCancel">取消</el-button>
<el-button v-if="mode == 'detail'" type="primary" @click="toggleEdit">
编辑
</el-button>
<el-button v-else type="primary" @click="handleCancel">确定</el-button>
<!-- sections的第二项必须是 属性列表 -->
<!-- <el-button
v-if="sections[1].allowAdd"
type="primary"
@click="handleAddAttr">
添加属性
</el-button> -->
</div>
</div>
<!-- 属性对话框 -->
<base-dialog
v-if="sections[1].allowAdd"
:dialogTitle="attrTitle"
:dialogVisible="attrFormVisible"
width="45%"
:append-to-body="true"
custom-class="baseDialog"
@close="closeAttrForm"
@cancel="closeAttrForm"
@confirm="submitAttrForm">
<DialogForm
v-if="attrFormVisible"
ref="attrForm"
:disabled="mode.includes('detail')"
v-model="attrForm"
:rows="attrRows" />
</base-dialog>
</el-drawer>
</template>
<script>
import BaseInfoForm from '@/components/DialogForm';
import DialogForm from './dialogForm';
const SmallTitle = {
name: 'SmallTitle',
props: ['size'],
data() {
return {};
},
methods: {},
render: function (h) {
return h(
'span',
{
class: 'small-title',
style: {
fontSize: '18px',
lineHeight:
this.size == 'lg' ? '24px' : this.size == 'sm' ? '18px' : '20px',
fontWeight: 500,
fontFamily: '微软雅黑, Microsoft YaHei, Arial, Helvetica, sans-serif',
},
},
this.$slots.default
);
},
};
export default {
components: { SmallTitle, DialogForm, BaseInfoForm },
props: ['sections', 'defaultMode', 'dataId'], // dataId 作为一个通用的存放id的字段
data() {
return {
mode: '',
visible: false,
showForm: false,
total: 0,
form: {},
list: [],
attrTitle: '',
attrForm: {
id: null,
equipmentGroupId: '',
code: '',
type: '',
grade: '',
alarmCode: '',
alarmContent: '',
plcParamName: '',
},
attrFormVisible: false,
attrRows: [
[
{
input: true,
label: '报警编码', // 自动生成
prop: 'code',
url: '/base/equipment-group-alarm/getCode',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
{
select: true,
label: '报警类型', // 固定选项
prop: 'type',
options: [
{ label: '布尔型', value: 2 },
{ label: '字符型', value: 1 },
],
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
],
[
{
select: true,
label: '报警级别', // 字典
prop: 'grade',
options: this.getDictDatas(this.DICT_TYPE.EQU_ALARM_LEVEL),
},
{
input: true,
label: '设备报警编码', // 对应到设备实际的报警编码
prop: 'alarmCode',
},
],
[
{
input: true,
label: '参数列名', // 在实时数据库的列名
prop: 'plcParamName',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
{
input: true,
label: '报警内容',
prop: 'alarmContent',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
],
],
attrQuery: {
params: {
pageNo: 1,
pageSize: 10,
},
}, // 属性列表的请求
infoQuery: null, // 基本信息的请求
attrFormSubmitting: false,
attrListLoading: false,
};
},
computed: {
formRows() {
return this.sections[0].rows.map((row) => {
return row.map((col) => {
return {
...col,
bind: {
// 详情 模式下,禁用各种输入
// disabled: this.mode == 'detail',
disabled: true
},
};
});
});
},
tableBtn() {
return this.mode == 'detail' ? [] : this.sections[1].tableBtn;
},
},
mounted() {
this.mode = this.defaultMode || 'detail';
for (const section of this.sections) {
// 请求具体信息
if ('url' in section) {
const query = {
url: section.url,
method: section.method || 'get',
params: section.queryParams || null,
data: section.data || null,
};
// debugger;
this.$axios(query).then(({ data }) => {
if (section.key == 'base') {
this.form = data;
this.showForm = true;
this.infoQuery = query;
} else if (section.key == 'attrs') {
this.attrQuery = query;
this.list = data.list;
this.total = data.total;
}
});
}
}
},
methods: {
handleTableBtnClick({ type, data }) {
switch (type) {
case 'edit':
this.handleEditAttr(data.id);
break;
case 'delete':
this.handleDeleteAttr(data.id);
break;
}
},
handleEmitFun(val) {
console.log('handleEmitFun', val);
},
init() {
this.visible = true;
},
async getAttrList() {
this.attrListLoading = true;
const res = await this.$axios(this.attrQuery);
if (res.code == 0) {
this.list = res.data.list;
this.total = res.data.total;
}
this.attrListLoading = false;
},
// 保存表单
handleSave() {
this.$refs['form'][0].validate(async (valid) => {
if (valid) {
const isEdit = this.mode == 'edit';
await this.$axios({
url: this.sections[0][isEdit ? 'urlUpdate' : 'urlCreate'],
method: isEdit ? 'put' : 'post',
data: this.form,
});
this.$modal.msgSuccess(`${isEdit ? '更新' : '创建'}成功`);
this.visible = false;
this.$emit('refreshDataList');
}
});
},
handleCancel() {
this.visible = false;
},
// 开启编辑
toggleEdit() {
this.mode = 'edit';
},
// 新增属性
handleAddAttr() {
if (!this.dataId) return this.$message.error('请先创建设备分组信息');
this.attrForm = {
id: null,
equipmentGroupId: this.dataId,
code: '',
type: '',
grade: '',
alarmCode: '',
alarmContent: '',
plcParamName: '',
};
this.attrTitle = '添加设备分组报警';
this.attrFormVisible = true;
},
// 编辑属性
async handleEditAttr(attrId) {
const res = await this.$axios({
url: this.sections[1].urlDetail,
method: 'get',
params: { id: attrId },
});
if (res.code == 0) {
this.attrForm = res.data;
this.attrTitle = '编辑设备分组报警';
this.attrFormVisible = true;
}
},
// 删除属性
handleDeleteAttr(attrId) {
this.$confirm('确定删除该分组报警?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
const res = await this.$axios({
url: this.sections[1].urlDelete,
method: 'delete',
params: { id: attrId },
});
if (res.code == 0) {
this.$message({
message: '删除成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getAttrList();
},
});
}
})
.catch(() => {});
},
// 提交属性表
async submitAttrForm() {
this.$refs['attrForm'].validate((valid) => {
if (!valid) {
return;
}
});
const isEdit = this.attrForm.id != null;
this.attrFormSubmitting = true;
const res = await this.$axios({
url: isEdit ? this.sections[1].urlUpdate : this.sections[1].urlCreate,
method: isEdit ? 'put' : 'post',
data: this.attrForm,
});
if (res.code == 0) {
this.closeAttrForm();
this.$message({
message: `${isEdit ? '更新' : '创建'}成功`,
type: 'success',
duration: 1500,
onClose: () => {
this.getAttrList();
},
});
}
this.attrFormSubmitting = false;
},
closeAttrForm() {
this.attrFormVisible = false;
},
handleClick(raw) {
if (raw.type === 'delete') {
this.$confirm(`确定删除该报警?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteProductAttr(raw.data.id).then(({ data }) => {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getList();
},
});
});
})
.catch(() => {});
} else {
this.addNew(raw.data.id);
}
},
},
};
</script>
<style scoped>
.drawer >>> .el-drawer {
border-radius: 8px 0 0 8px;
}
.drawer >>> .el-drawer__header {
margin: 0;
padding: 32px 32px 24px;
border-bottom: 1px solid #dcdfe6;
margin-bottom: 0px;
}
.small-title::before {
content: '';
display: inline-block;
vertical-align: top;
width: 4px;
height: 22px;
border-radius: 1px;
margin-right: 8px;
background-color: #0b58ff;
}
.drawer-body {
display: flex;
flex-direction: column;
height: 100%;
}
.drawer-body__content {
flex: 1;
/* background: #eee; */
padding: 20px 30px;
overflow-y: auto;
}
.drawer-body__footer {
display: flex;
justify-content: flex-end;
padding: 18px;
}
</style>

View File

@@ -0,0 +1,187 @@
<!--
filename: dialogForm.vue
author: liubin
date: 2023-09-11 15:55:13
description: DialogForm for equipmentBindSection only
-->
<template>
<el-form
ref="form"
:model="dataForm"
label-width="100px"
v-loading="formLoading">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="报警编码"
prop="code"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input
:disabled="disabled"
v-model="dataForm.code"
@change="$emit('update', dataForm)"
placeholder="请输入工段排序" />
</el-form-item>
<!--
<el-form-item
label="报警编码"
prop="code"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select
v-model="dataForm.code"
placeholder="请选择产线"
@change="handleProductlineChange">
<el-option
v-for="opt in productionLineList"
:key="opt.value"
:label="opt.label"
:value="opt.value" />
</el-select>
</el-form-item> -->
</el-col>
<el-col :span="12">
<el-form-item
label="报警类型"
prop="type"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select
:disabled="disabled"
v-model="dataForm.type"
placeholder="请选择报警类型"
@change="handleTypeChange">
<el-option
v-for="opt in [
{ label: '布尔型', value: 2 },
{ label: '字符型', value: 1 },
]"
:key="opt.value"
:label="opt.label"
:value="opt.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="报警级别"
prop="grade"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select
:disabled="disabled"
v-model="dataForm.grade"
placeholder="请选择报警级别"
@change="$emit('update', dataForm)">
<el-option
v-for="opt in getDictDatas(DICT_TYPE.EQU_ALARM_LEVEL)"
:key="opt.value"
:label="opt.label"
:value="opt.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
v-if="+dataForm.type == 1"
label="设备报警编码"
prop="alarmCode">
<el-input
:disabled="disabled"
v-model="dataForm.alarmCode"
@change="$emit('update', dataForm)"
placeholder="请输入设备报警编码" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item
label="参数列名"
prop="plcParamName"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input
:disabled="disabled"
v-model="dataForm.plcParamName"
placeholder="请输入参数列名"
@change="$emit('update', dataForm)"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="报警内容"
prop="alarmContent"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input
:disabled="disabled"
v-model="dataForm.alarmContent"
placeholder="请输入报警内容"
@change="$emit('update', dataForm)"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
export default {
name: 'DialogForm',
model: {
prop: 'dataForm',
event: 'update',
},
emits: ['update'],
components: {},
props: {
dataForm: {
type: Object,
default: () => ({}),
},
disabled: {
type: Boolean,
default: false,
},
},
data() {
return {
formLoading: true,
};
},
mounted() {
this.getCode('/base/equipment-group-alarm/getCode').then((code) => {
this.formLoading = false;
this.$emit('update', {
...this.dataForm,
code,
});
});
},
methods: {
/** 模拟透传 ref */
validate(cb) {
return this.$refs.form.validate(cb);
},
resetFields(args) {
return this.$refs.form.resetFields(args);
},
async handleTypeChange(id) {
// debugger;
this.dataForm.alarmCode = '';
this.$emit('update', this.dataForm);
},
async getCode(url) {
const response = await this.$axios(url);
return response.data;
},
},
};
</script>
<style scoped lang="scss">
.el-date-editor,
.el-select {
width: 100%;
}
</style>

View File

@@ -40,6 +40,57 @@
@confirm="submitForm"> @confirm="submitForm">
<DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" /> <DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" />
</base-dialog> </base-dialog>
<!-- 抽屉 详情 -->
<BasicDrawer
v-if="editVisible"
ref="drawer"
:default-mode="editMode"
:data-id="alarmForm.id"
:sections="[
{
name: '基本信息',
key: 'base',
rows: drawerBaseInfoRows,
url: '/base/equipment-group/get',
urlUpdate: '/base/equipment-group/update',
urlCreate: '/base/equipment-group/create',
queryParams: { id: alarmForm.id },
},
{
name: '属性列表',
key: 'attrs',
props: drawerListProps,
url: '/base/equipment-group-alarm/page',
urlCreate: '/base/equipment-group-alarm/create',
urlUpdate: '/base/equipment-group-alarm/update',
urlDelete: '/base/equipment-group-alarm/delete',
urlDetail: '/base/equipment-group-alarm/get',
queryParams: {
equipmentGroupId: alarmForm.id,
pageNo: 1,
pageSize: 10,
},
tableBtn: [
this.$auth.hasPermi('base:equipment-group:update')
? {
type: 'edit',
btnName: '修改',
}
: undefined,
this.$auth.hasPermi('base:equipment-group:delete')
? {
type: 'delete',
btnName: '删除',
}
: undefined,
].filter((v) => v),
allowAdd: true,
},
]"
@refreshDataList="getList"
@cancel="editVisible = false"
@destroy="editVisible = false" />
</div> </div>
</template> </template>
@@ -53,17 +104,27 @@ import {
exportEquipmentGroupExcel, exportEquipmentGroupExcel,
} from '@/api/base/equipmentGroup'; } from '@/api/base/equipmentGroup';
import moment from 'moment'; import moment from 'moment';
import { publicFormatter } from '@/utils/dict';
import basicPageMixin from '@/mixins/lb/basicPageMixin'; import basicPageMixin from '@/mixins/lb/basicPageMixin';
import { getAccessToken } from '@/utils/auth'; // import { getAccessToken } from '@/utils/auth';
import BasicDrawer from './components/BasicDrawer.vue';
export default { export default {
name: 'EquipmentGroup', name: 'EquipmentGroup',
mixins: [basicPageMixin], mixins: [basicPageMixin],
components: {}, components: { BasicDrawer },
data() { data() {
return { return {
editVisible: false,
editMode: '',
searchBarKeys: ['name', 'code'], searchBarKeys: ['name', 'code'],
tableBtn: [ tableBtn: [
this.$auth.hasPermi('base:equipment-group:update')
? {
type: 'detail',
btnName: '查看报警',
}
: undefined,
this.$auth.hasPermi('base:equipment-group:update') this.$auth.hasPermi('base:equipment-group:update')
? { ? {
type: 'edit', type: 'edit',
@@ -88,34 +149,84 @@ export default {
{ prop: 'name', label: '设备分组名称' }, { prop: 'name', label: '设备分组名称' },
{ prop: 'code', label: '设备分组编码' }, { prop: 'code', label: '设备分组编码' },
{ prop: 'remark', label: '备注' }, { prop: 'remark', label: '备注' },
{ // {
_action: 'equipment-group-show-alert', // _action: 'equipment-group-show-alert',
label: '报警', // label: '报警',
subcomponent: { // subcomponent: {
props: ['injectData'], // props: ['injectData'],
render: function (h) { // render: function (h) {
const _this = this; // const _this = this;
return h( // return h(
'el-button', // 'el-button',
{ // {
props: { type: 'text' }, // props: { type: 'text' },
on: { // on: {
click: function () { // click: function () {
console.log('inejctdata', _this.injectData); // console.log('inejctdata', _this.injectData);
_this.$emit('emitData', { // _this.$emit('emitData', {
action: _this.injectData._action, // action: _this.injectData._action,
// value: _this.injectData.id, // // value: _this.injectData.id,
value: _this.injectData, // value: _this.injectData,
}); // });
}, // },
}, // },
}, // },
'查看报警' // '查看报警'
); // );
}, // },
}, // },
}, // },
], ],
drawerBaseInfoRows: [
[
{
input: true,
label: '设备分组名称',
prop: 'name',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
// bind: {
// disabled: this.editMode == 'detail', // some condition, like detail mode...
// }
},
{
input: true,
label: '设备分组编码',
prop: 'code',
// url: '/base/equipment/getCode',
}
]
],
drawerListProps: [
{
prop: 'createTime',
label: '添加时间',
fixed: true,
width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
},
{ width: 240, prop: 'code', label: '报警编码' },
{
width: 100,
prop: 'type',
label: '报警类型',
filter: (val) =>
val != null ? ['-', '字符型', '布尔型', '-'][val] : '-',
},
{
width: 90,
prop: 'grade',
label: '报警级别',
filter: publicFormatter(this.DICT_TYPE.EQU_ALARM_LEVEL),
},
{ width: 180, prop: 'alarmCode', label: '设备报警编码' },
{ width: 128, prop: 'plcParamName', label: '参数列名' },
{ width: 128, prop: 'alarmContent', label: '报警内容' },
],
alarmForm: {
id: undefined,
equipmentGroupCode: undefined,
equipmentGroupName: undefined,
},
searchBarFormConfig: [ searchBarFormConfig: [
{ {
type: 'input', type: 'input',
@@ -299,6 +410,20 @@ export default {
}); });
}); });
}, },
// 查看报警
handleDetail(row) {
// debugger;
const { id, code, name, createTime } = row;
// 打开抽屉
this.editMode = 'detail';
this.alarmForm.id = id;
this.alarmForm.equipmentGroupCode = code;
this.alarmForm.equipmentGroupName = name;
this.editVisible = true;
this.$nextTick(() => {
this.$refs['drawer'].init();
});
},
/** 删除按钮操作 */ /** 删除按钮操作 */
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;

View File

@@ -18,24 +18,25 @@
prop="code" prop="code"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> :rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input <el-input
:disabled="disabled"
v-model="dataForm.code" v-model="dataForm.code"
@change="$emit('update', dataForm)" @change="$emit('update', dataForm)"
placeholder="请输入工段排序" /> placeholder="请输入工段排序" />
</el-form-item> </el-form-item>
<!-- <!--
<el-form-item <el-form-item
label="报警编码" label="报警编码"
prop="code" prop="code"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> :rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select <el-select
v-model="dataForm.code" v-model="dataForm.code"
placeholder="请选择产线" placeholder="请选择产线"
@change="handleProductlineChange"> @change="handleProductlineChange">
<el-option <el-option
v-for="opt in productionLineList" v-for="opt in productionLineList"
:key="opt.value" :key="opt.value"
:label="opt.label" :label="opt.label"
:value="opt.value" /> :value="opt.value" />
</el-select> </el-select>
</el-form-item> --> </el-form-item> -->
</el-col> </el-col>
@@ -47,6 +48,7 @@
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> :rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select <el-select
v-model="dataForm.type" v-model="dataForm.type"
:disabled="disabled"
placeholder="请选择报警类型" placeholder="请选择报警类型"
@change="$emit('update', dataForm)"> @change="$emit('update', dataForm)">
<el-option <el-option
@@ -68,6 +70,7 @@
prop="grade" prop="grade"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> :rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select <el-select
:disabled="disabled"
v-model="dataForm.grade" v-model="dataForm.grade"
placeholder="请选择报警级别" placeholder="请选择报警级别"
@change="$emit('update', dataForm)"> @change="$emit('update', dataForm)">
@@ -86,6 +89,7 @@
label="设备报警编码" label="设备报警编码"
prop="alarmCode"> prop="alarmCode">
<el-input <el-input
:disabled="disabled"
v-model="dataForm.alarmCode" v-model="dataForm.alarmCode"
@change="$emit('update', dataForm)" @change="$emit('update', dataForm)"
placeholder="请输入设备报警编码" /> placeholder="请输入设备报警编码" />
@@ -99,6 +103,7 @@
prop="plcParamName" prop="plcParamName"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> :rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input <el-input
:disabled="disabled"
v-model="dataForm.plcParamName" v-model="dataForm.plcParamName"
placeholder="请输入参数列名" placeholder="请输入参数列名"
@change="$emit('update', dataForm)"></el-input> @change="$emit('update', dataForm)"></el-input>
@@ -110,6 +115,7 @@
prop="alarmContent" prop="alarmContent"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> :rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input <el-input
:disabled="disabled"
v-model="dataForm.alarmContent" v-model="dataForm.alarmContent"
placeholder="请输入报警内容" placeholder="请输入报警内容"
@change="$emit('update', dataForm)"></el-input> @change="$emit('update', dataForm)"></el-input>
@@ -133,6 +139,10 @@ export default {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
disabled: {
type: Boolean,
default: false,
},
}, },
data() { data() {
return { return {

View File

@@ -0,0 +1,531 @@
<!--
filename: EquipmentDrawer.vue
author: liubin
date: 2023-08-22 14:38:56
description:
-->
<template>
<el-drawer
:visible="visible"
:show-close="false"
:wrapper-closable="false"
class="drawer"
custom-class="mes-drawer"
size="60%"
@closed="$emit('destroy')">
<SmallTitle slot="title">
{{
mode.includes('detail')
? '详情'
: mode.includes('edit')
? '编辑'
: '新增'
}}
</SmallTitle>
<div class="drawer-body flex">
<div class="drawer-body__content">
<section v-for="(section, index) in sections" :key="section.key">
<SmallTitle v-if="index != 0">{{ section.name }}</SmallTitle>
<div class="form-part" v-if="section.key == 'base'">
<el-skeleton v-if="!showForm" animated />
<BaseInfoForm
key="drawer-dialog-form"
v-if="showForm"
ref="form"
:disabled="mode.includes('detail')"
:dataForm="form"
:rows="formRows" />
</div>
<div v-if="section.key == 'attrs'" style="margin-top: 12px">
<base-table
v-loading="attrListLoading"
:table-props="section.props"
:page="attrQuery?.params.pageNo || 1"
:limit="attrQuery?.params.pageSize || 10"
:table-data="list"
:add-button-show="mode.includes('detail') ? null : '添加属性'"
@emitButtonClick="handleAddAttr"
@emitFun="handleEmitFun">
<method-btn
v-if="section.tableBtn"
slot="handleBtn"
label="操作"
:method-list="tableBtn"
@clickBtn="handleTableBtnClick" />
</base-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="attrQuery.params.pageNo"
:limit.sync="attrQuery.params.pageSize"
@pagination="getAttrList" />
</div>
</section>
</div>
<div class="drawer-body__footer">
<el-button style="" @click="handleCancel">取消</el-button>
<el-button v-if="mode == 'detail'" type="primary" @click="toggleEdit">
编辑
</el-button>
<el-button v-else type="primary" @click="handleCancel">确定</el-button>
<!-- sections的第二项必须是 属性列表 -->
<!-- <el-button
v-if="sections[1].allowAdd"
type="primary"
@click="handleAddAttr">
添加属性
</el-button> -->
</div>
</div>
<!-- 属性对话框 -->
<base-dialog
v-if="sections[1].allowAdd"
:dialogTitle="attrTitle"
:dialogVisible="attrFormVisible"
width="45%"
:append-to-body="true"
custom-class="baseDialog"
@close="closeAttrForm"
@cancel="closeAttrForm"
@confirm="submitAttrForm">
<DialogForm
v-if="attrFormVisible"
ref="attrForm"
:disabled="mode.includes('detail')"
v-model="attrForm"
:rows="attrRows" />
</base-dialog>
</el-drawer>
</template>
<script>
import BaseInfoForm from '@/components/DialogForm';
const SmallTitle = {
name: 'SmallTitle',
props: ['size'],
data() {
return {};
},
methods: {},
render: function (h) {
return h(
'span',
{
class: 'small-title',
style: {
fontSize: '18px',
lineHeight:
this.size == 'lg' ? '24px' : this.size == 'sm' ? '18px' : '20px',
fontWeight: 500,
fontFamily: '微软雅黑, Microsoft YaHei, Arial, Helvetica, sans-serif',
},
},
this.$slots.default
);
},
};
export default {
components: { SmallTitle, DialogForm: BaseInfoForm, BaseInfoForm },
props: ['sections', 'defaultMode', 'infoData'],
data() {
return {
mode: '',
visible: false,
showForm: false,
total: 0,
form: {},
list: [],
attrTitle: '',
attrForm: {
id: null,
name: '',
plcParamName: '',
unit: '',
collection: 1,
minValue: '',
maxValue: '',
defaultValue: '',
description: '',
remark: '',
alarmContent: '',
},
attrFormVisible: false,
attrRows: [
[
{
input: true,
label: '参数列名',
prop: 'plcParamName',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
{
input: true,
label: '参数名称',
prop: 'name',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
],
[
{
select: true,
label: '单位',
prop: 'unit',
options: this.getDictDatas(this.DICT_TYPE.UNIT_DICT),
// rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
{
switch: true,
label: '是否采集',
prop: 'collection',
bind: {
'active-value': 1,
'inactive-value': 0,
},
},
],
[
{
input: true,
label: '最小值',
prop: 'minValue',
rules: [
{
type: 'number',
message: '请输入正确的数字',
trigger: 'change',
transform: (val) => Number(val),
},
],
},
{
input: true,
label: '最大值',
prop: 'maxValue',
rules: [
{
required: false,
},
{
type: 'number',
message: '请输入正确的数字',
trigger: 'change',
transform: (val) => Number(val),
},
],
// rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
],
[
{
input: true,
label: '标准值',
prop: 'defaultValue',
// rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
{
input: true,
label: '描述',
prop: 'description',
// rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
],
[
{
input: true,
label: '备注',
prop: 'remark',
// rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
},
],
],
attrQuery: {
params: {
pageNo: 1,
pageSize: 10,
},
}, // 属性列表的请求
infoQuery: null, // 基本信息的请求
attrFormSubmitting: false,
attrListLoading: false,
};
},
computed: {
formRows() {
return this.sections[0].rows.map((row) => {
return row.map((col) => {
return {
...col,
bind: {
// 详情 模式下,禁用各种输入
// disabled: this.mode == 'detail',
disabled: true,
},
};
});
});
},
tableBtn() {
return this.mode == 'detail' ? [] : this.sections[1].tableBtn;
},
},
mounted() {
this.mode = this.defaultMode || 'detail';
for (const section of this.sections) {
// 请求具体信息
if ('url' in section) {
const query = {
url: section.url,
method: section.method || 'get',
params: section.queryParams || null,
data: section.data || null,
};
// debugger;
this.$axios(query).then(({ data }) => {
if (section.key == 'base') {
this.form = data;
this.showForm = true;
this.infoQuery = query;
} else if (section.key == 'attrs') {
this.attrQuery = query;
this.list = data.list;
this.total = data.total;
}
});
} else if (section.key == 'base') {
this.form = this.infoData;
this.showForm = true;
}
}
},
methods: {
handleTableBtnClick({ type, data }) {
switch (type) {
case 'edit':
this.handleEditAttr(data.id);
break;
case 'delete':
this.handleDeleteAttr(data.id);
break;
}
},
handleEmitFun(val) {
console.log('handleEmitFun', val);
},
init() {
this.visible = true;
},
async getAttrList() {
this.attrListLoading = true;
const res = await this.$axios(this.attrQuery);
if (res.code == 0) {
this.list = res.data.list;
this.total = res.data.total;
}
this.attrListLoading = false;
},
// 保存表单
handleSave() {
this.$refs['form'][0].validate(async (valid) => {
if (valid) {
const isEdit = this.mode == 'edit';
await this.$axios({
url: this.sections[0][isEdit ? 'urlUpdate' : 'urlCreate'],
method: isEdit ? 'put' : 'post',
data: this.form,
});
this.$modal.msgSuccess(`${isEdit ? '更新' : '创建'}成功`);
this.visible = false;
this.$emit('refreshDataList');
}
});
},
handleCancel() {
this.visible = false;
},
// 开启编辑
toggleEdit() {
this.mode = 'edit';
},
// 新增属性
handleAddAttr() {
this.attrForm = {
id: null,
name: '',
plcParamName: '',
unit: '',
collection: 1,
minValue: '',
maxValue: '',
defaultValue: '',
description: '',
remark: '',
alarmContent: '',
};
this.attrTitle = '添加设备绑定信息';
this.attrFormVisible = true;
},
// 编辑属性
async handleEditAttr(attrId) {
const res = await this.$axios({
url: this.sections[1].urlDetail,
method: 'get',
params: { id: attrId },
});
if (res.code == 0) {
this.attrForm = res.data;
this.attrTitle = '编辑设备绑定信息';
this.attrFormVisible = true;
}
},
// 删除属性
handleDeleteAttr(attrId) {
this.$confirm('确定删除该分组报警?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
const res = await this.$axios({
url: this.sections[1].urlDelete,
method: 'delete',
params: { id: attrId },
});
if (res.code == 0) {
this.$message({
message: '删除成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getAttrList();
},
});
}
})
.catch(() => {});
},
// 提交属性表
async submitAttrForm() {
this.$refs['attrForm'].validate((valid) => {
if (!valid) {
return;
}
});
const isEdit = this.attrForm.id != null;
this.attrFormSubmitting = true;
const res = await this.$axios({
url: isEdit ? this.sections[1].urlUpdate : this.sections[1].urlCreate,
method: isEdit ? 'put' : 'post',
data: {
...this.attrForm,
connectId: this.infoData.id
},
});
if (res.code == 0) {
this.closeAttrForm();
this.$message({
message: `${isEdit ? '更新' : '创建'}成功`,
type: 'success',
duration: 1500,
onClose: () => {
this.getAttrList();
},
});
}
this.attrFormSubmitting = false;
},
closeAttrForm() {
this.attrFormVisible = false;
},
handleClick(raw) {
if (raw.type === 'delete') {
this.$confirm(`确定删除该报警?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteProductAttr(raw.data.id).then(({ data }) => {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getList();
},
});
});
})
.catch(() => {});
} else {
this.addNew(raw.data.id);
}
},
},
};
</script>
<style scoped>
.drawer >>> .el-drawer {
border-radius: 8px 0 0 8px;
}
.drawer >>> .el-drawer__header {
margin: 0;
padding: 32px 32px 24px;
border-bottom: 1px solid #dcdfe6;
margin-bottom: 0px;
}
.small-title::before {
content: '';
display: inline-block;
vertical-align: top;
width: 4px;
height: 22px;
border-radius: 1px;
margin-right: 8px;
background-color: #0b58ff;
}
.drawer-body {
display: flex;
flex-direction: column;
height: 100%;
}
.drawer-body__content {
flex: 1;
/* background: #eee; */
padding: 20px 30px;
overflow-y: auto;
}
.drawer-body__footer {
display: flex;
justify-content: flex-end;
padding: 18px;
}
</style>

View File

@@ -40,6 +40,53 @@
@confirm="submitForm"> @confirm="submitForm">
<DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" /> <DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" />
</base-dialog> </base-dialog>
<!-- 抽屉 详情 -->
<BasicDrawer
v-if="editVisible"
ref="drawer"
:default-mode="editMode"
:info-data="alarmForm"
:sections="[
{
name: '基本信息',
key: 'base',
rows: drawerBaseInfoRows,
},
{
name: '属性列表',
key: 'attrs',
props: drawerListProps,
url: '/base/equipment-plc-param/page',
urlCreate: '/base/equipment-plc-param/create',
urlUpdate: '/base/equipment-plc-param/update',
urlDelete: '/base/equipment-plc-param/delete',
urlDetail: '/base/equipment-plc-param/get',
queryParams: {
connectId: alarmForm.id,
pageNo: 1,
pageSize: 10,
},
tableBtn: [
this.$auth.hasPermi('base:equipment-plc-param:update')
? {
type: 'edit',
btnName: '修改',
}
: undefined,
this.$auth.hasPermi('base:equipment-plc-param:delete')
? {
type: 'delete',
btnName: '删除',
}
: undefined,
].filter((v) => v),
allowAdd: true,
},
]"
@refreshDataList="getList"
@cancel="editVisible = false"
@destroy="editVisible = false" />
</div> </div>
</template> </template>
@@ -55,11 +102,13 @@ import {
import moment from 'moment'; import moment from 'moment';
import basicPageMixin from '@/mixins/lb/basicPageMixin'; import basicPageMixin from '@/mixins/lb/basicPageMixin';
// import './http'; // import './http';
import BasicDrawer from './components/BasicDrawer.vue';
import { publicFormatter } from '@/utils/dict';
export default { export default {
name: 'EquipmentPlcConnect', name: 'EquipmentPlcConnect',
mixins: [basicPageMixin], mixins: [basicPageMixin],
components: {}, components: { BasicDrawer },
data() { data() {
return { return {
searchBarKeys: ['equipmentId', 'plcId'], searchBarKeys: ['equipmentId', 'plcId'],
@@ -78,6 +127,10 @@ export default {
// : undefined, // : undefined,
// ].filter((v) => v), // ].filter((v) => v),
tableBtn: [ tableBtn: [
{
type: 'detail',
btnName: '参数绑定',
},
{ {
type: 'edit', type: 'edit',
btnName: '修改', btnName: '修改',
@@ -107,32 +160,32 @@ export default {
{ prop: 'plcTableName', label: '关联表名' }, { prop: 'plcTableName', label: '关联表名' },
{ prop: 'plcName', label: '标识名称' }, { prop: 'plcName', label: '标识名称' },
{ prop: 'bindingParameters', label: '绑定参数数量' }, { prop: 'bindingParameters', label: '绑定参数数量' },
{ // {
_action: 'params-bind', // _action: 'params-bind',
label: '查看绑定', // label: '查看绑定',
subcomponent: { // subcomponent: {
props: ['injectData'], // props: ['injectData'],
render: function (h) { // render: function (h) {
const _this = this; // const _this = this;
return h( // return h(
'el-button', // 'el-button',
{ // {
props: { type: 'text' }, // props: { type: 'text' },
on: { // on: {
click: function () { // click: function () {
console.log('inejctdata', _this.injectData); // console.log('inejctdata', _this.injectData);
_this.$emit('emitData', { // _this.$emit('emitData', {
action: _this.injectData._action, // action: _this.injectData._action,
payload: _this.injectData, // payload: _this.injectData,
}); // });
}, // },
}, // },
}, // },
'查看绑定' // '查看绑定'
); // );
}, // },
}, // },
}, // },
], ],
searchBarFormConfig: [ searchBarFormConfig: [
{ {
@@ -185,8 +238,8 @@ export default {
url: '/base/equipment-plc/listAll', url: '/base/equipment-plc/listAll',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
bind: { bind: {
filterable: true filterable: true,
} },
}, },
], ],
[ [
@@ -197,8 +250,8 @@ export default {
url: '/base/equipment/page?pageNo=1&pageSize=99', url: '/base/equipment/page?pageNo=1&pageSize=99',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
bind: { bind: {
filterable: true filterable: true,
} },
}, },
], ],
], ],
@@ -212,6 +265,52 @@ export default {
}, },
// 表单参数 // 表单参数
form: {}, form: {},
// 查看绑定配置
editVisible: false,
editMode: '',
drawerBaseInfoRows: [
[
{
input: true,
label: '设备名',
prop: 'equipmentName',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
// bind: {
// disabled: this.editMode == 'detail', // some condition, like detail mode...
// }
},
{
input: true,
label: '关联表名',
prop: 'plcTableName',
// url: '/base/equipment/getCode',
},
],
],
drawerListProps: [
{ prop: 'plcParamName', label: '参数列名' },
{ prop: 'name', label: '参数名称' },
{
prop: 'unit',
label: '单位',
filter: publicFormatter('unit_dict'),
},
{
prop: 'collection',
label: '是否采集',
filter: (val) => (val != null ? ['否', '是'][val] : '-'),
},
{ prop: 'minValue', label: '最小值' },
{ prop: 'maxValue', label: '最大值' },
{ prop: 'defaultValue', label: '标准值' },
{ prop: 'description', label: '描述' },
{ prop: 'remark', label: '备注' },
],
alarmForm: {
id: undefined,
equipmentName: undefined,
plcTableName: undefined,
},
}; };
}, },
created() { created() {
@@ -353,6 +452,34 @@ export default {
}); });
}); });
}, },
// 查看报警
handleDetail(row) {
// debugger;
const {
id,
bindingParameters,
equipmentCode,
equipmentId,
equipmentName,
plcCode,
plcId,
plcName,
plcTableName,
productionLine,
workshopSection,
} = row;
// 打开抽屉
this.editMode = 'detail';
this.alarmForm.id = id;
this.alarmForm.plcTableName = plcTableName; // 关联表名
this.alarmForm.equipmentName = equipmentName;
this.editVisible = true;
this.$nextTick(() => {
this.$refs['drawer'].init();
});
},
/** 删除按钮操作 */ /** 删除按钮操作 */
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
@@ -367,6 +494,7 @@ export default {
}) })
.catch(() => {}); .catch(() => {});
}, },
/** 导出按钮操作 */ /** 导出按钮操作 */
handleExport() { handleExport() {
// 处理查询参数 // 处理查询参数

View File

@@ -1,8 +1,8 @@
<!-- <!--
* @Author: zhp * @Author: zhp
* @Date: 2023-09-13 09:02:25 * @Date: 2023-09-13 09:02:25
* @LastEditTime: 2023-09-13 10:33:20 * @LastEditTime: 2023-10-16 14:56:58
* @LastEditors: zhp * @LastEditors: DY
* @Description: * @Description:
--> -->
<template> <template>
@@ -61,11 +61,12 @@ export default {
// }, // },
methods: { methods: {
initChart(xData, yData,lineName) { initChart(xData, yData,lineName) {
console.log(xData,yData); console.log( '打印结果', xData,yData, lineName);
this.chart = echarts.init(this.$el, 'macarons') this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(xData, yData, lineName) this.setOptions(xData, yData, lineName)
}, },
setOptions(xData, yData, lineName) { setOptions(xData, yData, lineName) {
console.log('da', lineName)
let seriesData = [] let seriesData = []
lineName.forEach((item,index) => { lineName.forEach((item,index) => {
seriesData.push({ seriesData.push({
@@ -86,7 +87,7 @@ export default {
type: 'value' type: 'value'
}, },
series: seriesData series: seriesData
}) }, true)
} }
} }
} }

View File

@@ -1,8 +1,12 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" /> <search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<base-table v-loading="dataListLoading" :span-method="mergeColumnHandler" :table-props="tableProps" :table-data="tableData" /> <div v-if="tableData.length">
<balance-chart ref="lineChart" /> <base-table v-loading="dataListLoading" :span-method="mergeColumnHandler" :table-props="tableProps" :table-data="tableData" />
<SearchBar :formConfigs="[{ label: '产线平衡分析图', type: 'title' }]" />
<balance-chart ref="lineChart" />
</div>
<div v-else class="no-data-bg"></div>
<!-- <pagination <!-- <pagination
:limit.sync="listQuery.pageSize" :limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo" :page.sync="listQuery.pageNo"
@@ -89,7 +93,7 @@ export default {
}, },
{ {
type: 'button', type: 'button',
btnName: '搜索', btnName: '查询',
name: 'search', name: 'search',
color: 'primary', color: 'primary',
} }
@@ -144,11 +148,6 @@ export default {
// this.listQuery.lineId = '1672847052717821953' // this.listQuery.lineId = '1672847052717821953'
// this.listQuery.startTime = '1693497600000'; // this.listQuery.startTime = '1693497600000';
// this.listQuery.endTime = '1693843200000'; // this.listQuery.endTime = '1693843200000';
this.tableData.splice(0)
this.xData.splice(0)
this.yData.splice(0)
this.tableProps.splice(0)
this.spanArr.splice(0)
this.urlOptions.getDataListURL(this.listQuery).then(res => { this.urlOptions.getDataListURL(this.listQuery).then(res => {
console.log(res) console.log(res)
let arr = [ let arr = [
@@ -228,7 +227,20 @@ export default {
this.listQuery.lineId = val.lineIds this.listQuery.lineId = val.lineIds
this.listQuery.startTime = val.time ? String(new Date(val.time[0]).getTime()) : undefined; this.listQuery.startTime = val.time ? String(new Date(val.time[0]).getTime()) : undefined;
this.listQuery.endTime = val.time ? String(new Date(val.time[1]).getTime()) : undefined; this.listQuery.endTime = val.time ? String(new Date(val.time[1]).getTime()) : undefined;
this.getData() if (val.time && val.lineIds) {
this.tableData = []
this.xData = []
this.yData = []
this.tableProps = []
this.spanArr = []
this.timeList = []
this.getData()
} else {
this.$message({
message: '请选择产线和时间',
type: 'warning'
});
}
break; break;
case 'reset': case 'reset':
this.$refs.searchBarForm.resetForm(); this.$refs.searchBarForm.resetForm();

View File

@@ -1,14 +1,14 @@
<!-- <!--
* @Author: zhp * @Author: zhp
* @Date: 2023-09-13 09:02:25 * @Date: 2023-09-13 09:02:25
* @LastEditTime: 2023-09-20 09:29:40 * @LastEditTime: 2023-10-08 16:36:37
* @LastEditors: DY * @LastEditors: DY
* @Description: * @Description:
--> -->
<template> <template>
<div> <div>
<div style="margin: 20px"> <div style="margin: 20px">
<el-button v-for="(item, index) in dataArray" :key="index" @click="changeChart(index)">{{ item.name }}</el-button> <el-button v-for="(item, index) in buttonList" :key="index" :class="[item.actived ? 'activeButton': 'normalButton']" @click="changeChart(index)">{{ item.name }}</el-button>
</div> </div>
<div ref="chartDiv" :class="className" :style="{height:height,width:width}" /> <div ref="chartDiv" :class="className" :style="{height:height,width:width}" />
</div> </div>
@@ -43,7 +43,8 @@ export default {
return { return {
chart: null, chart: null,
dataArray: [], dataArray: [],
xDatas: [] xDatas: [],
buttonList: []
} }
}, },
mounted() { mounted() {
@@ -51,12 +52,34 @@ export default {
methods: { methods: {
changeChart(index) { changeChart(index) {
this.setOptions(this.xDatas, this.dataArray[index]) this.setOptions(this.xDatas, this.dataArray[index])
this.buttonList.forEach((item, s) => {
if (index === s) {
// item.actived = true
this.$nextTick(() =>{
// item.actived = true
this.$set(item, 'actived', true)
})
} else {
// item.actived = false
this.$nextTick(() =>{
// item.actived = false
this.$set(item, 'actived', false)
})
// this.$set(item, 'actived', false)
}
})
console.log('看一下数22222据', this.dataArray)
}, },
initChart(xData, yData, lineName) { initChart(xData, yData, lineName) {
this.dataArray = yData this.dataArray = yData
this.buttonList = this.dataArray.map((item, index) => {
return {
'name': item.name,
'actived': index === 0 ? true : false
}
})
console.log('看一下数据', this.dataArray)
this.xDatas = xData this.xDatas = xData
console.log(xData,yData);
console.log('zale', yData[0].eqData)
this.chart = echarts.init(this.$refs.chartDiv, 'macarons') this.chart = echarts.init(this.$refs.chartDiv, 'macarons')
this.setOptions(xData, yData[0], lineName) this.setOptions(xData, yData[0], lineName)
}, },
@@ -100,3 +123,12 @@ export default {
} }
} }
</script> </script>
<style scoped>
.activeButton {
background-color: rgb(93,159,255);
}
.normalButton {
background-color: none;
}
</style>

View File

@@ -1,8 +1,12 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" /> <search-bar :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
<base-table v-loading="dataListLoading" :table-props="tableProps" :table-data="tableData" /> <div v-if="tableData.length">
<line-chart ref="lineChart" /> <base-table v-loading="dataListLoading" :table-props="tableProps" :table-data="tableData" />
<SearchBar :formConfigs="[{ label: '产品产量对比图', type: 'title' }]" />
<line-chart ref="lineChart" />
</div>
<div v-else class="no-data-bg"></div>
<!-- <pagination <!-- <pagination
:limit.sync="listQuery.pageSize" :limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo" :page.sync="listQuery.pageNo"
@@ -57,8 +61,7 @@ export default {
lineIds: [], lineIds: [],
time: '' time: ''
}, },
xData: [], dateLabelList: [],
yData:[],
optionArrUrl: [getProductionLinePage ], optionArrUrl: [getProductionLinePage ],
formConfig: [ formConfig: [
{ {
@@ -73,7 +76,7 @@ export default {
{ {
type: 'datePicker', type: 'datePicker',
label: '时间', label: '时间',
dateType: 'datetime', dateType: 'month',
format: 'yyyy-MM-dd', format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd HH:mm:ss', valueFormat: 'yyyy-MM-dd HH:mm:ss',
rangeSeparator: '-', rangeSeparator: '-',
@@ -83,7 +86,7 @@ export default {
}, },
{ {
type: 'button', type: 'button',
btnName: '搜索', btnName: '查询',
name: 'search', name: 'search',
color: 'primary', color: 'primary',
} }
@@ -102,6 +105,8 @@ export default {
this.optionArrUrl.forEach((item, index) => { this.optionArrUrl.forEach((item, index) => {
item(params).then((response) => { item(params).then((response) => {
this.formConfig[index].selectOptions = response.data.list this.formConfig[index].selectOptions = response.data.list
// this.formConfig[0].defaultSelect = response.data.list[0].id
this.$set(this.formConfig[0], 'defaultSelect', response.data.list[0].id)
}); });
}); });
}, },
@@ -114,74 +119,102 @@ export default {
{ {
prop: 'lineName', prop: 'lineName',
label: '产线', label: '产线',
align: 'center', fixed: 'left'
}, },
{ {
prop: 'sum', prop: 'sum',
label: '合计', label: '合计',
align: 'center', fixed: 'left'
}, },
{ {
prop: res.data.nameData[0].name, prop: res.data ? res.data.nameData[0].name : undefined,
label: res.data.nameData[0].name, label: res.data ? res.data.nameData[0].name : undefined,
align: 'center', align: 'center',
children:[ children:[
] ]
} }
] ]
console.log(res.data.nameData.slice(1)) // console.log(res.data.nameData.slice(1))
res.data.nameData.slice(1).forEach(item => {
const props = {
'prop': item.name,
'label': item.name,
'align': 'center'
}
arr[2].children.push(props)
})
let tableDataArr =[]
res.data.data.forEach(item => {
let obj = {}
obj.lineName= item.lineName,
obj.sum= item.sum,
item.data.forEach((ele, index) => {
// console.log(ele)
ele.children.forEach((e) => {
console.log(e.dynamicName)
obj['' + e.dynamicName + ''] = e.dynamicValue
console.log(obj['' + e.dynamicName + '']);
})
})
tableDataArr.push(obj)
});
this.tableData = tableDataArr
console.log(this.tableData)
console.log(arr)
this.tableProps = arr
let xData = [] let xData = []
res.data.nameData.slice(1).forEach(item => {
xData.push(item.name)
// arr[2].children.push(props)
})
let yAllData = [] let yAllData = []
let lineName = [] let lineName = []
res.data.data.forEach(item => { if (res.data) {
let yData = [] let tempDateList = []
lineName.push(item.lineName) res.data.nameData.forEach(date => {
// let obj = {} tempDateList.push(date.name)
// obj.lineName = item.lineName, })
// obj.sum = item.sum, this.dateLabelList = Array.from(new Set(tempDateList))
item.data.forEach((ele, index) => {
this.dateLabelList.forEach(item => {
if (item.indexOf('年') === -1) {
// 构造表头
const props = {
'prop': item,
'label': item
}
arr[2].children.push(props)
// 构造echarts横坐标
xData.push(item)
}
})
// res.data.nameData.slice(1).forEach(item => {
// const props = {
// 'prop': item.name,
// 'label': item.name,
// 'align': 'center'
// }
// arr[2].children.push(props)
// })
let tableDataArr =[]
res.data.data.forEach(item => {
let obj = {}
obj.lineName= item.lineName,
obj.sum= item.sum,
item.data.forEach((ele, index) => {
// console.log(ele) // console.log(ele)
ele.children.forEach((e) => { ele.children.forEach((e) => {
// let yData = [] console.log(e.dynamicName)
yData.push(e.dynamicValue) obj['' + e.dynamicName + ''] = e.dynamicValue
console.log(obj['' + e.dynamicName + '']);
}) })
}) })
yAllData.push(yData) tableDataArr.push(obj)
}); });
console.log(lineName) this.tableData = tableDataArr
this.tableProps = arr
// let tempList = []
// res.data.nameData.slice(1).forEach(item => {
// tempList.push(item.name)
// // arr[2].children.push(props)
// })
// xData = Array.from(new Set(tempList))
res.data.data.forEach(item => {
let yData = []
lineName.push(item.lineName)
// let obj = {}
// obj.lineName = item.lineName,
// obj.sum = item.sum,
item.data.forEach((ele, index) => {
// console.log(ele)
ele.children.forEach((e) => {
// let yData = []
yData.push(e.dynamicValue)
})
})
yAllData.push(yData)
});
console.log(lineName)
} else {
this.tableProps = arr
this.tableData = []
xData = []
yAllData = []
lineName = []
}
// res.data.data[0].data[0].children.forEach((item, index) => { // res.data.data[0].data[0].children.forEach((item, index) => {
// // console.log(item) // // console.log(item)
// yData.push(item.dynamicValue) // yData.push(item.dynamicValue)
@@ -195,7 +228,6 @@ export default {
}); });
}, },
buttonClick(val) { buttonClick(val) {
// console.log(val)
switch (val.btnName) { switch (val.btnName) {
case 'search': case 'search':
this.listQuery.lineIds = val.lineIds ? val.lineIds :undefined this.listQuery.lineIds = val.lineIds ? val.lineIds :undefined
@@ -203,7 +235,14 @@ export default {
this.listQuery.time = val.time ? new Date(val.time).getTime() : undefined this.listQuery.time = val.time ? new Date(val.time).getTime() : undefined
// this.listQuery.pageNo = 1; // this.listQuery.pageNo = 1;
// this.listQuery.pageSize = 10; // this.listQuery.pageSize = 10;
this.getData() if (val.time) {
this.getData()
} else {
this.$message({
message: '请选择时间',
type: 'warning'
});
}
break; break;
case 'reset': case 'reset':
this.$refs.searchBarForm.resetForm(); this.$refs.searchBarForm.resetForm();

View File

@@ -0,0 +1,412 @@
<!--
filename: AssetsUpload.vue
author: liubin
date: 2023-10-12 16:40:14
description: 上传资料/图片 组件
-->
<template>
<div class="assets-upload">
<section class="operations">
<el-button type="text" class="expand-btn" @click="expand = !expand">
<i class="el-icon-folder-opened" v-if="expand"></i>
<i class="el-icon-folder" v-else></i>
展开
</el-button>
<!-- <div class="preview-btn">
<i class="el-icon-view"></i>
预览
</div> -->
</section>
<section class="file-area">
<el-upload
class="equipment-upload"
:disabled="disabled"
drag
:action="uploadUrl"
:headers="headers"
multiple
:show-file-list="false"
:before-upload="beforeUpload"
:on-success="handleSuccess">
<i class="el-icon-upload"></i>
<div class="el-upload__text">
<span>将文件拖到此处或</span>
<em>点击上传</em>
</div>
<div class="el-upload__tip" slot="tip">
{{
isPicMode ? '仅支持上传 .jpg .png 格式文件, 且' : ''
}}文件大小不超过2MB
</div>
</el-upload>
<!-- <div
class="file-list__item"
v-for="n in 9"
:key="n"
:style="{
display: n > 4 && !expand ? 'none' : 'block',
}"
:data-name="n"
:class="{ 'default-icon': !isPicMode }">
<i class="el-icon-delete"></i>
</div> -->
<div
class="file-list__item"
v-for="(file, index) in files"
:key="file.fileName"
:style="{
display: index > 3 && !expand ? 'none' : 'block',
background: isPicMode
? `url(${file.fileUrl}) no-repeat`
: `url(${defaultBg}) no-repeat`,
backgroundSize: isPicMode ? '100% 100%' : '64px',
backgroundPosition: isPicMode ? '0% 0%' : 'center',
}"
:data-name="file.fileName">
<el-button
v-if="!disabled"
type="text"
class="el-icon-delete"
style="padding: 0"
@click="(e) => handleDelete(file)" />
</div>
</section>
</div>
</template>
<script>
import { getAccessToken } from '@/utils/auth';
import defaultBg from '../../../../../assets/images/default-file-icon.png';
function checkSize(file, message) {
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('上传文件大小不能超过 2MB!');
}
return isLt2M;
}
function checkPic(file, message) {
const isJPG = file.type === 'image/jpeg';
const isPNG = file.type === 'image/png';
const isPic = isJPG || isPNG;
if (!isPic) {
message.error('上传图片只能是 JPG/PNG 格式!');
}
return isPic;
}
export default {
name: 'AssetsUpload',
components: {},
model: {
prop: 'dataSource',
event: 'update',
},
props: {
type: {
type: String,
default: 'image',
},
dataSource: {
type: Array,
default: () => [],
},
equipmentId: {
type: String,
default: '',
},
isPicMode: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
},
emits: ['update-filelist'],
data() {
return {
defaultBg,
expand: false,
headers: { Authorization: 'Bearer ' + getAccessToken() }, // 设置上传的请求头部
fileList: [],
uploadUrl: process.env.VUE_APP_BASE_API + '/admin-api/infra/file/upload',
files: [
// 服务器返回的结构
// {
// fileName: 'sf.docs',
// fileType: 'asset',
// fileUrl:
// 'https://hbimg.b0.upaiyun.com/cc7475787bd08ed926b68eaf53fa1f2c5473259115e3c-gJdObd_fw658',
// },
// {
// fileName: 'sddf.docs',
// fileType: 'asset',
// fileUrl: 'https://img0.sc115.com/wm/xqx/pic1/1501xofo4ssolji.jpg',
// },
// {
// fileName: 'jjj.docs',
// fileType: 'asset',
// fileUrl:
// 'https://www.mms591.com/www.mms591.com-photo/2013081823/1-130QR34544.jpg',
// },
// {
// fileName: 'asdfasdf.docs',
// fileType: 'asset',
// fileUrl:
// 'https://hbimg.b0.upaiyun.com/8f6bced5f2e38d3a021b2c48d5a98dfb6317e3e12c6a0-QmhJ5v_fw658',
// },
// {
// fileName: 'asdkj.docs',
// fileType: 'asset',
// fileUrl:
// 'https://www.mms591.com/www.mms591.com-photo/2013013021/1-130130212034.jpg',
// },
// {
// fileName: 'lkasjdf.docs',
// fileType: 'asset',
// fileUrl:
// 'https://www.mms591.com/www.mms591.com-photo/2013072122/1-130H1223057.jpg',
// },
// {
// fileName: 'asdf.docs',
// fileType: 'asset',
// fileUrl:
// 'https://img.1ppt.com/uploads/allimg/1212/1-1212101ZH5A2.jpg',
// },
// {
// fileName: 'afdffff.docs',
// fileType: 'asset',
// fileUrl:
// 'https://www.mms591.com/www.mms591.com-photo/2013051721/1-13051H11945.jpg',
// },
],
updateTimer: null,
};
},
watch: {
dataSource: {
handler(val) {
this.files = JSON.parse(JSON.stringify(val));
},
immediate: true,
deep: true,
},
},
mounted() {},
methods: {
// handle success, per file!
handleSuccess(response, file, fileList) {
this.$notify({
title: '成功',
message: '上传成功! 点击确认保存上传结果',
type: 'success',
});
if (
response == null ||
!('data' in response) ||
response.data == null ||
response.data.trim() == ''
) {
this.$message.error('上传出错了!');
return;
}
this.files.push({
fileName: file.name,
fileUrl: response.data,
fileType: this.isPicMode ? 1 : 2,
});
// debugger;
// 延时更新
if (this.updateTimer) {
clearTimeout(this.updateTimer);
}
this.updateTimer = setTimeout(() => {
console.log('[AssetsUpload] 更新上传列表');
this.emitFilelist();
clearTimeout(this.updateTimer);
this.updateTimer = null;
}, 500);
},
emitFilelist() {
this.$emit('update', this.files);
},
handleRemove(file, fileList) {
debugger;
},
handleDelete(file) {
// fileName fileType 都可能一样,但 fileUrl 一定不一样
this.files = this.files.filter((item) => item.fileUrl != file.fileUrl);
this.$notify({
title: '成功',
message: '删除成功! 需点击确认保存删除结果',
type: 'success',
});
this.emitFilelist();
},
beforeUpload(file) {
if (this.isPicMode) {
return checkPic(file, this.$message) && checkSize(file, this.$message);
}
return checkSize(file, this.$message);
},
handleUpload() {
switch (this.type) {
case 'image':
break;
case 'asset':
break;
}
},
updateFileList(appendFilelist) {
// Array
this.$emit('update-filelist', this.appendFilelist);
},
},
};
</script>
<style scoped lang="scss">
.assets-upload {
position: relative;
}
.operations {
position: absolute;
top: -36px;
right: 0;
display: flex;
align-items: center;
}
.expand-btn,
.preview-btn {
font-size: 14px;
line-height: 1.2;
display: inline-block;
padding-left: 20px;
cursor: pointer;
z-index: 1000;
}
.expand-btn {
margin-right: 12px;
}
.preview-btn {
color: #ccc;
}
.expand-btn,
.preview-btn:hover,
.preview-btn.active {
// color: #0042d0;
color: #0b58ff;
}
:deep(.equipment-upload) {
background: #ccc4;
.el-upload-dragger {
width: 188px;
height: 128px;
}
.el-icon-upload {
margin: 12px 0;
font-size: 48px;
}
.el-upload__text {
font-size: 12px;
line-height: 2px;
display: flex;
flex-direction: column;
em {
margin-top: 12px;
}
}
.el-upload__tip {
font-size: 12px;
line-height: 1.5;
color: #d1d1d1;
margin: 0 0 12px;
transform: translateY(-12px);
user-select: none;
}
}
.file-list {
padding: 12px;
border: 1px solid #ccc;
}
// custom
.file-area {
display: grid;
grid-template-columns: repeat(5, 188px);
grid-auto-rows: 128px;
gap: 24px 18px;
place-content: center;
}
.file-list__item {
background-color: #fff;
border: 1px dashed #d9d9d9;
border-radius: 6px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
text-align: center;
cursor: pointer;
position: relative;
// overflow: hidden;
&:hover {
.el-icon-delete {
display: block;
}
}
.el-icon-delete {
color: #ccc;
position: absolute;
top: 8px;
right: 8px;
display: none;
&:hover {
color: rgb(210, 41, 41);
}
}
}
.file-list__item:hover {
border-color: #0b58ff;
}
.file-list__item::after {
content: attr(data-name);
position: absolute;
left: 0;
bottom: -26px;
font-size: 14px;
line-height: 2;
color: #616161;
}
.default-icon {
background: url(../../../../../assets/images/default-file-icon.png) no-repeat;
background-position: center;
background-size: 64px;
}
</style>

View File

@@ -0,0 +1,313 @@
<!--
filename: dialogForm.vue
author: liubin
date: 2023-08-15 10:32:36
description: 弹窗的表单组件
-->
<template>
<el-form
ref="form"
:model="form"
:label-width="`${labelWidth}px`"
:size="size"
:label-position="labelPosition"
v-loading="formLoading">
<el-row :gutter="20" v-for="(row, rindex) in rows" :key="rindex">
<el-col v-for="col in row" :key="col.label" :span="24 / row.length">
<el-form-item :label="col.label" :prop="col.prop" :rules="col.rules">
<el-input
v-if="col.input"
v-model="form[col.prop]"
@change="$emit('update', form)"
:placeholder="`请输入${col.label}`"
v-bind="col.bind" />
<el-input
v-if="col.textarea"
type="textarea"
v-model="form[col.prop]"
@change="$emit('update', form)"
:placeholder="`请输入${col.label}`"
v-bind="col.bind" />
<el-select
v-if="col.select"
v-model="form[col.prop]"
:placeholder="`请选择${col.label}`"
@change="$emit('update', form)"
v-bind="col.bind">
<el-option
v-for="opt in optionListOf[col.prop]"
:key="opt.value"
:label="opt.label"
:value="opt.value" />
</el-select>
<el-date-picker
v-if="col.datetime"
v-model="form[col.prop]"
type="datetime"
:placeholder="`请选择${col.label}`"
value-format="timestamp"
v-bind="col.bind"></el-date-picker>
<el-upload
class="upload-in-dialog"
v-if="col.upload"
:file-list="uploadedFileList"
:action="col.url"
:on-success="handleUploadSuccess"
v-bind="col.bind">
<el-button
size="small"
type="primary"
:disabled="col.bind?.disabled || false">
点击上传
</el-button>
<div class="el-upload__tip" slot="tip" v-if="col.uploadTips">
{{ col.uploadTips || '只能上传jpg/png文件大小不超过2MB' }}
</div>
</el-upload>
<el-switch
v-if="col.switch"
v-model="form[col.prop]"
active-color="#0b58ff"
inactive-color="#e1e1e1"
v-bind="col.bind"></el-switch>
<component
v-if="col.subcomponent"
:key="col.key"
:is="col.subcomponent"
v-bind="col.bind"
:inlineStyle="col.style"></component>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
/**
* 找到最长的label
* @param {*} options
*/
function findMaxLabelWidth(rows) {
let max = 0;
rows.forEach((row) => {
row.forEach((opt) => {
// debugger;
if (!opt.label) return 0;
if (opt.label.length > max) {
max = opt.label.length;
}
});
});
return max;
}
export default {
name: 'DialogForm',
model: {
prop: 'dataForm',
event: 'update',
},
emits: ['update'],
components: {},
props: {
rows: {
type: Array,
default: () => [],
},
dataForm: {
type: Object,
default: () => ({}),
},
disabled: {
type: Boolean,
default: false,
},
labelPosition: {
type: String,
default: 'right',
},
size: {
type: String,
default: '',
},
},
data() {
return {
formLoading: true,
optionListOf: {},
uploadedFileList: [],
dataLoaded: false,
};
},
computed: {
labelWidth() {
let max = findMaxLabelWidth(this.rows);
// 每个汉字占20px
return max * 20;
// return max * 20 + 'px';
},
form: {
get() {
// if (this.dataLoaded) return this.dataForm;
// else return {}
return this.dataForm;
},
set(val) {
console.log('set form', val);
},
},
},
watch: {
rows: {
handler() {
console.log('watch triggered!');
this.$nextTick(() => {
this.handleOptions('watch');
});
},
deep: true,
immediate: false,
},
},
mounted() {
// 处理 options
this.handleOptions();
},
methods: {
/** 模拟透传 ref */
validate(cb) {
return this.$refs.form.validate(cb);
},
resetFields(args) {
return this.$refs.form.resetFields(args);
},
// getCode
async getCode(url) {
const response = await this.$axios(url);
return response.data;
},
async handleOptions(trigger = 'monuted') {
console.log('[dialogForm:handleOptions]');
const promiseList = [];
this.rows.forEach((cols) => {
cols.forEach((opt) => {
if (opt.value && !this.form[opt.prop]) {
// 默认值
this.form[opt.prop] = opt.value;
}
if (opt.options) {
this.$set(this.optionListOf, opt.prop, opt.options);
} else if (opt.url) {
// 如果有 depends则暂时先不获取注册一个watcher
if (opt.depends) {
console.log('[handleOptions] setting watch');
this.$watch(
() => this.form[opt.depends],
(id) => {
console.log('<', opt.depends, '>', 'changed', id);
if (id == null) return;
// 清空原有选项
this.form[opt.prop] = null;
// 获取新的选项
this.$axios({
url: `${opt.url}?id=${id}`,
}).then((res) => {
this.$set(
this.optionListOf,
opt.prop,
res.data.map((item) => ({
label: item[opt.labelKey ?? 'name'],
value: item[opt.valueKey ?? 'id'],
}))
);
});
},
{
immediate: true,
}
);
return;
}
// 如果是下拉框,或者新增模式下的输入框,才去请求
if (opt.select || (opt.input && !this.form?.id)) {
promiseList.push(async () => {
const response = await this.$axios(opt.url, {
method: opt.method ?? 'get',
});
console.log('[dialogForm:handleOptions:response]', response);
if (opt.select) {
// 处理下拉框选项
const list =
'list' in response.data
? response.data.list
: response.data;
this.$set(
this.optionListOf,
opt.prop,
list.map((item) => ({
label: item[opt.labelKey ?? 'name'],
value: item[opt.valueKey ?? 'id'],
}))
);
} else if (opt.input) {
console.log('setting code: ', response.data);
// 处理输入框数据
this.form[opt.prop] = response.data;
}
});
}
}
});
});
console.log('[dialogForm:handleOptions] done!');
// 如果是 watch 触发的,不需要执行进一步的请求
if (trigger == 'watch') {
this.formLoading = false;
return;
}
try {
await Promise.all(promiseList.map((fn) => fn()));
this.formLoading = false;
this.dataLoaded = true;
// console.log("[dialogForm:handleOptions:optionListOf]", this.optionListOf)
} catch (error) {
console.log('[dialogForm:handleOptions:error]', error);
this.formLoading = false;
}
if (!promiseList.length) this.formLoading = false;
},
// 上传成功的特殊处理
beforeUpload() {},
// 上传前的验证规则可通过 bind 属性传入
handleUploadSuccess(response, file, fileList) {
console.log(
'[dialogForm:handleUploadSuccess]',
response,
file,
fileList,
this.form
);
// 保存原始文件名
if ('fileNames' in this.form) this.form.fileNames.push(file.name);
// 保存完整地址
if ('fileUrls' in this.form) this.form.fileUrls.push(response.data);
this.$modal.msgSuccess('上传成功');
},
getFileName(fileUrl) {
return fileUrl.split('/').pop();
},
},
};
</script>
<style scoped lang="scss">
.el-date-editor,
.el-select {
width: 100%;
}
</style>

View File

@@ -29,23 +29,28 @@
<section v-for="(section, index) in sections" :key="section.key"> <section v-for="(section, index) in sections" :key="section.key">
<SmallTitle v-if="index != 0">{{ section.name }}</SmallTitle> <SmallTitle v-if="index != 0">{{ section.name }}</SmallTitle>
<div class="form-part" v-if="section.key == 'base'"> <div
class="form-part"
v-if="section.key == 'base'"
style="margin-bottom: 32px">
<el-skeleton v-if="!showForm" animated /> <el-skeleton v-if="!showForm" animated />
<DialogForm <EquipmentInfoForm
key="drawer-dialog-form" key="drawer-dialog-form"
v-if="showForm" v-if="showForm"
ref="form" :disabled="mode.includes('detail')"
:dataForm="form" :sync-filelist="syncFileListFlag"
:rows="formRows" /> v-model="form" />
</div> </div>
<div v-if="section.key == 'attrs'" style="margin-top: 12px"> <div v-if="section.key == 'attrs'" style="margin-top: 12px">
<base-table <base-table
v-loading="attrListLoading" v-loading="attrListLoading"
:table-props="section.props" :table-props="section.props"
:page="section.pageNo || 1" :page="attrQuery?.params.pageNo || 1"
:limit="section.pageSize || 10" :limit="attrQuery?.params.pageSize || 10"
:table-data="list" :table-data="list"
:add-button-show="mode.includes('detail') ? null : '添加属性'"
@emitButtonClick="handleAddAttr"
@emitFun="handleEmitFun"> @emitFun="handleEmitFun">
<method-btn <method-btn
v-if="section.tableBtn" v-if="section.tableBtn"
@@ -54,27 +59,31 @@
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="attrQuery.params.pageNo"
:limit.sync="attrQuery.params.pageSize"
@pagination="getAttrList" />
</div> </div>
</section> </section>
</div> </div>
<div class="drawer-body__footer"> <div class="drawer-body__footer">
<el-button style="margin-right: 10px" @click="handleCancel"> <el-button style="" @click="handleCancel">取消</el-button>
返回
</el-button>
<el-button v-if="mode == 'detail'" type="primary" @click="toggleEdit"> <el-button v-if="mode == 'detail'" type="primary" @click="toggleEdit">
编辑 编辑
</el-button> </el-button>
<span v-else> <el-button v-else type="primary" @click="handleConfirm">确定</el-button>
<el-button type="primary" @click="handleSave">保存</el-button> <!-- sections的第二项必须是 属性列表 -->
<!-- sections的第二项必须是 属性列表 --> <!-- <el-button
<el-button
v-if="sections[1].allowAdd" v-if="sections[1].allowAdd"
type="primary" type="primary"
@click="handleAddAttr"> @click="handleAddAttr">
添加属性 添加属性
</el-button> </el-button> -->
</span>
</div> </div>
</div> </div>
@@ -99,11 +108,13 @@
</template> </template>
<script> <script>
import DialogForm from '@/components/DialogForm'; import DialogForm from './DialogForm';
import EquipmentInfoForm from './EquipmentInfoForm.vue';
const SmallTitle = { const SmallTitle = {
name: 'SmallTitle', name: 'SmallTitle',
props: ['size'], props: ['size'],
components: {},
data() { data() {
return {}; return {};
}, },
@@ -127,12 +138,13 @@ const SmallTitle = {
}; };
export default { export default {
components: { SmallTitle, DialogForm }, components: { SmallTitle, DialogForm, EquipmentInfoForm },
props: ['sections', 'mode', 'dataId'], // dataId 作为一个通用的存放id的字段 props: ['sections', 'mode', 'dataId'], // dataId 作为一个通用的存放id的字段
data() { data() {
return { return {
visible: false, visible: false,
showForm: false, showForm: false,
btnLoading: false,
total: 0, total: 0,
form: {}, form: {},
list: [], list: [],
@@ -161,10 +173,16 @@ export default {
}, },
], ],
], ],
attrQuery: null, // 属性列表的请求 attrQuery: {
params: {
pageNo: 1,
pageSize: 10,
},
}, // 属性列表的请求
infoQuery: null, // 基本信息的请求 infoQuery: null, // 基本信息的请求
attrFormSubmitting: false, attrFormSubmitting: false,
attrListLoading: false, attrListLoading: false,
syncFileListFlag: null,
}; };
}, },
computed: { computed: {
@@ -175,15 +193,19 @@ export default {
// 重置图片的位置 // 重置图片的位置
return { return {
...col, ...col,
bind: {
...col.bind,
},
style: { style: {
left: 0, left: 0,
right: 'unset' right: 'unset',
} },
} };
} }
return { return {
...col, ...col,
bind: { bind: {
...col.bind,
// 详情 模式下,禁用各种输入 // 详情 模式下,禁用各种输入
disabled: this.mode == 'detail', disabled: this.mode == 'detail',
}, },
@@ -208,9 +230,52 @@ export default {
this.$axios(query).then(({ data }) => { this.$axios(query).then(({ data }) => {
if (section.key == 'base') { if (section.key == 'base') {
this.form = data; this.form = data;
// this.form = {
// code: 'gj',
// name: '下片机',
// enName: 'unload',
// abbr: '',
// equipmentTypeId: 21084,
// remark: '备注',
// id: '1712367395052384257',
// createTime: 1697095176000,
// enterTime: 0,
// productionTime: 0,
// files: [
// {
// fileName: '测试.xlsx',
// fileUrl: 'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2022%2F0108%2F0f0c6f30j00r5cle9000sc000hs00gtc.jpg&thumbnail=660x2147483647&quality=80&type=jpg',
// fileType: 1
// },
// {
// fileName: '测试2.xlsx',
// fileUrl: 'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2022%2F0415%2F2cd23619j00racb96000kc000hs00hsc.jpg&thumbnail=660x2147483647&quality=80&type=jpg',
// fileType: 1
// },
// {
// fileName: '测试3.xlsx',
// fileUrl: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F1fea91a0-d088-409e-b145-e0e61254b28b%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1700031689&t=2e0fe7d1de7f54adff3007efe133d67c',
// fileType: 1
// },
// {
// fileName: '测试4.xlsx',
// fileUrl: 'https://pics5.baidu.com/feed/b7003af33a87e950cdfb4b4546eed044faf2b40d.jpeg?token=1d7484cfe4b014dd201f8c8725cab945',
// fileType: 2
// },
// {
// fileName: '测试5.xlsx',
// fileUrl: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fe3500876-9c46-4b70-8d37-4799520cdd13%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1700031689&t=4abc1df930e62730e5361a7d3765e0f2',
// fileType: 2
// },
// ],
// tvalue: 0,
// processingTime: 0,
// manufacturer: '',
// spec: '',
// description: '描述',
// };
this.showForm = true; this.showForm = true;
this.infoQuery = query; this.infoQuery = query;
console.log('setting form: ', this.form, data);
} else if (section.key == 'attrs') { } else if (section.key == 'attrs') {
this.attrQuery = query; this.attrQuery = query;
this.list = data.list; this.list = data.list;
@@ -231,6 +296,24 @@ export default {
break; break;
} }
}, },
async handleConfirm() {
this.btnLoading = true;
this.syncFileListFlag = Math.random();
this.$nextTick(async () => {
const { code, data } = await this.$axios({
url: this.sections[0].urlUpdate,
method: 'put',
data: this.form,
});
if (code == 0) {
this.$modal.msgSuccess('更新成功');
}
this.btnLoading = false;
this.handleCancel();
});
},
handleEmitFun(val) { handleEmitFun(val) {
console.log('handleEmitFun', val); console.log('handleEmitFun', val);
@@ -273,7 +356,7 @@ export default {
// 开启编辑 // 开启编辑
toggleEdit() { toggleEdit() {
this.mode = 'edit'; this.$emit('update-mode', 'edit');
}, },
// 新增属性 // 新增属性
@@ -331,33 +414,44 @@ export default {
}, },
// 提交属性表 // 提交属性表
async submitAttrForm() { submitAttrForm() {
this.$refs['attrForm'].validate((valid) => { this.$refs['attrForm'].validate(async (valid) => {
if (!valid) { if (!valid) {
return; return;
} }
});
console.log('this.attrform', this.attrForm);
const isEdit = this.attrForm.id != null;
this.attrFormSubmitting = true;
const res = await this.$axios({
url: isEdit ? this.sections[1].urlUpdate : this.sections[1].urlCreate,
method: isEdit ? 'put' : 'post',
data: this.attrForm,
});
if (res.code == 0) { try {
this.closeAttrForm(); const isEdit = this.attrForm.id != null;
this.$message({ this.attrFormSubmitting = true;
message: `${isEdit ? '更新' : '创建'}成功`, const res = await this.$axios({
type: 'success', url: isEdit
duration: 1500, ? this.sections[1].urlUpdate
onClose: () => { : this.sections[1].urlCreate,
this.getAttrList(); method: isEdit ? 'put' : 'post',
}, data: this.attrForm,
}); });
}
this.attrFormSubmitting = false; if (res.code == 0) {
this.closeAttrForm();
this.$message({
message: `${isEdit ? '更新' : '创建'}成功`,
type: 'success',
duration: 1500,
onClose: () => {
this.getAttrList();
},
});
}
this.attrFormSubmitting = false;
} catch (err) {
this.$message({
message: err,
type: 'error',
duration: 1500,
});
this.attrFormSubmitting = false;
}
});
}, },
closeAttrForm() { closeAttrForm() {

View File

@@ -0,0 +1,337 @@
<!--
filename: dialogForm.vue
author: liubin
date: 2023-08-15 10:32:36
description: 弹窗的表单组件
-->
<template>
<el-form
class="equipment-info-form"
ref="form"
:model="form"
label-width="200px"
label-position="top"
v-loading="formLoading">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item
label="设备名称"
prop="name"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-input
v-model="form.name"
:disabled="disabled"
placeholder="请输入设备名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="设备编码" prop="code" :rules="[]">
<el-input
v-model="form.code"
:disabled="disabled"
placeholder="请输入设备编码"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="英文名称" prop="enName" :rules="[]">
<el-input
v-model="form.enName"
:disabled="disabled"
placeholder="请输入英文名称"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="缩写" prop="abbr" :rules="[]">
<el-input
v-model="form.abbr"
:disabled="disabled"
placeholder="请输入名称缩写"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="设备类型" prop="equipmentTypeId" :rules="[]">
<el-select
v-model="form.equipmentTypeId"
:disabled="disabled"
filterable
placeholder="请选择设备类型">
<el-option
v-for="eqType in eqTypeList"
:key="eqType.id"
:label="eqType.name"
:value="eqType.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="备注" prop="remark" :rules="[]">
<el-input
v-model="form.remark"
:disabled="disabled"
placeholder="请输入备注"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="生产日期" prop="productionTime" :rules="[]">
<el-date-picker
v-model="form.enterTime"
:disabled="disabled"
type="datetime"
placeholder="请选择生产日期"
value-format="timestamp"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="进场日期" prop="enterTime" :rules="[]">
<el-date-picker
v-model="form.enterTime"
:disabled="disabled"
type="datetime"
placeholder="请选择进场日期"
value-format="timestamp"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="设备TT值"
prop="tvalue"
:rules="[
{ required: true, message: '不能为空', trigger: 'blur' },
{
type: 'number',
message: '请输入正确的数字值',
trigger: 'blur',
transform: (val) => Number(val),
},
]">
<el-input
v-model="form.tvalue"
:disabled="disabled"
placeholder="请输入设备TT值"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item
label="产品加工时间(s)"
prop="processingTime"
:rules="[
{ required: true, message: '不能为空', trigger: 'blur' },
{
type: 'number',
message: '请输入正确的数字值',
trigger: 'blur',
transform: (val) => Number(val),
},
]">
<el-input
v-model="form.processingTime"
:disabled="disabled"
placeholder="请输入产品加工时间"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="制造商" prop="manufacturer" :rules="[]">
<el-input
v-model="form.manufacturer"
:disabled="disabled"
placeholder="请输入制造商"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="设备规格" prop="spec" :rules="[]">
<el-input
v-model="form.spec"
:disabled="disabled"
placeholder="请输入设备规格"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<!-- 功能描述 -->
<el-col>
<el-form-item label="功能描述" prop="description" :rules="[]">
<el-input
type="textarea"
:disabled="disabled"
v-model="form.description"
placeholder="请填写功能描述"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<!-- 上传资料 -->
<el-col>
<el-form-item label="上传资料" prop="assets" :rules="[]">
<AssetsUpload v-model="form.assets" :disabled="disabled" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<!-- 上传图片 -->
<el-col>
<el-form-item label="上传图片" prop="pics" :rules="[]">
<AssetsUpload
:is-pic-mode="true"
v-model="form.pics"
:disabled="disabled" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
import AssetsUpload from './AssetsUpload.vue';
export default {
name: 'EquipmentInfoForm',
model: {
prop: 'dataForm',
event: 'update',
},
emits: ['update'],
components: { AssetsUpload },
props: {
dataForm: {
type: Object,
default: () => ({}),
},
disabled: {
type: Boolean,
default: false,
},
syncFilelist: {
type: Number,
default: null,
required: false,
},
},
data() {
return {
formLoading: false,
form: {
name: '',
code: '',
enName: '',
abbr: '',
equipmentTypeId: '',
remark: '',
productionTime: '',
enterTime: '',
tvalue: '',
processingTime: '',
manufacturer: '',
spec: '',
description: '',
assets: [],
pics: [],
},
eqTypeList: [],
dataLoaded: false,
};
},
watch: {
dataForm: {
handler(val) {
// debugger;
this.form = JSON.parse(JSON.stringify(val));
this.form.assets =
this.form.files?.filter((item) => item.fileType == '2') || [];
this.form.pics =
this.form.files?.filter((item) => item.fileType == '1') || [];
delete this.form.files;
},
immediate: true,
deep: true,
},
syncFilelist: {
handler(val) {
if (val != null) {
this.updateForm();
}
},
immediate: true,
},
},
mounted() {
this.getEqTypeList();
},
methods: {
updateForm() {
console.log('update form ==> ');
this.form.files = [...this.form.assets, ...this.form.pics];
delete this.form.assets;
delete this.form.pics;
this.$emit('update', this.form);
},
async getEqTypeList() {
this.formLoading = true;
const { code, data } = await this.$axios(
'/base/equipment-type/page?pageNo=1&pageSize=100'
);
// debugger;
if (code == 0) {
this.eqTypeList = data.list;
}
this.formLoading = false;
},
/** 模拟透传 ref */
validate(cb) {
return this.$refs.form.validate(cb);
},
resetFields(args) {
return this.$refs.form.resetFields(args);
},
// getCode
async getCode(url) {
const response = await this.$axios(url);
return response.data;
},
// 上传成功的特殊处理
beforeUpload() {},
// 上传前的验证规则可通过 bind 属性传入
handleUploadSuccess(response, file, fileList) {
console.log(
'[dialogForm:handleUploadSuccess]',
response,
file,
fileList,
this.form
);
// 保存原始文件名
if ('fileNames' in this.form) this.form.fileNames.push(file.name);
// 保存完整地址
if ('fileUrls' in this.form) this.form.fileUrls.push(response.data);
this.$modal.msgSuccess('上传成功');
},
getFileName(fileUrl) {
return fileUrl.split('/').pop();
},
},
};
</script>
<style scoped lang="scss">
.el-date-editor,
.el-select {
width: 100%;
}
</style>

View File

@@ -36,13 +36,16 @@
:dialogVisible="open" :dialogVisible="open"
@close="cancel" @close="cancel"
@cancel="cancel" @cancel="cancel"
width="60%"
@confirm="submitForm"> @confirm="submitForm">
<DialogForm <DialogForm
v-if="open" v-if="open"
key="index-dialog-form" key="index-dialog-form"
ref="form" ref="form"
label-position="top"
size="small"
:dataForm="form" :dataForm="form"
:rows="rows" /> :rows="computedRows" />
</base-dialog> </base-dialog>
<!-- 设备 详情 - 编辑 --> <!-- 设备 详情 - 编辑 -->
@@ -50,12 +53,13 @@
v-if="editVisible" v-if="editVisible"
ref="drawer" ref="drawer"
:mode="editMode" :mode="editMode"
@update-mode="editMode = $event"
:data-id="form.id" :data-id="form.id"
:sections="[ :sections="[
{ {
name: '基本信息', name: '基本信息',
key: 'base', key: 'base',
rows: rows, rows: computedRows,
url: '/base/equipment/get', url: '/base/equipment/get',
urlUpdate: '/base/equipment/update', urlUpdate: '/base/equipment/update',
urlCreate: '/base/equipment/create', urlCreate: '/base/equipment/create',
@@ -93,8 +97,8 @@
}, },
]" ]"
@refreshDataList="getList" @refreshDataList="getList"
@cancel="editVisible = false" @cancel="cancelEdit"
@destroy="editVisible = false" /> @destroy="cancelEdit" />
</div> </div>
</template> </template>
@@ -115,6 +119,7 @@ import {
exportEquipmentExcel, exportEquipmentExcel,
} from '@/api/base/equipment'; } from '@/api/base/equipment';
import Editor from '@/components/Editor'; import Editor from '@/components/Editor';
import AssetsUpload from './components/AssetsUpload.vue';
export default { export default {
name: 'Equipment', name: 'Equipment',
@@ -139,6 +144,12 @@ export default {
btnName: '删除', btnName: '删除',
} }
: undefined, : undefined,
this.$auth.hasPermi(`base:equipment:update`)
? {
type: 'detail',
btnName: '详情',
}
: undefined,
].filter((v) => v), ].filter((v) => v),
tableProps: [ tableProps: [
{ {
@@ -149,36 +160,36 @@ export default {
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'name', label: '设备名称' }, { prop: 'name', label: '设备名称' },
{ width: 256, prop: 'code', label: '检测编码' }, { width: 256, prop: 'code', label: '设备编码' },
{ prop: 'equipmentType', label: '设备类型' }, { prop: 'equipmentType', label: '设备类型' },
{ prop: 'enName', label: '英文名称' }, { prop: 'enName', label: '英文名称' },
{ prop: 'abbr', label: '缩写' }, { prop: 'abbr', label: '缩写' },
{ // {
action: 'show-detail', // action: 'show-detail',
label: '详情', // label: '详情',
subcomponent: { // subcomponent: {
props: ['injectData'], // props: ['injectData'],
render: function (h) { // render: function (h) {
const _this = this; // const _this = this;
return h( // return h(
'el-button', // 'el-button',
{ // {
props: { type: 'text', size: 'mini' }, // props: { type: 'text', size: 'mini' },
on: { // on: {
click: function () { // click: function () {
console.log('inejctdata', _this.injectData); // console.log('inejctdata', _this.injectData);
_this.$emit('emitData', { // _this.$emit('emitData', {
action: _this.injectData.action, // action: _this.injectData.action,
value: _this.injectData.id, // value: _this.injectData.id,
}); // });
}, // },
}, // },
}, // },
'查看详情' // '查看详情'
); // );
}, // },
}, // },
}, // },
], ],
searchBarFormConfig: [ searchBarFormConfig: [
{ {
@@ -233,8 +244,6 @@ export default {
prop: 'code', prop: 'code',
url: '/base/equipment/getCode', url: '/base/equipment/getCode',
}, },
],
[
{ {
input: true, input: true,
label: '英文名称', label: '英文名称',
@@ -244,6 +253,8 @@ export default {
// disabled: true, // some condition, like detail mode... // disabled: true, // some condition, like detail mode...
// } // }
}, },
],
[
{ {
input: true, input: true,
label: '缩写', label: '缩写',
@@ -253,13 +264,15 @@ export default {
// disabled: true, // some condition, like detail mode... // disabled: true, // some condition, like detail mode...
// } // }
}, },
],
[
{ {
select: true, select: true,
label: '设备类型', label: '设备类型',
prop: 'equipmentTypeId', prop: 'equipmentTypeId',
url: '/base/equipment-type/page?pageNo=1&pageSize=100', url: '/base/equipment-type/page?pageNo=1&pageSize=100',
bind: {
filterable: true,
},
}, },
// { // {
// select: true, // select: true,
@@ -280,8 +293,6 @@ export default {
label: '进厂日期', label: '进厂日期',
prop: 'enterTime', prop: 'enterTime',
}, },
],
[
{ {
input: true, input: true,
prop: 'tvalue', prop: 'tvalue',
@@ -296,11 +307,14 @@ export default {
}, },
], ],
}, },
],
[
{ {
input: true, input: true,
label: '产品加工时间', label: '产品加工时间s',
prop: 'processingTime', prop: 'processingTime',
rules: [ rules: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ {
type: 'number', type: 'number',
message: '请输入正确的数字值', message: '请输入正确的数字值',
@@ -309,8 +323,7 @@ export default {
}, },
], ],
}, },
],
[
{ {
input: true, input: true,
label: '制造商', label: '制造商',
@@ -331,40 +344,79 @@ export default {
prop: 'description', prop: 'description',
}, },
], ],
[ // [
{ // {
upload: true, // assetUpload: true,
label: '上传资料', // label: '上传资料',
prop: 'uploadFiles', // fieldName: 'assets',
url: process.env.VUE_APP_BASE_API + '/admin-api/infra/file/upload', // 请求地址 // subcomponent: AssetsUpload
bind: { // },
headers: { Authorization: 'Bearer ' + getAccessToken() }, // ],
'show-file-list': false, // [
}, // {
}, // assetUpload: true,
], // label: '上传图片',
[ // fieldName: 'images',
{ // subcomponent: AssetsUpload
diy: true, // },
key: 'eq-assets', // ],
label: '设备资料', // [
prop: 'fileNames', // {
subcomponent: EquipmentAssets, // upload: true,
}, // label: '上传资料',
], // prop: 'uploadFiles',
[ // url: process.env.VUE_APP_BASE_API + '/admin-api/infra/file/upload', // 请求地址
{ // uploadFnName: 'assetsUpload', // 上传方法名
diy: true, // bind: {
key: 'eq-pics', // headers: { Authorization: 'Bearer ' + getAccessToken() },
label: '设备图片', // 'show-file-list': false,
prop: 'fileUrls', // },
subcomponent: EquipmentPics, // },
pictures: async () => { // {
// some async request // diy: true,
return []; // key: 'eq-assets',
}, // label: '设备资料',
}, // prop: 'fileNames',
], // subcomponent: EquipmentAssets,
// },
// ],
// [
// {
// upload: true,
// label: '上传图片',
// prop: 'uploadImages',
// url: process.env.VUE_APP_BASE_API + '/admin-api/infra/file/upload', // 请求地址
// uploadFnName: 'imagesUpload', // 上传方法名
// bind: {
// headers: { Authorization: 'Bearer ' + getAccessToken() },
// 'show-file-list': false,
// },
// },
// {
// diy: true,
// key: 'eq-pics',
// label: '设备图片',
// prop: 'fileUrls',
// subcomponent: EquipmentPics,
// pictures: async () => {
// // some async request
// return [];
// },
// },
// ],
// [
// {
// diy: true,
// key: 'eq-pics',
// label: '设备图片',
// prop: 'fileUrls',
// subcomponent: EquipmentPics,
// pictures: async () => {
// // some async request
// return [];
// },
// },
// ],
], ],
editVisible: false, editVisible: false,
editMode: 'edit', // 'edit', 'detail' editMode: 'edit', // 'edit', 'detail'
@@ -393,11 +445,49 @@ export default {
form: { form: {
id: null, id: null,
}, },
showUploadComponents: false, // 是否显示上传组件
}; };
}, },
created() { created() {
this.getList(); this.getList();
}, },
computed: {
computedRows() {
return this.showUploadComponents
? [
...this.rows,
[
{
assetUpload: true,
key: 'eq-assets', // 用于区分不同的上传组件
label: '上传资料',
fieldName: 'assets',
subcomponent: AssetsUpload,
prop: 'uploadedAssets',
default: [],
bind: {
'is-pic-mode': false,
},
},
],
[
{
assetUpload: true,
key: 'eq-pics', // 用于区分不同的上传组件
label: '上传图片',
fieldName: 'images',
subcomponent: AssetsUpload,
// prop: '',
// default: [],
bind: {
'is-pic-mode': true,
},
},
],
]
: this.rows;
},
},
methods: { methods: {
/** 查询列表 */ /** 查询列表 */
getList() { getList() {
@@ -414,6 +504,10 @@ export default {
this.open = false; this.open = false;
this.reset(); this.reset();
}, },
cancelEdit() {
this.showUploadComponents = false;
this.editVisible = false;
},
/** 表单重置 */ /** 表单重置 */
reset() { reset() {
this.form = { this.form = {
@@ -439,11 +533,13 @@ export default {
handleAdd() { handleAdd() {
this.reset(); this.reset();
this.open = true; this.open = true;
this.showUploadComponents = false;
this.title = '添加设备'; this.title = '添加设备';
}, },
/** 修改按钮操作 */ /** 修改按钮操作 */
handleUpdate(row) { handleUpdate(row) {
this.reset(); this.reset();
this.showUploadComponents = true;
const id = row.id; const id = row.id;
getEquipment(id).then((response) => { getEquipment(id).then((response) => {
this.form = response.data; this.form = response.data;
@@ -478,7 +574,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm('是否确认删除设备编号为"' + id + '"的数据项?') .confirm('是否确认删除设备名称为"' + row.name + '"的数据项?')
.then(function () { .then(function () {
return deleteEquipment(id); return deleteEquipment(id);
}) })
@@ -510,6 +606,7 @@ export default {
viewDetail(id) { viewDetail(id) {
this.reset(); this.reset();
this.editMode = 'detail'; this.editMode = 'detail';
this.showUploadComponents = true;
this.form.id = id; this.form.id = id;
this.editVisible = true; this.editVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
@@ -518,11 +615,11 @@ export default {
}, },
// overwrite basicPageMixin 里的 处理表格按钮 方法 // overwrite basicPageMixin 里的 处理表格按钮 方法
handleTableBtnClick({ data, type }) { handleTableBtnClick({ data, type }) {
console.log('[handleTableBtnClick]', data, type);
switch (type) { switch (type) {
case 'edit': case 'edit':
this.reset(); this.reset();
this.editMode = 'edit'; this.editMode = 'edit';
this.showUploadComponents = true;
this.form.id = data.id; this.form.id = data.id;
this.editVisible = true; this.editVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
@@ -532,6 +629,10 @@ export default {
case 'delete': case 'delete':
this.handleDelete(data); this.handleDelete(data);
break; break;
case 'detail':
const { id } = data;
this.viewDetail(id);
break;
} }
}, },
}, },

View File

@@ -37,7 +37,7 @@
@close="cancel" @close="cancel"
@cancel="cancel" @cancel="cancel"
@confirm="submitForm"> @confirm="submitForm">
<DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" /> <DialogForm v-if="open" ref="form" v-model="form" :rows="rows" />
</base-dialog> </base-dialog>
</div> </div>
</template> </template>
@@ -55,7 +55,7 @@ import {
exportEquipmentTypeExcel, exportEquipmentTypeExcel,
} from '@/api/base/equipmentType'; } from '@/api/base/equipmentType';
import { getAccessToken } from '@/utils/auth'; // import { getAccessToken } from '@/utils/auth';
export default { export default {
name: 'EquipmentType', name: 'EquipmentType',
@@ -86,9 +86,9 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'name', label: '设备类型名称', }, { prop: 'name', label: '设备类型名称' },
{ prop: 'code', label: '检测类型编码', }, { prop: 'code', label: '检测类型编码' },
{ prop: 'remark', label: '备注', }, { prop: 'remark', label: '备注' },
], ],
searchBarFormConfig: [ searchBarFormConfig: [
{ {
@@ -149,15 +149,13 @@ export default {
prop: 'parentId', prop: 'parentId',
url: '/base/equipment-type/page?pageNo=1&pageSize=100', url: '/base/equipment-type/page?pageNo=1&pageSize=100',
}, },
{},
],
[
{ {
upload: true, upload: true,
label: '上传资料', label: '上传资料',
prop: 'uploadFiles', prop: 'files',
url: process.env.VUE_APP_BASE_API + '/admin-api/infra/file/upload', // 请求地址
bind: {
headers: { Authorization: 'Bearer ' + getAccessToken() },
'show-file-list': false,
},
}, },
], ],
[{ input: true, label: '备注', prop: 'remark' }], [{ input: true, label: '备注', prop: 'remark' }],
@@ -236,6 +234,25 @@ export default {
const id = row.id; const id = row.id;
getEquipmentType(id).then((response) => { getEquipmentType(id).then((response) => {
this.form = response.data; this.form = response.data;
// this.form = {
// code: 'SBLX20230925184444000041',
// name: '测试131',
// remark: '测试可删除',
// id: '1706258479729336322',
// files: [
// { fileName: '1.png', fileUrl: '', fileType: 2 },
// { fileName: '1.asdfaslkjfkasdf.png', fileUrl: '', fileType: 2 },
// { fileName: '2.txt', fileUrl: '', fileType: 2 },
// { fileName: '1.rar', fileUrl: '', fileType: 2 },
// { fileName: '1.kkk', fileUrl: '', fileType: 2 },
// { fileName: 'test.file', fileUrl: '', fileType: 2 },
// { fileName: '222', fileUrl: '', fileType: 2 },
// { fileName: 'g', fileUrl: '', fileType: 2 },
// ],
// createTime: 1695638697000,
// parentId: '1701869972319584257',
// };
// debugger;
this.open = true; this.open = true;
this.title = '修改设备类型'; this.title = '修改设备类型';
}); });
@@ -267,7 +284,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm('是否确认删除设备类型编号为"' + id + '"的数据项?') .confirm('是否确认删除设备类型"' + row.name + '"?')
.then(function () { .then(function () {
return deleteEquipmentType(id); return deleteEquipmentType(id);
}) })

View File

@@ -1,8 +1,8 @@
<!-- <!--
* @Author: zwq * @Author: zwq
* @Date: 2021-11-18 14:16:25 * @Date: 2021-11-18 14:16:25
* @LastEditors: zwq * @LastEditors: DY
* @LastEditTime: 2023-08-01 16:59:06 * @LastEditTime: 2023-10-16 11:16:48
* @Description: * @Description:
--> -->
<template> <template>
@@ -12,21 +12,30 @@
ref="dataForm" ref="dataForm"
@keyup.enter.native="dataFormSubmit()" @keyup.enter.native="dataFormSubmit()"
label-width="80px"> label-width="80px">
<el-form-item label="工厂编码" prop="code"> <el-row :gutter="20">
<el-input <el-col :span="12">
v-model="dataForm.code" <el-form-item label="工厂编码" prop="code">
clearable <el-input
placeholder="请输入工厂编码" /> v-model="dataForm.code"
</el-form-item> clearable
<el-form-item label="工厂名称" prop="name"> placeholder="请输入工厂编码" />
<el-input </el-form-item>
v-model="dataForm.name" </el-col>
clearable <el-col :span="12">
placeholder="请输入工厂名称" /> <el-form-item label="工厂名称" prop="name">
</el-form-item> <el-input
<el-form-item label="地址" prop="address"> v-model="dataForm.name"
<el-input v-model="dataForm.address" clearable placeholder="请输入地址" /> clearable
</el-form-item> placeholder="请输入工厂名称" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="地址" prop="address">
<el-input v-model="dataForm.address" clearable placeholder="请输入地址" />
</el-form-item>
</el-col>
<!-- <el-form-item label="启用状态" prop="enabled"> <!-- <el-form-item label="启用状态" prop="enabled">
<el-select <el-select
v-model="dataForm.enabled" v-model="dataForm.enabled"
@@ -38,9 +47,12 @@
:value="dict.value" /> :value="dict.value" />
</el-select> </el-select>
</el-form-item> --> </el-form-item> -->
<el-form-item label="备注" prop="remark"> <el-col :span="12">
<el-input v-model="dataForm.remark" clearable placeholder="请输入备注" /> <el-form-item label="备注" prop="remark">
</el-form-item> <el-input v-model="dataForm.remark" clearable placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
</template> </template>

View File

@@ -29,7 +29,7 @@
@cancel="handleCancel" @cancel="handleCancel"
@confirm="handleConfirm" @confirm="handleConfirm"
:before-close="handleCancel" :before-close="handleCancel"
width="70%"> width="50%">
<add-or-update <add-or-update
ref="addOrUpdate" ref="addOrUpdate"
@refreshDataList="successSubmit"></add-or-update> @refreshDataList="successSubmit"></add-or-update>
@@ -50,29 +50,24 @@ import {
const tableProps = [ const tableProps = [
{ {
prop: 'code', prop: 'code',
label: '工厂编码', label: '工厂编码'
align: 'center', },
},
{ {
prop: 'name', prop: 'name',
label: '工厂名称', label: '工厂名称'
align: 'center',
}, },
{ {
prop: 'address', prop: 'address',
label: '地址', label: '地址'
align: 'center',
}, },
{ {
prop: 'remark', prop: 'remark',
label: '备注', label: '备注'
align: 'center',
}, },
{ {
prop: 'createTime', prop: 'createTime',
label: '创建时间', label: '创建时间',
align: 'center', filter: parseTime
filter: parseTime,
}, },
]; ];
@@ -120,14 +115,14 @@ export default {
name: 'search', name: 'search',
color: 'primary', color: 'primary',
}, },
{ // {
type: 'separate', // type: 'separate',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '重置', // btnName: '重置',
name: 'reset', // name: 'reset',
}, // },
{ {
type: 'separate', type: 'separate',
}, },

View File

@@ -1,16 +1,15 @@
<!-- <!--
* @Author: zwq * @Author: zwq
* @Date: 2023-08-02 15:12:42 * @Date: 2023-08-02 15:12:42
* @LastEditors: zwq * @LastEditors: DY
* @LastEditTime: 2023-08-03 14:24:36 * @LastEditTime: 2023-10-13 16:35:03
* @Description: * @Description:
--> -->
<template> <template>
<div class="app-container"> <div class="app-container">
<search-bar <search-bar
:formConfigs="formConfig" :formConfigs="[{ label: '产线在制产品', type: 'title' }]"
ref="searchBarForm" ref="searchBarForm" />
@headBtnClick="buttonClick" />
<base-table <base-table
v-loading="dataListLoading" v-loading="dataListLoading"
:table-props="tableProps" :table-props="tableProps"
@@ -36,20 +35,17 @@ import { getProductPage } from '@/api/core/base/product';
const tableProps = [ const tableProps = [
{ {
prop: 'lineName', prop: 'lineName',
label: '产线', label: '产线'
align: 'center',
}, },
{ {
prop: 'productName', prop: 'productName',
label: '在制产品', label: '在制产品',
align: 'center',
list: [], list: [],
subcomponent: selectProduct, subcomponent: selectProduct,
}, },
{ {
prop: 'recordTime', prop: 'recordTime',
label: '开始时间', label: '开始时间',
align: 'center',
filter: parseTime, filter: parseTime,
}, },
]; ];
@@ -63,14 +59,14 @@ export default {
}, },
tableProps, tableProps,
tableData: [], tableData: [],
formConfig: [ // formConfig: [
{ // {
type: 'button', // type: 'button',
btnName: '同步', // btnName: '同步',
name: 'search', // name: 'search',
color: 'primary', // color: 'primary',
}, // },
], // ],
}; };
}, },
components: {}, components: {},
@@ -81,6 +77,7 @@ export default {
}; };
getProductPage(params).then((response) => { getProductPage(params).then((response) => {
this.tableProps[1].list = response.data.list; this.tableProps[1].list = response.data.list;
console.log('打印', this.tableProps[1].list)
}); });
}, },
methods: { methods: {

View File

@@ -1,13 +1,12 @@
<!-- <!--
* @Author: zwq * @Author: zwq
* @Date: 2023-08-03 14:09:18 * @Date: 2023-08-03 14:09:18
* @LastEditors: zwq * @LastEditors: DY
* @LastEditTime: 2023-08-03 14:51:16 * @LastEditTime: 2023-10-13 16:47:25
* @Description: * @Description:
--> -->
<template> <template>
<div class="tableInner"> <div class="tableInner">
<el-input readonly v-model="list.productName" style="width: 50%;" ></el-input>
<el-popover <el-popover
placement="top" placement="top"
title="切换在制产品" title="切换在制产品"
@@ -28,8 +27,11 @@
确定 确定
</el-button> </el-button>
</div> </div>
<el-button type="text" slot="reference">切换</el-button> <el-button type="text" slot="reference">
<svg-icon icon-class="changelogo"/>
</el-button>
</el-popover> </el-popover>
<el-input readonly v-model="list.productName" style="width: 50%;margin-left: 5px" ></el-input>
</div> </div>
</template> </template>
<script> <script>
@@ -48,6 +50,9 @@ export default {
visible: false, visible: false,
}; };
}, },
mounted() {
console.log('hello', this.list)
},
methods: { methods: {
changeInput() { changeInput() {
const data = { const data = {
@@ -58,6 +63,7 @@ export default {
switchLineBindProduct(data).then((response) => { switchLineBindProduct(data).then((response) => {
this.$modal.msgSuccess('修改成功'); this.$modal.msgSuccess('修改成功');
this.visible = false; this.visible = false;
this.list.productName = this.list.string.split('+')[1]
this.$emit('emitData'); this.$emit('emitData');
}); });
}, },

View File

@@ -28,24 +28,20 @@ import { getProductPage } from '@/api/core/base/product';
const tableProps = [ const tableProps = [
{ {
prop: 'productionLineName', prop: 'productionLineName',
label: '产线', label: '产线'
align: 'center',
}, },
{ {
prop: 'productName', prop: 'productName',
label: '在制产品', label: '在制产品'
align: 'center',
}, },
{ {
prop: 'startTime', prop: 'startTime',
label: '开始时间', label: '开始时间',
align: 'center',
filter: parseTime, filter: parseTime,
}, },
{ {
prop: 'endTime', prop: 'endTime',
label: '结束时间', label: '结束时间',
align: 'center',
filter: parseTime, filter: parseTime,
}, },
]; ];
@@ -86,7 +82,8 @@ export default {
rangeSeparator: '-', rangeSeparator: '-',
startPlaceholder: '开始时间', startPlaceholder: '开始时间',
endPlaceholder: '结束时间', endPlaceholder: '结束时间',
param: 'createTime', param: 'startTime',
valueFormat: 'timestamp'
}, },
{ {
type: 'button', type: 'button',
@@ -126,7 +123,7 @@ export default {
this.listQuery.pageSize = 10; this.listQuery.pageSize = 10;
this.listQuery.productionLineId = val.productionLineId; this.listQuery.productionLineId = val.productionLineId;
this.listQuery.productId = val.productId; this.listQuery.productId = val.productId;
this.listQuery.createTime = val.createTime; this.listQuery.startTime = val.startTime;
this.getDataList(); this.getDataList();
break; break;
case 'reset': case 'reset':

View File

@@ -43,7 +43,7 @@
<el-form-item label="产品类型" prop="typeDictValue"> <el-form-item label="产品类型" prop="typeDictValue">
<el-select <el-select
v-model="dataForm.typeDictValue" v-model="dataForm.typeDictValue"
style="width: 100%" style="width: 100%"
:disabled="isdetail" :disabled="isdetail"
placeholder="请选择产品类型"> placeholder="请选择产品类型">
<el-option <el-option
@@ -58,7 +58,7 @@
<el-form-item label="单位" prop="unitDictValue"> <el-form-item label="单位" prop="unitDictValue">
<el-select <el-select
v-model="dataForm.unitDictValue" v-model="dataForm.unitDictValue"
style="width: 100%" style="width: 100%"
:disabled="isdetail" :disabled="isdetail"
placeholder="请选择单位"> placeholder="请选择单位">
<el-option <el-option
@@ -83,8 +83,8 @@
<el-form-item label="单位平方数" prop="area"> <el-form-item label="单位平方数" prop="area">
<el-input <el-input
:disabled="isdetail" :disabled="isdetail"
v-model="dataForm.area" v-model="dataForm.area"
placeholder="请输入单位平方数" /> placeholder="请输入单位平方数" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -111,6 +111,8 @@
:table-props="tableProps" :table-props="tableProps"
:page="listQuery.pageNo" :page="listQuery.pageNo"
:limit="listQuery.pageSize" :limit="listQuery.pageSize"
:add-button-show="isdetail ? null : '添加属性'"
@emitButtonClick="addNew()"
:table-data="productAttributeList"> :table-data="productAttributeList">
<method-btn <method-btn
v-if="!isdetail" v-if="!isdetail"
@@ -123,15 +125,15 @@
<pagination <pagination
v-show="listQuery.total > 0" v-show="listQuery.total > 0"
:total="listQuery.total" :total="listQuery.total"
:page.sync="listQuery.current" :page.sync="listQuery.pageNo"
:limit.sync="listQuery.size" :limit.sync="listQuery.pageSize"
:page-sizes="[5, 10, 15]" :page-sizes="[5, 10, 15]"
@pagination="getList" /> @pagination="getList" />
</div> </div>
</div> </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 style="margin-right: 10px" @click="goback()">返回</el-button>
<el-button v-if="isdetail" type="primary" @click="goEdit()"> <el-button v-if="isdetail" type="primary" @click="goEdit()">
编辑 编辑
@@ -145,6 +147,14 @@
添加属性 添加属性
</el-button> </el-button>
</span> </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>
<product-attr-add <product-attr-add
@@ -250,12 +260,12 @@ export default {
}, },
], ],
area: [ area: [
// { {
// type: 'float', type: 'number',
// message: '单位平方数为浮点类型', message: '请输入正确的数值',
// trigger: 'blur', trigger: 'change',
// transfom: 'val => Float(val)', transform: (val) => Number(val),
// }, },
], ],
processTime: [ processTime: [
{ {
@@ -263,12 +273,12 @@ export default {
message: '完成单位产品用时不能为空', message: '完成单位产品用时不能为空',
trigger: 'blur', trigger: 'blur',
}, },
// { {
// type: 'number', type: 'number',
// message: '完成单位产品用时为浮点类型', message: '请输入正确的数值',
// trigger: 'blur', trigger: 'blur',
// transfom: 'val => Number(val)', transform: (val) => Number(val),
// }, },
], ],
}, },
isdetail: false, isdetail: false,
@@ -277,6 +287,7 @@ export default {
methods: { methods: {
initData() { initData() {
this.productAttributeList.splice(0); this.productAttributeList.splice(0);
this.listQuery.total = 0;
}, },
init(id, isdetail) { init(id, isdetail) {
this.initData(); this.initData();
@@ -304,12 +315,10 @@ export default {
getList() { getList() {
// 获取产品的属性列表 // 获取产品的属性列表
const params = { getProductAttrPage({
pageSize: 10, ...this.listQuery,
pageNo: 1,
productId: this.dataForm.id, productId: this.dataForm.id,
}; }).then((response) => {
getProductAttrPage(params).then((response) => {
this.productAttributeList = response.data.list; this.productAttributeList = response.data.list;
this.listQuery.total = response.data.total; this.listQuery.total = response.data.total;
}); });
@@ -390,6 +399,8 @@ export default {
<style scoped> <style scoped>
.drawer >>> .el-drawer { .drawer >>> .el-drawer {
border-radius: 8px 0 0 8px; border-radius: 8px 0 0 8px;
display: flex;
flex-direction: column;
} }
.drawer >>> .el-form-item__label { .drawer >>> .el-form-item__label {
@@ -400,14 +411,20 @@ export default {
margin: 0; margin: 0;
padding: 32px 32px 24px; padding: 32px 32px 24px;
border-bottom: 1px solid #dcdfe6; border-bottom: 1px solid #dcdfe6;
margin-bottom: 30px; }
.drawer >>> .el-drawer__body {
flex: 1;
height: 1px;
display: flex;
flex-direction: column;
} }
.drawer >>> .content { .drawer >>> .content {
padding: 0 24px 30px; padding: 30px 24px;
flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; /* height: 100%; */
} }
.drawer >>> .visual-part { .drawer >>> .visual-part {
@@ -422,4 +439,10 @@ export default {
.drawer >>> .attr-list { .drawer >>> .attr-list {
padding: 0 16px; padding: 0 16px;
} }
.drawer-body__footer {
display: flex;
justify-content: flex-end;
padding: 18px;
}
</style> </style>

View File

@@ -1,8 +1,8 @@
<!-- <!--
* @Author: zwq * @Author: zwq
* @Date: 2023-08-01 14:55:51 * @Date: 2023-08-01 14:55:51
* @LastEditors: zwq * @LastEditors: DY
* @LastEditTime: 2023-08-03 15:22:53 * @LastEditTime: 2023-10-13 10:27:00
* @Description: * @Description:
--> -->
<template> <template>
@@ -50,30 +50,25 @@ import {
const tableProps = [ const tableProps = [
{ {
prop: 'code', prop: 'code',
label: '产品编码', label: '产品编码'
align: 'center',
}, },
{ {
prop: 'name', prop: 'name',
label: '产品名称', label: '产品名称'
align: 'center',
}, },
{ {
prop: 'specifications', prop: 'specifications',
label: '规格', label: '规格'
align: 'center',
}, },
{ {
prop: 'unitDictValue', prop: 'unitDictValue',
label: '单位', label: '单位',
align: 'center',
subcomponent: unitDict, subcomponent: unitDict,
}, },
{ {
prop: 'createTime', prop: 'createTime',
label: '创建时间', label: '创建时间',
align: 'center', filter: parseTime
filter: parseTime,
}, },
]; ];
@@ -127,14 +122,14 @@ export default {
name: 'search', name: 'search',
color: 'primary', color: 'primary',
}, },
{ // {
type: 'separate', // type: 'separate',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '重置', // btnName: '重置',
name: 'reset', // name: 'reset',
}, // },
{ {
type: 'separate', type: 'separate',
}, },

View File

@@ -1,53 +1,90 @@
<!-- <!--
* @Author: zwq * @Author: zwq
* @Date: 2023-08-01 13:52:10 * @Date: 2023-08-01 13:52:10
* @LastEditors: zwq * @LastEditors: DY
* @LastEditTime: 2023-08-01 16:59:35 * @LastEditTime: 2023-09-21 15:45:07
* @Description: * @Description:
--> -->
<template> <template>
<el-form <el-form
class="dialog-inner__form"
:model="dataForm" :model="dataForm"
:rules="dataRule" :rules="dataRule"
ref="dataForm" ref="dataForm"
@keyup.enter.native="dataFormSubmit()" @keyup.enter.native="dataFormSubmit()"
label-width="90px"> label-width="90px">
<el-form-item label="产线编码" prop="code"> <el-row :gutter="20">
<el-input <el-col :span="12">
v-model="dataForm.code" <el-form-item label="产线编码" prop="code">
clearable <el-input
placeholder="请输入产线编码" /> v-model="dataForm.code"
</el-form-item> clearable
<el-form-item label="产线名称" prop="name"> placeholder="请输入产线编码" />
<el-input </el-form-item>
v-model="dataForm.name" </el-col>
clearable <el-col :span="12">
placeholder="请输入产线名称" /> <el-form-item label="产线名称" prop="name">
</el-form-item> <el-input
<el-form-item prop="factoryId" label="工厂名称"> v-model="dataForm.name"
<el-select clearable
v-model="dataForm.factoryId" filterable clearable placeholder="请选择工厂"> placeholder="请输入产线名称" />
<el-option </el-form-item>
v-for="item in urlOptions.optionArr.arr0" </el-col>
:key="item.id" </el-row>
:label="item.name" <el-row :gutter="20">
:value="item.id" <el-col :span="12">
> <el-form-item prop="factoryId" label="工厂名称">
</el-option> <el-select
</el-select> v-model="dataForm.factoryId"
</el-form-item> filterable
<el-form-item label="产线TT值(h)" prop="tvalue"> clearable
<el-input v-model="dataForm.tvalue" clearable placeholder="请输入每小时下片数量" /> placeholder="请选择工厂">
</el-form-item> <el-option
<el-form-item label="描述" prop="description"> v-for="item in urlOptions.optionArr.arr0"
<el-input :key="item.id"
v-model="dataForm.description" :label="item.name"
type="textarea" :value="item.id"></el-option>
placeholder="请输入内容" /> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> </el-col>
<el-input v-model="dataForm.remark" clearable placeholder="请输入备注" /> <el-col :span="12">
</el-form-item> <el-form-item label="产线TT值(h)" prop="tvalue">
<el-input
v-model="dataForm.tvalue"
clearable
placeholder="请输入每小时下片数量" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="额外编码" prop="externalCode">
<el-input
v-model="dataForm.externalCode"
clearable
placeholder="请输入额外编码" />
</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-row>
<el-col>
<el-form-item label="描述" prop="description">
<el-input
v-model="dataForm.description"
type="textarea"
placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
</template> </template>
@@ -59,22 +96,20 @@ import {
getProductionLine, getProductionLine,
getCode, getCode,
} from '@/api/core/base/productionLine'; } from '@/api/core/base/productionLine';
import { import { getFactoryPage } from '@/api/core/base/factory';
getFactoryPage,
} from '@/api/core/base/factory';
export default { export default {
mixins: [basicAdd], mixins: [basicAdd],
data() { data() {
return { return {
urlOptions: { urlOptions: {
getOption: true, getOption: true,
isGetCode: true, isGetCode: true,
codeURL: getCode, codeURL: getCode,
createURL: createProductionLine, createURL: createProductionLine,
updateURL: updateProductionLine, updateURL: updateProductionLine,
infoURL: getProductionLine, infoURL: getProductionLine,
optionArrUrl: [getFactoryPage], optionArrUrl: [getFactoryPage],
}, },
dataForm: { dataForm: {
id: undefined, id: undefined,
@@ -82,6 +117,7 @@ export default {
name: undefined, name: undefined,
tvalue: undefined, tvalue: undefined,
factoryId: undefined, factoryId: undefined,
externalCode: undefined,
remark: undefined, remark: undefined,
description: undefined, description: undefined,
}, },
@@ -92,13 +128,26 @@ export default {
name: [ name: [
{ required: true, message: '产线名称不能为空', trigger: 'blur' }, { required: true, message: '产线名称不能为空', trigger: 'blur' },
], ],
factoryId: [ factoryId: [
{ required: true, message: '工厂不能为空', trigger: 'change' }, { required: true, message: '工厂不能为空', trigger: 'change' },
], ],
tvalue: [
{
type: 'number',
message: '请输入正确的数字',
trigger: 'change',
transform: (val) => Number(val),
},
],
}, },
}; };
}, },
methods: { methods: {},
},
}; };
</script> </script>
<style scoped>
.dialog-inner__form >>> .el-select {
width: 100%;
}
</style>

View File

@@ -29,7 +29,7 @@
@cancel="handleCancel" @cancel="handleCancel"
@confirm="handleConfirm" @confirm="handleConfirm"
:before-close="handleCancel" :before-close="handleCancel"
width="70%"> width="50%">
<add-or-update <add-or-update
ref="addOrUpdate" ref="addOrUpdate"
@refreshDataList="successSubmit"></add-or-update> @refreshDataList="successSubmit"></add-or-update>
@@ -52,45 +52,37 @@ import {
const tableProps = [ const tableProps = [
{ {
prop: 'code', prop: 'code',
label: '产线编码', label: '产线编码'
align: 'center',
}, },
{ {
prop: 'name', prop: 'name',
label: '产线名称', label: '产线名称'
align: 'center',
}, },
{ {
prop: 'factoryName', prop: 'factoryName',
label: '工厂', label: '工厂'
align: 'center',
}, },
{ {
prop: 'externalCode', prop: 'externalCode',
label: '额外编码', label: '额外编码'
align: 'center',
}, },
{ {
prop: 'status', prop: 'status',
label: '当前状态', label: '当前状态',
align: 'center',
filter: codeFilter('lineStatus'), filter: codeFilter('lineStatus'),
}, },
{ {
prop: 'description', prop: 'description',
label: '描述', label: '描述'
align: 'center',
}, },
{ {
prop: 'remark', prop: 'remark',
label: '备注', label: '备注'
align: 'center',
}, },
{ {
prop: 'createTime', prop: 'createTime',
label: '创建时间', label: '创建时间',
align: 'center', filter: parseTime
filter: parseTime,
}, },
]; ];
@@ -132,14 +124,14 @@ export default {
name: 'search', name: 'search',
color: 'primary', color: 'primary',
}, },
{ // {
type: 'separate', // type: 'separate',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '重置', // btnName: '重置',
name: 'reset', // name: 'reset',
}, // },
{ {
type: 'separate', type: 'separate',
}, },
@@ -173,7 +165,7 @@ export default {
getDataList() { getDataList() {
this.dataListLoading = true; this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then((response) => { this.urlOptions.getDataListURL(this.listQuery).then((response) => {
this.total = response.data.total; this.listQuery.total = response.data.total;
this.getStatus(response.data.list); this.getStatus(response.data.list);
this.dataListLoading = false; this.dataListLoading = false;
}); });
@@ -195,7 +187,7 @@ export default {
switch (val.btnName) { switch (val.btnName) {
case 'search': case 'search':
this.listQuery.pageNo = 1; this.listQuery.pageNo = 1;
this.listQuery.pageSize = 10;.7 this.listQuery.pageSize = 10;
this.listQuery.name = val.name; this.listQuery.name = val.name;
this.getDataList(); this.getDataList();
break; break;

View File

@@ -1,8 +1,8 @@
<!-- <!--
* @Author: zwq * @Author: zwq
* @Date: 2023-08-01 13:52:10 * @Date: 2023-08-01 13:52:10
* @LastEditors: zwq * @LastEditors: DY
* @LastEditTime: 2023-08-01 16:58:44 * @LastEditTime: 2023-10-16 13:40:00
* @Description: * @Description:
--> -->
<template> <template>
@@ -12,33 +12,45 @@
ref="dataForm" ref="dataForm"
@keyup.enter.native="dataFormSubmit()" @keyup.enter.native="dataFormSubmit()"
label-width="90px"> label-width="90px">
<el-form-item label="工段编码" prop="code"> <el-row :gutter="20">
<el-input <el-col :span="12">
v-model="dataForm.code" <el-form-item label="工段编码" prop="code">
clearable <el-input
placeholder="请输入工段编码" /> v-model="dataForm.code"
</el-form-item> clearable
<el-form-item label="工段名称" prop="name"> placeholder="请输入工段编码" />
<el-input </el-form-item>
v-model="dataForm.name" </el-col>
clearable <el-col :span="12">
placeholder="请输入工段名称" /> <el-form-item label="工段名称" prop="name">
</el-form-item> <el-input
<el-form-item prop="productionLineId" label="产线"> v-model="dataForm.name"
<el-select clearable
v-model="dataForm.productionLineId" filterable clearable placeholder="请选择产线"> placeholder="请输入工段名称" />
<el-option </el-form-item>
v-for="item in urlOptions.optionArr.arr0" </el-col>
:key="item.id" </el-row>
:label="item.name" <el-row :gutter="20">
:value="item.id" <el-col :span="12">
> <el-form-item prop="productionLineId" label="产线">
</el-option> <el-select
</el-select> v-model="dataForm.productionLineId" filterable clearable placeholder="请选择产线">
</el-form-item> <el-option
<el-form-item label="排序" prop="sort"> v-for="item in urlOptions.optionArr.arr0"
<el-input-number v-model="dataForm.sort" :min="1" clearable placeholder="请输入排序" /> :key="item.id"
</el-form-item> :label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="排序" prop="sort">
<el-input-number v-model="dataForm.sort" :min="1" :max="100" clearable placeholder="请输入排序" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input v-model="dataForm.remark" clearable placeholder="请输入备注" /> <el-input v-model="dataForm.remark" clearable placeholder="请输入备注" />
</el-form-item> </el-form-item>

View File

@@ -29,7 +29,7 @@
@cancel="handleCancel" @cancel="handleCancel"
@confirm="handleConfirm" @confirm="handleConfirm"
:before-close="handleCancel" :before-close="handleCancel"
width="70%"> width="50%">
<add-or-update <add-or-update
ref="addOrUpdate" ref="addOrUpdate"
@refreshDataList="successSubmit"></add-or-update> @refreshDataList="successSubmit"></add-or-update>
@@ -50,34 +50,28 @@ import {
const tableProps = [ const tableProps = [
{ {
prop: 'code', prop: 'code',
label: '工段编码', label: '工段编码'
align: 'center',
}, },
{ {
prop: 'name', prop: 'name',
label: '工段名称', label: '工段名称'
align: 'center',
}, },
{ {
prop: 'productionLineName', prop: 'productionLineName',
label: '产线', label: '产线'
align: 'center',
}, },
{ {
prop: 'sort', prop: 'sort',
label: '排序', label: '排序'
align: 'center',
}, },
{ {
prop: 'remark', prop: 'remark',
label: '备注', label: '备注'
align: 'center',
}, },
{ {
prop: 'createTime', prop: 'createTime',
label: '创建时间', label: '创建时间',
align: 'center', filter: parseTime
filter: parseTime,
}, },
]; ];
@@ -119,14 +113,14 @@ export default {
name: 'search', name: 'search',
color: 'primary', color: 'primary',
}, },
{ // {
type: 'separate', // type: 'separate',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '重置', // btnName: '重置',
name: 'reset', // name: 'reset',
}, // },
{ {
type: 'separate', type: 'separate',
}, },

View File

@@ -1,8 +1,8 @@
/* /*
* @Author: zwq * @Author: zwq
* @Date: 2022-08-24 11:19:43 * @Date: 2022-08-24 11:19:43
* @LastEditors: zwq * @LastEditors: DY
* @LastEditTime: 2023-08-02 10:56:37 * @LastEditTime: 2023-09-21 16:02:07
* @Description: * @Description:
*/ */
export default { export default {
@@ -38,7 +38,7 @@ export default {
this.dataListLoading = true; this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then(response => { this.urlOptions.getDataListURL(this.listQuery).then(response => {
this.tableData = response.data.list; this.tableData = response.data.list;
this.total = response.data.total; this.listQuery.total = response.data.total;
this.dataListLoading = false; this.dataListLoading = false;
}); });
}, },

View File

@@ -1,7 +1,7 @@
<!-- <!--
* @Author: Do not edit * @Author: Do not edit
* @Date: 2023-08-29 14:59:29 * @Date: 2023-08-29 14:59:29
* @LastEditTime: 2023-09-16 17:34:17 * @LastEditTime: 2023-10-16 15:10:42
* @LastEditors: DY * @LastEditors: DY
* @Description: * @Description:
--> -->
@@ -12,22 +12,18 @@
ref="searchBarForm" ref="searchBarForm"
@headBtnClick="buttonClick" /> @headBtnClick="buttonClick" />
<base-table <base-table
v-if="showData.length"
class="right-aside"
v-loading="dataListLoading" v-loading="dataListLoading"
:table-props="tableProps" :table-props="tableProps"
:page="listQuery.pageNo" :page="listQuery.pageNo"
:limit="listQuery.pageSize" :limit="listQuery.pageSize"
:selectWidth="55" :selectWidth="55"
:table-data="tableData" :table-data="showData"
@selection-change="selectChange" @selection-change="selectChange"
> >
<method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="120"
label="操作"
:method-list="tableBtn"
@clickBtn="handleClick" />
</base-table> </base-table>
<div v-else class="no-data-bg"></div>
<pagination <pagination
:limit.sync="listQuery.pageSize" :limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo" :page.sync="listQuery.pageNo"
@@ -43,19 +39,6 @@
:table-data="selectedList" :table-data="selectedList"
/> />
</div> --> </div> -->
<div ref="pdf" v-show="false">
<el-table :data="selectedList" stripe border style="width: 100%">
<el-table-column prop="reportType" label="报表类型" />
<el-table-column prop="reportStartTime" label="统计开始时间" />
<el-table-column prop="reportEndTime" label="统计结束时间" />
<el-table-column prop="proLineName" label="产线名称" />
<el-table-column prop="inputNum" label="投入数量/片" />
<el-table-column prop="outputNum" label="产出数量/片" />
<el-table-column prop="outputArea" label="产出面积/㎡" />
<el-table-column prop="lossArea" label="损耗面积/㎡" />
<el-table-column prop="lossRatio" label="损耗比例%" />
</el-table>
</div>
<el-dialog <el-dialog
title="提示" title="提示"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
@@ -74,8 +57,8 @@
<script> <script>
import { parseTime } from '../../mixins/code-filter'; import { parseTime } from '../../mixins/code-filter';
import { getPdlAutoReport, getPdList } from '@/api/core/monitoring/auto' import { getPdlAutoReport, getPdList } from '@/api/core/monitoring/auto'
// import jsPDF from 'jspdf' import jsPDF from 'jspdf'
// import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
// import codeFilter from '../../mixins/code-filter' // import codeFilter from '../../mixins/code-filter'
import * as XLSX from 'xlsx' import * as XLSX from 'xlsx'
import FileSaver from 'file-saver' import FileSaver from 'file-saver'
@@ -83,55 +66,45 @@ import FileSaver from 'file-saver'
const tableProps = [ const tableProps = [
{ {
prop: 'reportType', prop: 'reportType',
label: '报表类型', label: '报表类型'
align: 'center'
}, },
{ {
prop: 'reportStartTime', prop: 'reportStartTime',
label: '统计开始时间', label: '统计开始时间',
align: 'center',
filter: parseTime, filter: parseTime,
}, },
{ {
prop: 'reportEndTime', prop: 'reportEndTime',
label: '统计结束时间', label: '统计结束时间',
align: 'center',
filter: parseTime, filter: parseTime,
}, },
{ {
prop: 'proLineName', prop: 'proLineName',
label: '产线名称', label: '产线名称'
align: 'center',
}, },
{ {
prop: 'inputNum', prop: 'inputNum',
label: '投入数量/片', label: '投入数量/片'
align: 'center',
}, },
{ {
prop: 'outputNum', prop: 'outputNum',
label: '产出数量/片', label: '产出数量/片'
align: 'center',
}, },
{ {
prop: 'outputArea', prop: 'outputArea',
label: '产出面积/㎡', label: '产出面积/㎡'
align: 'center',
}, },
{ {
prop: 'lossNum', prop: 'lossNum',
label: '损耗数量/片', label: '损耗数量/片'
align: 'center',
}, },
{ {
prop: 'lossArea', prop: 'lossArea',
label: '损耗面积/㎡', label: '损耗面积/㎡'
align: 'center',
}, },
{ {
prop: 'lossRatio', prop: 'lossRatio',
label: '损耗比例%', label: '损耗比例%'
align: 'center',
} }
]; ];
@@ -146,6 +119,7 @@ export default {
pageNo: 1, pageNo: 1,
total: 1, total: 1,
}, },
fileName: '',
exportLoading: false, exportLoading: false,
dataListLoading: false, dataListLoading: false,
dialogVisible: false, dialogVisible: false,
@@ -153,6 +127,7 @@ export default {
addOrUpdateVisible: false, addOrUpdateVisible: false,
tableProps, tableProps,
tableBtn: [], tableBtn: [],
showData: [],
selectedList: [], selectedList: [],
// tableBtn: [ // tableBtn: [
// this.$auth.hasPermi(`base:factory:update`) // this.$auth.hasPermi(`base:factory:update`)
@@ -222,7 +197,8 @@ export default {
type: 'button', type: 'button',
btnName: '导出', btnName: '导出',
name: 'export', name: 'export',
color: 'warning', color: 'primary',
plain: true
} }
], ],
}; };
@@ -232,44 +208,61 @@ export default {
this.getPdLineList() this.getPdLineList()
}, },
methods: { methods: {
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)
},
exportPdf() { exportPdf() {
this.pdf() this.test()
this.dialogVisible = false setTimeout(() =>{
this.dialogVisible = false
this.showData = this.tableData
}, 600)
}, },
exportXlsx() { exportXlsx() {
this.down() this.exportECL()
this.dialogVisible = false this.dialogVisible = false
}, this.showData = this.tableData
// imgDownload() {
// let that = this
// let img = this.$refs['pdf']
// // 图片高度
// var w = parseInt(window.getComputedStyle(img).width)
// // 图片宽度
// var h = parseInt(window.getComputedStyle(img).height)
// // 滚轮置顶,避免留白
// window.pageYOffset = 0
// html2canvas(img).then(function(canvas) {
// })
// },
pdf() {
// console.log('打印看看[df]')
// const pdf = new jsPDF()
// const content = this.$refs.pdf.innerHTML
// console.log('打印看看', content)
// pdf.text(content, 15, 15)
// // pdf.text('Hello world!', 10, 10)
// pdf.save('test.pdf')
// console.log('打印看看', pdf)
const printWindow = window.open('', '_blank')
const temp = this.$refs.pdf.innerHTML
console.log(temp)
printWindow.document.body.innerHTML = temp
printWindow.focus()
// printWindow.document.writeln(this.$refs.pdf.innerHTML)
printWindow.document.close()
printWindow.print()
}, },
handleClose(done) { handleClose(done) {
this.$confirm('确认关闭?') this.$confirm('确认关闭?')
@@ -278,23 +271,30 @@ export default {
}) })
.catch(_ => {}); .catch(_ => {});
}, },
down() { exportECL() {
//选中导出时可更改此处数组 选中的数组 let tables = document.querySelector('.el-table').cloneNode(true)
//构建导出的表格数据 const fix = tables.querySelector('.el-table__fixed')
const exportData = [ const fixRight = tables.querySelector('.el-table__fixed-right')
{reportType: '报表类型', reportStartTime: '统计开始时间', reportEndTime: '统计结束时间', proLineName: '产线名称', inputNum: '投入数量/片', outputNum: '产出数量/片', outputArea: '产出面积/㎡', lossNum: '损耗数量/片', lossArea: '损耗面积/㎡', lossRatio: '损耗比例%' }, if (fix) {
...this.selectedList tables.removeChild(tables.querySelector('.el-table__fixed'))
]
//注意表格上绑定id 获取dom元素
const worksheet = XLSX.utils.json_to_sheet(exportData, { skipHeader: true })
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1")
const workbookOutput = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' })
try {
FileSaver.saveAs(new Blob([workbookOutput], { type: 'application/octet-stream' }), '产线统计自动报表.xlsx')
} catch (e) {
console.log(e)
} }
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
}, },
selectChange(val) { selectChange(val) {
console.log(val) console.log(val)
@@ -313,8 +313,11 @@ export default {
this.listQuery.pageSize = 10; this.listQuery.pageSize = 10;
this.listQuery.lineId = val.line ? val.line : undefined; this.listQuery.lineId = val.line ? val.line : undefined;
this.listQuery.reportType = val.reportType ? val.reportType : undefined; this.listQuery.reportType = val.reportType ? val.reportType : undefined;
this.listQuery.reportStartTime = [new Date(val.timeVal[0]).getTime()]; this.listQuery.reportStartTime = val.timeVal ? [new Date(val.timeVal[0]).getTime()] : undefined;
this.listQuery.reportEndTime = [new Date(val.timeVal[1]).getTime()]; this.listQuery.reportEndTime = val.timeVal ? [new Date(val.timeVal[1]).getTime()] : undefined;
if (val.timeVal && val.timeVal.length > 0) {
this.fileName = val.timeVal[0].slice(0, 10) + '-' + val.timeVal[1].slice(0, 10) + '_'
}
this.getDataList(); this.getDataList();
break; break;
case 'export': case 'export':
@@ -328,12 +331,13 @@ export default {
getDataList() { getDataList() {
this.dataListLoading = true; this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then(response => { this.urlOptions.getDataListURL(this.listQuery).then(response => {
this.tableData = response.data.list.map(item => { this.tableData = response.data.list.map((item, index) => {
item.reportType = item.reportType === 1 ? '日' : item.reportType === 2 ? '周' : '月' item.reportType = item.reportType === 1 ? '日' : item.reportType === 2 ? '周' : '月'
return item return item
}); });
this.total = response.data.total; this.listQuery.total = response.data.total;
this.dataListLoading = false; this.dataListLoading = false;
this.showData = this.tableData
}); });
}, },
// 每页数 // 每页数
@@ -348,15 +352,10 @@ export default {
this.getDataList(); this.getDataList();
}, },
handleExport() { handleExport() {
if (this.selectedList.length === 0) { if (this.selectedList.length > 0) {
this.selectedList = this.tableData this.showData = this.selectedList
} }
this.dialogVisible = true this.dialogVisible = true
// this.$modal.confirm('是否确认导出所选数据项?').then(() => {
// this.exportLoading = true;
// // this.down()
// this.pdf()
// }).catch(() => { });
} }
}, },
}; };

View File

@@ -1,7 +1,7 @@
<!-- <!--
* @Author: Do not edit * @Author: Do not edit
* @Date: 2023-08-29 14:59:29 * @Date: 2023-08-29 14:59:29
* @LastEditTime: 2023-09-20 10:55:23 * @LastEditTime: 2023-10-16 15:18:23
* @LastEditors: DY * @LastEditors: DY
* @Description: * @Description:
--> -->
@@ -12,12 +12,14 @@
ref="searchBarForm" ref="searchBarForm"
@headBtnClick="buttonClick" /> @headBtnClick="buttonClick" />
<base-table <base-table
v-if="showData.length"
class="right-aside"
v-loading="dataListLoading" v-loading="dataListLoading"
:table-props="tableProps" :table-props="tableProps"
:page="listQuery.pageNo" :page="listQuery.pageNo"
:limit="listQuery.pageSize" :limit="listQuery.pageSize"
:selectWidth="55" :selectWidth="55"
:table-data="tableData" :table-data="showData"
@selection-change="selectChange" @selection-change="selectChange"
> >
<method-btn <method-btn
@@ -28,22 +30,12 @@
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleClick" /> @clickBtn="handleClick" />
</base-table> </base-table>
<div v-else class="no-data-bg"></div>
<pagination <pagination
:limit.sync="listQuery.pageSize" :limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo" :page.sync="listQuery.pageNo"
:total="listQuery.total" :total="listQuery.total"
@pagination="getDataList" /> @pagination="getDataList" />
<div ref="pdf" v-show="false">
<el-table :data="selectedList" stripe border style="width: 100%">
<el-table-column prop="proLineName" label="产线名称" />
<el-table-column prop="inputNum" label="投入数量/片" />
<el-table-column prop="outputNum" label="产出数量/片" />
<el-table-column prop="outputArea" label="产出面积/㎡" />
<el-table-column prop="lossNum" label="损耗数量/片" />
<el-table-column prop="lossArea" label="损耗面积/㎡" />
<el-table-column prop="lossRatio" label="损耗比例%" />
</el-table>
</div>
<el-dialog <el-dialog
title="提示" title="提示"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
@@ -63,6 +55,8 @@
import { getPdlDataSearch, getPdList } from '@/api/core/monitoring/data' import { getPdlDataSearch, getPdList } from '@/api/core/monitoring/data'
import * as XLSX from 'xlsx' import * as XLSX from 'xlsx'
import FileSaver from 'file-saver' import FileSaver from 'file-saver'
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
const tableProps = [ const tableProps = [
{ {
@@ -115,8 +109,10 @@ export default {
addOrUpdateVisible: false, addOrUpdateVisible: false,
tableProps, tableProps,
tableBtn: [], tableBtn: [],
showData: [],
tableData: [], tableData: [],
selectedList: [], selectedList: [],
fileName: '',
formConfig: [ formConfig: [
{ {
type: 'select', type: 'select',
@@ -151,7 +147,8 @@ export default {
type: 'button', type: 'button',
btnName: '导出', btnName: '导出',
name: 'export', name: 'export',
color: 'warning', color: 'primary',
plain: true
} }
], ],
}; };
@@ -166,40 +163,87 @@ export default {
this.getPdLineList() this.getPdLineList()
}, },
methods: { methods: {
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() { exportPdf() {
this.pdf() this.test()
this.dialogVisible = false setTimeout(() =>{
this.dialogVisible = false
this.showData = this.tableData
}, 600)
}, },
exportXlsx() { exportXlsx() {
this.down() this.exportECL()
this.dialogVisible = false this.dialogVisible = false
this.showData = this.tableData
}, },
pdf() {
const printWindow = window.open('', '_blank')
const temp = this.$refs.pdf.innerHTML
console.log(temp)
printWindow.document.body.innerHTML = temp
printWindow.document.close()
printWindow.print()
},
down() {
//选中导出时可更改此处数组 选中的数组
//构建导出的表格数据
const exportData = [
{proLineName: '产线名称', inputNum: '投入数量/片', outputNum: '产出数量/片', outputArea: '产出面积/㎡', lossNum: '损耗数量/片', lossArea: '损耗面积/㎡', lossRatio: '损耗比例%' },
...this.selectedList
]
//注意表格上绑定id 获取dom元素
const worksheet = XLSX.utils.json_to_sheet(exportData, { skipHeader: true })
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1")
const workbookOutput = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' })
try {
FileSaver.saveAs(new Blob([workbookOutput], { type: 'application/octet-stream' }), '产线统计数据查询.xlsx')
} catch (e) {
console.log(e)
}
},
handleClose(done) { handleClose(done) {
this.$confirm('确认关闭?') this.$confirm('确认关闭?')
.then(_ => { .then(_ => {
@@ -224,6 +268,9 @@ export default {
this.listQuery.proLineId = val.line ? val.line : undefined; this.listQuery.proLineId = val.line ? val.line : undefined;
this.listQuery.startTime = val.timeVal ? new Date(val.timeVal[0]).getTime() : undefined; this.listQuery.startTime = val.timeVal ? new Date(val.timeVal[0]).getTime() : undefined;
this.listQuery.endTime = val.timeVal ? new Date(val.timeVal[1]).getTime() : undefined; this.listQuery.endTime = val.timeVal ? new Date(val.timeVal[1]).getTime() : undefined;
if (val.timeVal && val.timeVal.length > 0) {
this.fileName = val.timeVal[0].slice(0, 10) + '-' + val.timeVal[1].slice(0, 10) + '_'
}
this.getDataList(); this.getDataList();
break; break;
case 'export': case 'export':
@@ -238,8 +285,9 @@ export default {
this.dataListLoading = true; this.dataListLoading = true;
this.urlOptions.getDataListURL(this.listQuery).then(response => { this.urlOptions.getDataListURL(this.listQuery).then(response => {
this.tableData = response.data; this.tableData = response.data;
this.total = response.data.total; this.listQuery.total = response.data.length;
this.dataListLoading = false; this.dataListLoading = false;
this.showData = this.tableData
}); });
}, },
// 每页数 // 每页数
@@ -254,21 +302,10 @@ export default {
this.getDataList(); this.getDataList();
}, },
handleExport() { handleExport() {
if (this.selectedList.length === 0) { if (this.selectedList.length > 0) {
this.selectedList = this.tableData this.showData = this.selectedList
} }
this.dialogVisible = true this.dialogVisible = true
// 处理查询参数
// 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

@@ -7,17 +7,18 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<SearchBar <SearchBar
:formConfigs="[{ label: '设备近24小时产线生产数据', type: 'title' }]" :formConfigs="[{ label: '近24小时产线生产数据', type: 'title' }]"
ref="search-bar" /> ref="search-bar" />
<el-skeleton v-if="initing" :rows="6" animated /> <el-skeleton v-if="initing" :rows="6" animated />
<!-- :span-method="mergeColumnHandler" -->
<div v-else :class="{ 'no-data-bg': !tableData || tableData.length == 0 }">
<base-table <base-table
v-else v-if="tableData && tableData.length > 0"
:span-method="mergeColumnHandler"
:table-props="tableProps" :table-props="tableProps"
:table-data="tableData" :table-data="tableData"
@emitFun="handleEmitFun"></base-table> @emitFun="handleEmitFun"
<!-- :page="queryParams.pageNo" />
:limit="queryParams.pageSize" --> </div>
</div> </div>
</template> </template>
@@ -62,13 +63,13 @@ export default {
for (const times of timeArray) { for (const times of timeArray) {
if (times !== '投入数量' && times !== '产出数量' && times !== '报废数量' && times !== '产出面积') { if (times !== '投入数量' && times !== '产出数量' && times !== '报废数量' && times !== '产出面积') {
const subprop = { const subprop = {
label: times, label: times.slice(0, 10) + ' ' + times.slice(11),
align: 'center', align: 'center',
children: [ children: [
{ prop: times + '_in', label: '投入数量', align: 'center' }, { prop: times + '_in', label: '投入数量' },
{ prop: times + '_out', label: '产出数量', align: 'center' }, { prop: times + '_out', label: '产出数量' },
{ prop: times + '_junk', label: '报废数量', align: 'center' }, { prop: times + '_junk', label: '报废数量' },
{ prop: times + '_area', label: '产出面积', align: 'center' } { prop: times + '_area', label: '产出面积' }
] ]
} }
this.arr.push(subprop) this.arr.push(subprop)
@@ -117,8 +118,8 @@ export default {
tempData['spec'] = ele.spec tempData['spec'] = ele.spec
this.tableData.push(tempData) this.tableData.push(tempData)
console.log('看看数据', this.tableData, tempData) console.log('看看数据', this.tableData, tempData)
const { sectionName } = tempData const { proLineName } = tempData
sectionArr.push(sectionName) sectionArr.push(proLineName)
}) })
this.setRowSpan(sectionArr) this.setRowSpan(sectionArr)
console.log('工段名称列表', sectionArr) console.log('工段名称列表', sectionArr)
@@ -149,12 +150,14 @@ export default {
{ {
prop: 'proLineName', prop: 'proLineName',
label: '生产线', label: '生产线',
align: 'center', fixed: 'left',
showOverflowTooltip: true
}, },
{ {
prop: 'spec', prop: 'spec',
label: '产品规格', label: '产品规格',
align: 'center', fixed: 'left',
showOverflowTooltip: true
} }
] ]
this.buildProps(res.data.nameData); this.buildProps(res.data.nameData);

View File

@@ -1,24 +1,26 @@
<!-- <!--
* @Author: Do not edit * @Author: Do not edit
* @Date: 2023-08-29 14:59:29 * @Date: 2023-08-29 14:59:29
* @LastEditTime: 2023-09-16 17:41:53 * @LastEditTime: 2023-10-16 15:19:04
* @LastEditors: DY * @LastEditors: DY
* @Description: * @Description:
--> -->
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- :isFold="true" 控制展开 -->
<search-bar <search-bar
:formConfigs="formConfig" :formConfigs="formConfig"
:isFold="true"
ref="searchBarForm" ref="searchBarForm"
@headBtnClick="buttonClick" /> @headBtnClick="buttonClick" />
<base-table <base-table
v-if="showData.length"
class="right-aside"
v-loading="dataListLoading" v-loading="dataListLoading"
:table-props="tableProps" :table-props="tableProps"
:page="listQuery.pageNo" :page="listQuery.pageNo"
:limit="listQuery.pageSize" :limit="listQuery.pageSize"
:selectWidth="55" :selectWidth="55"
:table-data="tableData" :table-data="showData"
@selection-change="selectChange" @selection-change="selectChange"
> >
<method-btn <method-btn
@@ -29,26 +31,12 @@
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleClick" /> @clickBtn="handleClick" />
</base-table> </base-table>
<div v-else class="no-data-bg"></div>
<pagination <pagination
:limit.sync="listQuery.pageSize" :limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo" :page.sync="listQuery.pageNo"
:total="listQuery.total" :total="listQuery.total"
@pagination="getDataList" /> @pagination="getDataList" />
<div ref="pdf" v-show="false">
<el-table :data="selectedList" stripe border style="width: 100%">
<el-table-column prop="reportType" label="产线类型" />
<el-table-column prop="reportStartTime" label="统计开始时间" />
<el-table-column prop="reportEndTime" label="统计结束时间" />
<el-table-column prop="proLineName" label="产线名称" />
<el-table-column prop="sectionName" label="工段名称" />
<el-table-column prop="inputNum" label="投入数量/片" />
<el-table-column prop="outputNum" label="产出数量/片" />
<el-table-column prop="outputArea" label="产出面积/㎡" />
<el-table-column prop="lossNum" label="损耗数量/片" />
<el-table-column prop="lossArea" label="损耗面积/㎡" />
<el-table-column prop="lossRatio" label="损耗比例%" />
</el-table>
</div>
<el-dialog <el-dialog
title="提示" title="提示"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
@@ -69,11 +57,13 @@ import { parseTime } from '../../mixins/code-filter';
import { getWorkshopSectionList, getPdList, getSectionAutoReport } from '@/api/core/monitoring/sectionStatistics' import { getWorkshopSectionList, getPdList, getSectionAutoReport } from '@/api/core/monitoring/sectionStatistics'
import * as XLSX from 'xlsx' import * as XLSX from 'xlsx'
import FileSaver from 'file-saver' import FileSaver from 'file-saver'
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
const tableProps = [ const tableProps = [
{ {
prop: 'reportType', prop: 'reportType',
label: '产线类型' label: '报表类型'
}, },
{ {
prop: 'reportStartTime', prop: 'reportStartTime',
@@ -140,6 +130,8 @@ export default {
tableProps, tableProps,
tableBtn: [], tableBtn: [],
tableData: [], tableData: [],
showData: [],
fileName: '',
formConfig: [ formConfig: [
{ {
type: 'select', type: 'select',
@@ -198,7 +190,8 @@ export default {
type: 'button', type: 'button',
btnName: '导出', btnName: '导出',
name: 'export', name: 'export',
color: 'warning', color: 'primary',
plain: true
} }
], ],
}; };
@@ -208,40 +201,87 @@ export default {
this.getPdLineList() this.getPdLineList()
}, },
methods: { methods: {
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() { exportPdf() {
this.pdf() this.test()
this.dialogVisible = false setTimeout(() =>{
this.dialogVisible = false
this.showData = this.tableData
}, 600)
}, },
exportXlsx() { exportXlsx() {
this.down() this.exportECL()
this.dialogVisible = false this.dialogVisible = false
this.showData = this.tableData
}, },
pdf() {
const printWindow = window.open('', '_blank')
const temp = this.$refs.pdf.innerHTML
console.log(temp)
printWindow.document.body.innerHTML = temp
printWindow.document.close()
printWindow.print()
},
down() {
//选中导出时可更改此处数组 选中的数组
//构建导出的表格数据
const exportData = [
{reportType: '产线类型', reportStartTime: '统计开始时间', reportEndTime: '统计结束时间', lineName: '产线名称', sectionName: '工段名称', inputNum: '投入数量/片', outputNum: '产出数量/片', outputArea: '产出面积/㎡', lossNum: '损耗数量/片', lossArea: '损耗面积/㎡', lossRatio: '损耗比例%' },
...this.selectedList
]
//注意表格上绑定id 获取dom元素
const worksheet = XLSX.utils.json_to_sheet(exportData, { skipHeader: true })
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1")
const workbookOutput = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' })
try {
FileSaver.saveAs(new Blob([workbookOutput], { type: 'application/octet-stream' }), '工段统计自动报表.xlsx')
} catch (e) {
console.log(e)
}
},
handleClose(done) { handleClose(done) {
this.$confirm('确认关闭?') this.$confirm('确认关闭?')
.then(_ => { .then(_ => {
@@ -270,8 +310,11 @@ export default {
this.listQuery.lineId = val.line ? val.line : undefined; this.listQuery.lineId = val.line ? val.line : undefined;
this.listQuery.sectionId = val.section ? val.section : undefined; this.listQuery.sectionId = val.section ? val.section : undefined;
this.listQuery.reportType = val.reportType ? val.reportType : undefined; this.listQuery.reportType = val.reportType ? val.reportType : undefined;
this.listQuery.reportStartTime = [new Date(val.timeVal[0]).getTime()]; this.listQuery.reportStartTime = val.timeVal ? [new Date(val.timeVal[0]).getTime()] : undefined;
this.listQuery.reportEndTime = [new Date(val.timeVal[1]).getTime()]; this.listQuery.reportEndTime = val.timeVal ? [new Date(val.timeVal[1]).getTime()] : undefined;
if (val.timeVal && val.timeVal.length > 0) {
this.fileName = val.timeVal[0].slice(0, 10) + '-' + val.timeVal[1].slice(0, 10) + '_'
}
this.getDataList(); this.getDataList();
break; break;
case 'export': case 'export':
@@ -289,7 +332,8 @@ export default {
item.reportType = item.reportType === 1 ? '日' : item.reportType === 2 ? '周' : '月' item.reportType = item.reportType === 1 ? '日' : item.reportType === 2 ? '周' : '月'
return item return item
}); });
this.total = response.data.total; this.showData = this.tableData
this.listQuery.total = response.data.total;
this.dataListLoading = false; this.dataListLoading = false;
}); });
}, },
@@ -305,21 +349,10 @@ export default {
this.getDataList(); this.getDataList();
}, },
handleExport() { handleExport() {
if (this.selectedList.length === 0) { if (this.selectedList.length > 0) {
this.selectedList = this.tableData this.showData = this.selectedList
} }
this.dialogVisible = true this.dialogVisible = true
// 处理查询参数
// 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

@@ -3,61 +3,78 @@
<search-bar <search-bar
:formConfigs="formConfig" :formConfigs="formConfig"
ref="searchBarForm" ref="searchBarForm"
@select-changed="handleSearchBarChanged"
@headBtnClick="buttonClick" /> @headBtnClick="buttonClick" />
<base-table <base-table
v-if="showData.length"
class="right-aside"
v-loading="dataListLoading" v-loading="dataListLoading"
:table-props="tableProps" :table-props="tableProps"
:table-data="tableData" /> :page="listQuery.pageNo"
<!-- <pagination :limit="listQuery.pageSize"
:selectWidth="55"
:table-data="showData"
@selection-change="selectChange"
/>
<div v-else class="no-data-bg"></div>
<pagination
:limit.sync="listQuery.pageSize" :limit.sync="listQuery.pageSize"
:page.sync="listQuery.pageNo" :page.sync="listQuery.pageNo"
:total="listQuery.total" :total="listQuery.total"
@pagination="getDataList" /> --> @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> </div>
</template> </template>
<script> <script>
// import basicPage from '../../mixins/basic-page'; // import basicPage from '../../mixins/basic-page';
import { parseTime } from '../../mixins/code-filter';
import { getSectionDataSearch } from '@/api/core/monitoring'; import { getSectionDataSearch } from '@/api/core/monitoring';
import { getProductionLinePage } from '@/api/core/base/productionLine'; import { getProductionLinePage } from '@/api/core/base/productionLine';
import { getWorkshopSectionPage } from '@/api/core/base/workshopSection'; import { getWorkshopSectionPage } from '@/api/core/base/workshopSection';
import * as XLSX from 'xlsx'
import FileSaver from 'file-saver'
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
const tableProps = [ const tableProps = [
{ {
prop: 'proLineName', prop: 'proLineName',
label: '产线名称', label: '产线名称'
align: 'center',
}, },
{ {
prop: 'sectionName', prop: 'sectionName',
label: '工段名称', label: '工段名称'
align: 'center',
}, },
{ {
prop: 'inputNum', prop: 'inputNum',
label: '进片数量/片', label: '进片数量/片'
align: 'center',
}, },
{ {
prop: 'outputNum', prop: 'outputNum',
label: '出片数量/片', label: '出片数量/片'
align: 'center',
}, },
{ {
prop: 'lossNum', prop: 'lossNum',
label: '损耗数量/片', label: '损耗数量/片'
align: 'center',
}, },
{ {
prop: 'lossArea', prop: 'lossArea',
label: '损耗面积/m²', label: '损耗面积/m²'
align: 'center',
}, },
{ {
prop: 'lossRate', prop: 'lossRate',
label: '损耗比例/%', label: '损耗比例/%'
align: 'center',
} }
]; ];
@@ -70,12 +87,18 @@ export default {
}, },
tableProps, tableProps,
tableData: [], tableData: [],
showData: [],
selectedList: [],
listQuery: { listQuery: {
proLineId:undefined, proLineId:undefined,
sectionId: undefined, sectionId: undefined,
startTime: undefined, startTime: undefined,
endTime: undefined, endTime: undefined,
total: 0
}, },
dataListLoading: false,
dialogVisible: false,
fileName: [],
optionArrUrl: [getProductionLinePage, getWorkshopSectionPage], optionArrUrl: [getProductionLinePage, getWorkshopSectionPage],
formConfig: [ formConfig: [
{ {
@@ -85,6 +108,7 @@ export default {
param: 'proLineId', param: 'proLineId',
defaultSelect: '', defaultSelect: '',
filterable: true, filterable: true,
onchange: true,
}, },
{ {
type: 'select', type: 'select',
@@ -107,15 +131,20 @@ export default {
}, },
{ {
type: 'button', type: 'button',
btnName: '搜索', btnName: '查询',
name: 'search', name: 'search',
color: 'primary', color: 'primary',
}, },
{
type: 'separate',
},
{ {
type: 'button', type: 'button',
btnName: '导出', btnName: '导出',
name: 'export', name: 'export',
}, color: 'primary',
plain: true
}
], ],
}; };
}, },
@@ -123,9 +152,126 @@ export default {
}, },
created() { created() {
this.getArr(); this.getArr();
this.getDataList() // this.getDataList()
}, },
methods: { methods: {
/** 根据产线获取工段 */
async getWorksectionById(lineId) {
const { code, data } = await this.$axios({
url: '/base/workshop-section/listByParentId',
method: 'get',
params: {
id: lineId,
},
});
if (code == 0) {
this.formConfig[1].selectOptions = data.map((item) => {
return {
name: item.name,
id: item.id,
};
});
}
},
handleSearchBarChanged({ param, value }) {
if (!value) {
this.formConfig[1].selectOptions = [];
return;
}
switch (param) {
case 'proLineId':
this.getWorksectionById(value);
break;
}
},
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[0] + '-' + that.fileName[1] + '_工段统计.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[0] + '-' + this.fileName[1] + '_工段统计.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(_ => {});
},
getArr() { getArr() {
const params = { const params = {
page: 1, page: 1,
@@ -143,9 +289,31 @@ export default {
// this.listQuery.endTime = '1693564257000' // this.listQuery.endTime = '1693564257000'
this.urlOptions.getDataListURL(this.listQuery).then(res => { this.urlOptions.getDataListURL(this.listQuery).then(res => {
this.tableData = res.data this.tableData = res.data
// this.total = response.data.total; this.listQuery.total = this.tableData.length;
this.dataListLoading = false; this.dataListLoading = false;
this.showData = this.tableData;
}); });
},
// 每页数
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
},
selectChange(val) {
console.log(val)
this.selectedList = val
}, },
buttonClick(val) { buttonClick(val) {
console.log(val) console.log(val)
@@ -159,7 +327,16 @@ export default {
this.listQuery.sectionId = val.sectionId ? val.sectionId : undefined this.listQuery.sectionId = val.sectionId ? val.sectionId : undefined
this.listQuery.startTime = val.timeSlot ? new Date(val.timeSlot[0]).getTime() : undefined this.listQuery.startTime = val.timeSlot ? new Date(val.timeSlot[0]).getTime() : undefined
this.listQuery.endTime = val.timeSlot ? new Date(val.timeSlot[1]).getTime() : undefined this.listQuery.endTime = val.timeSlot ? new Date(val.timeSlot[1]).getTime() : undefined
this.getDataList(); if (val.timeSlot && val.timeSlot.length > 0) {
this.fileName[0] = val.timeSlot[0].slice(0, 10)
this.fileName[1] = val.timeSlot[1].slice(0, 10)
this.getDataList()
} else {
this.$message({
message: '请选择时间',
type: 'warning'
});
}
break; break;
case 'reset': case 'reset':
this.$refs.searchBarForm.resetForm(); this.$refs.searchBarForm.resetForm();
@@ -170,6 +347,16 @@ export default {
}; };
this.getDataList(); this.getDataList();
break; break;
case 'export':
if (val.timeSlot && val.timeSlot.length > 0) {
this.handleExport();
} else {
this.$message({
message: '请选择时间',
type: 'warning'
});
}
break;
default: default:
console.log(val); console.log(val);
} }

View File

@@ -25,6 +25,10 @@ export default {
default: () => { default: () => {
return [] return []
} }
},
timeDim: {
type: String,
default: ''
} }
}, },
watch: { watch: {
@@ -58,32 +62,78 @@ export default {
tempArr = this.chartData[0].trendRespVOList tempArr = this.chartData[0].trendRespVOList
} }
for (let k = 0; k < tempArr.length; k++) { for (let k = 0; k < tempArr.length; k++) {
xData.push(tempArr[k].time) let time = ''
if (this.timeDim === '3') {
let year = tempArr[k].time.slice(0,4)
let weak = tempArr[k].time.slice(4,6)
time = year+' 第 '+weak+' 周'
} else {
time = tempArr[k].time
}
xData.push(time)
} }
for (let i = 0; i < this.chartData.length; i++) { for (let i = 0; i < this.chartData.length; i++) {
let obj = { let obj = {
name: this.chartData[i].objName + this.chartData[i].objCode, name: this.chartData[i].objName + this.chartData[i].objCode,
type: 'bar', type: 'bar',
barMaxWidth: 20,
label: {
show: true,
position: 'top'
},
data: [] data: []
} }
legendData.push(this.chartData[i].objName + this.chartData[i].objCode) legendData.push(this.chartData[i].objName + this.chartData[i].objCode)
let temp = this.chartData[i].trendRespVOList let temp = this.chartData[i].trendRespVOList
for (let j = 0; j < temp.length; j++) { for (let j = 0; j < temp.length; j++) {
let num = temp[j].useNum ? temp[j].useNum : 0 let num = temp[j].useNum ? temp[j].useNum : ''
obj.data.push(num) obj.data.push(num)
} }
yData.push(obj) yData.push(obj)
} }
var option = { var option = {
color:['#FFDC94','#8EF0AB','#63BDFF','#288AFF','#7164FF'],
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function(params) {
return (
params[0].axisValue +
`<br>` +
params.map((item) => {
let str = `<span style="display:inline-block;width:8px;height:8px;margin: 0 8px 0 -3px;border-radius:2px;background-color:${item.color};"></span>`
let seriesNameStr = `<span style="display:inline-block;">${item.seriesName}</span>`
let value = item.value ? item.value : '-'
let valueStr = `<span style="display:inline-block;margin-left:10px;color:rgba(0,0,0,0.45);">${value}</span>`
return `<span style="display:flex; justify-content:space-between; margin-bottom: 2px">
<span>${str}${seriesNameStr}</span>
<span>${valueStr}</span>
</span>`
}).join(``)
)
}
},
grid: {
left: '4%',
right: '1%',
bottom: '1%',
containLabel: true
}, },
legend: { legend: {
data: legendData data: legendData,
right: '1%',
icon: 'rect',
itemHeight: 8,
itemWidth: 8
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: xData data: xData,
axisLabel: {
rotate: "45"
}
}, },
yAxis: { yAxis: {
type: 'value' type: 'value'

View File

@@ -25,6 +25,10 @@ export default {
default: () => { default: () => {
return [] return []
} }
},
timeDim: {
type: String,
default: ''
} }
}, },
watch: { watch: {
@@ -58,7 +62,15 @@ export default {
tempArr = this.chartData[0].trendRespVOList tempArr = this.chartData[0].trendRespVOList
} }
for (let k = 0; k < tempArr.length; k++) { for (let k = 0; k < tempArr.length; k++) {
xData.push(tempArr[k].time) let time = ''
if (this.timeDim === '3') {
let year = tempArr[k].time.slice(0,4)
let weak = tempArr[k].time.slice(4,6)
time = year+' 第 '+weak+' 周'
} else {
time = tempArr[k].time
}
xData.push(time)
} }
for (let i = 0; i < this.chartData.length; i++) { for (let i = 0; i < this.chartData.length; i++) {
let obj = { let obj = {
@@ -70,22 +82,36 @@ export default {
legendData.push(this.chartData[i].objName + this.chartData[i].objCode) legendData.push(this.chartData[i].objName + this.chartData[i].objCode)
let temp = this.chartData[i].trendRespVOList let temp = this.chartData[i].trendRespVOList
for (let j = 0; j < temp.length; j++) { for (let j = 0; j < temp.length; j++) {
let num = temp[j].useNum ? temp[j].useNum : 0 let num = temp[j].useNum ? temp[j].useNum : ''
obj.data.push(num) obj.data.push(num)
} }
yData.push(obj) yData.push(obj)
} }
var option = { var option = {
color:['#FFDC94','#8EF0AB','#63BDFF','#288AFF','#7164FF'],
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
grid: {
left: '4%',
right: '1%',
bottom: '1%',
containLabel: true
}, },
legend: { legend: {
data: legendData data: legendData,
right: '1%'
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: xData data: xData,
axisLabel: {
rotate: "45"
}
}, },
yAxis: { yAxis: {
type: 'value' type: 'value'

View File

@@ -135,7 +135,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="对象选择"> <el-form-item label="对象选择">
<el-select v-model="queryParams.objIds" placeholder="请选择" multiple collapse-tags style="width: 200px;" size="small"> <el-select v-model="queryParams.objIds" placeholder="请选择" multiple :multiple-limit='5' collapse-tags style="width: 200px;" size="small">
<el-option <el-option
v-for="item in objectList" v-for="item in objectList"
:key="item.id" :key="item.id"
@@ -425,7 +425,7 @@ export default {
} }
} }
</script> </script>
<style> <style lang='scss'>
/* 时间整点 */ /* 时间整点 */
.noneMinute .el-time-spinner__wrapper { .noneMinute .el-time-spinner__wrapper {
width: 100%; width: 100%;
@@ -433,6 +433,16 @@ export default {
.noneMinute .el-scrollbar:nth-of-type(2) { .noneMinute .el-scrollbar:nth-of-type(2) {
display: none; display: none;
} }
.demo-form-inline {
.el-date-editor .el-range__icon {
font-size: 16px;
color: #0B58FF;
}
.el-input__prefix .el-icon-date {
font-size: 16px;
color: #0B58FF;
}
}
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
.demo-form-inline { .demo-form-inline {
@@ -446,8 +456,6 @@ export default {
margin-top: 12px; margin-top: 12px;
} }
} }
</style>
<style scoped>
.searchBarBox .foldClass { .searchBarBox .foldClass {
position: absolute; position: absolute;
top: 14px; top: 14px;

View File

@@ -1,15 +1,17 @@
<template> <template>
<div class="app-container" id="contrastAnalysisBox"> <div class="app-container contrastAnalysisBox" id="contrastAnalysisBox">
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<search-area :isFold="isFold" @submit="getList"/> <search-area :isFold="isFold" @submit="getList"/>
<el-tabs v-model="activeName" @tab-click="switchChart"> <el-tabs v-model="activeName" @tab-click="switchChart" v-show='chartData.length'>
<el-tab-pane label="柱状图" name="bar"> <el-tab-pane label="柱状图" name="bar">
<bar-chart ref="analysisBarChart" :chartData="chartData" /> <bar-chart ref="analysisBarChart" :chartData="chartData" :timeDim="timeDim" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="折线图" name="line"> <el-tab-pane label="折线图" name="line">
<line-chart ref="analysisLineChart" :chartData="chartData"/> <line-chart ref="analysisLineChart" :chartData="chartData" :timeDim="timeDim"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<!-- 没有数据 -->
<div class="no-data-bg" v-show='!chartData.length'></div>
</div> </div>
</template> </template>
<script> <script>
@@ -25,7 +27,8 @@ export default {
return { return {
isFold: false, isFold: false,
activeName: 'bar', activeName: 'bar',
chartData: [] chartData: [],
timeDim: ''
} }
}, },
mounted() { mounted() {
@@ -38,6 +41,7 @@ export default {
}, },
methods: { methods: {
getList(params) { getList(params) {
this.timeDim = params.timeDim
getCompare({ ...params }).then((res) => { getCompare({ ...params }).then((res) => {
console.log(res) console.log(res)
if (res.code === 0) { if (res.code === 0) {
@@ -60,4 +64,33 @@ export default {
} }
} }
} }
</script> </script>
<style lang='scss'>
.contrastAnalysisBox {
.el-tabs__nav::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 2px;
background-color: #e4e7ed;
/* z-index: 1; */
}
.el-tabs__nav-wrap::after {
width: 0;
}
.el-tabs__item {
padding: 0 10px;
}
.el-tabs__item:hover {
color: rgba(0, 0, 0, 0.85);
}
.el-tabs__item.is-active {
color: rgba(0, 0, 0, 0.85);
}
.el-tabs__item {
color: rgba(0, 0, 0, 0.45);
}
}
</style>

View File

@@ -15,7 +15,7 @@ export default {
return { return {
chartDom: '', chartDom: '',
chart: '', chart: '',
chartHeight: this.tableHeight(350) chartHeight: this.tableHeight(370)
} }
}, },
props: { props: {
@@ -34,7 +34,7 @@ export default {
}, },
mounted() { mounted() {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
this.chartHeight = this.tableHeight(350) this.chartHeight = this.tableHeight(370)
}) })
}, },
methods: { methods: {
@@ -57,12 +57,22 @@ export default {
{ {
name: '本期', name: '本期',
type: 'bar', type: 'bar',
data: [] data: [],
barWidth: 20,
label: {
show: true,
position: 'top'
}
}, },
{ {
name: '上期', name: '上期',
type: 'bar', type: 'bar',
data: [] data: [],
barWidth: 20,
label: {
show: true,
position: 'top'
}
} }
] ]
for (let j = 0; j < arr.length; j++) { for (let j = 0; j < arr.length; j++) {
@@ -76,19 +86,38 @@ export default {
} }
} }
var option = { var option = {
// title: { color:['#288AFF','#8EF0AB'],
// text: 'World Population'
// },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'shadow' type: 'shadow'
},
formatter: function(params) {
return (
params[0].axisValue +
`<br>` +
params.map((item) => {
let str = `<span style="display:inline-block;width:8px;height:8px;margin: 0 8px 0 -3px;border-radius:2px;background-color:${item.color};"></span>`
let seriesNameStr = `<span style="display:inline-block;">${item.seriesName}</span>`
let value = item.value ? item.value : '-'
let valueStr = `<span style="display:inline-block;margin-left:10px;color:rgba(0,0,0,0.45);">${value}</span>`
return `<span style="display:flex; justify-content:space-between; margin-bottom: 2px">
<span>${str}${seriesNameStr}</span>
<span>${valueStr}</span>
</span>`
}).join(``)
)
} }
}, },
legend: {}, legend: {
right: '1%',
icon: 'rect',
itemHeight: 8,
itemWidth: 8
},
grid: { grid: {
left: '3%', left: '1%',
right: '4%', right: '1%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true
}, },

View File

@@ -194,13 +194,23 @@ export default {
} }
} }
</script> </script>
<style> <style lang='scss'>
/* 级联选择器 */ /* 级联选择器 */
.cascaderParent .el-cascader-panel .el-scrollbar:first-child .el-radio { .cascaderParent .el-cascader-panel .el-scrollbar:first-child .el-radio {
display: none; display: none;
} }
.demo-form-inline {
.el-date-editor .el-range__icon {
font-size: 16px;
color: #0B58FF;
}
.el-input__prefix .el-icon-date {
font-size: 16px;
color: #0B58FF;
}
}
</style> </style>
<style scoped> <style lang="scss" scoped>
.separateStyle { .separateStyle {
display: inline-block; display: inline-block;
width: 1px; width: 1px;
@@ -208,8 +218,6 @@ export default {
background: #E8E8E8; background: #E8E8E8;
vertical-align: middle; vertical-align: middle;
} }
</style>
<style lang="scss" scoped>
.demo-form-inline { .demo-form-inline {
.blue-block { .blue-block {
display: inline-block; display: inline-block;

View File

@@ -3,14 +3,19 @@
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<search-area @submit="getList" @exportD="exportData"/> <search-area @submit="getList" @exportD="exportData"/>
<!-- 表格 --> <!-- 表格 -->
<base-table <div v-show="chartData.length">
:table-props="tableProps" <base-table
:table-data="list" :table-props="tableProps"
class="qoq-out-table" :table-data="list"
/> class="qoq-out-table"
<div style='width: 100%;height: 300px;padding-top: 30px;'> />
<line-chart ref="analysisLineChart" :chartData="chartData"/> <div class="chartTitle">环比分析图</div>
<div style='width: 100%'>
<line-chart ref="analysisLineChart" :chartData="chartData"/>
</div>
</div> </div>
<!-- 没有数据 -->
<div class="no-data-bg" v-show='!chartData.length'></div>
</div> </div>
</template> </template>
<script> <script>
@@ -107,4 +112,21 @@ export default {
} }
} }
} }
</script> </script>
<style lang='scss' scoped>
.chartTitle {
font-size: 16px;
color: #000;
margin-top: 20px;
}
.chartTitle::before {
content: '';
display: inline-block;
width: 4px;
height: 18px;
background-color: #0B58FF;
border-radius: 1px;
margin-right: 8px;
vertical-align: bottom;
}
</style>

View File

@@ -25,6 +25,10 @@ export default {
default: () => { default: () => {
return [] return []
} }
},
timeDim: {
type: String,
default: ''
} }
}, },
watch: { watch: {
@@ -51,13 +55,31 @@ export default {
let xData = [] let xData = []
let yData = [] let yData = []
for (let i = 0; i < this.chartData.length; i++) { for (let i = 0; i < this.chartData.length; i++) {
xData.push(this.chartData[i].time) let time = ""
if (this.timeDim === '3') {
let year = this.chartData[i].time.slice(0,4)
let weak = this.chartData[i].time.slice(4,6)
time = year+' 第 '+weak+' 周'
} else {
time = this.chartData[i].time
}
xData.push(time)
yData.push(this.chartData[i].useNum) yData.push(this.chartData[i].useNum)
} }
var option = { var option = {
color:['#288AFF'],
grid: {
left: '4%',
right: '1%',
bottom: '1%',
containLabel: true
},
xAxis: { xAxis: {
type: 'category', type: 'category',
data: xData data: xData,
axisLabel: {
rotate: "45"
}
}, },
yAxis: { yAxis: {
type: 'value' type: 'value'
@@ -65,7 +87,12 @@ export default {
series: [ series: [
{ {
data: yData, data: yData,
type: 'bar' type: 'bar',
barMaxWidth: 20,
label: {
show: true,
position: 'top'
}
} }
] ]
}; };

View File

@@ -8,6 +8,7 @@
<script> <script>
import * as echarts from 'echarts' import * as echarts from 'echarts'
import resize from '@/utils/chartMixins/resize' import resize from '@/utils/chartMixins/resize'
import moment from 'moment'
export default { export default {
name: "LineChart", name: "LineChart",
mixins: [resize], mixins: [resize],
@@ -25,6 +26,10 @@ export default {
default: () => { default: () => {
return [] return []
} }
},
timeDim: {
type: String,
default: ''
} }
}, },
watch: { watch: {
@@ -51,14 +56,35 @@ export default {
let xData = [] let xData = []
let yData = [] let yData = []
for (let i = 0; i < this.chartData.length; i++) { for (let i = 0; i < this.chartData.length; i++) {
xData.push(this.chartData[i].time) let time = ""
if (this.timeDim === '3') {
let year = this.chartData[i].time.slice(0,4)
let weak = this.chartData[i].time.slice(4,6)
time = year+' 第 '+weak+' 周'
} else {
time = this.chartData[i].time
}
xData.push(time)
yData.push(this.chartData[i].useNum) yData.push(this.chartData[i].useNum)
} }
var option = { var option = {
color:['#288AFF'],
// tooltip: {
// trigger: 'axis'
// },
grid: {
left: '4%',
right: '1%',
bottom: '1%',
containLabel: true
},
xAxis: { xAxis: {
type: 'category', type: 'category',
data: xData data: xData,
axisLabel: {
rotate: "45"
}
}, },
yAxis: { yAxis: {
type: 'value' type: 'value'

View File

@@ -385,7 +385,7 @@ export default {
} }
} }
</script> </script>
<style> <style lang='scss'>
/* 级联选择器 */ /* 级联选择器 */
.cascaderParent .el-cascader-panel .el-scrollbar:first-child .el-radio { .cascaderParent .el-cascader-panel .el-scrollbar:first-child .el-radio {
display: none; display: none;
@@ -397,6 +397,16 @@ export default {
.noneMinute .el-scrollbar:nth-of-type(2) { .noneMinute .el-scrollbar:nth-of-type(2) {
display: none; display: none;
} }
.demo-form-inline {
.el-date-editor .el-range__icon {
font-size: 16px;
color: #0B58FF;
}
.el-input__prefix .el-icon-date {
font-size: 16px;
color: #0B58FF;
}
}
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
.demo-form-inline { .demo-form-inline {
@@ -410,8 +420,6 @@ export default {
margin-top: 12px; margin-top: 12px;
} }
} }
</style>
<style scoped>
.searchBarBox .foldClass { .searchBarBox .foldClass {
position: absolute; position: absolute;
top: 14px; top: 14px;

View File

@@ -1,15 +1,17 @@
<template> <template>
<div class="app-container" id="trendAnalysisBox"> <div class="app-container trendAnalysisBox" id="trendAnalysisBox">
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<search-area :isFold="isFold" @submit="getList"/> <search-area :isFold="isFold" @submit="getList"/>
<el-tabs v-model="activeName" @tab-click="switchChart"> <el-tabs v-model="activeName" @tab-click="switchChart" v-show='chartData.length'>
<el-tab-pane label="柱状图" name="bar"> <el-tab-pane label="柱状图" name="bar">
<bar-chart ref="analysisBarChart" :chartData="chartData" /> <bar-chart ref="analysisBarChart" :chartData="chartData" :timeDim="timeDim"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="折线图" name="line"> <el-tab-pane label="折线图" name="line">
<line-chart ref="analysisLineChart" :chartData="chartData"/> <line-chart ref="analysisLineChart" :chartData="chartData" :timeDim="timeDim"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<!-- 没有数据 -->
<div class="no-data-bg" v-show='!chartData.length'></div>
</div> </div>
</template> </template>
<script> <script>
@@ -25,7 +27,8 @@ export default {
return { return {
isFold: false, isFold: false,
activeName: 'bar', activeName: 'bar',
chartData: [] chartData: [],
timeDim: ''
} }
}, },
mounted() { mounted() {
@@ -37,6 +40,7 @@ export default {
}, },
methods: { methods: {
getList(params) { getList(params) {
this.timeDim = params.timeDim
getEnergyTrend({ ...params }).then((res) => { getEnergyTrend({ ...params }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.chartData = res.data this.chartData = res.data
@@ -58,4 +62,33 @@ export default {
} }
} }
} }
</script> </script>
<style lang='scss'>
.trendAnalysisBox {
.el-tabs__nav::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 2px;
background-color: #e4e7ed;
/* z-index: 1; */
}
.el-tabs__nav-wrap::after {
width: 0;
}
.el-tabs__item {
padding: 0 10px;
}
.el-tabs__item:hover {
color: rgba(0, 0, 0, 0.85);
}
.el-tabs__item.is-active {
color: rgba(0, 0, 0, 0.85);
}
.el-tabs__item {
color: rgba(0, 0, 0, 0.45);
}
}
</style>

View File

@@ -75,15 +75,17 @@ export default {
} }
} }
var option = { var option = {
color:['#FFDC94','#8EF0AB','#63BDFF','#288AFF','#7164FF','#FF6860','#FF9747','#B0EB42','#D680FF','#0043D2'],
legend: { legend: {
data: keys data: keys,
right:'1%'
}, },
tooltip: { tooltip: {
trigger: 'axis' trigger: 'axis'
}, },
grid: { grid: {
left: '3%', left: '1%',
right: '4%', right: '1%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true
}, },

View File

@@ -125,7 +125,31 @@ export default {
this.$emit('submit', this.queryParams) this.$emit('submit', this.queryParams)
}, },
exportData() { exportData() {
this.$emit('exportD') let name
if (this.queryParams.objId) {
name = this.getObjName(this.objList, this.queryParams.objId)
} else {
this.$modal.msgWarning("对象不能为空")
return false
}
this.$emit('exportD', {name: name})
},
// 递归取对象name
getObjName(list, id) {
let _this = this
for (let i = 0; i < list.length; i++) {
let a = list[i]
if (a.id === id) {
return a.name
} else {
if (a.children && a.children.length > 0) {
let res = _this.getObjName(a.children, id)
if (res) {
return res
}
}
}
}
}, },
transformTime(timeStamp) {// 本月最后一天 transformTime(timeStamp) {// 本月最后一天
let year = moment(timeStamp).format('YYYY') let year = moment(timeStamp).format('YYYY')
@@ -143,19 +167,20 @@ export default {
} }
} }
</script> </script>
<style> <style lang='scss'>
/* 级联选择器 */ /* 级联选择器 */
.cascaderParent .el-cascader-panel .el-scrollbar:first-child .el-radio { .cascaderParent .el-cascader-panel .el-scrollbar:first-child .el-radio {
display: none; display: none;
} }
</style> .demo-form-inline {
<style scoped> .el-date-editor .el-range__icon {
.separateStyle { font-size: 16px;
display: inline-block; color: #0B58FF;
width: 1px; }
height: 24px; .el-input__prefix .el-icon-date {
background: #E8E8E8; font-size: 16px;
vertical-align: middle; color: #0B58FF;
}
} }
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -170,4 +195,11 @@ export default {
margin-top: 10px; margin-top: 10px;
} }
} }
.separateStyle {
display: inline-block;
width: 1px;
height: 24px;
background: #E8E8E8;
vertical-align: middle;
}
</style> </style>

View File

@@ -0,0 +1,18 @@
<template>
<div>
<svg-icon icon-class="upArrow" v-show='injectData[injectData.prop] > 0'/>
<svg-icon icon-class="downArrow" v-show='injectData[injectData.prop] < 0'/>
<span style="margin-left: 5px;">{{ injectData[injectData.prop] }}</span>
</div>
</template>
<script>
export default {
name: 'subRate',
props: {
injectData: {
type: Object,
default: () => ({})
}
}
}
</script>

View File

@@ -2,20 +2,26 @@
<div class="app-container"> <div class="app-container">
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<search-area @submit="getList" @exportD="exportData"/> <search-area @submit="getList" @exportD="exportData"/>
<div style='width: 100%;height: 300px;'> <div v-show='chartData.length'>
<line-chart ref="analysisLineChart" :chartData="chartData"/> <div class="chartTitle">同比分析图</div>
<div style='width: 100%;height: 400px;'>
<line-chart ref="analysisLineChart" :chartData="chartData"/>
</div>
<!-- 表格 -->
<base-table
:table-props="tableProps"
:table-data="list"
:max-height="tableH"
class="yoy-out-table"
/>
</div> </div>
<!-- 表格 --> <!-- 没有数据 -->
<base-table <div class="no-data-bg" v-show='!chartData.length'></div>
:table-props="tableProps"
:table-data="list"
:max-height="tableH"
class="yoy-out-table"
/>
</div> </div>
</template> </template>
<script> <script>
import { getYoy } from "@/api/analysis/energyAnalysis" import { getYoy } from "@/api/analysis/energyAnalysis"
import subRate from "./components/subRate.vue"
import SearchArea from "./components/searchArea" import SearchArea from "./components/searchArea"
import LineChart from "./components/lineChart" import LineChart from "./components/lineChart"
import FileSaver from "file-saver" import FileSaver from "file-saver"
@@ -28,12 +34,12 @@ export default {
chartData: [], chartData: [],
tableProps: [], tableProps: [],
list: [], list: [],
tableH: this.tableHeight(500) tableH: this.tableHeight(640)
} }
}, },
mounted() { mounted() {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
this.tableH = this.tableHeight(500) this.tableH = this.tableHeight(640)
}) })
}, },
methods: { methods: {
@@ -65,11 +71,15 @@ export default {
let obj = {} let obj = {}
obj.prop = tempX[j].prop + '_' + nameData[i].name obj.prop = tempX[j].prop + '_' + nameData[i].name
obj.label = nameData[i].name obj.label = nameData[i].name
if (obj.label.indexOf('同比')!= -1) {
obj.subcomponent = subRate
}
tempX[j].children.push(obj) tempX[j].children.push(obj)
} }
} }
} }
this.tableProps = [{prop: 'time',label: '时间'}].concat(tempX) this.tableProps = [{prop: 'time',label: '时间'}].concat(tempX)
console.log(this.tableProps)
// 数据 // 数据
this.list = [] this.list = []
for (let k = 0; k < data.length; k++) { for (let k = 0; k < data.length; k++) {
@@ -90,9 +100,10 @@ export default {
} }
this.chartData = this.list this.chartData = this.list
}, },
exportData() { exportData(val) {
if (this.list.length > 0) { if (this.list.length > 0) {
var wb = XLSX.utils.table_to_book(document.querySelector(".yoy-out-table")) var wb = XLSX.utils.table_to_book(document.querySelector(".yoy-out-table"))
let fileName = val.name + "同比分析.xlsx"
var wbout = XLSX.write(wb, { var wbout = XLSX.write(wb, {
bookType: "xlsx", bookType: "xlsx",
bookSST: true, bookSST: true,
@@ -101,7 +112,7 @@ export default {
try { try {
FileSaver.saveAs( FileSaver.saveAs(
new Blob([wbout], { type: "application/octet-stream" }), new Blob([wbout], { type: "application/octet-stream" }),
"同比分析.xlsx" fileName
) )
} catch (e) { } catch (e) {
if (typeof console !== "undefined") console.log(e, wbout); if (typeof console !== "undefined") console.log(e, wbout);
@@ -113,4 +124,20 @@ export default {
} }
} }
} }
</script> </script>
<style lang='scss' scoped>
.chartTitle {
font-size: 16px;
color: #000;
}
.chartTitle::before {
content: '';
display: inline-block;
width: 4px;
height: 18px;
background-color: #0B58FF;
border-radius: 1px;
margin-right: 8px;
vertical-align: bottom;
}
</style>

View File

@@ -130,7 +130,7 @@ export default {
this.plcTableName = data.plcTableName this.plcTableName = data.plcTableName
this.objName = data.objName this.objName = data.objName
this.getList() this.getList()
if (title === 'view') { if (title === 'detail') {
this.showBtn = false this.showBtn = false
this.tableBtn = [] this.tableBtn = []
} else { } else {

View File

@@ -113,22 +113,22 @@ export default {
], ],
tableProps, tableProps,
tableBtn: [ tableBtn: [
this.$auth.hasPermi('base:energy-plc-connect:bind')
? {
type: 'connect',
btnName: '绑定'
}
: undefined,
{
type: 'detail',
btnName: '详情'
},
this.$auth.hasPermi('base:energy-plc-connect:update') this.$auth.hasPermi('base:energy-plc-connect:update')
? { ? {
type: 'edit', type: 'edit',
btnName: '编辑' btnName: '编辑'
} }
: undefined, : undefined,
{
type: 'view',
btnName: '查看'
},
this.$auth.hasPermi('base:energy-plc-connect:bind')
? {
type: 'connect',
btnName: '绑定'
}
: undefined,
this.$auth.hasPermi('base:energy-plc-connect:delete') this.$auth.hasPermi('base:energy-plc-connect:delete')
? { ? {
type: 'delete', type: 'delete',
@@ -205,10 +205,10 @@ export default {
case 'delete': case 'delete':
this.handleDelete(val.data) this.handleDelete(val.data)
break break
case 'view': case 'detail':
this.paramVisible = true this.paramVisible = true
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.plcParam.init(val.data, 'view') this.$refs.plcParam.init(val.data, 'detail')
}) })
break break
default: default:

View File

@@ -1,54 +1,72 @@
<template> <template>
<el-form ref="form" :rules="rules" label-width="110px" :model="form"> <el-form ref="form" :rules="rules" label-width="110px" :model="form">
<el-form-item label="监控对象" prop="objectId"> <el-row>
<el-cascader <el-col :span="12">
style='width: 100%;' <el-form-item label="监控对象" prop="objectId">
v-model="objIds" <el-cascader
:options="objList" style='width: 100%;'
:props="{ checkStrictly: true, value: 'id', label: 'name' }" v-model="objIds"
popper-class="cascaderParent" :options="objList"
@change="selectObj" :props="{ checkStrictly: true, value: 'id', label: 'name' }"
clearable></el-cascader> popper-class="cascaderParent"
</el-form-item> @change="selectObj"
<el-form-item label="能源类型" prop="energyTypeId"> clearable></el-cascader>
<el-select v-model="form.energyTypeId" placeholder="请选择" style="width: 100%;" @change="toggleType"> </el-form-item>
<el-option </el-col>
v-for="item in this.energyTypeList" <el-col :span="12">
:key="item.id" <el-form-item label="能源类型" prop="energyTypeId">
:label="item.name" <el-select v-model="form.energyTypeId" placeholder="请选择" style="width: 100%;" @change="toggleType">
:value="item.id"> <el-option
</el-option> v-for="item in this.energyTypeList"
</el-select> :key="item.id"
</el-form-item> :label="item.name"
<el-form-item label="监控模式" prop="type"> :value="item.id">
<el-select v-model="form.type" placeholder="请选择" style="width: 100%;" @change="typeChange"> </el-option>
<el-option label="合并" :value= "1" ></el-option> </el-select>
<el-option label="详细" :value= "2" ></el-option> </el-form-item>
</el-select> </el-col>
</el-form-item> </el-row>
<el-form-item label="监控详细参数" prop="type" v-if="form.type === 2"> <el-row>
<el-select v-model="form.plcParamId" placeholder="请选择" style="width: 100%;" @change="selectDetail"> <el-col :span="12">
<el-option <el-form-item label="监控模式" prop="type">
v-for="item in detailList" <el-select v-model="form.type" placeholder="请选择" style="width: 100%;" @change="typeChange">
:key="item.id" <el-option label="合并" :value= "1" ></el-option>
:label="item.name" <el-option label="详细" :value= "2" ></el-option>
:value="item.id"> </el-select>
</el-option> </el-form-item>
</el-select> </el-col>
</el-form-item> <el-col :span="12">
<el-form-item label="指标类型" prop="limitType"> <el-form-item label="监控详细参数" prop="type" v-if="form.type === 2">
<el-select v-model="form.limitType" placeholder="请选择" style="width: 100%;"> <el-select v-model="form.plcParamId" placeholder="请选择" style="width: 100%;" @change="selectDetail">
<el-option <el-option
v-for="item in getDictDatas(DICT_TYPE.MONITOR_INDEX_TYPE)" v-for="item in detailList"
:key="item.value" :key="item.id"
:label="item.label" :label="item.name"
:value="item.value"> :value="item.id">
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="消耗量阈值" prop="limitValue"> </el-col>
<el-input-number v-model="form.limitValue" :min="0" :max="10000000000000000" style="width: 100%;"></el-input-number> </el-row>
</el-form-item> <el-row>
<el-col :span="12">
<el-form-item label="指标类型" prop="limitType">
<el-select v-model="form.limitType" placeholder="请选择" style="width: 100%;">
<el-option
v-for="item in getDictDatas(DICT_TYPE.MONITOR_INDEX_TYPE)"
: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="limitValue">
<el-input-number v-model="form.limitValue" :min="0" :max="10000000000000000" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
</template> </template>
<script> <script>

View File

@@ -38,15 +38,15 @@ const tableProps = [
label: '能源类型' label: '能源类型'
}, },
{ {
prop: 'startNum', prop: 'startValue',
label: '抄表数(起始)' label: '抄表数(起始)'
}, },
{ {
prop: 'endNum', prop: 'endValue',
label: '抄表数(结束)' label: '抄表数(结束)'
}, },
{ {
prop: 'useNum', prop: 'diffValue',
label: '消耗量' label: '消耗量'
} }
] ]

View File

@@ -124,7 +124,7 @@ export default {
this.energyType = data.energyType this.energyType = data.energyType
this.energyTypeId = data.energyTypeId this.energyTypeId = data.energyTypeId
this.getList() this.getList()
if (title === 'view') { if (title === 'detail') {
this.showBtn = false this.showBtn = false
this.tableBtn = [] this.tableBtn = []
} else { } else {

View File

@@ -111,22 +111,22 @@ export default {
], ],
tableProps, tableProps,
tableBtn: [ tableBtn: [
this.$auth.hasPermi('monitoring:energy-statistics:update')
? {
type: 'edit',
btnName: '编辑'
}
: undefined,
{
type: 'view',
btnName: '查看'
},
this.$auth.hasPermi('monitoring:energy-statistics:bind') this.$auth.hasPermi('monitoring:energy-statistics:bind')
? { ? {
type: 'connect', type: 'connect',
btnName: '绑定' btnName: '绑定'
} }
: undefined, : undefined,
{
type: 'detail',
btnName: '详情'
},
this.$auth.hasPermi('monitoring:energy-statistics:update')
? {
type: 'edit',
btnName: '编辑'
}
: undefined,
this.$auth.hasPermi('monitoring:energy-statistics:delete') this.$auth.hasPermi('monitoring:energy-statistics:delete')
? { ? {
type: 'delete', type: 'delete',
@@ -199,9 +199,9 @@ export default {
case 'delete': case 'delete':
this.handleDelete(val.data) this.handleDelete(val.data)
break break
case 'view': case 'detail':
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.plcParam.init(val.data, 'view') this.$refs.plcParam.init(val.data, 'detail')
}) })
break break
default: default:

View File

@@ -6,10 +6,11 @@
--> -->
<template> <template>
<div class="chart-grid-item" style=""> <div class="chart-grid-item" style="overflow: inherit">
<div <div
class="pie-chart" class="pie-chart"
ref="pieChart" ref="pieChart"
style="overflow: inherit;"
:data-eqname="value.equipmentName || 'Default'"></div> :data-eqname="value.equipmentName || 'Default'"></div>
<div class="data-view"> <div class="data-view">
<div class="data-view__item"> <div class="data-view__item">
@@ -93,7 +94,7 @@ export default {
name: '', name: '',
type: 'pie', type: 'pie',
radius: ['75%', '90%'], radius: ['75%', '90%'],
center: ['50%', '45%'], center: ['50%', '48%'],
label: { label: {
show: false, show: false,
}, },
@@ -108,7 +109,7 @@ export default {
// 内环 // 内环
name: '', name: '',
type: 'pie', type: 'pie',
center: ['50%', '45%'], center: ['50%', '48%'],
radius: ['60%', '75%'], radius: ['60%', '75%'],
itemStyle: { itemStyle: {
borderRadius: 10, borderRadius: 10,

View File

@@ -6,7 +6,7 @@
--> -->
<template> <template>
<div class="app-container"> <div class="app-container allow-overflow">
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<SearchBar <SearchBar
:formConfigs="searchBarFormConfig" :formConfigs="searchBarFormConfig"
@@ -29,11 +29,19 @@
:table-data="list" :table-data="list"
@emitFun="handleEmitFun"></base-table> @emitFun="handleEmitFun"></base-table>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="'\u3000可视化\u3000'" name="graph"> <el-tab-pane
:label="'\u3000可视化\u3000'"
name="graph"
style="overflow: inherit">
<div <div
v-if="activeName == 'graph'" v-if="activeName == 'graph'"
class="graph" class="graph"
style="display: flex; flex-direction: column; position: relative"> style="
overflow: inherit;
display: flex;
flex-direction: column;
position: relative;
">
<div class="blue-title">各设备加工数量</div> <div class="blue-title">各设备加工数量</div>
<div class="legend"> <div class="legend">
<div class="legend-item"> <div class="legend-item">
@@ -118,12 +126,12 @@ export default {
{ {
width: 128, width: 128,
prop: 'workTime', prop: 'workTime',
label: '工作时长', label: '工作时长[h]',
}, },
{ {
width: 128, width: 128,
prop: 'workRate', prop: 'workRate',
label: '百分比', label: '百分比[%]',
filter: (val) => (val != null ? +val.toFixed(3) : '-'), filter: (val) => (val != null ? +val.toFixed(3) : '-'),
}, },
], ],
@@ -134,9 +142,9 @@ export default {
{ {
width: 128, width: 128,
prop: 'stopTime', prop: 'stopTime',
label: '停机时长', label: '停机时长[h]',
}, },
{ width: 128, prop: 'stopRate', label: '百分比' }, { width: 128, prop: 'stopRate', label: '百分比[%]' },
], ],
}, },
{ {
@@ -145,10 +153,10 @@ export default {
{ {
width: 128, width: 128,
prop: 'downTime', prop: 'downTime',
label: '故障时长', label: '故障时长[h]',
filter: (val) => (val != null ? +val.toFixed(3) : '-'), filter: (val) => (val != null ? +val.toFixed(3) : '-'),
}, },
{ width: 128, prop: 'downRate', label: '百分比' }, { width: 128, prop: 'downRate', label: '百分比[%]' },
{ {
width: 128, width: 128,
prop: 'timeEfficiency', prop: 'timeEfficiency',
@@ -429,6 +437,10 @@ export default {
transform: translateY(-12px); transform: translateY(-12px);
} }
.el-tabs__content {
overflow: visible;
}
.el-tabs__item { .el-tabs__item {
padding-left: 0 !important; padding-left: 0 !important;
padding-right: 0 !important; padding-right: 0 !important;
@@ -462,6 +474,7 @@ export default {
border-radius: 12px; border-radius: 12px;
border: 1px solid #ccc; border: 1px solid #ccc;
// background: #0003; // background: #0003;
overflow: inherit;
} }
.bg-grid { .bg-grid {
display: grid; display: grid;
@@ -469,7 +482,7 @@ export default {
grid-template-columns: repeat(4, minmax(280px, 1fr)); grid-template-columns: repeat(4, minmax(280px, 1fr));
grid-auto-columns: 280px; grid-auto-columns: 280px;
grid-auto-rows: 290px; grid-auto-rows: 290px;
overflow: hidden; overflow: inherit;
position: relative; position: relative;
} }

View File

@@ -155,7 +155,7 @@ export default {
async getList() { async getList() {
this.loading = true; this.loading = true;
// 执行查询 // 执行查询
const { data } = await this.$axios({ const { code, data } = await this.$axios({
url: '/analysis/equipment-analysis/efficiency', url: '/analysis/equipment-analysis/efficiency',
method: 'get', method: 'get',
params: { params: {
@@ -163,6 +163,11 @@ export default {
recordTime: this.queryParams.recordTime || null, recordTime: this.queryParams.recordTime || null,
}, },
}); });
if (code === 0) {
this.list = data;
} else {
this.list.splice(0);
}
}, },
handleSearchBarBtnClick(btn) { handleSearchBarBtnClick(btn) {

View File

@@ -43,7 +43,7 @@ export default {
eq.okQuantity, eq.okQuantity,
eq.nokQuantity, eq.nokQuantity,
eq.totalQuantity, eq.totalQuantity,
eq.passRate.toFixed(2), eq.passRate?.toFixed(4),
]); ]);
}); });
return { return {
@@ -53,6 +53,30 @@ export default {
axisPointer: { axisPointer: {
type: 'shadow', type: 'shadow',
}, },
formatter: (params) => {
const name = params[0].name;
const goodRate = opt.find((item) => item[0] == name)[4];
return `
<h1 style="font-size: 18px; letter-spacing: 1px;">${
params[0].axisValue
} <small>${goodRate}%</small></h1>
<ul style="margin: 0; padding: 0; min-width: 128px;">
${params
.map(
(item, index) => `
<li style="list-style: none; display: flex; justify-content: space-between; align-items: center;">
<div>
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 2px; background-color: ${item.color}; margin-right: 5px;"></span>
${item.seriesName}
</div>
${item.value}
</li>
`
)
.join('')}
</ul>
`;
},
}, },
legend: { legend: {
itemWidth: 12, itemWidth: 12,
@@ -97,6 +121,7 @@ export default {
}, },
series: [ series: [
{ {
animation: false,
name: '合格数量', name: '合格数量',
type: 'bar', type: 'bar',
barWidth: 20, barWidth: 20,
@@ -104,23 +129,24 @@ export default {
data: opt.map((item) => item[1]), data: opt.map((item) => item[1]),
}, },
{ {
animation: false,
name: '不合格数量', name: '不合格数量',
type: 'bar', type: 'bar',
barWidth: 20, barWidth: 20,
stack: 's', stack: 's',
data: opt.map((item) => item[2]), data: opt.map((item) => item[2]),
}, },
{ // {
name: '加工数量', // name: '加工数量',
type: 'bar', // type: 'bar',
barWidth: 20, // barWidth: 20,
data: opt.map((item) => item[3]), // data: opt.map((item) => item[3]),
}, // },
{ // {
name: '合格率', // name: '合格率',
type: 'line', // type: 'line',
data: opt.map((item) => item[4]), // data: opt.map((item) => item[4]),
}, // },
], ],
}; };
}, },

View File

@@ -29,9 +29,13 @@
@emitFun="handleEmitFun"></base-table> @emitFun="handleEmitFun"></base-table>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="'\u3000柱状图\u3000'" name="graph"> <el-tab-pane :label="'\u3000柱状图\u3000'" name="graph">
<div v-if="activeName == 'graph'" class="graph" style="height: 40vh; display: flex; flex-direction: column;"> <div
v-if="activeName == 'graph'"
class="graph"
style="height: 40vh; display: flex; flex-direction: column">
<div class="blue-title">各设备加工数量</div> <div class="blue-title">各设备加工数量</div>
<LineChart :list="list" /> <LineChart v-if="list && list.length" :list="list" />
<div v-else class="no-data-bg"></div>
</div> </div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@@ -86,6 +90,7 @@ export default {
rangeSeparator: '-', rangeSeparator: '-',
startPlaceholder: '开始日期', startPlaceholder: '开始日期',
endPlaceholder: '结束日期', endPlaceholder: '结束日期',
defaultTime: ['00:00:00', '23:59:59'],
param: 'recordTime', param: 'recordTime',
defaultSelect: [ defaultSelect: [
new Date(y, m, d) new Date(y, m, d)
@@ -245,7 +250,9 @@ export default {
created() { created() {
this.fillLineOptions(); this.fillLineOptions();
this.fillProductOptions(); this.fillProductOptions();
this.getList(); },
mounted() {
this.$refs['search-bar'].headBtnClick('search');
}, },
methods: { methods: {
handleTabClick(tab, event) { handleTabClick(tab, event) {
@@ -301,17 +308,21 @@ export default {
}); });
this.list = data.map((item) => ({ this.list = data.map((item) => ({
...item, ...item,
products: item.products.join(','), products: item.products?.join(','),
})); }));
}, },
handleSearchBarBtnClick(btn) { handleSearchBarBtnClick(btn) {
console.log('handleSearchBarBtnClick', btn); // debugger;
switch (btn.btnName) { switch (btn.btnName) {
case 'search': case 'search':
this.queryParams.lineId = btn.lineId; this.queryParams.lineId = btn.lineId;
this.queryParams.productId = btn.productId; this.queryParams.productId = btn.productId;
this.queryParams.recordTime = btn.recordTime; this.queryParams.recordTime = btn.recordTime
? btn.recordTime.map((time) =>
moment(new Date(time)).format('YYYY-MM-DD HH:mm:ss')
)
: null;
this.$nextTick(() => { this.$nextTick(() => {
this.getList(); this.getList();
}); });
@@ -387,7 +398,7 @@ export default {
font-size: 14px; font-size: 14px;
&::before { &::before {
content: ""; content: '';
position: absolute; position: absolute;
left: 0; left: 0;
top: 6px; top: 6px;
@@ -397,5 +408,4 @@ export default {
background: #0b58ff; background: #0b58ff;
} }
} }
</style> </style>

View File

@@ -0,0 +1,191 @@
// filename: gantt.vue
// author: liubin
// date: 2023 -09 - 25 14: 28: 12
// description: 甘特图
import * as echarts from 'echarts';
/**
*
* @param {*} params
* @param {*} api
*
* https://echarts.apache.org/zh/option.html#series-custom.renderItem.arguments.params
*/
// function renderItem(params, api) { }
export default class Gantt {
constructor(el) {
this.chart = echarts.init(el);
let options = {
series: [
{
type: 'custom',
coordinateSystem: 'cartesian2d',
renderItem: renderItem,
}
]
}
this.chart.setOption(options);
}
update(options) {
this.chart.setOption(options);
}
resize() {
// todo
}
destroy() {
this.chart.dispose();
}
}
var data = [];
var categories = ['设备1', '设备2', '设备3'];
var types = [
{ name: '运行', color: '#7b9ce1' },
{ name: '故障', color: '#bd6d6c' },
{ name: '停机', color: '#75d874' },
];
// return new Date(new Date(timestamp).toLocaleDateString()).getTime()
// })(1691568181000))
function getStartTime(timestamp) {
return new Date(new Date(timestamp).toLocaleDateString()).getTime()
}
data.push({
name: 'running',
value: [0, 1691568181000, 1691568181000 + 60 * 60 * 1000, 60],
itemStyle: {
normal: {
color: types[0].color
}
}
})
function renderItem(params, api) {
var categoryIndex = api.value(0);
var start = api.coord([api.value(1), categoryIndex]);
var end = api.coord([api.value(2), categoryIndex]);
var height = api.size([0, 1])[1] * 0.8;
var rectShape = echarts.graphic.clipRectByRect(
{
x: start[0],
y: start[1] - height / 2,
width: end[0] - start[0],
height: height
},
{
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
}
);
return (
rectShape && {
type: 'rect',
transition: ['shape'],
shape: rectShape,
style: api.style()
}
);
}
option = {
tooltip: {
// show: false,
formatter: function (params) {
return params.marker + params.name + ': ' + new Date(params.value[1]).toLocaleTimeString() + ' - ' + new Date(params.value[2]).toLocaleTimeString();
}
},
// title: {
// text: 'Profile',
// left: 'center'
// },
// dataZoom: [
// {
// type: 'slider',
// filterMode: 'weakFilter',
// showDataShadow: false,
// top: 400,
// labelFormatter: ''
// },
// {
// type: 'inside',
// filterMode: 'weakFilter'
// }
// ],
grid: {
height: 300
},
xAxis: {
type: 'time',
min: getStartTime(1691568181000),
max: getStartTime(1691568181000 + 3600 * 24 * 1000),
splitNumber: 10,
// interval: 60*3600*1000,
// scale: true,
axisLabel: {
// rotate: -15,
formatter: function (val) {
return new Date(val).toLocaleTimeString()
}
},
axisTick: {
show: true
},
splitLine: {
show: false,
}
},
yAxis: [{
axisLine: {
// show: false,
lineStyle: {
color: ''
}
},
axisLabel: {
fontSize: 14,
},
axisTick: {
show: false,
},
splitLine: {
show: true
},
data: categories
}, {
axisLine: {
// show: false,
lineStyle: {
color: ''
}
},
data: []
}],
series: [
{
type: 'custom',
renderItem: renderItem,
itemStyle: {
opacity: 0.8
},
encode: {
x: [1, 2],
y: 0
},
data: data
}
]
};

View File

@@ -8,7 +8,7 @@
<template> <template>
<div <div
class="production-timegraph-container" class="production-timegraph-container"
style="background: #f2f4f9; flex: 1"> style="background: #f2f4f9; flex: 1; display: flex; flex-direction: column">
<el-row <el-row
class="" class=""
style=" style="
@@ -24,12 +24,16 @@
:formConfigs="searchBarFormConfig" :formConfigs="searchBarFormConfig"
ref="search-bar" ref="search-bar"
:remove-blue="true" :remove-blue="true"
@select-changed="handleSearchBarChanged"
@headBtnClick="handleSearchBarBtnClick" /> @headBtnClick="handleSearchBarBtnClick" />
</el-row> </el-row>
<el-row <el-row
class="" class=""
style=" style="
flex: 1;
display: flex;
flex-direction: column;
margin-bottom: 12px; margin-bottom: 12px;
background: #fff; background: #fff;
padding: 16px 16px 24px; padding: 16px 16px 24px;
@@ -37,7 +41,7 @@
"> ">
<div class="blue-title">设备产量时序图</div> <div class="blue-title">设备产量时序图</div>
<div class="main-area"> <div class="main-area" style="flex: 1">
<div class="graphs" v-if="graphList.length"> <div class="graphs" v-if="graphList.length">
<LineChart :config="templateConfig" /> <LineChart :config="templateConfig" />
</div> </div>
@@ -70,6 +74,7 @@
<script> <script>
import LineChart from './components/lineChart.vue'; import LineChart from './components/lineChart.vue';
// import response from './response.json';
export default { export default {
name: 'SGProduction', name: 'SGProduction',
@@ -77,6 +82,7 @@ export default {
props: {}, props: {},
data() { data() {
return { return {
startTime: null, // new Date(2023, 8, 26, 0, 0, 0, 0).getTime(),
accumulators: new Map(), accumulators: new Map(),
searchBarFormConfig: [ searchBarFormConfig: [
{ {
@@ -85,6 +91,7 @@ export default {
placeholder: '请选择产线', placeholder: '请选择产线',
selectOptions: [], selectOptions: [],
param: 'lineId', param: 'lineId',
onchange: true,
}, },
{ {
type: 'select', type: 'select',
@@ -108,6 +115,7 @@ export default {
// defaultTime: ['00:00:00', '23:59:59'], // defaultTime: ['00:00:00', '23:59:59'],
placeholder: '选择日期', placeholder: '选择日期',
param: 'recordTime', param: 'recordTime',
required: true,
}, },
{ {
type: 'button', type: 'button',
@@ -136,7 +144,18 @@ export default {
eqList: [], eqList: [],
graphList: [], graphList: [],
templateConfig: { templateConfig: {
color: ['#283D68', '#FFB61F', '#4481FF', '#5AD8A6', '#E97466'], color: [
'#283D68',
'#FFB61F',
'#4481FF',
'#5AD8A6',
'#E97466',
'#ccc', //<=== 需按情况更新
'#ccc',
'#ccc',
'#ccc',
'#ccc',
],
grid: { grid: {
top: 48, top: 48,
left: 48, left: 48,
@@ -159,25 +178,30 @@ export default {
tooltip: { tooltip: {
show: true, show: true,
trigger: 'axis', trigger: 'axis',
}, formatter: function (params) {
xAxis: { return `
type: 'category', <div style="margin-bottom: 8px;">${new Date(
boundaryGap: true, +params[0].name
axisTick: { ).toLocaleTimeString()}</div>
// show: false, ${params
alignWithLabel: true, .map(({ seriesName, color, data }) =>
lineStyle: { data != null
color: '#0003', ? `
}, <div style="min-width: 160px; display: flex; justify-content: space-between; align-items: center;">
<span style="display: flex; align-items: center;">
<span style="display: inline-block; margin-right: 8px; width: 12px; height: 12px; background: ${color}"></span>
<span style="">${seriesName}</span>
</span>
<span style="color: #c7c7c7; text-align: right;">${data}</span>
</div>
`
: ''
)
.join('')}
`;
}, },
axisLabel: {
color: '#0007',
},
data: [],
// data: Array(24)
// .fill(1)
// .map((item, index) => `${index}:00`),
}, },
xAxis: null,
yAxis: { yAxis: {
type: 'value', type: 'value',
name: '产量', name: '产量',
@@ -186,9 +210,12 @@ export default {
fontSize: 14, fontSize: 14,
align: 'center', align: 'center',
}, },
nameGap: 26, axisLine: {
show: true,
},
max: function (value) { max: function (value) {
return value.max + Math.floor(value.max / 5); return value.max + Math.ceil(value.max / 4);
// return value.max + 50
}, },
}, },
series: [ series: [
@@ -223,16 +250,29 @@ export default {
}, },
}; };
}, },
computed: {
timeArr() {
return Array(24)
.fill(this.startTime)
.map((time, index) => time + index * 3600000);
},
},
created() { created() {
this.initProductline(); this.initProductline();
this.initWorksection(); this.initWorksection();
this.initEquipment(); this.initEquipment();
this.getList(); // this.getList();
}, },
methods: { methods: {
handleSearchBarBtnClick({ btnName, ...payload }) { handleSearchBarBtnClick({ btnName, ...payload }) {
switch (btnName) { switch (btnName) {
case 'search': case 'search':
if (!payload.recordTime || payload.recordTime.length <= 0) {
this.$message.error('请选择时间段');
return;
}
this.startTime = new Date(payload.recordTime).getTime();
this.queryParams.lineId = payload.lineId || null; this.queryParams.lineId = payload.lineId || null;
this.queryParams.sectionId = payload.sectionId || null; this.queryParams.sectionId = payload.sectionId || null;
this.queryParams.equipmentId = payload.equipmentId || null; this.queryParams.equipmentId = payload.equipmentId || null;
@@ -250,6 +290,18 @@ export default {
} }
}, },
handleSearchBarChanged({ param, value }) {
if (!value) {
this.searchBarFormConfig[1].selectOptions = [];
return;
}
switch (param) {
case 'lineId':
this.getWorksectionById(value);
break;
}
},
/** 重置查询条件 */ /** 重置查询条件 */
initQuery() { initQuery() {
this.queryParams.lineId = null; this.queryParams.lineId = null;
@@ -267,43 +319,74 @@ export default {
}); });
}, },
initState() {
this.accumulators = new Map();
this.templateConfig.series = [];
},
async getList() { async getList() {
this.initState();
const { code, data } = await this.$axios({ const { code, data } = await this.$axios({
url: '/analysis/equipment-analysis/quantity', url: '/analysis/equipment-analysis/quantity',
method: 'get', method: 'get',
params: this.queryParams, params: this.queryParams,
}); });
// const { code, data } = response;
if (code == 0) { if (code == 0) {
this.graphList = this.objectToArray(data); this.graphList = this.objectToArray(data);
// const eq1 = [ this.setXaxis();
// { totalQuantity: 20, startTime: 1693964578000 },
// { totalQuantity: 43, startTime: 1693964678000 },
// { totalQuantity: 12, startTime: 1693964778000 },
// { totalQuantity: 11, startTime: 1693964878000 },
// { totalQuantity: 98, startTime: 1693965478000 },
// { totalQuantity: 87, startTime: 1693965578000 },
// ];
// eq1.key = 'SS1';
// const eq2 = [
// { totalQuantity: 23, startTime: 1693961578000 },
// { totalQuantity: 42, startTime: 1693964578000 },
// { totalQuantity: 51, startTime: 1693965578000 },
// { totalQuantity: 18, startTime: 1693966578000 },
// { totalQuantity: 77, startTime: 1693966778000 },
// { totalQuantity: 38, startTime: 1693967578000 },
// { totalQuantity: 57, startTime: 1693969578000 },
// ];
// eq2.key = 'SS2';
// this.graphList = [eq1, eq2];
this.graphList.forEach(this.setSeries); this.graphList.forEach(this.setSeries);
} else { } else {
this.graphList = []; this.graphList = [];
this.graphList.forEach(this.setSeries);
} }
}, },
setSeries(eqArr) { setXaxis() {
const xaxis = {
type: 'category',
// interval: 12,
axisTick: {
alignWithLabel: true,
lineStyle: {
color: '#0003',
},
},
axisLabel: {
formatter: function (value, index) {
return new Date(+value)
.toLocaleTimeString()
.split(':')
.slice(0, 2)
.join(':');
},
},
data: this.timeArr,
};
this.templateConfig.xAxis = xaxis;
},
setYaxis() {},
setSeries(list) {
const data = Array(24).fill(null);
list.map((item, index) => {
const idx = this.timeArr.indexOf(item.startTime);
if (idx != -1) {
data[idx] = item.totalQuantity;
}
});
const seriesItem = {
name: list.key,
type: 'line',
symbol: 'circle',
data,
};
this.templateConfig.series.push(seriesItem);
},
setSeriesOld(eqArr) {
if (eqArr.length == 0) { if (eqArr.length == 0) {
this.templateConfig.series = []; this.templateConfig.series = [];
return; return;
@@ -330,18 +413,6 @@ export default {
}); });
}, },
/** 获得设备产量 */
getEquipmentQuantity(equipmentArr) {
return equipmentArr.map((item) => item.totalQuantity);
},
/** 获得设备产量的时间 */
getTimeinfo(equipmentArr) {
return equipmentArr.map((item) =>
new Date(item.startTime).toLocaleTimeString()
);
},
/** 准备设备数据 */ /** 准备设备数据 */
async initEquipment() { async initEquipment() {
const { code, data } = await this.$axios({ const { code, data } = await this.$axios({
@@ -390,6 +461,25 @@ export default {
} }
}, },
/** 根据产线获取工段 */
async getWorksectionById(lineId) {
const { code, data } = await this.$axios({
url: '/base/workshop-section/listByParentId',
method: 'get',
params: {
id: lineId,
},
});
if (code == 0) {
this.searchBarFormConfig[1].selectOptions = data.map((item) => {
return {
name: item.name,
id: item.id,
};
});
}
},
async submitForm() { async submitForm() {
const { code, data } = await this.$axios({ const { code, data } = await this.$axios({
url: '/analysis/equipment-analysis/quantity', url: '/analysis/equipment-analysis/quantity',

View File

@@ -0,0 +1,333 @@
{
"code": 0,
"data": {
"上片机": [
{
"inQuantity": 200,
"totalQuantity": 199,
"outQuantity": 200,
"okQuantity": 199,
"nokQuantity": 1,
"recordTime": 1695657600000,
"startTime": 1695657600000
},
{
"inQuantity": 200,
"totalQuantity": 189,
"outQuantity": 200,
"okQuantity": 189,
"nokQuantity": 11,
"recordTime": 1695661200000,
"startTime": 1695661200000
},
{
"inQuantity": 200,
"totalQuantity": 198,
"outQuantity": 200,
"okQuantity": 198,
"nokQuantity": 2,
"recordTime": 1695664800000,
"startTime": 1695664800000
},
{
"inQuantity": 200,
"totalQuantity": 197,
"outQuantity": 200,
"okQuantity": 197,
"nokQuantity": 3,
"recordTime": 1695668400000,
"startTime": 1695668400000
},
{
"inQuantity": 200,
"totalQuantity": 200,
"outQuantity": 200,
"okQuantity": 200,
"nokQuantity": 0,
"recordTime": 1695672000000,
"startTime": 1695672000000
},
{
"inQuantity": 200,
"totalQuantity": 170,
"outQuantity": 200,
"okQuantity": 170,
"nokQuantity": 30,
"recordTime": 1695675600000,
"startTime": 1695675600000
},
{
"inQuantity": 200,
"totalQuantity": 199,
"outQuantity": 200,
"okQuantity": 199,
"nokQuantity": 1,
"recordTime": 1695679200000,
"startTime": 1695679200000
},
{
"inQuantity": 200,
"totalQuantity": 199,
"outQuantity": 200,
"okQuantity": 199,
"nokQuantity": 1,
"recordTime": 1695682800000,
"startTime": 1695682800000
},
{
"inQuantity": 200,
"totalQuantity": 195,
"outQuantity": 200,
"okQuantity": 195,
"nokQuantity": 5,
"recordTime": 1695686400000,
"startTime": 1695686400000
},
{
"inQuantity": 200,
"totalQuantity": 198,
"outQuantity": 200,
"okQuantity": 198,
"nokQuantity": 2,
"recordTime": 1695690000000,
"startTime": 1695690000000
},
{
"inQuantity": 200,
"totalQuantity": 199,
"outQuantity": 200,
"okQuantity": 199,
"nokQuantity": 1,
"recordTime": 1695693600000,
"startTime": 1695693600000
},
{
"inQuantity": 200,
"totalQuantity": 197,
"outQuantity": 200,
"okQuantity": 197,
"nokQuantity": 3,
"recordTime": 1695697200000,
"startTime": 1695697200000
},
{
"inQuantity": 200,
"totalQuantity": 197,
"outQuantity": 200,
"okQuantity": 197,
"nokQuantity": 3,
"recordTime": 1695700800000,
"startTime": 1695700800000
},
{
"inQuantity": 200,
"totalQuantity": 196,
"outQuantity": 200,
"okQuantity": 196,
"nokQuantity": 4,
"recordTime": 1695704400000,
"startTime": 1695704400000
},
{
"inQuantity": 200,
"totalQuantity": 193,
"outQuantity": 200,
"okQuantity": 193,
"nokQuantity": 7,
"recordTime": 1695708000000,
"startTime": 1695708000000
},
{
"inQuantity": 200,
"totalQuantity": 190,
"outQuantity": 200,
"okQuantity": 190,
"nokQuantity": 10,
"recordTime": 1695711600000,
"startTime": 1695711600000
},
{
"inQuantity": 200,
"totalQuantity": 199,
"outQuantity": 200,
"okQuantity": 199,
"nokQuantity": 1,
"recordTime": 1695715200000,
"startTime": 1695715200000
},
{
"inQuantity": 200,
"totalQuantity": 200,
"outQuantity": 200,
"okQuantity": 200,
"nokQuantity": 0,
"recordTime": 1695718800000,
"startTime": 1695718800000
},
{
"inQuantity": 200,
"totalQuantity": 200,
"outQuantity": 200,
"okQuantity": 200,
"nokQuantity": 0,
"recordTime": 1695722400000,
"startTime": 1695722400000
},
{
"inQuantity": 200,
"totalQuantity": 198,
"outQuantity": 200,
"okQuantity": 198,
"nokQuantity": 2,
"recordTime": 1695726000000,
"startTime": 1695726000000
},
{
"inQuantity": 200,
"totalQuantity": 199,
"outQuantity": 200,
"okQuantity": 199,
"nokQuantity": 1,
"recordTime": 1695729600000,
"startTime": 1695729600000
},
{
"inQuantity": 200,
"totalQuantity": 189,
"outQuantity": 200,
"okQuantity": 189,
"nokQuantity": 11,
"recordTime": 1695733200000,
"startTime": 1695733200000
},
{
"inQuantity": 200,
"totalQuantity": 179,
"outQuantity": 200,
"okQuantity": 179,
"nokQuantity": 21,
"recordTime": 1695736800000,
"startTime": 1695736800000
},
{
"inQuantity": 200,
"totalQuantity": 200,
"outQuantity": 200,
"okQuantity": 200,
"nokQuantity": 0,
"recordTime": 1695740400000,
"startTime": 1695740400000
}
],
"下片机": [
{
"inQuantity": 200,
"totalQuantity": 190,
"outQuantity": 200,
"okQuantity": 190,
"nokQuantity": 10,
"recordTime": 1695672000000,
"startTime": 1695672000000
},
{
"inQuantity": 200,
"totalQuantity": 177,
"outQuantity": 200,
"okQuantity": 177,
"nokQuantity": 23,
"recordTime": 1695675600000,
"startTime": 1695675600000
},
{
"inQuantity": 200,
"totalQuantity": 198,
"outQuantity": 200,
"okQuantity": 198,
"nokQuantity": 2,
"recordTime": 1695679200000,
"startTime": 1695679200000
},
{
"inQuantity": 200,
"totalQuantity": 200,
"outQuantity": 200,
"okQuantity": 200,
"nokQuantity": 0,
"recordTime": 1695682800000,
"startTime": 1695682800000
},
{
"inQuantity": 200,
"totalQuantity": 185,
"outQuantity": 200,
"okQuantity": 185,
"nokQuantity": 15,
"recordTime": 1695686400000,
"startTime": 1695686400000
},
{
"inQuantity": 200,
"totalQuantity": 198,
"outQuantity": 200,
"okQuantity": 198,
"nokQuantity": 2,
"recordTime": 1695690000000,
"startTime": 1695690000000
},
{
"inQuantity": 200,
"totalQuantity": 200,
"outQuantity": 200,
"okQuantity": 200,
"nokQuantity": 0,
"recordTime": 1695693600000,
"startTime": 1695693600000
},
{
"inQuantity": 200,
"totalQuantity": 193,
"outQuantity": 200,
"okQuantity": 193,
"nokQuantity": 7,
"recordTime": 1695697200000,
"startTime": 1695697200000
},
{
"inQuantity": 200,
"totalQuantity": 200,
"outQuantity": 200,
"okQuantity": 200,
"nokQuantity": 0,
"recordTime": 1695700800000,
"startTime": 1695700800000
},
{
"inQuantity": 200,
"totalQuantity": 200,
"outQuantity": 200,
"okQuantity": 200,
"nokQuantity": 0,
"recordTime": 1695704400000,
"startTime": 1695704400000
},
{
"inQuantity": 200,
"totalQuantity": 192,
"outQuantity": 200,
"okQuantity": 192,
"nokQuantity": 8,
"recordTime": 1695708000000,
"startTime": 1695708000000
},
{
"inQuantity": 200,
"totalQuantity": 199,
"outQuantity": 200,
"okQuantity": 199,
"nokQuantity": 1,
"recordTime": 1695711600000,
"startTime": 1695711600000
}
]
}
}

View File

@@ -0,0 +1,328 @@
import * as echarts from 'echarts'
function getStartTime(timestamp) {
return new Date(new Date(timestamp).toLocaleDateString()).getTime();
}
function renderItem(params, api) {
var categoryIndex = api.value(0);
var start = api.coord([api.value(1), categoryIndex]);
var end = api.coord([api.value(2), categoryIndex]);
var height = api.size([0, 1])[1] * 2;
var rectShape = echarts.graphic.clipRectByRect(
{
x: start[0],
y: start[1] - height / 2,
width: end[0] - start[0],
height: height,
},
{
x: params.coordSys.x,
y: params.coordSys.y - 16,
width: params.coordSys.width,
height: params.coordSys.height,
}
);
return (
rectShape && {
type: 'rect',
transition: ['shape'],
shape: rectShape,
style: api.style(),
}
);
}
// unused
function getXaxisRange(startTime) {
return Array(24)
.fill(startTime)
.map((item, index) => {
return new Date(item + index * 3600 * 1000)
.toLocaleTimeString()
.split(':')
.slice(0, 2)
.join(':');
});
}
function getTodayStart(today) {
const [y, m, d] = [
today.getFullYear(),
today.getMonth(),
today.getDate(),
];
// debugger;
return new Date(y, m, d).getTime();
}
/** 颜色配置 */
const types = [
{ name: '运行', color: '#288AFF' },
{ name: '故障', color: '#FC9C91' },
{ name: '计划停机', color: '#FFDC94' },
{ name: '空白', color: '#F2F4F9' },
];
export default class GanttGraph {
// tooltip - 基本是固定的
tooltip = {
trigger: 'item',
axisPointer: {
type: 'none',
},
formatter: (params) => {
// debugger;
return `
<div style="display: flex; flex-direction: column;">
<span>${new Date(params.value[1]).toLocaleTimeString()} ~ ${new Date(params.value[2]).toLocaleTimeString()}</span>
<div style="display: flex; align-items: center; justify-content: space-between;">
<div style="display: flex; align-items: center;">
<span class="icon" style="width: 8px; height: 8px; border-radius: 2px; background: ${params.color}"></span>
<span class="eq-name" style="margin-left: 4px;">${params.seriesName}</span>
</div>
<span class="run-status" style="margin-left: 8px; opacity: 0.6">${params.name}</span>
</div>
`
}
}
grid = []
xAxis = []
yAxis = []
series = []
constructor(el, startTime) {
this.el = el;
this.gridIndex = -1;
this.currentGraphIndex = -2;
this.startTime = new Date(startTime);
// this.startTime = new Date(new Date('2023/10/8').toLocaleDateString());
// console.log('<> Gantt Created', this.startTime);
}
// 构造一个新的 grid
makeGrid() {
this.gridIndex++;
return {
id: 'GRID_' + this.gridIndex,
// top: 12 + 128 * this.gridIndex,
top: 12 + 104 * this.gridIndex,
right: 48,
left: 88,
height: 56
}
}
// 构造一个 xAxis
makeXaxis() {
const [id1, id2] = ['' + Math.random(), '' + Math.random()]
return [
{
id: id1,
gridIndex: this.gridIndex,
axisTick: {
alignWithLabel: true,
inside: true,
},
type: 'time',
min: getTodayStart(this.startTime),
max: getStartTime(this.startTime.getTime() + 3600 * 24 * 1000),
splitNumber: 10,
axisLabel: {
margin: 12,
formatter: function (val) {
return new Date(val)
.toLocaleTimeString()
.split(':')
.slice(0, 2)
.join(':');
},
},
axisLine: {
lineStyle: {
color: '#0005',
},
},
boundaryGap: false,
// data: getXaxisRange(getTodayStart(new Date())),
},
{
id: id2,
gridIndex: this.gridIndex,
axisLabel: { show: false },
axisLine: { show: false },
},
]
}
// 构造一个 yAxis
makeYaxis(equipmentName) {
const [id1, id2] = ['' + Math.random(), '' + Math.random()]
return [
// 主y轴
{
id: id1,
gridIndex: this.gridIndex,
type: 'value',
splitLine: { show: false },
name: equipmentName,
nameLocation: 'center',
nameGap: 14,
nameRotate: 0,
nameTextStyle: {
fontSize: 16,
color: '#000A'
},
axisLine: {
show: true,
lineStyle: {
color: '#0005',
},
},
axisLabel: { show: false },
axisTick: { show: false },
},
// 辅y轴
{
id: id2,
gridIndex: this.gridIndex,
type: 'value',
splitLine: { show: false },
axisLine: {
show: true,
lineStyle: {
color: '#0005',
},
},
axisLabel: { show: false },
axisTick: { show: false },
},
]
}
// 构造一个 series
makeSeries({ equipmentName, arr }) {
this.currentGraphIndex += 2;
const bgStartTime = this.startTime.getTime();
const bgEndTime = bgStartTime + 3600 * 24 * 1000;
return [
// 沉默的背景
{
xAxisIndex: this.currentGraphIndex,
yAxisIndex: this.currentGraphIndex,
type: 'custom',
renderItem: renderItem,
silent: true,
itemStyle: {
opacity: 0.8,
},
encode: {
x: [1, 2],
y: 0,
},
data: [
{
name: '无数据',
value: [0, bgStartTime, bgEndTime, 0],
tooltip: { show: false },
itemStyle: {
color: '#F2F4F9',
opacity: 0.3,
}
},
]
},
{
name: equipmentName,
xAxisIndex: this.currentGraphIndex,
yAxisIndex: this.currentGraphIndex,
type: 'custom',
renderItem: renderItem,
itemStyle: {
opacity: 0.8,
},
encode: {
x: [1, 2],
y: 0,
},
data: arr.map(item => ({
name: ['运行', '故障', '计划停机'][item.status],
value: [0, item.startTime, item.startTime + item.duration * 60 * 1000, 0],
itemStyle: {
color: types[item.status].color,
}
})),
},
]
}
init(data) {
if (!this.el) throw new Error('没有可供echarts初始化的容器')
if (typeof this.el == 'string') {
this.el = document.querySelector(this.el);
}
this.chart = echarts.init(this.el);
this.handleProps(data);
setTimeout(() => {
// debugger;
this.chart.setOption(this.option);
}, 200);
}
update(data) {
this.clear();
this.init(data);
}
resize() {
this.chart.resize();
}
get option() {
return {
tooltip: this.tooltip,
grid: this.grid,
xAxis: this.xAxis,
yAxis: this.yAxis,
series: this.series,
}
}
// 每次 graphList 刷新都会重新渲染整个所有图表
// 可以改进的地方:添加一个 handleAdd() 方法,一次添加一个新的
handleProps(props) {
// props 是父组件的 graphList
console.log('props: ', props);
props.forEach(eqArr => {
this.grid.push(this.makeGrid());
this.xAxis.push(...this.makeXaxis());
this.yAxis.push(...this.makeYaxis(eqArr.key));
this.series.push(...this.makeSeries({ equipmentName: eqArr.key, arr: eqArr }))
});
}
// handleAdd
handleAdd() { }
clear() {
this.grid = [];
this.xAxis = [];
this.yAxis = [];
this.series = [];
this.currentGraphIndex = -2;
this.gridIndex = -1;
this.chart.dispose();
}
// print option
print() {
console.log(JSON.stringify(this.option, null, 2));
}
}

View File

@@ -6,30 +6,19 @@
--> -->
<template> <template>
<div <div class="status-timegraph-container" style="background: #f2f4f9; flex: 1; display: flex; flex-direction: column">
class="status-timegraph-container" <el-row class="" style="
style="background: #f2f4f9; flex: 1; display: flex; flex-direction: column">
<el-row
class=""
style="
margin-bottom: 12px; margin-bottom: 12px;
background: #fff; background: #fff;
padding: 16px 16px 0; padding: 16px 16px 0;
border-radius: 8px; border-radius: 8px;
"> ">
<div class="blue-title">生产节拍时序图</div> <div class="blue-title">生产节拍时序图</div>
<!-- <h1>设备状态时序图</h1> --> <SearchBar :formConfigs="searchBarFormConfig" ref="search-bar" :remove-blue="true"
<!-- 搜索工作栏 --> @select-changed="handleSearchBarSelectChange" @headBtnClick="handleSearchBarBtnClick" />
<SearchBar
:formConfigs="searchBarFormConfig"
ref="search-bar"
:remove-blue="true"
@headBtnClick="handleSearchBarBtnClick" />
</el-row> </el-row>
<el-row <el-row class="" style="
class=""
style="
height: 1px; height: 1px;
flex: 1; flex: 1;
margin-bottom: 12px; margin-bottom: 12px;
@@ -48,138 +37,34 @@
<div class="icon running"></div> <div class="icon running"></div>
<div>运行中</div> <div>运行中</div>
</div> </div>
<!-- <div class="legend">
<div class="icon waiting"></div>
<div>待机</div>
</div> -->
<div class="legend"> <div class="legend">
<div class="icon fault"></div> <div class="icon fault"></div>
<div>故障</div> <div>故障</div>
</div> </div>
<!-- <div class="legend">
<div class="icon lack"></div>
<div>缺料</div>
</div>
<div class="legend">
<div class="icon full"></div>
<div>满料</div>
</div> -->
<div class="legend"> <div class="legend">
<div class="icon stop"></div> <div class="icon stop"></div>
<div>计划停机</div> <div>计划停机</div>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<div <div class="main-area" style="flex: 1; display: flex; flex-direction: column">
class="main-area" <div class="graphs" v-show="graphList.length" id="status-chart" style="height: 1px; flex: 1"></div>
style="flex: 1; display: flex; flex-direction: column">
<div
class="graphs"
v-show="graphList.length"
id="status-chart"
style="height: 1px; flex: 1">
<!-- <div class="graph" v-for="eq in graphList" :key="eq.key">
<h2 class="graph-title">{{ eq.key }}</h2>
<div class="graph-content">
<el-popover
trigger="hover"
v-for="blc in eq"
:key="blc.startTime"
:title="
blc.status == 0
? '运行'
: blc.status == 2
? '故障'
: '计划停机'
"
placement="top"
:content="new Date(blc.startTime).toLocaleTimeString()">
<div
slot="reference"
:key="blc.startTime + '__div'"
class="graph-item-fixed tick"
:class="{
running: blc.status == 0,
fault: blc.status == 2,
stop: blc.status == 1,
}"
:style="{ height: '32px', width: blc.duration * 2 + 'px' }"
:data-time="
new Date(blc.startTime).toLocaleTimeString()
"></div>
</el-popover>
</div>
</div> -->
</div>
<h2 v-if="!graphList || graphList.length == 0" class="no-data-bg"></h2> <h2 v-if="!graphList || graphList.length == 0" class="no-data-bg"></h2>
</div> </div>
</el-row> </el-row>
<!-- 对话框(添加 / 修改) --> <!-- 对话框(添加 / 修改) -->
<base-dialog <base-dialog dialogTitle="添加设备" :dialogVisible="open" width="500px" @close="open = false" @cancel="open = false"
dialogTitle="添加设备"
:dialogVisible="open"
width="500px"
@close="open = false"
@cancel="open = false"
@confirm="submitForm"> @confirm="submitForm">
<el-select <el-select v-if="open" style="width: 100%" v-model="queryParams.equipmentId" placeholder="请选择一个设备">
v-if="open" <el-option v-for="eq in eqList" :key="eq.id" :value="eq.id" :label="eq.name"></el-option>
style="width: 100%"
v-model="queryParams.equipmentId"
placeholder="请选择一个设备">
<el-option
v-for="eq in eqList"
:key="eq.id"
:value="eq.id"
:label="eq.name"></el-option>
</el-select> </el-select>
</base-dialog> </base-dialog>
</div> </div>
</template> </template>
<script> <script>
import * as echarts from 'echarts'; import Gantt from './chart';
var types = [
{ name: '运行', color: '#5ad8a6' },
{ name: '故障', color: '#fc9c91' },
{ name: '计划停机', color: '#000' },
];
function getStartTime(timestamp) {
return new Date(new Date(timestamp).toLocaleDateString()).getTime();
}
function renderItem(params, api) {
var categoryIndex = api.value(0);
var start = api.coord([api.value(1), categoryIndex]);
var end = api.coord([api.value(2), categoryIndex]);
var height = api.size([0, 1])[1] * 1;
// var height = api.size([0, 1])[1] * 0.8;
// var height = 56;
var rectShape = echarts.graphic.clipRectByRect(
{
x: start[0],
y: start[1] - height / 2,
width: end[0] - start[0],
height: height,
},
{
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height,
}
);
return (
rectShape && {
type: 'rect',
transition: ['shape'],
shape: rectShape,
style: api.style(),
}
);
}
export default { export default {
name: 'SGStatus', name: 'SGStatus',
@@ -195,6 +80,7 @@ export default {
placeholder: '请选择产线', placeholder: '请选择产线',
selectOptions: [], selectOptions: [],
param: 'lineId', param: 'lineId',
onchange: true,
}, },
{ {
type: 'select', type: 'select',
@@ -207,18 +93,13 @@ export default {
{ {
type: 'datePicker', type: 'datePicker',
label: '时间段', label: '时间段',
// dateType: 'daterange', // datetimerange
dateType: 'date', dateType: 'date',
// format: 'yyyy-MM-dd HH:mm:ss',
format: 'yyyy-MM-dd', format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd HH:mm:ss', valueFormat: 'yyyy-MM-dd HH:mm:ss',
// valueFormat: 'timestamp',
rangeSeparator: '-', rangeSeparator: '-',
// startPlaceholder: '开始日期',
// endPlaceholder: '结束日期',
placeholder: '选择日期', placeholder: '选择日期',
// defaultTime: ['00:00:00', '23:59:59'],
param: 'recordTime', param: 'recordTime',
required: true,
}, },
{ {
type: 'button', type: 'button',
@@ -244,8 +125,11 @@ export default {
recordTime: null, recordTime: null,
}, },
graphList: [], graphList: [],
existingEquipments: [],
open: false, open: false,
eqList: [], eqList: [],
startTime: null,
gantt: null
// demo: [ // demo: [
// [ // [
// { // {
@@ -266,176 +150,6 @@ export default {
// }, // },
// ], // ],
// ], // ],
chartOption: {
grid: {
top: 32,
left: 128,
right: 128,
bottom: 64,
},
tooltip: {
// show: false,
formatter: function (params) {
return (
params.marker +
params.name +
': ' +
new Date(params.value[1]).toLocaleTimeString() +
' - ' +
new Date(params.value[2]).toLocaleTimeString()
);
},
},
xAxis: {
type: 'time',
min: getStartTime(1691568181000), // <===
max: getStartTime(1691568181000 + 3600 * 24 * 1000), // <===
splitNumber: 10,
axisLabel: {
// rotate: -15,
formatter: function (val) {
return new Date(val).toLocaleTimeString();
},
},
axisTick: {
show: true,
},
splitLine: {
show: false,
},
},
yAxis: [
{
interval: 40,
axisLine: {
// show: false,
lineStyle: {
color: '',
},
},
axisLabel: {
fontSize: 18,
},
axisTick: {
show: false,
},
splitLine: {
show: true,
},
// data: [], // <====
data: ['设备1', '设备2', '设备3', '设备4'],
},
{
axisLine: {
// show: false,
lineStyle: {
color: '',
},
},
data: [],
},
],
series: [
{
type: 'custom',
renderItem: renderItem,
itemStyle: {
opacity: 0.8,
},
encode: {
x: [1, 2],
y: 0,
},
// data: [], // <===
data: [
{
name: '运行',
value: [
0,
1691568181000,
1691568181000 + 60 * 60 * 1000,
60 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[0].color,
},
},
},
{
name: '计划停机',
value: [
0,
1691578581000,
1691578581000 + 10 * 60 * 1000,
60 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[2].color,
},
},
},
{
name: '运行',
value: [
1,
1691568181000,
1691568181000 + 60 * 60 * 1000,
60 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[0].color,
},
},
},
{
name: '故障',
value: [
2,
1691538181000,
1691538181000 + 60 * 60 * 1000,
60 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[1].color,
},
},
},
{
name: '运行',
value: [
2,
1691578181000,
1691578181000 + 90 * 60 * 1000,
90 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[0].color,
},
},
},
{
name: '计划停机',
value: [
3,
1691528181000,
1691528181000 + 240 * 60 * 1000,
240 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[2].color,
},
},
},
],
},
],
},
}; };
}, },
computed: {}, computed: {},
@@ -443,17 +157,21 @@ export default {
this.initProductline(); this.initProductline();
this.initWorksection(); this.initWorksection();
this.initEquipment(); this.initEquipment();
this.getList(); // this.getList();
}, },
mounted() {}, mounted() { },
watch: { watch: {
graphList: { graphList: {
handler(val) { handler(val) {
if (val && val.length) { if (val && val.length) {
this.$nextTick(() => { this.$nextTick(() => {
if (!this.chart) this.initChart(); if (!this.gantt) {
this.setInitialConfig(); this.gantt = new Gantt('#status-chart', this.startTime);
this.handleGraphList(); this.gantt.init(val);
return;
}
this.gantt.update(val);
}); });
} }
return; return;
@@ -463,43 +181,6 @@ export default {
}, },
}, },
methods: { methods: {
setInitialConfig() {
console.log('in setInitialConfig', this.chartOption);
this.chart.setOption(this.chartOption);
},
handleGraphList() {
console.log('in handleGraphList:', this.graphList);
return;
const min = this.queryParams.recordTime
? new Date(this.queryParams.recordTime).getTime()
: this.findMin();
console.log('min is', min);
this.chartOption.xAxis.min = getStartTime(min);
this.chartOption.xAxis.max = getStartTime(min + 3600 * 24 * 1000);
this.graphList.forEach((arr) => {
this.chartOption.yAxis[0].data.push(arr.key);
arr.forEach((item) => {
this.chartOption.series[0].data.push({
name: ['运行', '故障', '计划停机'][item.status],
value: [
0,
item.startTime,
item.startTime + item.duration * 60 * 1000,
item.duration * 60 * 1000,
],
itemStyle: {
normal: {
color: types[item.status].color,
},
},
});
});
console.log('chartOptions', this.chartOption);
});
},
findMin() { findMin() {
let min = 0; let min = 0;
this.graphList.forEach((arr) => { this.graphList.forEach((arr) => {
@@ -510,11 +191,6 @@ export default {
return min; return min;
}, },
initChart() {
const el = document.getElementById('status-chart');
this.chart = echarts.init(el);
},
/** 重置查询条件 */ /** 重置查询条件 */
initQuery() { initQuery() {
this.queryParams.lineId = null; this.queryParams.lineId = null;
@@ -539,8 +215,8 @@ export default {
params: this.queryParams, params: this.queryParams,
}); });
if (code == 0) { if (code == 0) {
this.existingEquipments = Object.values(data).map((eq) => eq[0].equipmentId);
this.graphList = this.objectToArray(data); this.graphList = this.objectToArray(data);
console.log('graph list', this.graphList);
} }
}, },
@@ -592,28 +268,60 @@ export default {
} }
}, },
handleSearchBarSelectChange({ param, value }) {
if (!value) {
this.searchBarFormConfig[1].selectOptions = [];
return;
}
switch (param) {
case 'lineId':
this.$axios({
url: '/base/workshop-section/listByParentId',
method: 'get',
params: {
id: value,
},
}).then(({ code, data }) => {
if (code == 0) {
this.searchBarFormConfig[1].selectOptions = data.map((item) => {
return {
name: item.name,
id: item.id,
};
});
}
});
}
},
handleSearchBarBtnClick({ btnName, ...payload }) { handleSearchBarBtnClick({ btnName, ...payload }) {
switch (btnName) { switch (btnName) {
case 'search': case 'search':
if (!payload.recordTime || payload.recordTime.length <= 0) {
this.$message.error('请选择时间段');
return;
}
this.startTime = new Date(payload.recordTime)
this.queryParams.lineId = payload.lineId || null; this.queryParams.lineId = payload.lineId || null;
this.queryParams.sectionId = payload.sectionId || null; this.queryParams.sectionId = payload.sectionId || null;
this.queryParams.equipmentId = payload.equipmentId || null; this.queryParams.equipmentId = payload.equipmentId || null;
this.queryParams.recordTime = payload.recordTime this.queryParams.recordTime = payload.recordTime
? [ ? [
payload.recordTime, payload.recordTime,
new Date( new Date(
new Date(payload.recordTime).getTime() + 24 * 3600 * 1000 new Date(payload.recordTime).getTime() + 24 * 3600 * 1000
) )
.toLocaleDateString() .toLocaleDateString()
.split('/') .split('/')
.map((value, index) => { .map((value, index) => {
if (index == 1 || index == 2) { if (index == 1 || index == 2) {
return value.padStart(2, '0'); return value.padStart(2, '0');
} }
return value; return value;
}) })
.join('-') + ' 00:00:00', .join('-') + ' 00:00:00',
] ]
: null; : null;
this.getList(); this.getList();
break; break;
@@ -624,6 +332,10 @@ export default {
}, },
async submitForm() { async submitForm() {
if (this.existingEquipments.indexOf(this.queryParams.equipmentId) >= 0) {
this.$message.error('该设备已存在');
return;
}
const { code, data } = await this.$axios({ const { code, data } = await this.$axios({
url: '/analysis/equipment-analysis/status', url: '/analysis/equipment-analysis/status',
method: 'get', method: 'get',
@@ -732,7 +444,7 @@ export default {
} }
.running { .running {
background-color: #5ad8a6; background-color: #288AFF;
// background-color: #84f04e; // background-color: #84f04e;
} }
@@ -743,7 +455,7 @@ export default {
.fault { .fault {
// background-color: #ea5b5b; // background-color: #ea5b5b;
background-color: #fc9c91; background-color: #FC9C91;
} }
.full { .full {
@@ -757,7 +469,7 @@ export default {
} }
.stop { .stop {
background-color: #000; background-color: #FFDC94;
} }
.legend-row { .legend-row {
@@ -766,7 +478,7 @@ export default {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
> .legend:not(:last-child) { >.legend:not(:last-child) {
margin-right: 12px; margin-right: 12px;
} }
@@ -809,7 +521,7 @@ export default {
background: #ccc; background: #ccc;
} }
.echarts__status-chart > div { .echarts__status-chart>div {
height: 100% !important; height: 100% !important;
width: 100% !important; width: 100% !important;
} }

View File

@@ -37,7 +37,7 @@
@cancel="handleCancel" @cancel="handleCancel"
@confirm="handleConfirm" @confirm="handleConfirm"
:before-close="handleCancel" :before-close="handleCancel"
width='70%' width='50%'
> >
<group-class-add ref="classList" @successSubmit="successSubmit" /> <group-class-add ref="classList" @successSubmit="successSubmit" />
</base-dialog> </base-dialog>
@@ -108,12 +108,6 @@ export default {
], ],
tableProps, tableProps,
tableBtn: [ tableBtn: [
this.$auth.hasPermi('base:group-classes:update')
? {
type: 'edit',
btnName: '编辑'
}
: undefined,
{ {
type: 'cancel', type: 'cancel',
btnName: '作废', btnName: '作废',
@@ -128,6 +122,12 @@ export default {
] ]
} }
}, },
this.$auth.hasPermi('base:group-classes:update')
? {
type: 'edit',
btnName: '编辑'
}
: undefined,
this.$auth.hasPermi('base:group-classes:delete') this.$auth.hasPermi('base:group-classes:delete')
? { ? {
type: 'delete', type: 'delete',

View File

@@ -1,17 +1,29 @@
<template> <template>
<el-form ref="form" :rules="rules" label-width="80px" :model="form"> <el-form ref="form" :rules="rules" label-width="80px" :model="form">
<el-form-item label="班组名称" prop="name"> <el-row>
<el-input v-model="form.name"></el-input> <el-col :span="12">
</el-form-item> <el-form-item label="班组名称" prop="name">
<el-form-item label="班组编码" prop="code"> <el-input v-model="form.name"></el-input>
<el-input v-model="form.code" disabled></el-input> </el-form-item>
</el-form-item> </el-col>
<el-form-item label="班组人数" prop="num"> <el-col :span="12">
<el-input-number v-model="form.num" :min="1" :max="99999999" style="width: 100%;"></el-input-number> <el-form-item label="班组编码" prop="code">
</el-form-item> <el-input v-model="form.code" disabled></el-input>
<el-form-item label="班组组长" prop="leaderName"> </el-form-item>
<el-input v-model="form.leaderName"></el-input> </el-col>
</el-form-item> </el-row>
<el-row>
<el-col :span="12">
<el-form-item label="班组人数" prop="num">
<el-input-number v-model="form.num" :min="1" :max="99999999" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班组组长" prop="leaderName">
<el-input v-model="form.leaderName"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
</template> </template>
<script> <script>

View File

@@ -308,6 +308,16 @@ export default {
} }
</script> </script>
<style lang='scss'> <style lang='scss'>
.demo-form-inline {
.el-date-editor .el-range__icon {
font-size: 16px;
color: #0B58FF;
}
.el-input__prefix .el-icon-date {
font-size: 16px;
color: #0B58FF;
}
}
.groupTeamScheduling { .groupTeamScheduling {
background-color: #F2F4F9; background-color: #F2F4F9;
.operationArea { .operationArea {

View File

@@ -35,8 +35,7 @@
" /> " />
</div> </div>
</div> </div>
<div v-else style="margin-top: 20px; color: #c7c7c7; text-align: center"> <div v-else class="no-data-bg">
暂无数据
</div> </div>
</div> </div>
</template> </template>

View File

@@ -45,7 +45,7 @@ export default {
axisLine: { axisLine: {
show: true, show: true,
lineStyle: { lineStyle: {
color: '#ccc', color: '#777',
}, },
}, },
axisTick: { axisTick: {
@@ -63,7 +63,7 @@ export default {
axisLine: { axisLine: {
show: true, show: true,
lineStyle: { lineStyle: {
color: '#ccc', color: '#777',
}, },
}, },
name: '数量', name: '数量',

View File

@@ -33,7 +33,13 @@
:data-value="fc.id" :data-value="fc.id"
class="factory-list__item" class="factory-list__item"
:class="{ 'is-current': fc.id == currentFactory?.id }"> :class="{ 'is-current': fc.id == currentFactory?.id }">
{{ fc.label }} <span>
{{ fc.label }}
</span>
<svg-icon
v-if="fc.id == currentFactory?.id"
icon-class="Confirm"
style="height: 14px; width: 14px" />
</li> </li>
</ul> </ul>
<div v-else style="color: #0008; width: 128px; text-align: center"> <div v-else style="color: #0008; width: 128px; text-align: center">
@@ -93,15 +99,7 @@
v-if="list.length" v-if="list.length"
:equipment-list="list" :equipment-list="list"
:render="renderKey" /> :render="renderKey" />
<div <div v-if="list.length == 0" class="no-data-bg"></div>
v-if="list.length == 0"
style="
color: #c7c7c7;
text-align: center;
margin-top: 20px;
">
没有设备
</div>
</div> </div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@@ -507,9 +505,12 @@ li {
.factory-list__item { .factory-list__item {
font-size: 16px; font-size: 16px;
line-height: 1; line-height: 1;
padding: 8px 64px 8px 16px; padding: 8px 16px 8px 16px;
/* min-width: 64px; */ min-width: 128px;
position: relative; position: relative;
display: flex;
align-items: center;
justify-content: space-between;
} }
.factory-list__item:hover, .factory-list__item:hover,
@@ -518,13 +519,13 @@ li {
color: #0b58ff; color: #0b58ff;
} }
.factory-list__item.is-current::after { /* .factory-list__item.is-current::after {
content: '√'; content: '√';
position: absolute; position: absolute;
top: 8px; top: 8px;
right: 16px; right: 16px;
font-weight: bold; font-weight: bold;
} } */
.custom-tree-class >>> .el-tree-node__content { .custom-tree-class >>> .el-tree-node__content {
width: 100%; width: 100%;
@@ -551,8 +552,8 @@ li {
.custom-icon-class { .custom-icon-class {
margin-right: 8px; margin-right: 8px;
width: 20px; width: 20px;
height: 24px; height: 20px;
background: url('../../../assets/images/tree-icon-1.png') 100% / contain background: url('../../../assets/images/Qian.png') center center / contain
no-repeat; no-repeat;
} }

View File

@@ -116,7 +116,7 @@ export default {
}, },
{ {
_action: 'params-monitor', _action: 'params-monitor',
label: '参数监控', label: '操作',
subcomponent: { subcomponent: {
props: ['injectData'], props: ['injectData'],
render: function (h) { render: function (h) {
@@ -136,7 +136,7 @@ export default {
}, },
}, },
}, },
'查看监控' '查看详情'
); );
}, },
}, },

271
src/views/print/design.vue Normal file
View File

@@ -0,0 +1,271 @@
<!--
filename: design.vue
author: liubin
date: 2023-10-07 13:14:40
description: 设计打印模板
-->
<template>
<div class="print-design">
<section id="menu">
<NavMenu @action="handleAction" :menu="menu" />
</section>
<section id="main-panel">
<section id="left-panel" v-drag="'right'">
<h2>组件</h2>
<div id="components"></div>
</section>
<section id="edit-panel"></section>
<section id="right-panel">
<h2>设置</h2>
<div id="settings"></div>
</section>
</section>
</div>
</template>
<script>
import vDrag from './drag'
import { hiprint, defaultElementTypeProvider } from "vue-plugin-hiprint";
hiprint.init({
providers: [defaultElementTypeProvider()],
});
const NavSelector = {
name: 'NavSelector',
props: ['name', 'options'],
data() {
return {
open: false,
};
},
methods: {
handleOpen() {
// this.$emit('closeOtherMenu', this)
// this.$emit('hover', this)
this.open = !this.open;
},
handleClickItem(e) {
this.$emit('click', e.target.innerText)
},
// 暴露一个close方法供父组件调用
closeSubMenu() {
this.open = false;
},
},
render: function (h) {
return (
<li class={'nav-selector__item'} onMouseenter={this.handleOpen} onMouseleave={this.handleOpen}>
<span class={'nav-selector__name'}>{this.name}</span>
<ul class={`${this.open ? '' : 'hidden'} nav-selector__submenu-items`}>
{this.options.map((item) => {
return <li onClick={this.handleClickItem}>{item}</li>;
})}
</ul>
</li>
);
},
};
const NavMenu = {
name: 'NavMenu',
props: ['menu'],
components: { NavSelector },
data() {
return {}
},
methods: {
handleCloseOtherMenu(vueInstance) {
this.$children.forEach(vm => {
if (vm != vueInstance) {
vm.closeSubMenu()
}
})
},
handleItemClicked(e) {
this.$emit('action', e);
}
},
render: function (h) {
return (
<ul class={'nav-menu'}>
{this.menu.map((item) => {
return <NavSelector onClick={this.handleItemClicked} onCloseOtherMenu={this.handleCloseOtherMenu} name={item.name} options={item.options} />;
})}
</ul>
);
},
};
export default {
name: 'PrintDesignPage',
components: { NavMenu },
props: {},
directives: { drag: vDrag },
data() {
return {
hiprintTemplate: null,
menu: [
{
name: '菜单',
options: ['新建', '打开', '保存', '另存为', '导出', '打印', '退出'],
},
{
name: '视图',
options: ['编辑', '布局'],
},
],
};
},
computed: {},
mounted() {
this.loadSideBar();
this.loadDesigner();
},
methods: {
loadDesigner() {
$("#edit-panel").empty();
this.hiprintTemplate = new hiprint.PrintTemplate({
settingContainer: "#settings",
});
this.hiprintTemplate.design("#edit-panel");
},
loadSideBar() {
this.loadProviders();
this.loadSettings();
},
loadProviders() { },
loadSettings() { },
handleAction(name) {
console.log('name', name)
}
},
};
</script>
<style scoped lang="scss">
::-webkit-scrollbar {
width: 0;
}
::-webkit-scrollbar-button {
// background: red;
}
.print-design {
height: 100vh;
display: flex;
flex-direction: column;
}
#main-panel {
height: 1px;
flex: 1;
display: grid;
grid-template-columns: 1fr 3fr 1fr;
grid-auto-rows: 1fr;
grid-template-areas: 'left main right';
}
#menu {
// background: black;
// color: white;
background: #fff;
border-bottom: 1px solid #ccc;
>ul {
// background: #32aa32;
padding: 0;
list-style: none;
margin: 0;
margin-left: 10px;
display: flex;
}
}
#left-panel {
overflow: auto;
grid-area: left;
background: #fff;
h2 {
margin: 0;
font-weight: 400;
padding: 4px 20px;
border-bottom: 1px solid #ccc;
background: #ccc;
}
}
#edit-panel {
padding: 20px;
overflow: auto;
grid-area: main;
justify-items: center;
background: #fff;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
}
#right-panel {
overflow: auto;
grid-area: right;
background: #fff;
h2 {
margin: 0;
font-weight: 400;
padding: 4px 12px;
border-bottom: 1px solid #ccc;
background: #ccc;
}
}
</style>
<style lang="scss">
// 子组件的样式
.nav-selector__item {
font-size: 18px;
letter-spacing: 1px;
// font-weight: 600;
padding: 6px 12px;
line-height: 1.25;
position: relative;
cursor: pointer;
&:hover {
background: #f1f1f1;
}
ul {
list-style: none;
margin: 0;
padding: 0;
position: absolute;
top: 100%;
min-width: 128px;
left: 0px;
background: #fff;
border: 1px solid #ccc;
// border-radius: 4px;
margin: 0;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
z-index: 100;
li {
padding: 6px 12px;
cursor: pointer;
&:hover {
background: #f1f1f1;
}
}
}
.hidden {
display: none;
}
}
</style>

41
src/views/print/drag.css Normal file
View File

@@ -0,0 +1,41 @@
.v-drag-border-left {
border-left: 1px solid #ccc;
}
.v-drag-border-left > div.pseudo-border {
position: absolute;
top: 0;
right: -2px;
width: 4px;
height: 100%;
background: transparent;
z-index: 1;
transition: all linear 0.3s;
cursor: col-resize;
}
.v-drag-border-left > div.pseudo-border:hover {
background: #ccc;
}
.v-drag-border-right {
/* border-right: 1px solid #ccc; */
position: relative;
transition: width linear 0.3s;
}
.v-drag-border-right > div.pseudo-border {
position: absolute;
top: 0;
right: -2px;
width: 4px;
height: 100%;
background: transparent;
z-index: 1;
transition: all linear 0.3s;
cursor: col-resize;
}
.v-drag-border-right > div.pseudo-border:hover {
background: #ccc;
}

96
src/views/print/drag.js Normal file
View File

@@ -0,0 +1,96 @@
import './drag.css';
export default {
bind(el, { name, value }, vnode) {
switch (name) {
case 'drag': {
if (['left', 'right', 'top', 'bottom'].indexOf(value) == -1) return;
// 创建边框子元素
const border = document.createElement('div');
border.classList.add("pseudo-border");
el.appendChild(border);
// 添加拖拽样式
addDragStyle(el, value);
// 添加拖拽事件
addDragEvent(el, border);
break;
}
}
},
// inserted()
// update()
// componentUpdated()
// unbind()
}
function addDragEvent(el, border, direction) {
// border.setAttribute('draggable', true)
const onMouseMove = function (e) {
console.log('move', e);
}
const onMouseUp = function (e) {
console.log('up')
border.removeEventListener('mousemove', onMouseMove);
}
border.addEventListener('mousedown', function (e) {
console.log('down');
document.body.removeEventListener('mouseup', onMouseUp)
document.body.addEventListener('mouseup', onMouseUp)
border.addEventListener('mousemove', onMouseMove);
});
// TODO: 有点bug
// el.addEventListener('mousedown', function (e) {
// console.log('mousedown', e);
// const { clientX, clientY } = e;
// const { left, top, width, height } = el.getBoundingClientRect();
// const { offsetLeft, offsetTop } = el;
// const startX = clientX;
// const startY = clientY;
// const startLeft = left;
// const startTop = top;
// const startWidth = width;
// const startHeight = height;
// const startOffsetLeft = offsetLeft;
// const startOffsetTop = offsetTop;
// const move = function (e) {
// console.log('mousemove', e);
// const { clientX, clientY } = e;
// const offsetX = clientX - startX;
// const offsetY = clientY - startY;
// switch (direction) {
// case 'left': {
// el.style.left = startLeft + offsetX + 'px';
// el.style.width = startWidth - offsetX + 'px';
// break;
// }
// case 'right': {
// el.style.width = startWidth + offsetX + 'px';
// break;
// }
// case 'top': {
// el.style.top = startTop + offsetY + 'px';
// el.style.height = startHeight - offsetY + 'px';
// break;
// }
// case 'bottom': {
// el.style.height = startHeight + offsetY + 'px';
// break;
// }
// }
// }
// const up = function (e) {
// console.log('mouseup', e);
// document.removeEventListener('mousemove', move);
// document.removeEventListener('mouseup', up);
// }
// document.addEventListener('mousemove', move);
// document.addEventListener('mouseup', up);
// });
}
function addDragStyle(el, direction) {
el.classList.add('v-drag-border-' + direction)
}

View File

@@ -9,10 +9,10 @@
<el-form <el-form
ref="form" ref="form"
:model="dataForm" :model="dataForm"
label-width="100px" label-width="120px"
v-loading="formLoading"> v-loading="formLoading">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col> <el-col :span="12">
<el-form-item <el-form-item
label="产线" label="产线"
prop="productionId" prop="productionId"
@@ -30,7 +30,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col> <el-col :span="12">
<el-form-item <el-form-item
label="工段" label="工段"
prop="sectionId" prop="sectionId"
@@ -49,7 +49,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col> <el-col :span="12">
<el-form-item <el-form-item
label="按钮盒识别码" label="按钮盒识别码"
prop="buttonId" prop="buttonId"
@@ -85,7 +85,7 @@
</el-form-item> --> </el-form-item> -->
</el-col> </el-col>
<el-col> <el-col :span="12">
<el-form-item label="按钮盒模式" prop="model"> <el-form-item label="按钮盒模式" prop="model">
<el-input <el-input
v-model="dataForm.model" v-model="dataForm.model"
@@ -94,7 +94,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col> <el-col :span="12">
<el-form-item <el-form-item
label="按钮值" label="按钮值"
prop="keyValue" prop="keyValue"
@@ -121,7 +121,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col> <el-col :span="12">
<el-form-item label="检测内容" prop="inspectionDetContent"> <el-form-item label="检测内容" prop="inspectionDetContent">
<el-input <el-input
type="textarea" type="textarea"

Some files were not shown because too many files have changed in this diff Show More