@@ -20,3 +20,4 @@ selenium-debug.log | |||
*.local | |||
package-lock.json | |||
echarts.js |
@@ -50,6 +50,7 @@ | |||
"code-brick-zj": "^1.0.2", | |||
"core-js": "^3.26.0", | |||
"crypto-js": "^4.0.0", | |||
"diagram-js": "^12.3.0", | |||
"echarts": "5.4.0", | |||
"element-ui": "2.15.12", | |||
"file-saver": "^2.0.5", | |||
@@ -64,21 +65,25 @@ | |||
"moment": "^2.29.4", | |||
"nprogress": "0.2.0", | |||
"qrcode.vue": "^1.7.0", | |||
"qs": "^6.11.2", | |||
"quill": "1.3.7", | |||
"screenfull": "5.0.2", | |||
"sortablejs": "1.10.2", | |||
"throttle-debounce": "2.1.0", | |||
"video.js": "^8.5.2", | |||
"vue": "2.7.14", | |||
"vue-count-to": "1.0.13", | |||
"vue-cropper": "0.5.8", | |||
"vue-meta": "^2.4.0", | |||
"vue-plugin-hiprint": "^0.0.54-fix", | |||
"vue-quill-editor": "^3.0.6", | |||
"vue-router": "3.4.9", | |||
"vue-video-player": "^5.0.2", | |||
"vuedraggable": "2.24.3", | |||
"vuex": "3.6.2", | |||
"xlsx": "^0.18.5", | |||
"xml-js": "1.6.11" | |||
"xml-js": "1.6.11", | |||
"yorkie": "^2.0.0" | |||
}, | |||
"devDependencies": { | |||
"@vue/cli-plugin-babel": "4.5.18", | |||
@@ -92,7 +97,7 @@ | |||
"chalk": "4.1.0", | |||
"compression-webpack-plugin": "5.0.2", | |||
"connect": "3.6.6", | |||
"eslint": "7.15.0", | |||
"eslint": "6.8.0", | |||
"eslint-config-airbnb-base": "^14.0.0", | |||
"eslint-plugin-import": "^2.26.0", | |||
"eslint-plugin-prettier": "^3.1.0", | |||
@@ -1,215 +1,218 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta charset="utf-8" /> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> | |||
<meta name="renderer" content="webkit" /> | |||
<meta | |||
name="viewport" | |||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> | |||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" /> | |||
<title><%= webpackConfig.name %></title> | |||
<!--[if lt IE 11]> | |||
<head> | |||
<meta charset="utf-8" /> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> | |||
<meta name="renderer" content="webkit" /> | |||
<meta name="viewport" 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" /> | |||
<title> | |||
<%= webpackConfig.name %> | |||
</title> | |||
<!--[if lt IE 11]> | |||
<script> | |||
window.location.href = 'html/ie.html'; | |||
</script> | |||
<![endif]--> | |||
<style> | |||
html, | |||
body, | |||
#app { | |||
height: 100%; | |||
margin: 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); | |||
} | |||
100% { | |||
-webkit-transform: rotate(360deg); | |||
-ms-transform: rotate(360deg); | |||
transform: rotate(360deg); | |||
} | |||
} | |||
@keyframes spin { | |||
0% { | |||
-webkit-transform: rotate(0deg); | |||
-ms-transform: rotate(0deg); | |||
transform: rotate(0deg); | |||
} | |||
100% { | |||
-webkit-transform: rotate(360deg); | |||
-ms-transform: rotate(360deg); | |||
transform: rotate(360deg); | |||
} | |||
} | |||
#loader-wrapper .loader-section { | |||
position: fixed; | |||
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 { | |||
left: 0; | |||
} | |||
#loader-wrapper .loader-section.section-right { | |||
right: 0; | |||
} | |||
.loaded #loader-wrapper .loader-section.section-left { | |||
-webkit-transform: translateX(-100%); | |||
-ms-transform: translateX(-100%); | |||
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); | |||
} | |||
.loaded #loader-wrapper .loader-section.section-right { | |||
-webkit-transform: translateX(100%); | |||
-ms-transform: translateX(100%); | |||
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); | |||
} | |||
.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> | |||
<style> | |||
html, | |||
body, | |||
#app { | |||
height: 100%; | |||
margin: 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); | |||
} | |||
100% { | |||
-webkit-transform: rotate(360deg); | |||
-ms-transform: rotate(360deg); | |||
transform: rotate(360deg); | |||
} | |||
} | |||
@keyframes spin { | |||
0% { | |||
-webkit-transform: rotate(0deg); | |||
-ms-transform: rotate(0deg); | |||
transform: rotate(0deg); | |||
} | |||
100% { | |||
-webkit-transform: rotate(360deg); | |||
-ms-transform: rotate(360deg); | |||
transform: rotate(360deg); | |||
} | |||
} | |||
#loader-wrapper .loader-section { | |||
position: fixed; | |||
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 { | |||
left: 0; | |||
} | |||
#loader-wrapper .loader-section.section-right { | |||
right: 0; | |||
} | |||
.loaded #loader-wrapper .loader-section.section-left { | |||
-webkit-transform: translateX(-100%); | |||
-ms-transform: translateX(-100%); | |||
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); | |||
} | |||
.loaded #loader-wrapper .loader-section.section-right { | |||
-webkit-transform: translateX(100%); | |||
-ms-transform: translateX(100%); | |||
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); | |||
} | |||
.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> | |||
</body> | |||
</html> | |||
</div> | |||
</body> | |||
</html> |
@@ -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; | |||
} |
@@ -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> |
@@ -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> |
@@ -89,6 +89,8 @@ 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; | |||
} | |||
@@ -114,6 +116,10 @@ export default { | |||
type: Object, | |||
default: () => ({}), | |||
}, | |||
disabled: { | |||
type: Boolean, | |||
default: false, | |||
} | |||
}, | |||
data() { | |||
return { | |||
@@ -32,7 +32,7 @@ export default { | |||
min-height: calc(100vh - 56px); | |||
min-width: calc(100vh - 280px); | |||
position: relative; | |||
overflow: hidden; | |||
overflow: visible; | |||
margin: 8px 14px 0px 16px; | |||
border-radius: 8px; | |||
background-color: #fff; | |||
@@ -53,6 +53,9 @@ export default { | |||
case 'delete': | |||
this.handleDelete(data); | |||
break; | |||
case 'detail': | |||
this.handleDetail(data); | |||
break; | |||
} | |||
}, | |||
// 处理搜索栏按钮 | |||
@@ -64,6 +64,11 @@ export const constantRoutes = [ | |||
component: (resolve) => require(['@/views/error/401'], resolve), | |||
hidden: true | |||
}, | |||
{ | |||
path: '/print-design', | |||
component: (resolve) => require(['@/views/print/design'], resolve), | |||
hidden: true | |||
}, | |||
{ | |||
path: '/', | |||
component: Layout, | |||
@@ -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> |
@@ -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> |
@@ -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> |
@@ -40,6 +40,57 @@ | |||
@confirm="submitForm"> | |||
<DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" /> | |||
</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> | |||
</template> | |||
@@ -55,16 +106,24 @@ import { | |||
import { getEquipmentGroupPage } from '@/api/base/equipmentGroup'; | |||
import moment from 'moment'; | |||
import { publicFormatter } from '@/utils/dict'; | |||
import basicPageMixin from '@/mixins/lb/basicPageMixin'; | |||
import BasicDrawer from './components/BasicDrawer.vue'; | |||
export default { | |||
name: 'EquipmentBindGroup', | |||
components: {}, | |||
components: { BasicDrawer }, | |||
mixins: [basicPageMixin], | |||
data() { | |||
return { | |||
searchBarKeys: ['groupId', 'equipmentName'], | |||
tableBtn: [ | |||
this.$auth.hasPermi('base:equipment-bind-group:update') | |||
? { | |||
type: 'detail', | |||
btnName: '查看报警', | |||
} | |||
: undefined, | |||
this.$auth.hasPermi('base:equipment-bind-group:update') | |||
? { | |||
type: 'edit', | |||
@@ -88,32 +147,32 @@ export default { | |||
}, | |||
{ prop: 'equipmentName', label: '设备' }, | |||
{ prop: 'groupName', label: '分组' }, | |||
{ | |||
_action: 'equipment-bind-group-show-alert', | |||
label: '分组报警', | |||
subcomponent: { | |||
props: ['injectData'], | |||
render: function (h) { | |||
const _this = this; | |||
return h( | |||
'el-button', | |||
{ | |||
props: { type: 'text' }, | |||
on: { | |||
click: function () { | |||
console.log('inejctdata', _this.injectData); | |||
_this.$emit('emitData', { | |||
action: _this.injectData._action, | |||
value: _this.injectData, | |||
}); | |||
}, | |||
}, | |||
}, | |||
'查看报警' | |||
); | |||
}, | |||
}, | |||
}, | |||
// { | |||
// _action: 'equipment-bind-group-show-alert', | |||
// label: '分组报警', | |||
// subcomponent: { | |||
// props: ['injectData'], | |||
// render: function (h) { | |||
// const _this = this; | |||
// return h( | |||
// 'el-button', | |||
// { | |||
// props: { type: 'text' }, | |||
// on: { | |||
// click: function () { | |||
// console.log('inejctdata', _this.injectData); | |||
// _this.$emit('emitData', { | |||
// action: _this.injectData._action, | |||
// value: _this.injectData, | |||
// }); | |||
// }, | |||
// }, | |||
// }, | |||
// '查看报警' | |||
// ); | |||
// }, | |||
// }, | |||
// }, | |||
], | |||
searchBarFormConfig: [ | |||
{ | |||
@@ -196,6 +255,59 @@ export default { | |||
form: {}, | |||
// 表单校验 | |||
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() { | |||
@@ -229,7 +341,6 @@ export default { | |||
/** 查询列表 */ | |||
getList() { | |||
this.loading = true; | |||
_; // 执行查询 | |||
getEquipmentBindGroupPage(this.queryParams).then((response) => { | |||
this.list = response.data.list; | |||
this.total = response.data.total; | |||
@@ -299,6 +410,20 @@ 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) { | |||
const id = row.id; | |||
@@ -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> |
@@ -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> |
@@ -40,6 +40,57 @@ | |||
@confirm="submitForm"> | |||
<DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" /> | |||
</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> | |||
</template> | |||
@@ -53,17 +104,27 @@ import { | |||
exportEquipmentGroupExcel, | |||
} from '@/api/base/equipmentGroup'; | |||
import moment from 'moment'; | |||
import { publicFormatter } from '@/utils/dict'; | |||
import basicPageMixin from '@/mixins/lb/basicPageMixin'; | |||
import { getAccessToken } from '@/utils/auth'; | |||
// import { getAccessToken } from '@/utils/auth'; | |||
import BasicDrawer from './components/BasicDrawer.vue'; | |||
export default { | |||
name: 'EquipmentGroup', | |||
mixins: [basicPageMixin], | |||
components: {}, | |||
components: { BasicDrawer }, | |||
data() { | |||
return { | |||
editVisible: false, | |||
editMode: '', | |||
searchBarKeys: ['name', 'code'], | |||
tableBtn: [ | |||
this.$auth.hasPermi('base:equipment-group:update') | |||
? { | |||
type: 'detail', | |||
btnName: '查看报警', | |||
} | |||
: undefined, | |||
this.$auth.hasPermi('base:equipment-group:update') | |||
? { | |||
type: 'edit', | |||
@@ -88,34 +149,84 @@ export default { | |||
{ prop: 'name', label: '设备分组名称' }, | |||
{ prop: 'code', label: '设备分组编码' }, | |||
{ prop: 'remark', label: '备注' }, | |||
{ | |||
_action: 'equipment-group-show-alert', | |||
label: '报警', | |||
subcomponent: { | |||
props: ['injectData'], | |||
render: function (h) { | |||
const _this = this; | |||
return h( | |||
'el-button', | |||
{ | |||
props: { type: 'text' }, | |||
on: { | |||
click: function () { | |||
console.log('inejctdata', _this.injectData); | |||
_this.$emit('emitData', { | |||
action: _this.injectData._action, | |||
// value: _this.injectData.id, | |||
value: _this.injectData, | |||
}); | |||
}, | |||
}, | |||
}, | |||
'查看报警' | |||
); | |||
}, | |||
// { | |||
// _action: 'equipment-group-show-alert', | |||
// label: '报警', | |||
// subcomponent: { | |||
// props: ['injectData'], | |||
// render: function (h) { | |||
// const _this = this; | |||
// return h( | |||
// 'el-button', | |||
// { | |||
// props: { type: 'text' }, | |||
// on: { | |||
// click: function () { | |||
// console.log('inejctdata', _this.injectData); | |||
// _this.$emit('emitData', { | |||
// action: _this.injectData._action, | |||
// // value: _this.injectData.id, | |||
// 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: [ | |||
{ | |||
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) { | |||
const id = row.id; | |||
@@ -18,24 +18,25 @@ | |||
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 | |||
<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" /> | |||
v-for="opt in productionLineList" | |||
:key="opt.value" | |||
:label="opt.label" | |||
:value="opt.value" /> | |||
</el-select> | |||
</el-form-item> --> | |||
</el-col> | |||
@@ -47,6 +48,7 @@ | |||
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> | |||
<el-select | |||
v-model="dataForm.type" | |||
:disabled="disabled" | |||
placeholder="请选择报警类型" | |||
@change="$emit('update', dataForm)"> | |||
<el-option | |||
@@ -68,6 +70,7 @@ | |||
prop="grade" | |||
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> | |||
<el-select | |||
:disabled="disabled" | |||
v-model="dataForm.grade" | |||
placeholder="请选择报警级别" | |||
@change="$emit('update', dataForm)"> | |||
@@ -86,6 +89,7 @@ | |||
label="设备报警编码" | |||
prop="alarmCode"> | |||
<el-input | |||
:disabled="disabled" | |||
v-model="dataForm.alarmCode" | |||
@change="$emit('update', dataForm)" | |||
placeholder="请输入设备报警编码" /> | |||
@@ -99,6 +103,7 @@ | |||
prop="plcParamName" | |||
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> | |||
<el-input | |||
:disabled="disabled" | |||
v-model="dataForm.plcParamName" | |||
placeholder="请输入参数列名" | |||
@change="$emit('update', dataForm)"></el-input> | |||
@@ -110,6 +115,7 @@ | |||
prop="alarmContent" | |||
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]"> | |||
<el-input | |||
:disabled="disabled" | |||
v-model="dataForm.alarmContent" | |||
placeholder="请输入报警内容" | |||
@change="$emit('update', dataForm)"></el-input> | |||
@@ -133,6 +139,10 @@ export default { | |||
type: Object, | |||
default: () => ({}), | |||
}, | |||
disabled: { | |||
type: Boolean, | |||
default: false, | |||
}, | |||
}, | |||
data() { | |||
return { | |||
@@ -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: '', | |||
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: '', | |||
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> |
@@ -40,6 +40,53 @@ | |||
@confirm="submitForm"> | |||
<DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" /> | |||
</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> | |||
</template> | |||
@@ -55,11 +102,13 @@ import { | |||
import moment from 'moment'; | |||
import basicPageMixin from '@/mixins/lb/basicPageMixin'; | |||
// import './http'; | |||
import BasicDrawer from './components/BasicDrawer.vue'; | |||
import { publicFormatter } from '@/utils/dict'; | |||
export default { | |||
name: 'EquipmentPlcConnect', | |||
mixins: [basicPageMixin], | |||
components: {}, | |||
components: { BasicDrawer }, | |||
data() { | |||
return { | |||
searchBarKeys: ['equipmentId', 'plcId'], | |||
@@ -78,6 +127,10 @@ export default { | |||
// : undefined, | |||
// ].filter((v) => v), | |||
tableBtn: [ | |||
{ | |||
type: 'detail', | |||
btnName: '参数绑定', | |||
}, | |||
{ | |||
type: 'edit', | |||
btnName: '修改', | |||
@@ -107,32 +160,32 @@ export default { | |||
{ prop: 'plcTableName', label: '关联表名' }, | |||
{ prop: 'plcName', label: '标识名称' }, | |||
{ prop: 'bindingParameters', label: '绑定参数数量' }, | |||
{ | |||
_action: 'params-bind', | |||
label: '查看绑定', | |||
subcomponent: { | |||
props: ['injectData'], | |||
render: function (h) { | |||
const _this = this; | |||
return h( | |||
'el-button', | |||
{ | |||
props: { type: 'text' }, | |||
on: { | |||
click: function () { | |||
console.log('inejctdata', _this.injectData); | |||
_this.$emit('emitData', { | |||
action: _this.injectData._action, | |||
payload: _this.injectData, | |||
}); | |||
}, | |||
}, | |||
}, | |||
'查看绑定' | |||
); | |||
}, | |||
}, | |||
}, | |||
// { | |||
// _action: 'params-bind', | |||
// label: '查看绑定', | |||
// subcomponent: { | |||
// props: ['injectData'], | |||
// render: function (h) { | |||
// const _this = this; | |||
// return h( | |||
// 'el-button', | |||
// { | |||
// props: { type: 'text' }, | |||
// on: { | |||
// click: function () { | |||
// console.log('inejctdata', _this.injectData); | |||
// _this.$emit('emitData', { | |||
// action: _this.injectData._action, | |||
// payload: _this.injectData, | |||
// }); | |||
// }, | |||
// }, | |||
// }, | |||
// '查看绑定' | |||
// ); | |||
// }, | |||
// }, | |||
// }, | |||
], | |||
searchBarFormConfig: [ | |||
{ | |||
@@ -185,8 +238,8 @@ export default { | |||
url: '/base/equipment-plc/listAll', | |||
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], | |||
bind: { | |||
filterable: true | |||
} | |||
filterable: true, | |||
}, | |||
}, | |||
], | |||
[ | |||
@@ -197,8 +250,8 @@ export default { | |||
url: '/base/equipment/page?pageNo=1&pageSize=99', | |||
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], | |||
bind: { | |||
filterable: true | |||
} | |||
filterable: true, | |||
}, | |||
}, | |||
], | |||
], | |||
@@ -212,6 +265,52 @@ export default { | |||
}, | |||
// 表单参数 | |||
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() { | |||
@@ -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) { | |||
const id = row.id; | |||
@@ -367,6 +494,7 @@ export default { | |||
}) | |||
.catch(() => {}); | |||
}, | |||
/** 导出按钮操作 */ | |||
handleExport() { | |||
// 处理查询参数 | |||
@@ -139,6 +139,12 @@ export default { | |||
btnName: '删除', | |||
} | |||
: undefined, | |||
this.$auth.hasPermi(`base:equipment:update`) | |||
? { | |||
type: 'detail', | |||
btnName: '详情', | |||
} | |||
: undefined, | |||
].filter((v) => v), | |||
tableProps: [ | |||
{ | |||
@@ -153,32 +159,32 @@ export default { | |||
{ prop: 'equipmentType', label: '设备类型' }, | |||
{ prop: 'enName', label: '英文名称' }, | |||
{ prop: 'abbr', label: '缩写' }, | |||
{ | |||
action: 'show-detail', | |||
label: '详情', | |||
subcomponent: { | |||
props: ['injectData'], | |||
render: function (h) { | |||
const _this = this; | |||
return h( | |||
'el-button', | |||
{ | |||
props: { type: 'text', size: 'mini' }, | |||
on: { | |||
click: function () { | |||
console.log('inejctdata', _this.injectData); | |||
_this.$emit('emitData', { | |||
action: _this.injectData.action, | |||
value: _this.injectData.id, | |||
}); | |||
}, | |||
}, | |||
}, | |||
'查看详情' | |||
); | |||
}, | |||
}, | |||
}, | |||
// { | |||
// action: 'show-detail', | |||
// label: '详情', | |||
// subcomponent: { | |||
// props: ['injectData'], | |||
// render: function (h) { | |||
// const _this = this; | |||
// return h( | |||
// 'el-button', | |||
// { | |||
// props: { type: 'text', size: 'mini' }, | |||
// on: { | |||
// click: function () { | |||
// console.log('inejctdata', _this.injectData); | |||
// _this.$emit('emitData', { | |||
// action: _this.injectData.action, | |||
// value: _this.injectData.id, | |||
// }); | |||
// }, | |||
// }, | |||
// }, | |||
// '查看详情' | |||
// ); | |||
// }, | |||
// }, | |||
// }, | |||
], | |||
searchBarFormConfig: [ | |||
{ | |||
@@ -532,6 +538,10 @@ export default { | |||
case 'delete': | |||
this.handleDelete(data); | |||
break; | |||
case 'detail': | |||
const { id } = data; | |||
this.viewDetail(id); | |||
break; | |||
} | |||
}, | |||
}, | |||
@@ -97,6 +97,22 @@ export default { | |||
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: { | |||
@@ -106,7 +122,11 @@ export default { | |||
containLabel: true | |||
}, | |||
legend: { | |||
data: legendData | |||
data: legendData, | |||
right: '1%', | |||
icon: 'rect', | |||
itemHeight: 8, | |||
itemWidth: 8 | |||
}, | |||
xAxis: { | |||
type: 'category', | |||
@@ -103,7 +103,8 @@ export default { | |||
containLabel: true | |||
}, | |||
legend: { | |||
data: legendData | |||
data: legendData, | |||
right: '1%' | |||
}, | |||
xAxis: { | |||
type: 'category', | |||
@@ -425,7 +425,7 @@ export default { | |||
} | |||
} | |||
</script> | |||
<style> | |||
<style lang='scss'> | |||
/* 时间整点 */ | |||
.noneMinute .el-time-spinner__wrapper { | |||
width: 100%; | |||
@@ -433,6 +433,16 @@ export default { | |||
.noneMinute .el-scrollbar:nth-of-type(2) { | |||
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 lang="scss" scoped> | |||
.demo-form-inline { | |||
@@ -446,8 +456,6 @@ export default { | |||
margin-top: 12px; | |||
} | |||
} | |||
</style> | |||
<style scoped> | |||
.searchBarBox .foldClass { | |||
position: absolute; | |||
top: 14px; | |||
@@ -15,7 +15,7 @@ export default { | |||
return { | |||
chartDom: '', | |||
chart: '', | |||
chartHeight: this.tableHeight(350) | |||
chartHeight: this.tableHeight(370) | |||
} | |||
}, | |||
props: { | |||
@@ -34,7 +34,7 @@ export default { | |||
}, | |||
mounted() { | |||
window.addEventListener('resize', () => { | |||
this.chartHeight = this.tableHeight(350) | |||
this.chartHeight = this.tableHeight(370) | |||
}) | |||
}, | |||
methods: { | |||
@@ -86,17 +86,35 @@ export default { | |||
} | |||
} | |||
var option = { | |||
// title: { | |||
// text: 'World Population' | |||
// }, | |||
color:['#288AFF','#8EF0AB'], | |||
tooltip: { | |||
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(``) | |||
) | |||
} | |||
}, | |||
legend: {}, | |||
legend: { | |||
right: '1%', | |||
icon: 'rect', | |||
itemHeight: 8, | |||
itemWidth: 8 | |||
}, | |||
grid: { | |||
left: '1%', | |||
right: '1%', | |||
@@ -194,13 +194,23 @@ export default { | |||
} | |||
} | |||
</script> | |||
<style> | |||
<style lang='scss'> | |||
/* 级联选择器 */ | |||
.cascaderParent .el-cascader-panel .el-scrollbar:first-child .el-radio { | |||
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 scoped> | |||
<style lang="scss" scoped> | |||
.separateStyle { | |||
display: inline-block; | |||
width: 1px; | |||
@@ -208,8 +218,6 @@ export default { | |||
background: #E8E8E8; | |||
vertical-align: middle; | |||
} | |||
</style> | |||
<style lang="scss" scoped> | |||
.demo-form-inline { | |||
.blue-block { | |||
display: inline-block; | |||
@@ -9,7 +9,8 @@ | |||
:table-data="list" | |||
class="qoq-out-table" | |||
/> | |||
<div style='width: 100%;height: 300px;padding-top: 30px;'> | |||
<div class="chartTitle">环比分析图</div> | |||
<div style='width: 100%'> | |||
<line-chart ref="analysisLineChart" :chartData="chartData"/> | |||
</div> | |||
</div> | |||
@@ -111,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> |
@@ -68,12 +68,6 @@ export default { | |||
} | |||
var option = { | |||
color:['#288AFF'], | |||
tooltip: { | |||
trigger: 'axis', | |||
axisPointer: { | |||
type: 'shadow' | |||
} | |||
}, | |||
grid: { | |||
left: '4%', | |||
right: '1%', | |||
@@ -70,9 +70,9 @@ export default { | |||
var option = { | |||
color:['#288AFF'], | |||
tooltip: { | |||
trigger: 'axis' | |||
}, | |||
// tooltip: { | |||
// trigger: 'axis' | |||
// }, | |||
grid: { | |||
left: '4%', | |||
right: '1%', | |||
@@ -385,7 +385,7 @@ export default { | |||
} | |||
} | |||
</script> | |||
<style> | |||
<style lang='scss'> | |||
/* 级联选择器 */ | |||
.cascaderParent .el-cascader-panel .el-scrollbar:first-child .el-radio { | |||
display: none; | |||
@@ -397,6 +397,16 @@ export default { | |||
.noneMinute .el-scrollbar:nth-of-type(2) { | |||
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 lang="scss" scoped> | |||
.demo-form-inline { | |||
@@ -410,8 +420,6 @@ export default { | |||
margin-top: 12px; | |||
} | |||
} | |||
</style> | |||
<style scoped> | |||
.searchBarBox .foldClass { | |||
position: absolute; | |||
top: 14px; | |||
@@ -77,7 +77,8 @@ export default { | |||
var option = { | |||
color:['#FFDC94','#8EF0AB','#63BDFF','#288AFF','#7164FF','#FF6860','#FF9747','#B0EB42','#D680FF','#0043D2'], | |||
legend: { | |||
data: keys | |||
data: keys, | |||
right:'1%' | |||
}, | |||
tooltip: { | |||
trigger: 'axis' | |||
@@ -125,7 +125,31 @@ export default { | |||
this.$emit('submit', this.queryParams) | |||
}, | |||
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) {// 本月最后一天 | |||
let year = moment(timeStamp).format('YYYY') | |||
@@ -143,19 +167,20 @@ export default { | |||
} | |||
} | |||
</script> | |||
<style> | |||
<style lang='scss'> | |||
/* 级联选择器 */ | |||
.cascaderParent .el-cascader-panel .el-scrollbar:first-child .el-radio { | |||
display: none; | |||
} | |||
</style> | |||
<style scoped> | |||
.separateStyle { | |||
display: inline-block; | |||
width: 1px; | |||
height: 24px; | |||
background: #E8E8E8; | |||
vertical-align: middle; | |||
.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 lang="scss" scoped> | |||
@@ -170,4 +195,11 @@ export default { | |||
margin-top: 10px; | |||
} | |||
} | |||
.separateStyle { | |||
display: inline-block; | |||
width: 1px; | |||
height: 24px; | |||
background: #E8E8E8; | |||
vertical-align: middle; | |||
} | |||
</style> |
@@ -3,6 +3,7 @@ | |||
<!-- 搜索工作栏 --> | |||
<search-area @submit="getList" @exportD="exportData"/> | |||
<div v-show='chartData.length'> | |||
<div class="chartTitle">同比分析图</div> | |||
<div style='width: 100%;height: 400px;'> | |||
<line-chart ref="analysisLineChart" :chartData="chartData"/> | |||
</div> | |||
@@ -33,12 +34,12 @@ export default { | |||
chartData: [], | |||
tableProps: [], | |||
list: [], | |||
tableH: this.tableHeight(600) | |||
tableH: this.tableHeight(640) | |||
} | |||
}, | |||
mounted() { | |||
window.addEventListener('resize', () => { | |||
this.tableH = this.tableHeight(600) | |||
this.tableH = this.tableHeight(640) | |||
}) | |||
}, | |||
methods: { | |||
@@ -99,9 +100,10 @@ export default { | |||
} | |||
this.chartData = this.list | |||
}, | |||
exportData() { | |||
exportData(val) { | |||
if (this.list.length > 0) { | |||
var wb = XLSX.utils.table_to_book(document.querySelector(".yoy-out-table")) | |||
let fileName = val.name + "同比分析.xlsx" | |||
var wbout = XLSX.write(wb, { | |||
bookType: "xlsx", | |||
bookSST: true, | |||
@@ -110,7 +112,7 @@ export default { | |||
try { | |||
FileSaver.saveAs( | |||
new Blob([wbout], { type: "application/octet-stream" }), | |||
"同比分析.xlsx" | |||
fileName | |||
) | |||
} catch (e) { | |||
if (typeof console !== "undefined") console.log(e, wbout); | |||
@@ -122,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> |
@@ -130,7 +130,7 @@ export default { | |||
this.plcTableName = data.plcTableName | |||
this.objName = data.objName | |||
this.getList() | |||
if (title === 'view') { | |||
if (title === 'detail') { | |||
this.showBtn = false | |||
this.tableBtn = [] | |||
} else { | |||
@@ -113,22 +113,22 @@ export default { | |||
], | |||
tableProps, | |||
tableBtn: [ | |||
this.$auth.hasPermi('base:energy-plc-connect:bind') | |||
? { | |||
type: 'connect', | |||
btnName: '绑定' | |||
} | |||
: undefined, | |||
{ | |||
type: 'detail', | |||
btnName: '详情' | |||
}, | |||
this.$auth.hasPermi('base:energy-plc-connect:update') | |||
? { | |||
type: 'edit', | |||
btnName: '编辑' | |||
} | |||
: undefined, | |||
{ | |||
type: 'view', | |||
btnName: '查看' | |||
}, | |||
this.$auth.hasPermi('base:energy-plc-connect:bind') | |||
? { | |||
type: 'connect', | |||
btnName: '绑定' | |||
} | |||
: undefined, | |||
this.$auth.hasPermi('base:energy-plc-connect:delete') | |||
? { | |||
type: 'delete', | |||
@@ -205,10 +205,10 @@ export default { | |||
case 'delete': | |||
this.handleDelete(val.data) | |||
break | |||
case 'view': | |||
case 'detail': | |||
this.paramVisible = true | |||
this.$nextTick(() => { | |||
this.$refs.plcParam.init(val.data, 'view') | |||
this.$refs.plcParam.init(val.data, 'detail') | |||
}) | |||
break | |||
default: | |||
@@ -1,54 +1,72 @@ | |||
<template> | |||
<el-form ref="form" :rules="rules" label-width="110px" :model="form"> | |||
<el-form-item label="监控对象" prop="objectId"> | |||
<el-cascader | |||
style='width: 100%;' | |||
v-model="objIds" | |||
:options="objList" | |||
:props="{ checkStrictly: true, value: 'id', label: 'name' }" | |||
popper-class="cascaderParent" | |||
@change="selectObj" | |||
clearable></el-cascader> | |||
</el-form-item> | |||
<el-form-item label="能源类型" prop="energyTypeId"> | |||
<el-select v-model="form.energyTypeId" placeholder="请选择" style="width: 100%;" @change="toggleType"> | |||
<el-option | |||
v-for="item in this.energyTypeList" | |||
:key="item.id" | |||
:label="item.name" | |||
:value="item.id"> | |||
</el-option> | |||
</el-select> | |||
</el-form-item> | |||
<el-form-item label="监控模式" prop="type"> | |||
<el-select v-model="form.type" placeholder="请选择" style="width: 100%;" @change="typeChange"> | |||
<el-option label="合并" :value= "1" ></el-option> | |||
<el-option label="详细" :value= "2" ></el-option> | |||
</el-select> | |||
</el-form-item> | |||
<el-form-item label="监控详细参数" prop="type" v-if="form.type === 2"> | |||
<el-select v-model="form.plcParamId" placeholder="请选择" style="width: 100%;" @change="selectDetail"> | |||
<el-option | |||
v-for="item in detailList" | |||
:key="item.id" | |||
:label="item.name" | |||
:value="item.id"> | |||
</el-option> | |||
</el-select> | |||
</el-form-item> | |||
<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-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-row> | |||
<el-col :span="12"> | |||
<el-form-item label="监控对象" prop="objectId"> | |||
<el-cascader | |||
style='width: 100%;' | |||
v-model="objIds" | |||
:options="objList" | |||
:props="{ checkStrictly: true, value: 'id', label: 'name' }" | |||
popper-class="cascaderParent" | |||
@change="selectObj" | |||
clearable></el-cascader> | |||
</el-form-item> | |||
</el-col> | |||
<el-col :span="12"> | |||
<el-form-item label="能源类型" prop="energyTypeId"> | |||
<el-select v-model="form.energyTypeId" placeholder="请选择" style="width: 100%;" @change="toggleType"> | |||
<el-option | |||
v-for="item in this.energyTypeList" | |||
:key="item.id" | |||
:label="item.name" | |||
:value="item.id"> | |||
</el-option> | |||
</el-select> | |||
</el-form-item> | |||
</el-col> | |||
</el-row> | |||
<el-row> | |||
<el-col :span="12"> | |||
<el-form-item label="监控模式" prop="type"> | |||
<el-select v-model="form.type" placeholder="请选择" style="width: 100%;" @change="typeChange"> | |||
<el-option label="合并" :value= "1" ></el-option> | |||
<el-option label="详细" :value= "2" ></el-option> | |||
</el-select> | |||
</el-form-item> | |||
</el-col> | |||
<el-col :span="12"> | |||
<el-form-item label="监控详细参数" prop="type" v-if="form.type === 2"> | |||
<el-select v-model="form.plcParamId" placeholder="请选择" style="width: 100%;" @change="selectDetail"> | |||
<el-option | |||
v-for="item in detailList" | |||
:key="item.id" | |||
:label="item.name" | |||
:value="item.id"> | |||
</el-option> | |||
</el-select> | |||
</el-form-item> | |||
</el-col> | |||
</el-row> | |||
<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> | |||
</template> | |||
<script> | |||
@@ -124,7 +124,7 @@ export default { | |||
this.energyType = data.energyType | |||
this.energyTypeId = data.energyTypeId | |||
this.getList() | |||
if (title === 'view') { | |||
if (title === 'detail') { | |||
this.showBtn = false | |||
this.tableBtn = [] | |||
} else { | |||
@@ -111,22 +111,22 @@ export default { | |||
], | |||
tableProps, | |||
tableBtn: [ | |||
this.$auth.hasPermi('monitoring:energy-statistics:update') | |||
? { | |||
type: 'edit', | |||
btnName: '编辑' | |||
} | |||
: undefined, | |||
{ | |||
type: 'view', | |||
btnName: '查看' | |||
}, | |||
this.$auth.hasPermi('monitoring:energy-statistics:bind') | |||
? { | |||
type: 'connect', | |||
btnName: '绑定' | |||
} | |||
: undefined, | |||
{ | |||
type: 'detail', | |||
btnName: '详情' | |||
}, | |||
this.$auth.hasPermi('monitoring:energy-statistics:update') | |||
? { | |||
type: 'edit', | |||
btnName: '编辑' | |||
} | |||
: undefined, | |||
this.$auth.hasPermi('monitoring:energy-statistics:delete') | |||
? { | |||
type: 'delete', | |||
@@ -199,9 +199,9 @@ export default { | |||
case 'delete': | |||
this.handleDelete(val.data) | |||
break | |||
case 'view': | |||
case 'detail': | |||
this.$nextTick(() => { | |||
this.$refs.plcParam.init(val.data, 'view') | |||
this.$refs.plcParam.init(val.data, 'detail') | |||
}) | |||
break | |||
default: | |||
@@ -6,10 +6,11 @@ | |||
--> | |||
<template> | |||
<div class="chart-grid-item" style=""> | |||
<div class="chart-grid-item" style="overflow: inherit"> | |||
<div | |||
class="pie-chart" | |||
ref="pieChart" | |||
style="overflow: inherit;" | |||
:data-eqname="value.equipmentName || 'Default'"></div> | |||
<div class="data-view"> | |||
<div class="data-view__item"> | |||
@@ -93,7 +94,7 @@ export default { | |||
name: '', | |||
type: 'pie', | |||
radius: ['75%', '90%'], | |||
center: ['50%', '45%'], | |||
center: ['50%', '48%'], | |||
label: { | |||
show: false, | |||
}, | |||
@@ -108,7 +109,7 @@ export default { | |||
// 内环 | |||
name: '', | |||
type: 'pie', | |||
center: ['50%', '45%'], | |||
center: ['50%', '48%'], | |||
radius: ['60%', '75%'], | |||
itemStyle: { | |||
borderRadius: 10, | |||
@@ -6,7 +6,7 @@ | |||
--> | |||
<template> | |||
<div class="app-container"> | |||
<div class="app-container allow-overflow"> | |||
<!-- 搜索工作栏 --> | |||
<SearchBar | |||
:formConfigs="searchBarFormConfig" | |||
@@ -29,11 +29,19 @@ | |||
:table-data="list" | |||
@emitFun="handleEmitFun"></base-table> | |||
</el-tab-pane> | |||
<el-tab-pane :label="'\u3000可视化\u3000'" name="graph"> | |||
<el-tab-pane | |||
:label="'\u3000可视化\u3000'" | |||
name="graph" | |||
style="overflow: inherit"> | |||
<div | |||
v-if="activeName == '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="legend"> | |||
<div class="legend-item"> | |||
@@ -429,6 +437,10 @@ export default { | |||
transform: translateY(-12px); | |||
} | |||
.el-tabs__content { | |||
overflow: visible; | |||
} | |||
.el-tabs__item { | |||
padding-left: 0 !important; | |||
padding-right: 0 !important; | |||
@@ -462,6 +474,7 @@ export default { | |||
border-radius: 12px; | |||
border: 1px solid #ccc; | |||
// background: #0003; | |||
overflow: inherit; | |||
} | |||
.bg-grid { | |||
display: grid; | |||
@@ -469,7 +482,7 @@ export default { | |||
grid-template-columns: repeat(4, minmax(280px, 1fr)); | |||
grid-auto-columns: 280px; | |||
grid-auto-rows: 290px; | |||
overflow: hidden; | |||
overflow: inherit; | |||
position: relative; | |||
} | |||
@@ -66,7 +66,7 @@ export default { | |||
(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: 50%; background-color: ${item.color}; margin-right: 5px;"></span> | |||
<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} | |||
@@ -121,6 +121,7 @@ export default { | |||
}, | |||
series: [ | |||
{ | |||
animation: false, | |||
name: '合格数量', | |||
type: 'bar', | |||
barWidth: 20, | |||
@@ -128,6 +129,7 @@ export default { | |||
data: opt.map((item) => item[1]), | |||
}, | |||
{ | |||
animation: false, | |||
name: '不合格数量', | |||
type: 'bar', | |||
barWidth: 20, | |||
@@ -302,7 +302,7 @@ export default { | |||
}); | |||
this.list = data.map((item) => ({ | |||
...item, | |||
products: item.products.join(','), | |||
products: item.products?.join(','), | |||
})); | |||
}, | |||
@@ -312,7 +312,7 @@ export default { | |||
case 'search': | |||
this.queryParams.lineId = btn.lineId; | |||
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.getList(); | |||
}); | |||
@@ -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)); | |||
} | |||
} |
@@ -1,26 +1,26 @@ | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="UTF-8" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||
<title>Document</title> | |||
</head> | |||
<body> | |||
<div | |||
id="app" | |||
style="width: 100vw; height: 80vh; background: #ccc3; margin: 24px"></div> | |||
<script src="./echarts.js"></script> | |||
<script> | |||
function getStartTime(timestamp) { | |||
return new Date(new Date(timestamp).toLocaleDateString()).getTime(); | |||
} | |||
<head> | |||
<meta charset="UTF-8" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||
<title>Document</title> | |||
</head> | |||
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]); | |||
console.log(` | |||
<body> | |||
<div id="app" style="width: 100vw; height: 80vh; background: #fff; margin: 24px"></div> | |||
<script src="./echarts.js"></script> | |||
<script> | |||
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]); | |||
console.log(` | |||
categoryIndex: ${categoryIndex}, | |||
start: ${start}, | |||
end: ${end}, | |||
@@ -32,249 +32,466 @@ | |||
api.style(): ${JSON.stringify(api.style())} | |||
`); | |||
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, | |||
}, | |||
// 截取矩形 | |||
{ | |||
// 截取掉grid以外的部分,实质是计算方块的偏移量 | |||
x: params.coordSys.x, // {number} grid rect 的 x | |||
// y: params.coordSys.y, // {number} grid rect 的 y | |||
y: params.coordSys.y - 16, // {number} grid rect 的 y,并多减掉 16 个单位 | |||
width: params.coordSys.width, // {number} grid rect 的 width | |||
height: params.coordSys.height, // {number} grid rect 的 height | |||
} | |||
); | |||
console.log(`------------- ${JSON.stringify(rectShape)} -------------- `) | |||
return ( | |||
rectShape && { | |||
type: 'rect', | |||
transition: ['shape'], | |||
shape: rectShape, | |||
style: api.style(), | |||
} | |||
); | |||
} | |||
function getXaxisRange(startTime) { | |||
return Array(24) | |||
.fill(startTime) | |||
.map((item, index) => { | |||
return new Date(item + index * 3600 * 1000) | |||
.toLocaleTimeString() | |||
.split(':') | |||
.slice(0, 2) | |||
.join(':'); | |||
}); | |||
} | |||
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: 0, y: 0, width: 2000, height: 60 } // <== 也行... | |||
// 截取矩形 | |||
{ | |||
// 截取掉grid以外的部分,实质是计算方块的偏移量 | |||
x: params.coordSys.x, // {number} grid rect 的 x | |||
// y: params.coordSys.y, // {number} grid rect 的 y | |||
y: params.coordSys.y - 16, // {number} grid rect 的 y,并多减掉 16 个单位 | |||
// width: end[0] - start[0], | |||
width: params.coordSys.width, // {number} grid rect 的 width | |||
height: params.coordSys.height, // {number} grid rect 的 height | |||
} | |||
); | |||
console.log(`------------- ${JSON.stringify(rectShape)} -------------- `) | |||
return ( | |||
rectShape && { | |||
type: 'rect', | |||
transition: ['shape'], | |||
shape: rectShape, | |||
style: api.style(), | |||
} | |||
); | |||
} | |||
function getTodayStart(today) { | |||
const [y, m, d] = [ | |||
today.getFullYear(), | |||
today.getMonth(), | |||
today.getDate(), | |||
]; | |||
return new Date(y, m, d).getTime(); | |||
} | |||
function getXaxisRange(startTime) { | |||
return Array(24) | |||
.fill(startTime) | |||
.map((item, index) => { | |||
return new Date(item + index * 3600 * 1000) | |||
.toLocaleTimeString() | |||
.split(':') | |||
.slice(0, 2) | |||
.join(':'); | |||
}); | |||
} | |||
/** 颜色配置 */ | |||
const types = [ | |||
{ name: '运行', color: '#288AFF' }, | |||
{ name: '故障', color: '#FC9C91' }, | |||
{ name: '计划停机', color: '#FFDC94' }, | |||
{ name: '空白', color: '#F2F4F9' }, | |||
function getTodayStart(today) { | |||
const [y, m, d] = [ | |||
today.getFullYear(), | |||
today.getMonth(), | |||
today.getDate(), | |||
]; | |||
return new Date(y, m, d).getTime(); | |||
} | |||
const option = { | |||
tooltip: { | |||
trigger: 'axis', | |||
axisPointer: { | |||
type: 'none', | |||
// label: { | |||
// backgroundColor: '#6a7985', | |||
// }, | |||
}, | |||
formatter: (params) => { | |||
// console.log('formatter', params) | |||
// return ` | |||
// <div>${new Date(params[0].value[1]).toLocaleString()} ~ ${new Date(params[0].value[2]).toLocaleString()}</div> | |||
// <div style="display: flex; justify-content: space-between; min-width: 128px; align-items: center;"> | |||
// <span>${params[0].seriesName}</span> | |||
// <span>${params[0].name}</span> | |||
// </div> | |||
// ` | |||
return ` | |||
<h1 style="font-size: 18px; font-weight: 600; letter-spacing: 1px;">${params[0].name} <span style="display: inline-block; margin-left: 8px; width: 12px; height: 12px; border-radius: 50%; background: ${params[0].color} "></span></h1> | |||
<div>${new Date(params[0].value[1]).toLocaleString()} ~ ${new Date(params[0].value[2]).toLocaleString()}</div> | |||
/** 颜色配置 */ | |||
const types = [ | |||
{ name: '运行', color: '#288AFF' }, | |||
{ name: '故障', color: '#FC9C91' }, | |||
{ name: '计划停机', color: '#FFDC94' }, | |||
{ name: '空白', color: '#F2F4F9' }, | |||
]; | |||
const option = { | |||
tooltip: { | |||
trigger: 'item', | |||
axisPointer: { | |||
type: 'none', | |||
// label: { | |||
// backgroundColor: '#6a7985', | |||
// }, | |||
}, | |||
formatter: (params) => { | |||
// console.log('formatter', params) | |||
// return ` | |||
// <div>${new Date(params[0].value[1]).toLocaleString()} ~ ${new Date(params[0].value[2]).toLocaleString()}</div> | |||
// <div style="display: flex; justify-content: space-between; min-width: 128px; align-items: center;"> | |||
// <span>${params[0].seriesName}</span> | |||
// <span>${params[0].name}</span> | |||
// </div> | |||
// ` | |||
return ` | |||
<div style="display: flex; align-items: center; justify-content: space-between"> | |||
<h1 style="font-size: 18px; font-weight: 600;">${params.seriesName}</h1> | |||
<h2 style="font-size: 18px; font-weight: 400; letter-spacing: 1px;">${params.name} <span style="display: inline-block; margin-left: 8px; width: 12px; height: 12px; border-radius: 50%; background: ${params.color} "></span></h2> | |||
</div> | |||
<div>${new Date(params.value[1]).toLocaleString()} ~ ${new Date(params.value[2]).toLocaleString()}</div> | |||
` | |||
} | |||
} | |||
}, | |||
grid: [ | |||
{ | |||
id: 0, | |||
top: 10, | |||
left: 128, | |||
right: 64, | |||
height: 56, | |||
}, | |||
grid: [ | |||
{ | |||
id: 0, | |||
top: 10, | |||
left: 128, | |||
right: 64, | |||
height: 56, | |||
// { | |||
// id: 1, | |||
// top: 80, | |||
// height: 56, | |||
// }, | |||
// ***************** 添加第二个grid ***************** | |||
{ | |||
id: 1, | |||
top: 180, | |||
left: 128, | |||
right: 64, | |||
height: 56 | |||
} | |||
], | |||
xAxis: [ | |||
{ | |||
id: 'asdf', | |||
gridIndex: 0, | |||
axisTick: { | |||
alignWithLabel: true, | |||
inside: true, | |||
}, | |||
// { | |||
// id: 1, | |||
// top: 80, | |||
// height: 56, | |||
// }, | |||
], | |||
xAxis: [ | |||
{ | |||
id: 0, | |||
gridIndex: 0, | |||
axisTick: { | |||
alignWithLabel: true, | |||
inside: true, | |||
type: 'time', | |||
min: getTodayStart(new Date()), // <=== | |||
max: getStartTime(new Date().getTime() + 3600 * 24 * 1000), // <=== | |||
splitNumber: 10, | |||
axisLabel: { | |||
margin: 12, | |||
// rotate: -15, | |||
formatter: function (val) { | |||
return new Date(val) | |||
.toLocaleTimeString() | |||
.split(':') | |||
.slice(0, 2) | |||
.join(':'); | |||
}, | |||
type: 'time', | |||
min: getTodayStart(new Date()), // <=== | |||
max: getStartTime(new Date().getTime() + 3600 * 24 * 1000), // <=== | |||
splitNumber: 10, | |||
axisLabel: { | |||
margin: 12, | |||
// rotate: -15, | |||
formatter: function (val) { | |||
return new Date(val) | |||
.toLocaleTimeString() | |||
.split(':') | |||
.slice(0, 2) | |||
.join(':'); | |||
}, | |||
}, | |||
boundaryGap: false, | |||
// data: getXaxisRange(getTodayStart(new Date())), | |||
}, | |||
{ | |||
id: 'asdff', | |||
gridIndex: 0, | |||
axisLabel: { show: false }, | |||
axisLine: { show: false }, | |||
}, | |||
// ***************** 添加第二个 xAxis ***************** | |||
{ | |||
id: 'asdfjk', | |||
gridIndex: 1, | |||
axisTick: { | |||
alignWithLabel: true, | |||
inside: true, | |||
}, | |||
type: 'time', | |||
min: getTodayStart(new Date()), // <=== | |||
max: getStartTime(new Date().getTime() + 3600 * 24 * 1000), // <=== | |||
splitNumber: 10, | |||
axisLabel: { | |||
margin: 12, | |||
// rotate: -15, | |||
formatter: function (val) { | |||
return new Date(val) | |||
.toLocaleTimeString() | |||
.split(':') | |||
.slice(0, 2) | |||
.join(':'); | |||
}, | |||
boundaryGap: false, | |||
// data: getXaxisRange(getTodayStart(new Date())), | |||
}, | |||
{ | |||
id: 1, | |||
gridIndex: 0, | |||
axisLabel: { show: false }, | |||
axisLine: { show: false }, | |||
boundaryGap: false, | |||
// data: getXaxisRange(getTodayStart(new Date())), | |||
}, | |||
{ | |||
id: 'fjkd', | |||
gridIndex: 1, | |||
axisLabel: { show: false }, | |||
axisLine: { show: false }, | |||
}, | |||
], | |||
yAxis: [ | |||
// 主y轴 | |||
{ | |||
id: 0, | |||
gridIndex: 0, | |||
type: 'value', | |||
splitLine: { show: false }, | |||
name: '设备1', | |||
nameLocation: 'center', | |||
nameGap: 56, | |||
nameRotate: 0, | |||
nameTextStyle: { | |||
fontSize: 18, | |||
}, | |||
axisLine: { | |||
show: true, | |||
lineStyle: {}, | |||
}, | |||
axisLabel: { show: false }, | |||
axisTick: { show: false }, | |||
}, | |||
// 辅y轴 | |||
{ | |||
id: 1, | |||
gridIndex: 0, | |||
type: 'value', | |||
splitLine: { show: false }, | |||
axisLabel: { show: false }, | |||
axisTick: { show: false }, | |||
}, | |||
// ***************** 添加第二个 yAxis ***************** | |||
{ | |||
id: 2, | |||
gridIndex: 1, | |||
type: 'value', | |||
splitLine: { show: false }, | |||
name: '设备1', | |||
nameLocation: 'center', | |||
nameGap: 56, | |||
nameRotate: 0, | |||
nameTextStyle: { | |||
fontSize: 18, | |||
}, | |||
], | |||
yAxis: [ | |||
// 主y轴 | |||
{ | |||
id: 0, | |||
gridIndex: 0, | |||
type: 'value', | |||
splitLine: { show: false }, | |||
name: '设备1', | |||
nameLocation: 'center', | |||
nameGap: 56, | |||
nameRotate: 0, | |||
nameTextStyle: { | |||
fontSize: 18, | |||
axisLine: { | |||
show: true, | |||
lineStyle: {}, | |||
}, | |||
axisLabel: { show: false }, | |||
axisTick: { show: false }, | |||
}, | |||
// 辅y轴 | |||
{ | |||
id: 3, | |||
gridIndex: 1, | |||
type: 'value', | |||
splitLine: { show: false }, | |||
axisLabel: { show: false }, | |||
axisTick: { show: false }, | |||
}, | |||
], | |||
series: [ | |||
{ | |||
// 沉默的背景 | |||
xAxisIndex: 0, | |||
yAxisIndex: 0, | |||
type: 'custom', | |||
renderItem: renderItem, | |||
silent: true, | |||
itemStyle: { | |||
opacity: 0.8, | |||
}, | |||
encode: { | |||
x: [1, 2], | |||
y: 0, | |||
}, | |||
data: [ | |||
{ | |||
name: '无数据', | |||
value: [0, 1696694400000, 1696780800000, 0], | |||
tooltip: { show: false }, | |||
// silent: true, | |||
// animation: false, | |||
// universalTransition: { enable: false }, | |||
// hoverAnimation: false, | |||
// selectMode: true, | |||
// select: { disabled: true }, | |||
// emphasis: { disabled: false, focus: 'none', itemStyle: { opacity: 0 } }, | |||
// z: 0, | |||
// zlevel: 0, | |||
// emphasis: { disabled: true }, | |||
itemStyle: { | |||
color: '#ccc', | |||
opacity: 0.3, | |||
} | |||
} | |||
] | |||
}, | |||
{ | |||
name: '设备1', | |||
xAxisIndex: 0, | |||
yAxisIndex: 0, | |||
type: 'custom', | |||
renderItem: renderItem, | |||
itemStyle: { | |||
opacity: 0.8, | |||
}, | |||
encode: { | |||
x: [1, 2], | |||
y: 0, | |||
}, | |||
data: [ | |||
{ | |||
name: '运行', | |||
value: [0, 1696694400000, 1696699400000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[0].color, | |||
}, | |||
}, | |||
}, | |||
{ | |||
name: '运行', | |||
value: [0, 1696730000000, 1696734040450, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[0].color, | |||
}, | |||
}, | |||
}, | |||
axisLine: { | |||
show: true, | |||
lineStyle: {}, | |||
{ | |||
name: '故障', | |||
value: [0, 1696737040000, 1696754040450, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[1].color, | |||
}, | |||
}, | |||
}, | |||
{ | |||
name: '计划停机', | |||
value: [0, 1696755000000, 1696759000000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[2].color, | |||
}, | |||
}, | |||
}, | |||
{ | |||
name: '运行', | |||
value: [0, 1696759000000, 1696769000000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[0].color, | |||
}, | |||
}, | |||
}, | |||
{ | |||
name: '计划停机', | |||
value: [0, 1696769400000, 1696779000000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[2].color, | |||
}, | |||
}, | |||
}, | |||
axisLabel: { show: false }, | |||
axisTick: { show: false }, | |||
], | |||
}, | |||
// ***************** 添加第二个设备 ***************** | |||
{ | |||
// 沉默的背景 | |||
xAxisIndex: 2, | |||
yAxisIndex: 2, | |||
type: 'custom', | |||
renderItem: renderItem, | |||
silent: true, | |||
itemStyle: { | |||
opacity: 0.8, | |||
}, | |||
// 辅y轴 | |||
{ | |||
id: 1, | |||
gridIndex: 0, | |||
type: 'value', | |||
splitLine: { show: false }, | |||
axisLabel: { show: false }, | |||
axisTick: { show: false }, | |||
encode: { | |||
x: [1, 2], | |||
y: 0, | |||
}, | |||
], | |||
series: [ | |||
{ | |||
name: '设备1', | |||
xAxisIndex: 0, | |||
yAxisIndex: 0, | |||
type: 'custom', | |||
renderItem: renderItem, | |||
itemStyle: { | |||
opacity: 0.8, | |||
}, | |||
encode: { | |||
x: [1, 2], | |||
y: 0, | |||
data: [ | |||
{ | |||
name: '无数据', | |||
value: [0, 1696694400000, 1696780800000, 0], | |||
tooltip: { show: false }, | |||
// silent: true, | |||
// animation: false, | |||
// universalTransition: { enable: false }, | |||
// hoverAnimation: false, | |||
// selectMode: true, | |||
// select: { disabled: true }, | |||
// emphasis: { disabled: false, focus: 'none', itemStyle: { opacity: 0 } }, | |||
// z: 0, | |||
// zlevel: 0, | |||
// emphasis: { disabled: true }, | |||
itemStyle: { | |||
color: '#ccc', | |||
opacity: 0.3, | |||
} | |||
} | |||
] | |||
}, | |||
{ | |||
name: '设备2', | |||
xAxisIndex: 2, | |||
yAxisIndex: 2, | |||
type: 'custom', | |||
renderItem: renderItem, | |||
itemStyle: { | |||
opacity: 0.8, | |||
}, | |||
encode: { | |||
x: [1, 2], | |||
y: 0, | |||
}, | |||
data: [ | |||
{ | |||
name: '运行', | |||
value: [0, 1696694400000, 1696699400000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[0].color, | |||
}, | |||
}, | |||
// showBackground: true, | |||
}, | |||
//function getTimestamp(point) { | |||
// const t = new Date(); | |||
// const [y,m,d] = [t.getFullYear(),t.getMonth(),t.getDate()] | |||
// const [h,M] = point.split(':') | |||
// return new Date(y,m,d,h,M,0,0).getTime() | |||
//} | |||
data: [ | |||
{ | |||
// 01:00 ~ 05:00 | |||
name: '运行', | |||
value: [0, 1695747600000, 1695762000000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[0].color, | |||
}, | |||
{ | |||
name: '运行', | |||
value: [0, 1696730000000, 1696734040450, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[0].color, | |||
}, | |||
}, | |||
{ | |||
// 5:00 ~ 8:00 | |||
name: '故障', | |||
value: [0, 1695762000000, 1695772800000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[1].color, | |||
}, | |||
}, | |||
{ | |||
name: '故障', | |||
value: [0, 1696737040000, 1696754040450, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[1].color, | |||
}, | |||
}, | |||
{ | |||
// 8:00 ~ 8:39 | |||
name: '计划停机', | |||
value: [0, 1695772800000, 1695775140000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[2].color, | |||
}, | |||
}, | |||
{ | |||
name: '计划停机', | |||
value: [0, 1696755000000, 1696759000000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[2].color, | |||
}, | |||
}, | |||
{ | |||
// 08:39 ~ 18:00 | |||
name: '运行', | |||
value: [0, 1695775140000, 1695808800000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[0].color, | |||
}, | |||
}, | |||
{ | |||
name: '运行', | |||
value: [0, 1696759000000, 1696769000000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[0].color, | |||
}, | |||
}, | |||
{ | |||
// 18:00 ~ 23:30 | |||
name: '计划停机', | |||
value: [0, 1695808800000, 1695828600000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[2].color, | |||
}, | |||
}, | |||
{ | |||
name: '计划停机', | |||
value: [0, 1696769400000, 1696779000000, 0], | |||
itemStyle: { | |||
normal: { | |||
color: types[2].color, | |||
}, | |||
}, | |||
], | |||
}, | |||
], | |||
}; | |||
}, | |||
], | |||
}, | |||
], | |||
}; | |||
const el = document.getElementById('app'); | |||
const myChart = echarts.init(el); | |||
const el = document.getElementById('app'); | |||
const myChart = echarts.init(el); | |||
console.log('mychart', myChart); | |||
myChart.setOption(option); | |||
</script> | |||
</body> | |||
console.log('mychart', myChart); | |||
myChart.setOption(option); | |||
</script> | |||
</body> | |||
</html> | |||
</html> |
@@ -1,235 +0,0 @@ | |||
import * as echarts from 'echarts'; | |||
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(), | |||
} | |||
); | |||
} | |||
/** 颜色配置 */ | |||
const types = [ | |||
{ name: '运行', color: '#5ad8a6' }, | |||
{ name: '故障', color: '#fc9c91' }, | |||
{ name: '计划停机', color: '#000' }, | |||
]; | |||
/** 从时间戳获取 startTime */ | |||
function getStartTime(timestamp) { | |||
return new Date(new Date(timestamp).toLocaleDateString()).getTime(); | |||
} | |||
export default class GanttGraph { | |||
constructor(el) { | |||
this.chart = null; | |||
this.el = el; | |||
/** 默认配置 */ | |||
this.grid = { | |||
top: 32, | |||
left: 128, | |||
right: 128, | |||
bottom: 64, | |||
} | |||
this.tooltip = { | |||
formatter: function (params) { | |||
return ( | |||
params.marker + | |||
params.name + | |||
': ' + | |||
new Date(params.value[1]).toLocaleTimeString() + | |||
' - ' + | |||
new Date(params.value[2]).toLocaleTimeString() | |||
); | |||
}, | |||
} | |||
this.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, | |||
}, | |||
} | |||
this.yAxis = [ | |||
{ | |||
interval: 40, | |||
axisLine: { | |||
lineStyle: { | |||
color: '', | |||
}, | |||
}, | |||
axisLabel: { | |||
fontSize: 18, | |||
}, | |||
axisTick: { | |||
show: false, | |||
}, | |||
splitLine: { | |||
show: true, | |||
}, | |||
// data: [], // <==== | |||
data: ['设备1', '设备2', '设备3', '设备4'], | |||
}, | |||
{ | |||
axisLine: { | |||
lineStyle: { | |||
color: '', | |||
}, | |||
}, | |||
data: [], | |||
}, | |||
] | |||
this.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, | |||
}, | |||
}, | |||
}, | |||
], | |||
}, | |||
] | |||
} | |||
init() { | |||
this.chart = echarts.init(this.el); | |||
this.chart.setOption(this.getOption()) | |||
} | |||
getOption() { | |||
const { grid, xAxis, yAxis, series, tooltip } = this; | |||
return { | |||
grid, xAxis, yAxis, series, tooltip | |||
} | |||
} | |||
} |
@@ -6,31 +6,19 @@ | |||
--> | |||
<template> | |||
<div | |||
class="status-timegraph-container" | |||
style="background: #f2f4f9; flex: 1; display: flex; flex-direction: column"> | |||
<el-row | |||
class="" | |||
style=" | |||
<div class="status-timegraph-container" style="background: #f2f4f9; flex: 1; display: flex; flex-direction: column"> | |||
<el-row class="" style=" | |||
margin-bottom: 12px; | |||
background: #fff; | |||
padding: 16px 16px 0; | |||
border-radius: 8px; | |||
"> | |||
<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" | |||
@select-changed="handleSearchBarSelectChange" @headBtnClick="handleSearchBarBtnClick" /> | |||
</el-row> | |||
<el-row | |||
class="" | |||
style=" | |||
<el-row class="" style=" | |||
height: 1px; | |||
flex: 1; | |||
margin-bottom: 12px; | |||
@@ -49,66 +37,34 @@ | |||
<div class="icon running"></div> | |||
<div>运行中</div> | |||
</div> | |||
<!-- <div class="legend"> | |||
<div class="icon waiting"></div> | |||
<div>待机</div> | |||
</div> --> | |||
<div class="legend"> | |||
<div class="icon fault"></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="icon stop"></div> | |||
<div>计划停机</div> | |||
</div> | |||
</el-col> | |||
</el-row> | |||
<div | |||
class="main-area" | |||
style="flex: 1; display: flex; flex-direction: column"> | |||
<div | |||
class="graphs" | |||
v-show="graphList.length" | |||
id="status-chart" | |||
style="height: 1px; flex: 1"></div> | |||
<div class="main-area" style="flex: 1; display: flex; flex-direction: column"> | |||
<div class="graphs" v-show="graphList.length" id="status-chart" style="height: 1px; flex: 1"></div> | |||
<h2 v-if="!graphList || graphList.length == 0" class="no-data-bg"></h2> | |||
</div> | |||
</el-row> | |||
<!-- 对话框(添加 / 修改) --> | |||
<base-dialog | |||
dialogTitle="添加设备" | |||
:dialogVisible="open" | |||
width="500px" | |||
@close="open = false" | |||
@cancel="open = false" | |||
<base-dialog dialogTitle="添加设备" :dialogVisible="open" width="500px" @close="open = false" @cancel="open = false" | |||
@confirm="submitForm"> | |||
<el-select | |||
v-if="open" | |||
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 v-if="open" 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> | |||
</base-dialog> | |||
</div> | |||
</template> | |||
<script> | |||
// import * as echarts from 'echarts'; | |||
import Gantt from './gantt'; | |||
import Gantt from './chart'; | |||
export default { | |||
name: 'SGStatus', | |||
@@ -169,6 +125,7 @@ export default { | |||
recordTime: null, | |||
}, | |||
graphList: [], | |||
existingEquipments: [], | |||
open: false, | |||
eqList: [], | |||
startTime: null, | |||
@@ -202,15 +159,19 @@ export default { | |||
this.initEquipment(); | |||
// this.getList(); | |||
}, | |||
mounted() {}, | |||
mounted() { }, | |||
watch: { | |||
graphList: { | |||
handler(val) { | |||
if (val && val.length) { | |||
this.$nextTick(() => { | |||
if (!this.chart) this.initChart(); | |||
this.setInitialConfig(); | |||
this.handleGraphList(); | |||
if (!this.gantt) { | |||
this.gantt = new Gantt('#status-chart', this.startTime); | |||
this.gantt.init(val); | |||
return; | |||
} | |||
this.gantt.update(val); | |||
}); | |||
} | |||
return; | |||
@@ -220,43 +181,6 @@ export default { | |||
}, | |||
}, | |||
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() { | |||
let min = 0; | |||
this.graphList.forEach((arr) => { | |||
@@ -267,12 +191,6 @@ export default { | |||
return min; | |||
}, | |||
initChart() { | |||
const el = document.getElementById('status-chart'); | |||
this.gantt = new Gantt(el); | |||
this.gantt.init(); | |||
}, | |||
/** 重置查询条件 */ | |||
initQuery() { | |||
this.queryParams.lineId = null; | |||
@@ -297,8 +215,8 @@ export default { | |||
params: this.queryParams, | |||
}); | |||
if (code == 0) { | |||
this.existingEquipments = Object.values(data).map((eq) => eq[0].equipmentId); | |||
this.graphList = this.objectToArray(data); | |||
console.log('graph list', this.graphList); | |||
} | |||
}, | |||
@@ -384,27 +302,26 @@ export default { | |||
return; | |||
} | |||
this.startTime = new Date(payload.recordTime).getTime(); | |||
this.startTime = new Date(payload.recordTime) | |||
this.queryParams.lineId = payload.lineId || null; | |||
this.queryParams.sectionId = payload.sectionId || null; | |||
this.queryParams.equipmentId = payload.equipmentId || null; | |||
this.queryParams.recordTime = payload.recordTime | |||
? [ | |||
payload.recordTime, | |||
new Date( | |||
new Date(payload.recordTime).getTime() + 24 * 3600 * 1000 | |||
) | |||
.toLocaleDateString() | |||
.split('/') | |||
.map((value, index) => { | |||
if (index == 1 || index == 2) { | |||
return value.padStart(2, '0'); | |||
} | |||
return value; | |||
}) | |||
.join('-') + ' 00:00:00', | |||
] | |||
payload.recordTime, | |||
new Date( | |||
new Date(payload.recordTime).getTime() + 24 * 3600 * 1000 | |||
) | |||
.toLocaleDateString() | |||
.split('/') | |||
.map((value, index) => { | |||
if (index == 1 || index == 2) { | |||
return value.padStart(2, '0'); | |||
} | |||
return value; | |||
}) | |||
.join('-') + ' 00:00:00', | |||
] | |||
: null; | |||
this.getList(); | |||
break; | |||
@@ -415,6 +332,10 @@ export default { | |||
}, | |||
async submitForm() { | |||
if (this.existingEquipments.indexOf(this.queryParams.equipmentId) >= 0) { | |||
this.$message.error('该设备已存在'); | |||
return; | |||
} | |||
const { code, data } = await this.$axios({ | |||
url: '/analysis/equipment-analysis/status', | |||
method: 'get', | |||
@@ -523,7 +444,7 @@ export default { | |||
} | |||
.running { | |||
background-color: #5ad8a6; | |||
background-color: #288AFF; | |||
// background-color: #84f04e; | |||
} | |||
@@ -534,7 +455,7 @@ export default { | |||
.fault { | |||
// background-color: #ea5b5b; | |||
background-color: #fc9c91; | |||
background-color: #FC9C91; | |||
} | |||
.full { | |||
@@ -548,7 +469,7 @@ export default { | |||
} | |||
.stop { | |||
background-color: #000; | |||
background-color: #FFDC94; | |||
} | |||
.legend-row { | |||
@@ -557,7 +478,7 @@ export default { | |||
display: flex; | |||
justify-content: flex-end; | |||
> .legend:not(:last-child) { | |||
>.legend:not(:last-child) { | |||
margin-right: 12px; | |||
} | |||
@@ -600,7 +521,7 @@ export default { | |||
background: #ccc; | |||
} | |||
.echarts__status-chart > div { | |||
.echarts__status-chart>div { | |||
height: 100% !important; | |||
width: 100% !important; | |||
} | |||
@@ -37,7 +37,7 @@ | |||
@cancel="handleCancel" | |||
@confirm="handleConfirm" | |||
:before-close="handleCancel" | |||
width='70%' | |||
width='50%' | |||
> | |||
<group-class-add ref="classList" @successSubmit="successSubmit" /> | |||
</base-dialog> | |||
@@ -108,12 +108,6 @@ export default { | |||
], | |||
tableProps, | |||
tableBtn: [ | |||
this.$auth.hasPermi('base:group-classes:update') | |||
? { | |||
type: 'edit', | |||
btnName: '编辑' | |||
} | |||
: undefined, | |||
{ | |||
type: 'cancel', | |||
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') | |||
? { | |||
type: 'delete', | |||
@@ -1,17 +1,29 @@ | |||
<template> | |||
<el-form ref="form" :rules="rules" label-width="80px" :model="form"> | |||
<el-form-item label="班组名称" prop="name"> | |||
<el-input v-model="form.name"></el-input> | |||
</el-form-item> | |||
<el-form-item label="班组编码" prop="code"> | |||
<el-input v-model="form.code" disabled></el-input> | |||
</el-form-item> | |||
<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-form-item label="班组组长" prop="leaderName"> | |||
<el-input v-model="form.leaderName"></el-input> | |||
</el-form-item> | |||
<el-row> | |||
<el-col :span="12"> | |||
<el-form-item label="班组名称" prop="name"> | |||
<el-input v-model="form.name"></el-input> | |||
</el-form-item> | |||
</el-col> | |||
<el-col :span="12"> | |||
<el-form-item label="班组编码" prop="code"> | |||
<el-input v-model="form.code" disabled></el-input> | |||
</el-form-item> | |||
</el-col> | |||
</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> | |||
</template> | |||
<script> | |||
@@ -308,6 +308,16 @@ export default { | |||
} | |||
</script> | |||
<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 { | |||
background-color: #F2F4F9; | |||
.operationArea { | |||
@@ -35,8 +35,7 @@ | |||
" /> | |||
</div> | |||
</div> | |||
<div v-else style="margin-top: 20px; color: #c7c7c7; text-align: center"> | |||
暂无数据 | |||
<div v-else class="no-data-bg"> | |||
</div> | |||
</div> | |||
</template> | |||
@@ -45,7 +45,7 @@ export default { | |||
axisLine: { | |||
show: true, | |||
lineStyle: { | |||
color: '#ccc', | |||
color: '#777', | |||
}, | |||
}, | |||
axisTick: { | |||
@@ -63,7 +63,7 @@ export default { | |||
axisLine: { | |||
show: true, | |||
lineStyle: { | |||
color: '#ccc', | |||
color: '#777', | |||
}, | |||
}, | |||
name: '数量', | |||
@@ -33,7 +33,13 @@ | |||
:data-value="fc.id" | |||
class="factory-list__item" | |||
: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> | |||
</ul> | |||
<div v-else style="color: #0008; width: 128px; text-align: center"> | |||
@@ -93,15 +99,7 @@ | |||
v-if="list.length" | |||
:equipment-list="list" | |||
:render="renderKey" /> | |||
<div | |||
v-if="list.length == 0" | |||
style=" | |||
color: #c7c7c7; | |||
text-align: center; | |||
margin-top: 20px; | |||
"> | |||
没有设备 | |||
</div> | |||
<div v-if="list.length == 0" class="no-data-bg"></div> | |||
</div> | |||
</el-tab-pane> | |||
</el-tabs> | |||
@@ -507,9 +505,12 @@ li { | |||
.factory-list__item { | |||
font-size: 16px; | |||
line-height: 1; | |||
padding: 8px 64px 8px 16px; | |||
/* min-width: 64px; */ | |||
padding: 8px 16px 8px 16px; | |||
min-width: 128px; | |||
position: relative; | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
} | |||
.factory-list__item:hover, | |||
@@ -518,13 +519,13 @@ li { | |||
color: #0b58ff; | |||
} | |||
.factory-list__item.is-current::after { | |||
/* .factory-list__item.is-current::after { | |||
content: '√'; | |||
position: absolute; | |||
top: 8px; | |||
right: 16px; | |||
font-weight: bold; | |||
} | |||
} */ | |||
.custom-tree-class >>> .el-tree-node__content { | |||
width: 100%; | |||
@@ -551,8 +552,8 @@ li { | |||
.custom-icon-class { | |||
margin-right: 8px; | |||
width: 20px; | |||
height: 24px; | |||
background: url('../../../assets/images/tree-icon-1.png') 100% / contain | |||
height: 20px; | |||
background: url('../../../assets/images/Qian.png') center center / contain | |||
no-repeat; | |||
} | |||
@@ -116,7 +116,7 @@ export default { | |||
}, | |||
{ | |||
_action: 'params-monitor', | |||
label: '参数监控', | |||
label: '操作', | |||
subcomponent: { | |||
props: ['injectData'], | |||
render: function (h) { | |||
@@ -136,7 +136,7 @@ export default { | |||
}, | |||
}, | |||
}, | |||
'查看监控' | |||
'查看详情' | |||
); | |||
}, | |||
}, | |||
@@ -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> |
@@ -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; | |||
} |
@@ -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) | |||
} |
@@ -9,10 +9,10 @@ | |||
<el-form | |||
ref="form" | |||
:model="dataForm" | |||
label-width="100px" | |||
label-width="120px" | |||
v-loading="formLoading"> | |||
<el-row :gutter="20"> | |||
<el-col> | |||
<el-col :span="12"> | |||
<el-form-item | |||
label="产线" | |||
prop="productionId" | |||
@@ -30,7 +30,7 @@ | |||
</el-select> | |||
</el-form-item> | |||
</el-col> | |||
<el-col> | |||
<el-col :span="12"> | |||
<el-form-item | |||
label="工段" | |||
prop="sectionId" | |||
@@ -49,7 +49,7 @@ | |||
</el-form-item> | |||
</el-col> | |||
<el-col> | |||
<el-col :span="12"> | |||
<el-form-item | |||
label="按钮盒识别码" | |||
prop="buttonId" | |||
@@ -85,7 +85,7 @@ | |||
</el-form-item> --> | |||
</el-col> | |||
<el-col> | |||
<el-col :span="12"> | |||
<el-form-item label="按钮盒模式" prop="model"> | |||
<el-input | |||
v-model="dataForm.model" | |||
@@ -94,7 +94,7 @@ | |||
</el-form-item> | |||
</el-col> | |||
<el-col> | |||
<el-col :span="12"> | |||
<el-form-item | |||
label="按钮值" | |||
prop="keyValue" | |||
@@ -121,7 +121,7 @@ | |||
</el-form-item> | |||
</el-col> | |||
<el-col> | |||
<el-col :span="12"> | |||
<el-form-item label="检测内容" prop="inspectionDetContent"> | |||
<el-input | |||
type="textarea" | |||
@@ -75,8 +75,6 @@ export default { | |||
filterable: true, | |||
}, | |||
}, | |||
], | |||
[ | |||
{ | |||
select: true, | |||
label: '工段', | |||
@@ -103,8 +101,8 @@ export default { | |||
}, | |||
], | |||
}, | |||
{ input: true, label: '按钮盒模式', prop: 'model' }, | |||
], | |||
[{ input: true, label: '按钮盒模式', prop: 'model' }], | |||
[ | |||
{ | |||
input: true, | |||
@@ -121,8 +119,6 @@ export default { | |||
], | |||
bind: { type: 'number', min: 0, max: 100 }, | |||
}, | |||
], | |||
[ | |||
{ | |||
textarea: true, | |||
label: '检测内容', | |||
@@ -15,7 +15,8 @@ | |||
</DetailGraph> --> | |||
<!-- <DetailGraph id="dg3" key="dg3" ref="dg3" /> | |||
<DetailGraph id="dg4" key="dg4" ref="dg4" /> --> | |||
<div v-if="!series || series.length == 0" style="color: #777; font-size: 16px; letter-spacing: 1px; text-align: center; padding-top: 56px; text-decoration: underline;">暂无数据</div> | |||
<!-- <div v-if="!series || series.length == 0" style="color: #777; font-size: 16px; letter-spacing: 1px; text-align: center; padding-top: 56px; text-decoration: underline;">暂无数据</div> --> | |||
<div v-if="!series || series.length == 0" class="no-data-bg" /> | |||
<LineGraph | |||
v-else | |||
:x-props="lineData.xProps" | |||
@@ -6,7 +6,7 @@ | |||
--> | |||
<template> | |||
<div class="quality-container" style="background: #f2f4f9; flex: 1"> | |||
<div class="quality-container" style="background: #f2f4f9; flex: 1; display: flex; flex-direction: column;"> | |||
<el-row | |||
class="" | |||
style=" | |||
@@ -43,6 +43,7 @@ | |||
background: #fff; | |||
padding: 12px 16px 16px; | |||
border-radius: 8px; | |||
flex: 1; | |||
"> | |||
<el-row style="display: flex; align-items: center"> | |||
<div class="blue-title">产线检测详细</div> | |||
@@ -64,12 +65,13 @@ | |||
<transition mode="out-in" name="fade-down"> | |||
<template v-if="mode == 'table'"> | |||
<base-table | |||
v-if="mode == 'table'" | |||
v-if="mode == 'table' && list.length" | |||
:table-props="tableProps" | |||
:page="queryParams.pageNo" | |||
:limit="queryParams.pageSize" | |||
:table-data="list" | |||
@emitFun="handleEmitFun"></base-table> | |||
<div v-else class="no-data-bg"></div> | |||
</template> | |||
<GraphPage | |||
@@ -302,8 +304,8 @@ export default { | |||
}, | |||
{ | |||
// width: 128, | |||
prop: 'ratio', | |||
label: '比例', | |||
prop: 'scrapRatio', | |||
label: '比例(%)', | |||
// subcomponent: { | |||
// name: 'TextOnly', | |||
// props: { | |||
@@ -445,6 +447,7 @@ export default { | |||
inspectionContent: item.inspectionContent, | |||
...keyValuePairs, | |||
sumInput: item.sumInput, | |||
scrapRatio: item.scrapRatio, | |||
}; | |||
}); | |||
}, | |||