dy #33

Merged
hellody merged 2 commits from dy into test 2023-09-22 16:59:55 +08:00
52 changed files with 2848 additions and 998 deletions
Showing only changes of commit 699810f40a - Show all commits

View File

@ -19,9 +19,10 @@ export function createOrUpdateList(data) {
} }
// 自动排班,填充上月已有的排班 // 自动排班,填充上月已有的排班
export function autoSet() { export function autoSet(query) {
return request({ return request({
url: '/base/group-team-scheduling/autoSet', url: '/base/group-team-scheduling/autoSet',
method: 'get' method: 'get',
params: query
}) })
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 710 B

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -6,11 +6,12 @@
@import './btn.scss'; @import './btn.scss';
body { body {
height: 100%; height: 100%;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
Microsoft YaHei, Arial, sans-serif;
} }
// label { // label {
@ -27,170 +28,195 @@ body {
} }
html { html {
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
} }
#app { #app {
height: 100%; height: 100%;
} }
*, *,
*:before, *:before,
*:after { *:after {
box-sizing: inherit; box-sizing: inherit;
} }
.no-padding { .no-padding {
padding: 0px !important; padding: 0px !important;
} }
.padding-content { .padding-content {
padding: 4px 0; padding: 4px 0;
} }
a:focus, a:focus,
a:active { a:active {
outline: none; outline: none;
} }
a, a,
a:focus, a:focus,
a:hover { a:hover {
cursor: pointer; cursor: pointer;
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
} }
div:focus { div:focus {
outline: none; outline: none;
} }
.fr { .fr {
float: right; float: right;
} }
.fl { .fl {
float: left; float: left;
} }
.pr-5 { .pr-5 {
padding-right: 5px; padding-right: 5px;
} }
.pl-5 { .pl-5 {
padding-left: 5px; padding-left: 5px;
} }
.block { .block {
display: block; display: block;
} }
.pointer { .pointer {
cursor: pointer; cursor: pointer;
} }
.inlineBlock { .inlineBlock {
display: block; display: block;
} }
.clearfix { .clearfix {
&:after { &:after {
visibility: hidden; visibility: hidden;
display: block; display: block;
font-size: 0; font-size: 0;
content: " "; content: ' ';
clear: both; clear: both;
height: 0; height: 0;
} }
} }
aside { aside {
background: #eef1f6; background: #eef1f6;
padding: 8px 24px; padding: 8px 24px;
margin-bottom: 20px; margin-bottom: 20px;
border-radius: 2px; border-radius: 2px;
display: block; display: block;
line-height: 32px; line-height: 32px;
font-size: 16px; font-size: 16px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
color: #2c3e50; Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased; color: #2c3e50;
-moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
a { a {
color: #337ab7; color: #337ab7;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: rgb(32, 160, 255); color: rgb(32, 160, 255);
} }
} }
} }
//main-container全局样式 //main-container全局样式
.app-container { .app-container {
padding: 16px; padding: 16px;
} }
.components-container { .components-container {
margin: 30px 50px; margin: 30px 50px;
position: relative; position: relative;
} }
.text-center { .text-center {
text-align: center text-align: center;
} }
.sub-navbar { .sub-navbar {
height: 50px; height: 50px;
line-height: 50px; line-height: 50px;
position: relative; position: relative;
width: 100%; width: 100%;
text-align: right; text-align: right;
padding-right: 20px; padding-right: 20px;
transition: 600ms ease position; transition: 600ms ease position;
background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); background: linear-gradient(
90deg,
rgba(32, 182, 249, 1) 0%,
rgba(32, 182, 249, 1) 0%,
rgba(33, 120, 241, 1) 100%,
rgba(33, 120, 241, 1) 100%
);
.subtitle { .subtitle {
font-size: 20px; font-size: 20px;
color: #fff; color: #fff;
} }
&.draft { &.draft {
background: #d0d0d0; background: #d0d0d0;
} }
&.deleted { &.deleted {
background: #d0d0d0; background: #d0d0d0;
} }
} }
.link-type, .link-type,
.link-type:focus { .link-type:focus {
color: #337ab7; color: #337ab7;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: rgb(32, 160, 255); color: rgb(32, 160, 255);
} }
} }
.filter-container { .filter-container {
padding-bottom: 10px; padding-bottom: 10px;
.filter-item { .filter-item {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
margin-bottom: 10px; margin-bottom: 10px;
} }
} }
//refine vue-multiselect plugin //refine vue-multiselect plugin
.multiselect { .multiselect {
line-height: 16px; line-height: 16px;
} }
.multiselect--active { .multiselect--active {
z-index: 1000 !important; z-index: 1000 !important;
}
.no-data-bg {
height: 240px;
background: url(../images/no-data-bg.png) 50% 100% / contain
no-repeat;
position: relative;
&::after {
content: '暂无数据';
position: absolute;
bottom: 12px;
left: 50%;
transform: translateX(-50%);
color: #ccc;
font-size: 18px;
letter-spacing: 1px;
}
} }

View File

@ -5,110 +5,123 @@
/** 基础通用 **/ /** 基础通用 **/
.pt5 { .pt5 {
padding-top: 5px; padding-top: 5px;
} }
.pr5 { .pr5 {
padding-right: 5px; padding-right: 5px;
} }
.pb5 { .pb5 {
padding-bottom: 5px; padding-bottom: 5px;
} }
.mt5 { .mt5 {
margin-top: 5px; margin-top: 5px;
} }
.mr5 { .mr5 {
margin-right: 5px; margin-right: 5px;
} }
.mb5 { .mb5 {
margin-bottom: 5px; margin-bottom: 5px;
} }
.mb8 { .mb8 {
margin-bottom: 8px; margin-bottom: 8px;
} }
.ml5 { .ml5 {
margin-left: 5px; margin-left: 5px;
} }
.mt10 { .mt10 {
margin-top: 10px; margin-top: 10px;
} }
.mr10 { .mr10 {
margin-right: 10px; margin-right: 10px;
} }
.mb10 { .mb10 {
margin-bottom: 10px; margin-bottom: 10px;
} }
.ml10 { .ml10 {
margin-left: 10px; margin-left: 10px;
} }
.mt20 { .mt20 {
margin-top: 20px; margin-top: 20px;
} }
.mr20 { .mr20 {
margin-right: 20px; margin-right: 20px;
} }
.mb20 { .mb20 {
margin-bottom: 20px; margin-bottom: 20px;
} }
.ml20 { .ml20 {
margin-left: 20px; margin-left: 20px;
} }
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { .h1,
font-family: inherit; .h2,
font-weight: 500; .h3,
line-height: 1.1; .h4,
color: inherit; .h5,
.h6,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.1;
color: inherit;
} }
.el-message-box__status + .el-message-box__message{ .el-message-box__status + .el-message-box__message {
word-break: break-word; word-break: break-word;
} }
.el-dialog:not(.is-fullscreen) { .el-dialog:not(.is-fullscreen) {
margin-top: 6vh !important; // margin-top: 6vh !important;
margin-top: 12vh !important;
} }
.el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body { .el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body {
overflow: auto; overflow: auto;
overflow-x: hidden; overflow-x: hidden;
max-height: 70vh; max-height: 70vh;
padding: 10px 20px 0; padding: 10px 20px 0;
} }
.el-dialog{ .el-dialog {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
max-width: calc(100% - 30px); max-width: calc(100% - 30px);
max-height: calc(100% - 70px); max-height: calc(100% - 70px);
.el-dialog__body { .el-dialog__body {
overflow: auto; overflow: auto;
} }
} }
.el-table { .el-table {
.el-table__header-wrapper, .el-table__fixed-header-wrapper { .el-table__header-wrapper,
th { .el-table__fixed-header-wrapper {
word-break: break-word; th {
background-color: #f8f8f9; word-break: break-word;
color: #515a6e; background-color: #f8f8f9;
height: 40px; color: #515a6e;
font-size: 13px; height: 40px;
} font-size: 13px;
} }
.el-table__body-wrapper { }
.el-button [class*="el-icon-"] + span { .el-table__body-wrapper {
margin-left: 1px; .el-button [class*='el-icon-'] + span {
} margin-left: 1px;
} }
}
} }
/** 表单布局 **/ /** 表单布局 **/
.form-header { .form-header {
font-size:15px; font-size: 15px;
color:#6379bb; color: #6379bb;
border-bottom:1px solid #ddd; border-bottom: 1px solid #ddd;
margin:8px 10px 25px 10px; margin: 8px 10px 25px 10px;
padding-bottom:5px padding-bottom: 5px;
} }
/** 表格布局 **/ /** 表格布局 **/
@ -122,169 +135,171 @@
/* tree border */ /* tree border */
.tree-border { .tree-border {
margin-top: 5px; margin-top: 5px;
border: 1px solid #e5e6e7; border: 1px solid #e5e6e7;
background: #FFFFFF none; background: #ffffff none;
border-radius:4px; border-radius: 4px;
} }
.pagination-container .el-pagination { .pagination-container .el-pagination {
right: 0; right: 0;
position: absolute; position: absolute;
} }
@media ( max-width : 768px) { @media (max-width: 768px) {
.pagination-container .el-pagination > .el-pagination__jump { .pagination-container .el-pagination > .el-pagination__jump {
display: none !important; display: none !important;
} }
.pagination-container .el-pagination > .el-pagination__sizes { .pagination-container .el-pagination > .el-pagination__sizes {
display: none !important; display: none !important;
} }
} }
.el-table .fixed-width .el-button--mini { .el-table .fixed-width .el-button--mini {
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
width: inherit; width: inherit;
} }
/** 表格更多操作下拉样式 */ /** 表格更多操作下拉样式 */
.el-table .el-dropdown-link,.el-table .el-dropdown-selfdefine { .el-table .el-dropdown-link,
cursor: pointer; .el-table .el-dropdown-selfdefine {
margin-left: 5px; cursor: pointer;
margin-left: 5px;
} }
.el-table .el-dropdown, .el-icon-arrow-down { .el-table .el-dropdown,
font-size: 12px; .el-icon-arrow-down {
font-size: 12px;
} }
.el-tree-node__content > .el-checkbox { .el-tree-node__content > .el-checkbox {
margin-right: 8px; margin-right: 8px;
} }
.list-group-striped > .list-group-item { .list-group-striped > .list-group-item {
border-left: 0; border-left: 0;
border-right: 0; border-right: 0;
border-radius: 0; border-radius: 0;
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
&:not(:last-child) { &:not(:last-child) {
border-bottom: 1px solid #e7eaec; border-bottom: 1px solid #e7eaec;
} }
} }
.list-group { .list-group {
padding-left: 0px; padding-left: 0px;
list-style: none; list-style: none;
} }
.list-group-item { .list-group-item {
// border-bottom: 1px solid #e7eaec; // border-bottom: 1px solid #e7eaec;
// border-top: 1px solid #e7eaec; // border-top: 1px solid #e7eaec;
margin-bottom: -1px; margin-bottom: -1px;
padding: 11px 0px; padding: 11px 0px;
font-size: 13px; font-size: 13px;
} }
.pull-right { .pull-right {
float: right !important; float: right !important;
} }
.el-card__header { .el-card__header {
padding: 14px 15px 7px; padding: 14px 15px 7px;
min-height: 40px; min-height: 40px;
} }
.el-card__body { .el-card__body {
padding: 15px 20px 20px 20px; padding: 15px 20px 20px 20px;
} }
.card-box { .card-box {
padding-right: 15px; padding-right: 15px;
padding-left: 15px; padding-left: 15px;
margin-bottom: 10px; margin-bottom: 10px;
} }
/* button color */ /* button color */
.el-button--cyan.is-active, .el-button--cyan.is-active,
.el-button--cyan:active { .el-button--cyan:active {
background: #20B2AA; background: #20b2aa;
border-color: #20B2AA; border-color: #20b2aa;
color: #FFFFFF; color: #ffffff;
} }
.el-button--cyan:focus, .el-button--cyan:focus,
.el-button--cyan:hover { .el-button--cyan:hover {
background: #48D1CC; background: #48d1cc;
border-color: #48D1CC; border-color: #48d1cc;
color: #FFFFFF; color: #ffffff;
} }
.el-button--cyan { .el-button--cyan {
background-color: #20B2AA; background-color: #20b2aa;
border-color: #20B2AA; border-color: #20b2aa;
color: #FFFFFF; color: #ffffff;
} }
/* text color */ /* text color */
.text-navy { .text-navy {
color: #1ab394; color: #1ab394;
} }
.text-primary { .text-primary {
color: inherit; color: inherit;
} }
.text-success { .text-success {
color: #1c84c6; color: #1c84c6;
} }
.text-info { .text-info {
color: #23c6c8; color: #23c6c8;
} }
.text-warning { .text-warning {
color: #f8ac59; color: #f8ac59;
} }
.text-danger { .text-danger {
color: #ed5565; color: #ed5565;
} }
.text-muted { .text-muted {
color: #888888; color: #888888;
} }
/* image */ /* image */
.img-circle { .img-circle {
border-radius: 50%; border-radius: 50%;
} }
.img-lg { .img-lg {
width: 120px; width: 120px;
height: 120px; height: 120px;
} }
.avatar-upload-preview { .avatar-upload-preview {
position: absolute; position: absolute;
top: 50%; top: 50%;
transform: translate(50%, -50%); transform: translate(50%, -50%);
width: 200px; width: 200px;
height: 200px; height: 200px;
border-radius: 50%; border-radius: 50%;
box-shadow: 0 0 4px #ccc; box-shadow: 0 0 4px #ccc;
overflow: hidden; overflow: hidden;
} }
/* 拖拽列样式 */ /* 拖拽列样式 */
.sortable-ghost{ .sortable-ghost {
opacity: .8; opacity: 0.8;
color: #fff!important; color: #fff !important;
background: #42b983!important; background: #42b983 !important;
} }
.top-right-btn { .top-right-btn {
position: relative; position: relative;
float: right; float: right;
} }

View File

@ -1,3 +1,5 @@
import moment from 'moment';
/** /**
* 用于动态表结构的 tableProps 生成 * 用于动态表结构的 tableProps 生成
* @param {*} nameData * @param {*} nameData
@ -16,10 +18,9 @@ export function handleNameData(nameData) {
function step1(tree1) { function step1(tree1) {
return Array.from(new Set(tree1.map((item) => item.name))) return Array.from(new Set(tree1.map((item) => item.name)))
.sort() .sort()
.map((item) => ({ .map((time) => ({
prop: item, prop: time,
label: item, label: moment(time).format('YYYY-MM-DD HH:mm:ss'),
align: 'center',
children: [], children: [],
})); }));
} }
@ -33,7 +34,6 @@ function step2(firstTierProps, tree2) {
parent.children.push({ parent.children.push({
label: nd.name, label: nd.name,
prop: `${parent.prop}-${nd.name}`, prop: `${parent.prop}-${nd.name}`,
align: 'center',
}); });
} }
}); });

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
@ -85,12 +86,11 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'equipmentName', label: '设备', align: 'center' }, { prop: 'equipmentName', label: '设备' },
{ prop: 'groupName', label: '分组', align: 'center' }, { prop: 'groupName', label: '分组' },
{ {
_action: 'equipment-bind-group-show-alert', _action: 'equipment-bind-group-show-alert',
label: '分组报警', label: '分组报警',
align: 'center',
subcomponent: { subcomponent: {
props: ['injectData'], props: ['injectData'],
render: function (h) { render: function (h) {
@ -98,7 +98,7 @@ export default {
return h( return h(
'el-button', 'el-button',
{ {
props: { type: 'text', size: 'mini' }, props: { type: 'text' },
on: { on: {
click: function () { click: function () {
console.log('inejctdata', _this.injectData); console.log('inejctdata', _this.injectData);
@ -162,6 +162,9 @@ export default {
label: '设备', label: '设备',
url: '/base/equipment/page?pageNo=1&pageSize=100', url: '/base/equipment/page?pageNo=1&pageSize=100',
prop: 'equipmentId', prop: 'equipmentId',
bind: {
filterable: true,
},
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
}, },
], ],
@ -173,6 +176,9 @@ export default {
// depends: '__product_line', // 线 // depends: '__product_line', // 线
// depends: 'productionLineId', // depends: 'productionLineId',
prop: 'groupId', prop: 'groupId',
bind: {
filterable: true,
},
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
}, },
], ],
@ -223,7 +229,7 @@ export default {
/** 查询列表 */ /** 查询列表 */
getList() { getList() {
this.loading = true; this.loading = true;
_// _; //
getEquipmentBindGroupPage(this.queryParams).then((response) => { getEquipmentBindGroupPage(this.queryParams).then((response) => {
this.list = response.data.list; this.list = response.data.list;
this.total = response.data.total; this.total = response.data.total;

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
@ -83,28 +84,26 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'productionLine', label: '产线名称', align: 'center' }, { prop: 'productionLine', label: '产线名称' },
{ prop: 'workshopSection', label: '工段名称', align: 'center' }, { prop: 'workshopSection', label: '工段名称' },
{ prop: 'equipment', label: '设备名称', align: 'center' }, { prop: 'equipment', label: '设备名称' },
{ prop: 'sort', label: '工段中排序', align: 'center' }, { prop: 'sort', label: '工段中排序' },
{ {
prop: 'lineDataType', prop: 'lineDataType',
label: '产线数据类型', label: '产线数据类型',
align: 'center',
filter: (val) => filter: (val) =>
val != null ? ['无类型', '进口计数', '出口计数'][val] : '-', val != null ? ['无类型', '进口计数', '出口计数'][val] : '-',
}, },
{ {
prop: 'sectionDataType', prop: 'sectionDataType',
label: '工段数据类型', label: '工段数据类型',
align: 'center',
filter: (val) => filter: (val) =>
val != null ? ['无类型', '进口计数', '出口计数'][val] : '-', val != null ? ['无类型', '进口计数', '出口计数'][val] : '-',
}, },
// { // {
// action: 'show-alert', // action: 'show-alert',
// label: '', // label: '',
// align: 'center', // ,
// subcomponent: { // subcomponent: {
// props: ['injectData'], // props: ['injectData'],
// render: function (h) { // render: function (h) {

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
@ -84,13 +85,12 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'name', label: '设备分组名称', align: 'center' }, { prop: 'name', label: '设备分组名称' },
{ prop: 'code', label: '检测分组编码', align: 'center' }, { prop: 'code', label: '设备分组编码' },
{ prop: 'remark', label: '备注', align: 'center' }, { prop: 'remark', label: '备注' },
{ {
_action: 'equipment-group-show-alert', _action: 'equipment-group-show-alert',
label: '报警', label: '报警',
align: 'center',
subcomponent: { subcomponent: {
props: ['injectData'], props: ['injectData'],
render: function (h) { render: function (h) {
@ -98,7 +98,7 @@ export default {
return h( return h(
'el-button', 'el-button',
{ {
props: { type: 'text', size: 'mini' }, props: { type: 'text' },
on: { on: {
click: function () { click: function () {
console.log('inejctdata', _this.injectData); console.log('inejctdata', _this.injectData);
@ -127,7 +127,7 @@ export default {
type: 'input', type: 'input',
label: '分组编码', label: '分组编码',
placeholder: '请输入设备分组编码', placeholder: '请输入设备分组编码',
param: 'codes', param: 'code',
}, },
{ {
type: 'button', type: 'button',
@ -172,6 +172,7 @@ export default {
label: '分组编码', label: '分组编码',
prop: 'code', prop: 'code',
url: '/base/equipment-group/getCode', url: '/base/equipment-group/getCode',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
}, },
], ],
[ [
@ -302,11 +303,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm( .confirm('是否确认删除设备分组 "' + row.name + '"?')
'是否确认删除设备分组(用于同类型不同厂家的设备区分)编号为"' +
id +
'"的数据项?'
)
.then(function () { .then(function () {
return deleteEquipmentGroup(id); return deleteEquipmentGroup(id);
}) })

View File

@ -0,0 +1,177 @@
<!--
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
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
v-model="dataForm.type"
placeholder="请选择报警类型"
@change="$emit('update', dataForm)">
<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
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
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
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
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: () => ({}),
},
},
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 handleProductlineChange(id) {
await this.getWorksectionList(id);
this.dataForm.workshopSectionId = null;
this.$emit('update', this.dataForm);
},
async getCode(url) {
const response = await this.$axios(url);
return response.data;
},
},
};
</script>
<style scoped lang="scss">
.el-date-editor,
.el-select {
width: 100%;
}
</style>

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
@ -37,7 +38,7 @@
@close="cancel" @close="cancel"
@cancel="cancel" @cancel="cancel"
@confirm="submitForm"> @confirm="submitForm">
<DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" /> <DialogForm v-if="open" ref="form" v-model="form" :rows="rows" />
</base-dialog> </base-dialog>
</div> </div>
</template> </template>
@ -54,10 +55,11 @@ import {
import basicPageMixin from '@/mixins/lb/basicPageMixin'; import basicPageMixin from '@/mixins/lb/basicPageMixin';
import moment from 'moment'; import moment from 'moment';
import { publicFormatter } from '@/utils/dict'; import { publicFormatter } from '@/utils/dict';
import DialogForm from './dialogForm.vue';
export default { export default {
name: 'EquipmentGroupAlarm', name: 'EquipmentGroupAlarm',
components: {}, components: { DialogForm },
mixins: [basicPageMixin], mixins: [basicPageMixin],
data() { data() {
return { return {
@ -84,22 +86,27 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'code', label: '报警编码', align: 'center' }, { width: 240, prop: 'code', label: '报警编码' },
{ prop: 'type', label: '报警类型', align: 'center' }, {
prop: 'type',
label: '报警类型',
filter: (val) =>
val != null ? ['-', '字符型', '布尔型', '-'][val] : '-',
},
{ {
prop: 'grade', prop: 'grade',
label: '报警级别', label: '报警级别',
align: 'center',
filter: publicFormatter(this.DICT_TYPE.EQU_ALARM_LEVEL), filter: publicFormatter(this.DICT_TYPE.EQU_ALARM_LEVEL),
}, },
{ prop: 'alarmCode', label: '设备报警编码', align: 'center' }, { prop: 'alarmCode', label: '设备报警编码' },
{ prop: 'plcParamName', label: '参数列名', align: 'center' }, { prop: 'plcParamName', label: '参数列名' },
{ prop: 'alarmContent', label: '报警内容', align: 'center' }, { prop: 'alarmContent', label: '报警内容' },
], ],
searchBarFormConfig: [ searchBarFormConfig: [
{ {
type: 'input', type: 'input',
label: '设备分组编码', label: '设备分组编码',
width: '220',
placeholder: '/', placeholder: '/',
param: 'equipmentGroupCode', param: 'equipmentGroupCode',
defaultSelect: null, defaultSelect: null,
@ -181,14 +188,14 @@ export default {
}, },
// //
form: { form: {
id: undefined, id: null,
equipmentGroupId: undefined, equipmentGroupId: null,
code: undefined, code: null,
type: undefined, type: null,
grade: undefined, grade: null,
alarmCode: undefined, alarmCode: null,
alarmContent: undefined, alarmContent: null,
plcParamName: undefined, plcParamName: null,
}, },
// // // //
// rules: { // rules: {
@ -249,14 +256,14 @@ export default {
/** 表单重置 */ /** 表单重置 */
reset() { reset() {
this.form = { this.form = {
id: undefined, id: null,
equipmentGroupId: undefined, equipmentGroupId: null,
code: undefined, code: null,
type: undefined, type: null,
grade: undefined, grade: null,
alarmCode: undefined, alarmCode: null,
alarmContent: undefined, alarmContent: null,
plcParamName: undefined, plcParamName: null,
}; };
this.resetForm('form'); this.resetForm('form');
}, },
@ -321,7 +328,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm('是否确认删除设备分组报警明细编号为"' + id + '"的数据项?') .confirm('是否确认删除该报警?')
.then(function () { .then(function () {
return deleteEquipmentGroupAlarm(id); return deleteEquipmentGroupAlarm(id);
}) })

View File

@ -12,6 +12,7 @@
:page="queryParams.pageNo" :page="queryParams.pageNo"
:limit="queryParams.pageSize" :limit="queryParams.pageSize"
:table-data="list" :table-data="list"
ref="pageTable"
@emitFun="handleEmitFun"> @emitFun="handleEmitFun">
<method-btn <method-btn
v-if="tableBtn.length" v-if="tableBtn.length"
@ -58,9 +59,12 @@ const switchBtn = {
name: 'SwitchBtn', name: 'SwitchBtn',
props: ['injectData'], props: ['injectData'],
data() { data() {
return { return {};
active: +this.injectData[this.injectData.prop] == 1 ? true : false, },
}; computed: {
active() {
return +this.injectData[this.injectData.prop] == 1 ? true : false;
},
}, },
methods: {}, methods: {},
render: function (h) { render: function (h) {
@ -72,8 +76,6 @@ const switchBtn = {
}, },
on: { on: {
change: (newVal) => { change: (newVal) => {
this.active = !this.active;
console.log('changed emit', newVal);
this.$emit('emitData', { this.$emit('emitData', {
action: 'update-collect', action: 'update-collect',
payload: { payload: {
@ -95,6 +97,7 @@ export default {
components: {}, components: {},
data() { data() {
return { return {
tableKey: Math.random(),
searchBarKeys: ['name', 'plcTableName'], searchBarKeys: ['name', 'plcTableName'],
tableBtn: [ tableBtn: [
this.$auth.hasPermi('base:equipment-plc:update') this.$auth.hasPermi('base:equipment-plc:update')
@ -118,17 +121,16 @@ export default {
// width: 180, // width: 180,
// filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), // filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
// }, // },
{ prop: 'code', label: '编码', align: 'center' }, { prop: 'code', label: '编码' },
{ prop: 'plcTableName', label: '关联表名', align: 'center' }, { prop: 'plcTableName', label: '关联表名' },
{ prop: 'name', label: '标识名称', align: 'center' }, { prop: 'name', label: '标识名称' },
{ prop: 'enName', label: '英文名称', align: 'center' }, { prop: 'enName', label: '英文名称' },
{ {
prop: 'collection', prop: 'collection',
label: '是否采集', label: '是否采集',
align: 'center',
subcomponent: switchBtn, subcomponent: switchBtn,
}, },
{ prop: 'description', label: '描述', align: 'center' }, { prop: 'description', label: '描述' },
], ],
searchBarFormConfig: [ searchBarFormConfig: [
{ {
@ -185,6 +187,7 @@ export default {
label: '编码', label: '编码',
prop: 'code', prop: 'code',
url: '/base/equipment-group/getCode', url: '/base/equipment-group/getCode',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
}, },
], ],
[ [
@ -192,7 +195,7 @@ export default {
input: true, input: true,
label: '标识', label: '标识',
prop: 'name', prop: 'name',
// rules: [{ required: true, message: '', trigger: 'blur' }], rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
// bind: { // bind: {
// disabled: true, // some condition, like detail mode... // disabled: true, // some condition, like detail mode...
// } // }
@ -246,7 +249,7 @@ export default {
handleEmitFun({ action, payload }) { handleEmitFun({ action, payload }) {
switch (action) { switch (action) {
case 'update-collect': case 'update-collect':
this.reset(); this.reset();
const tempForm = {}; const tempForm = {};
Object.keys(this.form).forEach((key) => { Object.keys(this.form).forEach((key) => {
tempForm[key] = payload[key]; tempForm[key] = payload[key];
@ -267,6 +270,7 @@ export default {
this.list = response.data.list; this.list = response.data.list;
this.total = response.data.total; this.total = response.data.total;
this.loading = false; this.loading = false;
// this.tableKey = Math.random(); // method 1
}); });
}, },
/** 取消按钮 */ /** 取消按钮 */

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
@ -61,7 +62,7 @@ export default {
components: {}, components: {},
data() { data() {
return { return {
searchBarKeys: ['name', 'plcTableName'], searchBarKeys: ['equipmentId', 'plcId'],
// tableBtn: [ // tableBtn: [
// this.$auth.hasPermi('base:equipment-plc:update') // this.$auth.hasPermi('base:equipment-plc:update')
// ? { // ? {
@ -98,18 +99,17 @@ export default {
// width: 180, // width: 180,
// filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), // filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
// }, // },
{ prop: 'productionLine', label: '产线', align: 'center' }, { prop: 'productionLine', label: '产线' },
{ prop: 'workshopSection', label: '工段', align: 'center' }, { prop: 'workshopSection', label: '工段' },
{ prop: 'equipmentName', label: '设备名', align: 'center' }, { prop: 'equipmentName', label: '设备名' },
{ prop: 'equipmentCode', label: '设备编码', align: 'center' }, { prop: 'equipmentCode', label: '设备编码' },
{ prop: 'plcCode', label: '关联表编码', align: 'center' }, { prop: 'plcCode', label: '关联表编码' },
{ prop: 'plcTableName', label: '关联表名', align: 'center' }, { prop: 'plcTableName', label: '关联表名' },
{ prop: 'plcName', label: '标识名称', align: 'center' }, { prop: 'plcName', label: '标识名称' },
{ prop: 'bindingParameters', label: '绑定参数数量', align: 'center' }, { prop: 'bindingParameters', label: '绑定参数数量' },
{ {
_action: 'params-bind', _action: 'params-bind',
label: '查看绑定', label: '查看绑定',
align: 'center',
subcomponent: { subcomponent: {
props: ['injectData'], props: ['injectData'],
render: function (h) { render: function (h) {
@ -136,16 +136,18 @@ export default {
], ],
searchBarFormConfig: [ searchBarFormConfig: [
{ {
type: 'input', type: 'select',
label: '设备名', label: '设备名',
placeholder: '请输入设备名', placeholder: '请选择设备',
param: 'equipmentId', param: 'equipmentId',
selectOptions: [],
}, },
{ {
type: 'input', type: 'select',
label: '编码', label: 'PLC编码',
placeholder: '请输入编码', placeholder: '请选择编码',
param: 'plcId', param: 'plcId',
selectOptions: [],
}, },
{ {
type: 'button', type: 'button',
@ -179,6 +181,7 @@ export default {
select: true, select: true,
label: '关联表名', label: '关联表名',
prop: 'plcId', prop: 'plcId',
labelKey: `plcTableName`,
url: '/base/equipment-plc/listAll', url: '/base/equipment-plc/listAll',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
}, },
@ -207,8 +210,44 @@ export default {
}, },
created() { created() {
this.getList(); this.getList();
this.initSearchOptions();
}, },
methods: { methods: {
async getEquipmentOptions() {
const res = await this.$axios({
url: '/base/equipment/listAll',
method: 'get',
});
return res.data;
},
async getPlcOptions() {
const res = await this.$axios({
url: '/base/equipment-plc/listAll',
method: 'get',
});
return res.data;
},
/** 初始化查询条件 */
async initSearchOptions() {
Promise.all([this.getEquipmentOptions(), this.getPlcOptions()]).then(
([eqList, plcList]) => {
this.searchBarFormConfig[0].selectOptions = eqList.map((item) => {
return {
name: item.name,
id: item.id,
};
});
this.searchBarFormConfig[1].selectOptions = plcList.map((item) => {
return {
name: item.name,
id: item.id,
};
});
}
);
},
/** 覆盖 handleEmitFun 的默认实现 */ /** 覆盖 handleEmitFun 的默认实现 */
handleEmitFun({ action, payload }) { handleEmitFun({ action, payload }) {
switch (action) { switch (action) {

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
@ -68,29 +69,27 @@ export default {
// width: 180, // width: 180,
// filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), // filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
// }, // },
{ prop: 'plcParamName', label: '参数列名', align: 'center' }, { prop: 'plcParamName', label: '参数列名' },
{ prop: 'name', label: '参数名称', align: 'center' }, { prop: 'name', label: '参数名称' },
{ {
prop: 'unit', prop: 'unit',
label: '单位', label: '单位',
align: 'center',
filter: publicFormatter('unit_dict'), filter: publicFormatter('unit_dict'),
}, },
{ {
prop: 'collection', prop: 'collection',
label: '是否采集', label: '是否采集',
align: 'center',
filter: (val) => (val != null ? ['否', '是'][val] : '-'), filter: (val) => (val != null ? ['否', '是'][val] : '-'),
}, },
{ prop: 'minValue', label: '最小值', align: 'center' }, { prop: 'minValue', label: '最小值' },
{ prop: 'maxValue', label: '最大值', align: 'center' }, { prop: 'maxValue', label: '最大值' },
{ prop: 'defaultValue', label: '标准值', align: 'center' }, { prop: 'defaultValue', label: '标准值' },
{ prop: 'description', label: '描述', align: 'center' }, { prop: 'description', label: '描述' },
{ prop: 'remark', label: '备注', align: 'center' }, { prop: 'remark', label: '备注' },
// { // {
// _action: 'params-bind', // _action: 'params-bind',
// label: '', // label: '',
// align: 'center', // ,
// subcomponent: { // subcomponent: {
// props: ['injectData'], // props: ['injectData'],
// render: function (h) { // render: function (h) {
@ -157,7 +156,7 @@ export default {
{ {
type: 'number', type: 'number',
message: '请输入正确的数字', message: '请输入正确的数字',
trigger: 'blur', trigger: 'change',
transform: (val) => Number(val), transform: (val) => Number(val),
}, },
], ],
@ -167,10 +166,13 @@ export default {
label: '最大值', label: '最大值',
prop: 'maxValue', prop: 'maxValue',
rules: [ rules: [
{
required: false,
},
{ {
type: 'number', type: 'number',
message: '请输入正确的数字', message: '请输入正确的数字',
trigger: 'blur', trigger: 'change',
transform: (val) => Number(val), transform: (val) => Number(val),
}, },
], ],
@ -349,8 +351,8 @@ export default {
plcParamName: undefined, plcParamName: undefined,
name: undefined, name: undefined,
unit: undefined, unit: undefined,
minValue: undefined, minValue: null,
maxValue: undefined, maxValue: null,
defaultValue: undefined, defaultValue: undefined,
collection: undefined, collection: undefined,
description: undefined, description: undefined,

View File

@ -16,6 +16,7 @@
<method-btn <method-btn
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
:width="120"
label="操作" label="操作"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
@ -147,15 +148,14 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'name', label: '设备名称', align: 'center' }, { prop: 'name', label: '设备名称' },
{ prop: 'code', label: '检测编码', align: 'center' }, { width: 256, prop: 'code', label: '检测编码' },
{ prop: 'equipmentType', label: '设备类型', align: 'center' }, { prop: 'equipmentType', label: '设备类型' },
{ prop: 'enName', label: '英文名称', align: 'center' }, { prop: 'enName', label: '英文名称' },
{ prop: 'abbr', label: '缩写', align: 'center' }, { prop: 'abbr', label: '缩写' },
{ {
action: 'show-detail', action: 'show-detail',
label: '详情', label: '详情',
align: 'center',
subcomponent: { subcomponent: {
props: ['injectData'], props: ['injectData'],
render: function (h) { render: function (h) {
@ -377,8 +377,8 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'name', label: '属性名称', align: 'center' }, { prop: 'name', label: '属性名称' },
{ prop: 'value', label: '属性值', align: 'center' }, { prop: 'value', label: '属性值' },
], ],
// //
open: false, open: false,

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
@ -85,9 +86,9 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'name', label: '设备类型名称', align: 'center' }, { prop: 'name', label: '设备类型名称', },
{ prop: 'code', label: '检测类型编码', align: 'center' }, { prop: 'code', label: '检测类型编码', },
{ prop: 'remark', label: '备注', align: 'center' }, { prop: 'remark', label: '备注', },
], ],
searchBarFormConfig: [ searchBarFormConfig: [
{ {

View File

@ -443,7 +443,7 @@ export default {
background-color: #0B58FF; background-color: #0B58FF;
border-radius: 1px; border-radius: 1px;
margin-right: 8px; margin-right: 8px;
margin-top: 10px; margin-top: 12px;
} }
} }
</style> </style>

View File

@ -8,6 +8,7 @@
:props="{ checkStrictly: true, value: 'id', label: 'name' }" :props="{ checkStrictly: true, value: 'id', label: 'name' }"
popper-class="cascaderParent" popper-class="cascaderParent"
size="small" size="small"
style="width: 250px;"
clearable></el-cascader> clearable></el-cascader>
</el-form-item> </el-form-item>
<el-form-item label="时间维度"> <el-form-item label="时间维度">
@ -157,7 +158,31 @@ export default {
this.$emit('submit', this.queryParams) this.$emit('submit', this.queryParams)
}, },
exportData() { exportData() {
this.$emit('exportD') let name
if (this.queryParams.objId) {
name = this.getObjName(this.objList, this.queryParams.objId)
} else {
this.$modal.msgWarning("对象不能为空")
return false
}
this.$emit('exportD', {name: name})
},
// name
getObjName(list, id) {
let _this = this
for (let i = 0; i < list.length; i++) {
let a = list[i]
if (a.id === id) {
return a.name
} else {
if (a.children && a.children.length > 0) {
let res = _this.getObjName(a.children, id)
if (res) {
return res
}
}
}
}
}, },
transformTime(timeStamp) {// transformTime(timeStamp) {//
let year = moment(timeStamp).format('YYYY') let year = moment(timeStamp).format('YYYY')

View File

@ -83,9 +83,10 @@ export default {
} }
this.chartData = this.list this.chartData = this.list
}, },
exportData() { exportData(val) {
if (this.list.length > 0) { if (this.list.length > 0) {
var wb = XLSX.utils.table_to_book(document.querySelector(".qoq-out-table")) var wb = XLSX.utils.table_to_book(document.querySelector(".qoq-out-table"))
let fileName = val.name + "环比分析.xlsx"
var wbout = XLSX.write(wb, { var wbout = XLSX.write(wb, {
bookType: "xlsx", bookType: "xlsx",
bookSST: true, bookSST: true,
@ -94,7 +95,7 @@ export default {
try { try {
FileSaver.saveAs( FileSaver.saveAs(
new Blob([wbout], { type: "application/octet-stream" }), new Blob([wbout], { type: "application/octet-stream" }),
"环比分析.xlsx" fileName
) )
} catch (e) { } catch (e) {
if (typeof console !== "undefined") console.log(e, wbout); if (typeof console !== "undefined") console.log(e, wbout);

View File

@ -407,7 +407,7 @@ export default {
background-color: #0B58FF; background-color: #0B58FF;
border-radius: 1px; border-radius: 1px;
margin-right: 8px; margin-right: 8px;
margin-top: 10px; margin-top: 12px;
} }
} }
</style> </style>

View File

@ -13,7 +13,9 @@
:limit="queryParams.pageSize" :limit="queryParams.pageSize"
:table-props="tableProps" :table-props="tableProps"
:table-data="list" :table-data="list"
:selectWidth="55"
:max-height="tableH" :max-height="tableH"
@selection-change="selectChange"
/> />
<pagination <pagination
:page.sync="queryParams.pageNo" :page.sync="queryParams.pageNo"
@ -25,9 +27,11 @@
</template> </template>
<script> <script>
import { getEnergyQuantityRealtimePage, exportEnergyQuantityRealtimeExcel } from "@/api/base/energyQuantityRealtime"; import { getEnergyQuantityRealtimePage, exportEnergyQuantityRealtimeExcel } from "@/api/base/energyQuantityRealtime"
import { getEnergyTypeListAll } from "@/api/base/energyType"; import { getEnergyTypeListAll } from "@/api/base/energyType"
// import { publicFormatter } from '@/utils/dict' // import { publicFormatter } from '@/utils/dict'
import FileSaver from "file-saver"
import * as XLSX from 'xlsx/xlsx.mjs'
const tableProps = [ const tableProps = [
{ {
prop: 'objName', prop: 'objName',
@ -113,7 +117,8 @@ export default {
startTime: null, startTime: null,
endTime: null endTime: null
}, },
energyTypeList: [] energyTypeList: [],
exportList: []
}; };
}, },
created() { created() {
@ -134,12 +139,13 @@ export default {
this.getList() this.getList()
break break
default: default:
this.$modal.confirm('是否确认导出').then(() => { this.exportTable()
return exportEnergyQuantityRealtimeExcel({...this.queryParams}); // this.$modal.confirm('').then(() => {
}).then(response => { // return exportEnergyQuantityRealtimeExcel({...this.queryParams});
console.log(response) // }).then(response => {
this.$download.excel(response, '能源抄表.xls'); // console.log(response)
}).catch(() => {}) // this.$download.excel(response, '.xls');
// }).catch(() => {})
} }
}, },
/** 查询列表 */ /** 查询列表 */
@ -147,6 +153,7 @@ export default {
getEnergyQuantityRealtimePage(this.queryParams).then(response => { getEnergyQuantityRealtimePage(this.queryParams).then(response => {
this.list = response.data.list || [] this.list = response.data.list || []
this.total = response.data.total; this.total = response.data.total;
this.exportList = []
}); });
}, },
getTypeList() { getTypeList() {
@ -154,6 +161,48 @@ export default {
this.formConfig[0].selectOptions = res.data || [] this.formConfig[0].selectOptions = res.data || []
this.energyTypeList = res.data || [] this.energyTypeList = res.data || []
}) })
},
selectChange(val) {
console.log(val)
this.exportList = val
},
//
exportTable() {
if (this.exportList.length > 0) {
let body = this.exportList.map((x) => [
x.objName,
x.objRemark,
x.energyTypeName,
x.startValue,
x.endValue,
x.diffValue,
x.amount
])
let header = []
this.tableProps.map((y) => {
header.push(y.label)
})
body.unshift(header)
console.log(body)
const filename = '能源抄表.xlsx'
const ws_name = 'Sheet1'
const wb = XLSX.utils.book_new()
const ws = XLSX.utils.aoa_to_sheet(body)
XLSX.utils.book_append_sheet(wb, ws, ws_name)
let wbout = XLSX.write(wb, {
bookType: 'xlsx',
bookSST: false,
type: 'array'
})
FileSaver.saveAs(
new Blob([wbout], {
type: 'application/octet-stream'
}),
filename
)
} else {
this.$modal.msgWarning('请勾选需要导出的数据')
}
} }
} }
}; };

View File

@ -1,5 +1,5 @@
<template> <template>
<el-form ref="form" :rules="rules" label-width="100px" :model="form"> <el-form ref="form" :rules="rules" label-width="110px" :model="form">
<el-form-item label="监控对象" prop="objectId"> <el-form-item label="监控对象" prop="objectId">
<el-cascader <el-cascader
style='width: 100%;' style='width: 100%;'
@ -11,7 +11,7 @@
clearable></el-cascader> clearable></el-cascader>
</el-form-item> </el-form-item>
<el-form-item label="能源类型" prop="energyTypeId"> <el-form-item label="能源类型" prop="energyTypeId">
<el-select v-model="form.energyTypeId" placeholder="请选择" style="width: 100%;"> <el-select v-model="form.energyTypeId" placeholder="请选择" style="width: 100%;" @change="toggleType">
<el-option <el-option
v-for="item in this.energyTypeList" v-for="item in this.energyTypeList"
:key="item.id" :key="item.id"
@ -26,7 +26,7 @@
<el-option label="详细" :value= "2" ></el-option> <el-option label="详细" :value= "2" ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="监控详细参数" prop="plcParamId" v-if="form.type === 2"> <el-form-item label="监控详细参数" prop="type" v-if="form.type === 2">
<el-select v-model="form.plcParamId" placeholder="请选择" style="width: 100%;" @change="selectDetail"> <el-select v-model="form.plcParamId" placeholder="请选择" style="width: 100%;" @change="selectDetail">
<el-option <el-option
v-for="item in detailList" v-for="item in detailList"
@ -99,7 +99,7 @@ export default {
if (res.code === 0) { if (res.code === 0) {
this.form = res.data this.form = res.data
this.form.plcParamId = res.data.plcParamId || '' this.form.plcParamId = res.data.plcParamId || ''
this.form.limitType = this.form.limitType + '' this.form.limitType = this.form.limitType ? this.form.limitType + '' : ''
this.objIds = this.changeDetSelect(this.form.objectId, this.objList) this.objIds = this.changeDetSelect(this.form.objectId, this.objList)
if (this.form.type === 2) { if (this.form.type === 2) {
this.getDetailList() this.getDetailList()
@ -128,7 +128,15 @@ export default {
console.log(this.form) console.log(this.form)
this.form.plcParamId = '' this.form.plcParamId = ''
if (val === 2) { if (val === 2) {
if (this.form.objectId && this.form.energyTypeId) {
this.getDetailList()
}
}
},
toggleType() {
if (this.form.energyTypeId && this.form.type) {
this.getDetailList() this.getDetailList()
this.form.plcParamId = ''
} }
}, },
// //
@ -158,6 +166,10 @@ export default {
selectObj(val) { selectObj(val) {
this.form.objectId = val[val.length-1] this.form.objectId = val[val.length-1]
this.form.objectType = val.length-1 this.form.objectType = val.length-1
if (this.form.energyTypeId && this.form.type) {
this.getDetailList()
this.form.plcParamId = ''
}
}, },
selectDetail() { selectDetail() {
this.$forceUpdate() this.$forceUpdate()

View File

@ -65,7 +65,8 @@ const tableProps = [
}, },
{ {
prop: 'type', prop: 'type',
label: '监控模式' label: '监控模式',
filter: (val) => (val != null ? ['合并', '详细'][val-1] : '-'),
}, },
{ {
prop: 'plcParamName', prop: 'plcParamName',

View File

@ -46,7 +46,7 @@ const tableProps = [
label: '监控模式' label: '监控模式'
}, },
{ {
prop: 'plcParamId', prop: 'paramName',
label: '监控参数' label: '监控参数'
}, },
{ {

View File

@ -116,7 +116,7 @@ export default {
energyTypeList: [] energyTypeList: []
}; };
}, },
created() { mounted() {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
this.tableH = this.tableHeight(260) this.tableH = this.tableHeight(260)
this.isFold = this.searchBarWidth('energyReportSearchBox', 1180) this.isFold = this.searchBarWidth('energyReportSearchBox', 1180)
@ -130,11 +130,24 @@ export default {
this.queryParams.endTime = this.formConfig[2].defaultSelect[1] this.queryParams.endTime = this.formConfig[2].defaultSelect[1]
this.getList() this.getList()
this.getTypeList() this.getTypeList()
},
mounted() {
this.isFold = this.searchBarWidth('energyReportSearchBox', 1180) this.isFold = this.searchBarWidth('energyReportSearchBox', 1180)
}, },
watch: {
$route: 'initData'
},
methods: { methods: {
initData(to) {
if (to.name === 'EnergyReportSearch') {
if (this.$route.params.startTime && this.$route.params.endTime) {
this.formConfig[2].defaultSelect = [this.$route.params.startTime, this.$route.params.endTime]
} else {
this.formConfig[2].defaultSelect = [Date.now() - 7*24*3600000, Date.now()]
}
this.queryParams.startTime = this.formConfig[2].defaultSelect[0]
this.queryParams.endTime = this.formConfig[2].defaultSelect[1]
this.getList()
}
},
getTypeList() { getTypeList() {
getEnergyTypeListAll().then((res) => { getEnergyTypeListAll().then((res) => {
this.formConfig[1].selectOptions = res.data || [] this.formConfig[1].selectOptions = res.data || []

View File

@ -6,7 +6,34 @@
--> -->
<template> <template>
<div class="pie-chart" :data-eqname="value.equipmentName || 'Default'"></div> <div class="chart-grid-item" style="">
<div
class="pie-chart"
ref="pieChart"
:data-eqname="value.equipmentName || 'Default'"></div>
<div class="data-view">
<div class="data-view__item">
<!-- <div class="data-view__item__value">111</div> -->
<div class="data-view__item__value">{{ textData.workTime }}</div>
<div class="data-view__item__title blue">工作时长</div>
</div>
<div class="data-view__item">
<!-- <div class="data-view__item__value">22</div> -->
<div class="data-view__item__value">{{ textData.stopTime }}</div>
<div class="data-view__item__title green">停机时长</div>
</div>
<div class="data-view__item">
<!-- <div class="data-view__item__value">10</div> -->
<div class="data-view__item__value">{{ textData.downTime }}</div>
<div class="data-view__item__title purple">故障时长</div>
</div>
<div class="data-view__item">
<!-- <div class="data-view__item__value">100%</div> -->
<div class="data-view__item__value">{{ textData.peEfficiency }}</div>
<div class="data-view__item__title yellow">速度开动率</div>
</div>
</div>
</div>
</template> </template>
<script> <script>
@ -19,7 +46,27 @@ export default {
data() { data() {
return { return {
chart: null, chart: null,
textData: {
workTime: '',
downTime: '',
stopTime: '',
peEfficiency: '',
},
config: { config: {
title: {
text: '产线1', //<=========
top: '35%',
left: '49%',
textAlign: 'center',
textStyle: {
fontSize: 18,
},
subtext: '设备', //<=========
subtextStyle: {
fontSize: 14,
},
},
color: ['#3da8fd', '#8ef0ab', '#6b5cfd', '#FFC72A', 'transparent'],
grid: { grid: {
top: 0, top: 0,
left: 0, left: 0,
@ -30,6 +77,7 @@ export default {
trigger: 'item', trigger: 'item',
}, },
legend: { legend: {
show: false,
top: '0%', top: '0%',
left: 'center', left: 'center',
textStyle: { textStyle: {
@ -39,34 +87,70 @@ export default {
itemHeight: 10, itemHeight: 10,
}, },
series: [ series: [
//
{ {
name: this.value.equipmentName || 'Default', //
name: '',
type: 'pie', type: 'pie',
radius: ['40%', '75%'], radius: ['75%', '90%'],
avoidLabelOverlap: false, center: ['50%', '45%'],
label: { label: {
show: false, show: false,
position: 'center',
}, },
data: ['workTime', 'stopTime', 'downTime'].map((v, index) => ({ data: [
name: ['工作时长', '停机时长', '故障时长'][index], //<=========
value: this.value[v], { name: '工作时长', value: 1048 },
})), { name: '停机时长', value: 735 },
// data: [ { name: '故障时长', value: 580 },
// { value: 1048, name: 'Search Engine' }, ],
// { value: 735, name: 'Direct' },
// { value: 580, name: 'Email' },
// { value: 484, name: 'Union Ads' },
// { value: 300, name: 'Video Ads' },
// ],
}, },
{
//
name: '',
type: 'pie',
center: ['50%', '45%'],
radius: ['60%', '75%'],
itemStyle: {
borderRadius: 10,
},
label: {
show: false,
},
data: [
//<=========
{ name: '总', value: 3000 },
{ name: '', value: 1400 },
],
},
// {
// name: this.value.equipmentName || 'Default',
// type: 'pie',
// radius: ['40%', '75%'],
// avoidLabelOverlap: false,
// label: {
// show: false,
// position: 'center',
// },
// data: ['workTime', 'stopTime', 'downTime'].map((v, index) => ({
// name: ['', '', ''][index],
// value: this.value[v],
// })),
// // data: [
// // { value: 1048, name: 'Search Engine' },
// // { value: 735, name: 'Direct' },
// // { value: 580, name: 'Email' },
// // { value: 484, name: 'Union Ads' },
// // { value: 300, name: 'Video Ads' },
// // ],
// },
], ],
}, },
}; };
}, },
mounted() { mounted() {
console.log('value', this.value);
if (!this.chart) { if (!this.chart) {
this.chart = echarts.init(this.$el); this.chart = echarts.init(this.$refs.pieChart);
this.$nextTick(() => { this.$nextTick(() => {
this.chart.setOption(this.config); this.chart.setOption(this.config);
}); });
@ -75,24 +159,120 @@ export default {
beforeDestroy() { beforeDestroy() {
if (this.chart) this.chart.dispose(); if (this.chart) this.chart.dispose();
}, },
methods: {}, watch: {
value: {
handler(val) {
this.updateConfig(val);
if (this.chart) this.chart.setOption(this.config);
},
deep: true,
immediate: true,
},
},
methods: {
updateConfig(item) {
const {
lineName, // 线
equipmentName, //
downTime, // (h)
stopTime, // (h)
workTime, // (h)
peEfficiency, //
timeEfficiency, //
//===============//
sectionName,
workRate,
stopRate,
downRate,
realProcSpeed,
designProcSpeed,
oee,
teep,
downCount,
mtbf,
mttr,
} = item;
this.config.title.text = lineName;
this.config.title.subtext = equipmentName;
this.config.series[0].data = [
{ name: '工作时长', value: workTime },
{ name: '停机时长', value: stopTime },
{ name: '故障时长', value: downTime },
];
this.config.series[1].data = [
{ name: '速度开动率', value: peEfficiency },
{ name: '', value: Math.ceil(peEfficiency) - peEfficiency },
];
//
this.textData = {
workTime: +workTime.toFixed(2),
stopTime: +stopTime.toFixed(2),
downTime: +downTime.toFixed(2),
peEfficiency: +peEfficiency.toFixed(2),
};
},
},
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.pie-chart { .chart-grid-item {
padding: 12px;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.pie-chart {
height: 1px;
width: 100%;
flex: 1;
padding: 12px; padding: 12px;
min-height: 320px;
background: #f1f1f1;
position: relative; position: relative;
} }
.pie-chart::before { .data-view {
content: attr(data-eqname); display: flex;
justify-content: center;
}
.data-view__item {
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
user-select: none;
padding: 0 6px;
}
.data-view__item:not(:last-child) {
border-right: 1px solid #f1f1f1;
}
.data-view__item__value {
font-size: 16px; font-size: 16px;
line-height: 1; line-height: 24px;
position: absolute; }
top: -16px;
left: 0; .data-view__item__title {
font-size: 8px;
line-height: 14px;
}
.blue {
color: #3da8fd;
}
.green {
color: #8ef0ab;
}
.purple {
color: #6b5cfd;
}
.yellow {
color: #ffc72a;
} }
</style> </style>

View File

@ -13,46 +13,64 @@
ref="search-bar" ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" /> @headBtnClick="handleSearchBarBtnClick" />
<!-- 列表 --> <el-row>
<base-table <el-col class="custom-tabs">
class="base-table__margin" <el-tabs
:table-props="tableProps" v-model="activeName"
:table-data="list" :stretch="true"
@emitFun="handleEmitFun"> @tab-click="handleTabClick">
<!-- :page="queryParams.pageNo" <el-tab-pane :label="'\u2002数据列表\u2002'" name="table">
:limit="queryParams.pageSize" --> <!-- 列表 -->
<!-- <method-btn <base-table
v-if="tableBtn.length" class="base-table__margin"
slot="handleBtn" :table-props="tableProps"
label="操作" :page="1"
:method-list="tableBtn" :limit="10"
@clickBtn="handleTableBtnClick" /> --> :table-data="list"
</base-table> @emitFun="handleEmitFun"></base-table>
</el-tab-pane>
<el-tab-pane :label="'\u3000可视化\u3000'" name="graph">
<div
v-if="activeName == 'graph'"
class="graph"
style="display: flex; flex-direction: column; position: relative">
<div class="blue-title">各设备加工数量</div>
<div class="legend">
<div class="legend-item">
<span class="icon blue"></span>
<span class="text">工作时长</span>
</div>
<div class="legend-item">
<span class="icon green"></span>
<span class="text">停机时长</span>
</div>
<div class="legend-item">
<span class="icon purple"></span>
<span class="text">故障时长</span>
</div>
<div class="legend-item">
<span class="icon yellow"></span>
<span class="text">速度开动率</span>
</div>
</div>
<div class="graph-grid">
<div class="bg-grid grid-line">
<div class="grid-item" v-for="item in list.length" :key="item"></div>
</div>
<!-- 分页组件 --> <div class="bg-grid grid-charts">
<!-- <pagination <pie-chart
v-show="total > 0" v-for="item in list"
:total="total" :key="item.id"
:page.sync="queryParams.pageNo" :value="item" />
:limit.sync="queryParams.pageSize" <!-- <pie-chart v-for="item in 5" :key="item" :value="item" /> -->
@pagination="getList" /> --> </div>
</div>
<!-- 对话框(添加 / 修改) --> </div>
<base-dialog </el-tab-pane>
:dialogTitle="visualizationOpen ? '设备可视化' : '查看趋势'" </el-tabs>
:dialogVisible="open" </el-col>
:width="visualizationOpen ? '80%' : '700px'" </el-row>
@closed="closed"
@close="cancel"
@cancel="cancel"
@confirm="submitForm">
<div class="visualization" v-if="visualizationOpen">
<pie-chart v-for="item in list" :key="item.id" :value="item" />
</div>
<div v-if="trendOpen">
<h1>查看趋势</h1>
</div>
</base-dialog>
</div> </div>
</template> </template>
@ -68,6 +86,7 @@ export default {
props: {}, props: {},
data() { data() {
return { return {
activeName: 'table',
open: false, open: false,
visualizationOpen: false, visualizationOpen: false,
trendOpen: false, trendOpen: false,
@ -86,84 +105,72 @@ export default {
// : undefined, // : undefined,
// ].filter((v) => v), // ].filter((v) => v),
tableProps: [ tableProps: [
{ prop: 'factoryName', label: '工厂', align: 'center' }, { prop: 'factoryName', label: '工厂' },
{ prop: 'lineName', label: '产线', align: 'center' }, { prop: 'lineName', label: '产线' },
{ prop: 'sectionName', label: '工段', align: 'center' }, { prop: 'sectionName', label: '工段' },
{ prop: 'equipmentName', label: '设备', align: 'center' }, { prop: 'equipmentName', label: '设备' },
{ {
label: '有效时间', label: '有效时间',
align: 'center',
children: [ children: [
{ {
width: 128, width: 128,
prop: 'workTime', prop: 'workTime',
label: '工作时长', label: '工作时长',
align: 'center',
}, },
{ {
width: 128, width: 128,
prop: 'workRate', prop: 'workRate',
label: '百分比', label: '百分比',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'), filter: (val) => (val != null ? +val.toFixed(3) : '-'),
}, },
], ],
}, },
{ {
label: '关机时间', label: '关机时间',
align: 'center',
children: [ children: [
{ {
width: 128, width: 128,
prop: 'stopTime', prop: 'stopTime',
label: '停机时长', label: '停机时长',
align: 'center',
}, },
{ width: 128, prop: 'stopRate', label: '百分比', align: 'center' }, { width: 128, prop: 'stopRate', label: '百分比' },
], ],
}, },
{ {
label: '中断损失', label: '中断损失',
align: 'center',
children: [ children: [
{ {
width: 128, width: 128,
prop: 'downTime', prop: 'downTime',
label: '故障时长', label: '故障时长',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'), filter: (val) => (val != null ? +val.toFixed(3) : '-'),
}, },
{ width: 128, prop: 'downRate', label: '百分比', align: 'center' }, { width: 128, prop: 'downRate', label: '百分比' },
{ {
width: 128, width: 128,
prop: 'timeEfficiency', prop: 'timeEfficiency',
label: '时间开动率', label: '时间开动率',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'), filter: (val) => (val != null ? +val.toFixed(3) : '-'),
}, },
], ],
}, },
{ {
label: '速度损失', label: '速度损失',
align: 'center',
children: [ children: [
{ {
width: 128, width: 128,
prop: 'realProcSpeed', prop: 'realProcSpeed',
label: '实际加工速度', label: '实际加工速度',
align: 'center',
}, },
{ {
width: 128, width: 128,
prop: 'designProcSpeed', prop: 'designProcSpeed',
label: '理论加工速度', label: '理论加工速度',
align: 'center',
}, },
{ {
width: 128, width: 128,
prop: 'peEfficiency', prop: 'peEfficiency',
label: '速度开动率', label: '速度开动率',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'), filter: (val) => (val != null ? +val.toFixed(3) : '-'),
}, },
], ],
@ -171,19 +178,17 @@ export default {
{ {
prop: 'oee', prop: 'oee',
label: 'OEE', label: 'OEE',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'), filter: (val) => (val != null ? +val.toFixed(3) : '-'),
}, },
{ {
prop: 'teep', prop: 'teep',
label: 'TEEP', label: 'TEEP',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'), filter: (val) => (val != null ? +val.toFixed(3) : '-'),
}, },
// { // {
// _action: 'view-trend', // _action: 'view-trend',
// label: '', // label: '',
// align: 'center', // ,
// subcomponent: { // subcomponent: {
// props: ['injectData'], // props: ['injectData'],
// render: function (h) { // render: function (h) {
@ -240,7 +245,7 @@ export default {
parent: 'dateFilterType', parent: 'dateFilterType',
// //
type: 'datePicker', type: 'datePicker',
label: '时间段', // label: '',
dateType: 'daterange', dateType: 'daterange',
format: 'yyyy-MM-dd', format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd HH:mm:ss', valueFormat: 'yyyy-MM-dd HH:mm:ss',
@ -254,7 +259,7 @@ export default {
parent: 'dateFilterType', parent: 'dateFilterType',
// //
type: 'datePicker', type: 'datePicker',
label: '日期', // label: '',
dateType: 'date', dateType: 'date',
placeholder: '选择日期', placeholder: '选择日期',
format: 'yyyy-MM-dd', format: 'yyyy-MM-dd',
@ -269,16 +274,16 @@ export default {
name: 'search', name: 'search',
color: 'primary', color: 'primary',
}, },
{ // {
type: 'separate', // type: 'separate',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '设备可视化', // btnName: '',
name: 'visualization', // name: 'visualization',
plain: true, // plain: true,
color: 'success', // color: 'success',
}, // },
// { // {
// type: 'button', // type: 'button',
// btnName: 'OEE', // btnName: 'OEE',
@ -364,7 +369,6 @@ export default {
params: this.queryParams, params: this.queryParams,
}); });
if (code == 0) { if (code == 0) {
console.log('data', data);
this.list = data; this.list = data;
} }
}, },
@ -407,6 +411,8 @@ export default {
}, },
submitForm() {}, submitForm() {},
handleTabClick() {},
}, },
}; };
</script> </script>
@ -416,4 +422,141 @@ export default {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(240px, 1fr)); grid-template-columns: repeat(3, minmax(240px, 1fr));
} }
:deep(.custom-tabs) {
.el-tabs__header {
margin-bottom: 8px;
display: inline-block;
transform: translateY(-12px);
}
.el-tabs__item {
padding-left: 0 !important;
padding-right: 0 !important;
line-height: 36px !important;
height: 36px;
}
}
.blue-title {
position: relative;
padding: 4px 0;
padding-left: 12px;
font-size: 14px;
&::before {
content: '';
position: absolute;
left: 0;
top: 6px;
height: 16px;
width: 4px;
border-radius: 1px;
background: #0b58ff;
}
}
.graph-grid {
margin-top: 8px;
padding: 12px;
position: relative;
border-radius: 12px;
border: 1px solid #ccc;
// background: #0003;
}
.bg-grid {
display: grid;
place-content: center;
grid-template-columns: repeat(4, minmax(280px, 1fr));
grid-auto-columns: 280px;
grid-auto-rows: 290px;
overflow: hidden;
position: relative;
}
.grid-line::after {
content: '';
position: absolute;
top: -1px;
left: -1px;
width: calc(100% + 2px);
height: calc(100% + 2px);
display: inline-block;
border: 8px solid #fff;
}
.grid-charts {
position: absolute;
width: calc(100% - 24px);
top: 12px;
left: 12px;
}
.grid-item {
border: 1px solid #ccc;
}
.grid-item:not(:first-child) {
border-left: 0;
border-top: 0;
}
.legend {
position: absolute;
top: 8px;
right: 12px;
display: flex;
}
.legend .legend-item {
display: flex;
align-items: center;
margin-left: 12px;
}
.legend .legend-item .icon {
width: 10px;
height: 10px;
border-radius: 1px;
margin-right: 4px;
margin-top: 1px;
}
.legend .legend-item .text {
color: #8c8c8c;
}
.blue {
background-color: #3da8fd;
}
.green {
background-color: #8ef0ab;
}
.purple {
background-color: #6b5cfd;
}
.yellow {
background-color: #ffc72a;
}
@media screen and (max-width: 1390px) {
.bg-grid {
grid-template-columns: repeat(3, minmax(280px, 1fr));
}
}
@media screen and (max-width: 1190px) {
.bg-grid {
grid-template-columns: repeat(2, minmax(280px, 1fr));
}
}
@media screen and (max-width: 640px) {
.bg-grid {
grid-template-columns: repeat(1, minmax(280px, 1fr));
}
}
</style> </style>

View File

@ -50,39 +50,47 @@ export default {
data() { data() {
return { return {
searchBarKeys: ['name', 'code'], searchBarKeys: ['name', 'code'],
// 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),
tableBtn: [ 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),
tableProps: [
{ prop: 'lineName', label: '产线', align: 'center' },
{ prop: 'sectionName', label: '工段', align: 'center' },
{ prop: 'equipmentName', label: '设备', align: 'center' },
{ {
width: 188, type: 'edit',
btnName: '修改',
},
{
type: 'delete',
btnName: '删除',
},
],
tableProps: [
{ prop: 'lineName', label: '产线' },
{ prop: 'sectionName', label: '工段' },
{ prop: 'equipmentName', label: '设备' },
{
width: 240,
prop: 'mtbf', prop: 'mtbf',
label: '平均故障间隔时间[MTBF](h)', label: '平均故障间隔时间[MTBF](h)',
align: 'center',
}, },
{ {
width: 180, width: 240,
prop: 'mttr', prop: 'mttr',
label: '平均维修时间[MTTR](h)', label: '平均维修时间[MTTR](h)',
align: 'center',
}, },
{ prop: 'workTime', label: '工作时长(h)', align: 'center' }, { width: 128, prop: 'workTime', label: '工作时长(h)' },
{ prop: 'downTime', label: '故障时长(h)', align: 'center' }, { width: 128, prop: 'downTime', label: '故障时长(h)' },
{ prop: 'downCount', label: '故障次数', align: 'center' }, { prop: 'downCount', label: '故障次数' },
], ],
searchBarFormConfig: [ searchBarFormConfig: [
{ {
@ -113,6 +121,8 @@ export default {
], ],
// //
queryParams: { queryParams: {
pageNo: 1,
pageSize: 10,
lineId: null, lineId: null,
factoryId: null, factoryId: null,
recordTime: null, recordTime: null,
@ -162,15 +172,13 @@ export default {
this.queryParams.recordTime = [ this.queryParams.recordTime = [
moment() moment()
.month(btn.month - 1) .month(btn.month - 1)
.format('YYYY-MM')+'-01 00:00:00', .format('YYYY-MM') + '-01 00:00:00',
moment() moment().month(btn.month).format('YYYY-MM') + '-01 00:00:00',
.month(btn.month)
.format('YYYY-MM')+'-01 00:00:00',
]; ];
} else { } else {
this.queryParams.recordTime = null; this.queryParams.recordTime = null;
} }
this.queryParams.lineId = btn.lineId || null; this.queryParams.lineId = btn.lineId || null;
this.handleQuery(); this.handleQuery();
break; break;
} }

View File

@ -15,27 +15,143 @@ import * as echarts from 'echarts';
export default { export default {
name: 'LineChart', name: 'LineChart',
components: {}, components: {},
props: ['config'], props: ['config', 'list'],
data() { data() {
return { return {
chart: null, chart: null,
}; };
}, },
computed: {}, // watch: {
// list: {
// handler(listdata) {
// if (listdata && listdata.length) {
// console.log('[linechart] list changed', listdata);
// const option = this.handleList(listdata);
// this.setOption(option);
// }
// },
// immediate: true,
// },
// },
computed: {
option() {
const opt = [];
this.list.map((eq) => {
/** [设备名, ok数量, 不ok数量] */
opt.push([eq.equipmentName, eq.okQuantity, eq.nokQuantity]);
});
return {
color: ['#288AFF', '#8EF0AB'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
legend: {
itemWidth: 12,
itemHeight: 12,
right: 0
},
grid: {
left: '1%',
right: '1%',
top: '8%',
bottom: '3%',
containLabel: true,
},
// xAxis: [
// {
// type: 'category',
// data: ['1', '2', '3', '4', '5'],
// },
// ],
// yAxis: [
// {
// type: 'value',
// splitLine: {
// lineStyle: {
// color: '#0001',
// },
// },
// },
// ],
xAxis: {
type: 'category',
axisTick: { show: false },
data: opt.map((item) => item[0]),
},
yAxis: {
type: 'value',
splitLine: {
lineStyle: {
color: '#0001',
},
},
},
series: [
{
name: '合格数量',
type: 'bar',
barWidth: 20,
stack: 's',
data: opt.map((item) => item[1]),
},
{
name: '不合格数量',
type: 'bar',
barWidth: 20,
stack: 's',
data: opt.map((item) => item[2]),
},
],
};
},
},
mounted() { mounted() {
console.log('[linechart] mounted');
this.init(); this.init();
}, },
beforeDestroy() { beforeDestroy() {
if (this.chart) { if (this.chart) {
this.chart.dispose(); this.chart.dispose();
} }
console.log('[linechart] destroyed');
}, },
methods: { methods: {
init() { init() {
console.log('thsi el', this.$el);
if (!this.chart) this.chart = echarts.init(this.$el); if (!this.chart) this.chart = echarts.init(this.$el);
this.chart.setOption(this.config); console.log('[linechart] initialized', this.$el);
this.$nextTick(() => {
this.setOption();
});
}, },
setOption() {
if (this.chart) this.chart.setOption(this.option);
console.log('[linechart] option settled');
},
// handleList(list) {
// /** */
// this.option.series[0].data.splice(0);
// this.option.series[1].data.splice(0);
// this.option.xAxis.data.splice(0);
// list.map((eq) => {
// this.option.xAxis.data.push(eq.equipmentName);
// this.option.series[0].data.push(eq.nokQuantity);
// this.option.series[1].data.push(eq.okQuantity);
// });
// this.setOption();
// // const pureList = list.map((eq) => ({
// // name: eq.equipmentName,
// // ok: eq.okQuantity,
// // nok: eq.nokQuantity,
// // }));
// },
}, },
}; };
</script> </script>
@ -43,7 +159,7 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
.line-chart { .line-chart {
padding: 0 12px; padding: 0 12px;
background: #e1e1e1; height: 1px;
min-height: 320px; flex: 1;
} }
</style> </style>

View File

@ -13,28 +13,35 @@
ref="search-bar" ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" /> @headBtnClick="handleSearchBarBtnClick" />
<!-- 列表 --> <el-row>
<base-table <el-col class="custom-tabs">
:table-props="tableProps" <el-tabs
:page="queryParams.pageNo" v-model="activeName"
:limit="queryParams.pageSize" :stretch="true"
:table-data="list" @tab-click="handleTabClick">
@emitFun="handleEmitFun"></base-table> <el-tab-pane :label="'数据列表'" name="table">
<!-- 列表 -->
<!-- 图形分析 dialog --> <base-table
<!-- <base-dialog :table-props="tableProps"
dialogTitle="图形视角" :page="queryParams.pageNo"
:dialogVisible="dialogVisible" :limit="queryParams.pageSize"
width="60%" :table-data="list"
@close="dialogClose" @emitFun="handleEmitFun"></base-table>
@cancel="dialogClose" </el-tab-pane>
@confirm="dialogClose"> <el-tab-pane :label="'\u3000柱状图\u3000'" name="graph">
<LineChart v-if="dialogVisible" :config="lineChartConfig" /> <div v-if="activeName == 'graph'" class="graph" style="height: 40vh; display: flex; flex-direction: column;">
</base-dialog> --> <div class="blue-title">各设备加工数量</div>
<LineChart :list="list" />
</div>
</el-tab-pane>
</el-tabs>
</el-col>
</el-row>
</div> </div>
</template> </template>
<script> <script>
import moment from 'moment';
import LineChart from './components/lineChart.vue'; import LineChart from './components/lineChart.vue';
export default { export default {
@ -42,12 +49,14 @@ export default {
components: { LineChart }, components: { LineChart },
props: {}, props: {},
data() { data() {
const now = new Date();
const [y, m, d] = [now.getFullYear(), now.getMonth(), now.getDate()];
return { return {
dialogVisible: false, dialogVisible: false,
urls: { urls: {
page: '/analysis/equipment-analysis/quality', page: '/analysis/equipment-analysis/quality',
}, },
mode: 'table', // defaults to 'table' activeName: 'table', // defaults to 'table'
searchBarFormConfig: [ searchBarFormConfig: [
// //
{ {
@ -72,13 +81,30 @@ export default {
dateType: 'daterange', // datetimerange dateType: 'daterange', // datetimerange
// format: 'yyyy-MM-dd HH:mm:ss', // format: 'yyyy-MM-dd HH:mm:ss',
format: 'yyyy-MM-dd', format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd HH:mm:ss',
// valueFormat: 'timestamp', // valueFormat: 'timestamp',
valueFormat: 'yyyy-MM-dd HH:mm:ss',
rangeSeparator: '-', rangeSeparator: '-',
startPlaceholder: '开始日期', startPlaceholder: '开始日期',
endPlaceholder: '结束日期', endPlaceholder: '结束日期',
defaultTime: ['00:00:00', '23:59:59'],
param: 'recordTime', param: 'recordTime',
defaultSelect: [
new Date(y, m, d)
.toLocaleString()
.split('/')
.map((item, index) => {
if (index == 1 || index == 2) return item.padStart(2, '0');
return item;
})
.join('-'),
new Date(y, m, d, 23, 59, 59)
.toLocaleString()
.split('/')
.map((item, index) => {
if (index == 1 || index == 2) return item.padStart(2, '0');
return item;
})
.join('-'),
],
}, },
{ {
type: 'button', type: 'button',
@ -86,9 +112,9 @@ export default {
name: 'search', name: 'search',
color: 'primary', color: 'primary',
}, },
{ // {
type: 'separate', // type: 'separate',
}, // },
// { // {
// type: 'button', // type: 'button',
// btnName: '', // btnName: '',
@ -111,43 +137,36 @@ export default {
// width: 160, // width: 160,
prop: 'sectionName', prop: 'sectionName',
label: '工段', label: '工段',
align: 'center',
}, },
{ {
// width: 160, // width: 160,
prop: 'equipmentName', prop: 'equipmentName',
label: '设备名称', label: '设备名称',
align: 'center',
}, },
{ {
// width: 160, // width: 160,
prop: 'products', prop: 'products',
label: '产品名称', label: '产品名称',
align: 'center',
}, },
{ {
// width: 160, // width: 160,
prop: 'inQuantity', prop: 'inQuantity',
label: '进片数量', label: '进片数量',
align: 'center',
}, },
{ {
// width: 160, // width: 160,
prop: 'outQuantity', prop: 'outQuantity',
label: '出片数量', label: '出片数量',
align: 'center',
}, },
{ {
// width: 160, // width: 160,
prop: 'nokQuantity', prop: 'nokQuantity',
label: '破损/不合格数', label: '破损/不合格数',
align: 'center',
}, },
{ {
// width: 160, // width: 160,
prop: 'passRate', prop: 'passRate',
label: '合格率', label: '合格率',
align: 'center',
}, },
], ],
lineChartConfig: { lineChartConfig: {
@ -161,14 +180,14 @@ export default {
top: 0, top: 0,
left: 0, left: 0,
padding: 5, padding: 5,
icon: 'roundRect', icon: 'roundRect',
itemWidth: 12, itemWidth: 12,
itemHeight: 12, itemHeight: 12,
itemGap: 20, itemGap: 20,
textStyle: { textStyle: {
fontSize: 14, fontSize: 14,
lineHeight: 14 lineHeight: 14,
}, },
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
@ -176,13 +195,13 @@ export default {
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
name: '合格率', name: '合格率',
nameLocation: 'end', nameLocation: 'end',
nameTextStyle: { nameTextStyle: {
fontSize: 14, fontSize: 14,
align: 'right' align: 'right',
}, },
nameGap: 26 nameGap: 26,
}, },
series: [ series: [
{ {
@ -229,6 +248,11 @@ export default {
this.getList(); this.getList();
}, },
methods: { methods: {
handleTabClick(tab, event) {
// console.log('tab event', tab, event);
// tab is el-tab vue component.
},
async fillLineOptions() { async fillLineOptions() {
const { data } = await this.$axios({ const { data } = await this.$axios({
url: '/base/production-line/listAll', url: '/base/production-line/listAll',
@ -340,4 +364,38 @@ export default {
} }
} }
} }
:deep(.custom-tabs) {
.el-tabs__header {
margin-bottom: 8px;
display: inline-block;
transform: translateY(-12px);
}
.el-tabs__item {
padding-left: 0 !important;
padding-right: 0 !important;
line-height: 36px !important;
height: 36px;
}
}
.blue-title {
position: relative;
padding: 4px 0;
padding-left: 12px;
font-size: 14px;
&::before {
content: "";
position: absolute;
left: 0;
top: 6px;
height: 16px;
width: 4px;
border-radius: 1px;
background: #0b58ff;
}
}
</style> </style>

View File

@ -21,7 +21,14 @@ export default {
chart: null, chart: null,
}; };
}, },
computed: {}, watch: {
config: {
handler(value) {
if (value != null) this.setOption(value);
},
deep: true,
},
},
mounted() { mounted() {
this.init(); this.init();
}, },
@ -32,9 +39,11 @@ export default {
}, },
methods: { methods: {
init() { init() {
console.log('thsi el', this.$el);
if (!this.chart) this.chart = echarts.init(this.$el); if (!this.chart) this.chart = echarts.init(this.$el);
this.chart.setOption(this.config); this.setOption(this.config);
},
setOption(option) {
if (this.chart) this.chart.setOption(option);
}, },
}, },
}; };

View File

@ -6,25 +6,44 @@
--> -->
<template> <template>
<div class="app-container"> <div
<h1>设备产量时序图</h1> class="production-timegraph-container"
<!-- 搜索工作栏 --> style="background: #f2f4f9; flex: 1">
<SearchBar <el-row
:formConfigs="searchBarFormConfig" class=""
ref="search-bar" style="
@headBtnClick="handleSearchBarBtnClick" /> 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"
@headBtnClick="handleSearchBarBtnClick" />
</el-row>
<div class="main-area"> <el-row
<div class="graphs" v-if="graphList.length"> class=""
<div class="graph" v-for="(eq, index) in graphList" :key="eq.key"> style="
<h2 class="graph-title">{{ eq.key }}</h2> margin-bottom: 12px;
<LineChart background: #fff;
:key="eq.key + '__linechart'" padding: 16px 16px 24px;
:config="getRealConfig(index)" /> border-radius: 8px;
">
<div class="blue-title">设备产量时序图</div>
<div class="main-area">
<div class="graphs" v-if="graphList.length">
<LineChart :config="templateConfig" />
</div> </div>
<div class="no-data-bg" v-else></div>
</div> </div>
<h2 v-else>请添加设备</h2> </el-row>
</div>
<!-- 对话框(添加 / 修改) --> <!-- 对话框(添加 / 修改) -->
<base-dialog <base-dialog
@ -58,6 +77,7 @@ export default {
props: {}, props: {},
data() { data() {
return { return {
accumulators: new Map(),
searchBarFormConfig: [ searchBarFormConfig: [
{ {
type: 'select', type: 'select',
@ -77,15 +97,16 @@ export default {
{ {
type: 'datePicker', type: 'datePicker',
label: '时间段', label: '时间段',
dateType: 'daterange', // datetimerange dateType: 'date', // datetimerange
// format: 'yyyy-MM-dd HH:mm:ss', // format: 'yyyy-MM-dd HH:mm:ss',
format: 'yyyy-MM-dd', format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd HH:mm:ss', valueFormat: 'yyyy-MM-dd HH:mm:ss',
// valueFormat: 'timestamp', // valueFormat: 'timestamp',
rangeSeparator: '-', // rangeSeparator: '-',
startPlaceholder: '开始日期', // startPlaceholder: '',
endPlaceholder: '结束日期', // endPlaceholder: '',
defaultTime: ['00:00:00', '23:59:59'], // defaultTime: ['00:00:00', '23:59:59'],
placeholder: '选择日期',
param: 'recordTime', param: 'recordTime',
}, },
{ {
@ -99,9 +120,9 @@ export default {
}, },
{ {
type: 'button', type: 'button',
btnName: '设备对比', btnName: '添加对比',
name: 'compare', name: 'compare',
color: 'warning', color: 'primary',
plain: true, plain: true,
}, },
], ],
@ -115,30 +136,47 @@ export default {
eqList: [], eqList: [],
graphList: [], graphList: [],
templateConfig: { templateConfig: {
color: ['#283D68', '#FFB61F', '#4481FF', '#5AD8A6', '#E97466'],
grid: { grid: {
top: 88, top: 48,
left: 56, left: 48,
right: 56, right: 24,
bottom: 56, bottom: 24,
}, },
legend: { legend: {
top: 0, top: 0,
left: 0, right: 12,
padding: 5, padding: 6,
icon: 'roundRect', itemWidth: 16,
itemWidth: 12, itemHeight: 8,
itemHeight: 12,
itemGap: 20, itemGap: 20,
textStyle: { textStyle: {
fontSize: 14, fontSize: 12,
lineHeight: 14, lineHeight: 12,
color: '#0007',
}, },
}, },
tooltip: {
show: true,
trigger: 'axis',
},
xAxis: { xAxis: {
type: 'category', type: 'category',
data: Array(24) boundaryGap: true,
.fill(1) axisTick: {
.map((item, index) => `${index}:00`), // show: false,
alignWithLabel: true,
lineStyle: {
color: '#0003',
},
},
axisLabel: {
color: '#0007',
},
data: [],
// data: Array(24)
// .fill(1)
// .map((item, index) => `${index}:00`),
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
@ -146,19 +184,41 @@ export default {
nameLocation: 'end', nameLocation: 'end',
nameTextStyle: { nameTextStyle: {
fontSize: 14, fontSize: 14,
align: 'right', align: 'center',
}, },
nameGap: 26, nameGap: 26,
max: function (value) {
return value.max + Math.floor(value.max / 5);
},
}, },
series: [ series: [
{ // {
name: '产线1', // name: '线1',
data: Array(24) // data: Array(24)
.fill(1) // .fill(1)
.map(() => Math.random() * 100), // .map(() => Math.random() * 100),
type: 'line', // type: 'line',
smooth: true, // symbol: 'circle',
}, // // smooth: true,
// },
// {
// name: '线2',
// data: Array(24)
// .fill(1)
// .map(() => Math.random() * 100),
// type: 'line',
// symbol: 'circle',
// // smooth: true,
// },
// {
// name: '线3',
// data: Array(24)
// .fill(1)
// .map(() => Math.random() * 100),
// type: 'line',
// symbol: 'circle',
// // smooth: true,
// },
], ],
}, },
}; };
@ -176,7 +236,12 @@ export default {
this.queryParams.lineId = payload.lineId || null; this.queryParams.lineId = payload.lineId || null;
this.queryParams.sectionId = payload.sectionId || null; this.queryParams.sectionId = payload.sectionId || null;
this.queryParams.equipmentId = payload.equipmentId || null; this.queryParams.equipmentId = payload.equipmentId || null;
this.queryParams.recordTime = payload.recordTime || null; this.queryParams.recordTime = payload.recordTime
? [
payload.recordTime,
payload.recordTime.replace('00:00:00', '23:59:59'),
]
: null;
this.getList(); this.getList();
break; break;
case 'compare': case 'compare':
@ -230,10 +295,41 @@ export default {
// ]; // ];
// eq2.key = 'SS2'; // eq2.key = 'SS2';
// this.graphList = [eq1, eq2]; // this.graphList = [eq1, eq2];
console.log('graph list', this.graphList);
this.graphList.forEach(this.setSeries);
} else {
this.graphList = [];
this.graphList.forEach(this.setSeries);
} }
}, },
setSeries(eqArr) {
if (eqArr.length == 0) {
this.templateConfig.series = [];
return;
}
let isInit = false;
if (this.accumulators.has(eqArr.key) == false) {
this.accumulators.set(eqArr.key, 0);
isInit = true;
}
let accumulator = this.accumulators.get(eqArr.key);
if ((accumulator && !isInit) || (accumulator == 0 && isInit == false)) {
accumulator++;
this.accumulators.set(eqArr.key, accumulator);
}
this.templateConfig.series.push({
name: eqArr.key + (accumulator == 0 ? '' : '-' + accumulator),
// name: Math.random(),
type: 'line',
symbol: 'circle',
data: this.getEquipmentQuantity(eqArr),
});
},
/** 获得设备产量 */ /** 获得设备产量 */
getEquipmentQuantity(equipmentArr) { getEquipmentQuantity(equipmentArr) {
return equipmentArr.map((item) => item.totalQuantity); return equipmentArr.map((item) => item.totalQuantity);
@ -246,19 +342,6 @@ export default {
); );
}, },
getRealConfig(index) {
// if (!this.graphList || this.graphList.length == 0) return;
const config = JSON.parse(JSON.stringify(this.templateConfig));
// config.legend.data = this.graphList[index].key;
config.series[0].name = this.graphList[index]?.key;
// console.log("this.graphList?.[index]", this.graphList?.[index]);
config.series[0].data = this.getEquipmentQuantity(
this.graphList?.[index] || []
);
config.xAxis.data = this.getTimeinfo(this.graphList?.[index] || []);
return config;
},
/** 准备设备数据 */ /** 准备设备数据 */
async initEquipment() { async initEquipment() {
const { code, data } = await this.$axios({ const { code, data } = await this.$axios({
@ -313,18 +396,42 @@ export default {
method: 'get', method: 'get',
params: this.queryParams, params: this.queryParams,
}); });
this.queryParams.equipmentId = null; //
if (code == 0) { if (code == 0) {
const newEqlist = this.objectToArray(data); const newEqlist = this.objectToArray(data);
if (!newEqlist || newEqlist.length == 0) { if (!newEqlist || newEqlist.length == 0) {
this.$message.error('该设备没有产量数据'); this.$message.error('该设备没有产量数据');
return; return;
} }
this.graphList.push(newEqlist[0]); this.graphList.push(...newEqlist);
newEqlist.forEach(this.setSeries);
} }
this.open = false; this.open = false;
} },
}, },
}; };
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss">
.blue-title {
position: relative;
padding: 4px 0;
padding-left: 12px;
font-size: 14px;
color: #606266;
font-weight: 700;
margin-bottom: 12px;
&::before {
content: '';
position: absolute;
left: 0;
top: 6px;
height: 16px;
width: 4px;
border-radius: 1px;
background: #0b58ff;
}
}
</style>

View File

@ -6,29 +6,51 @@
--> -->
<template> <template>
<div class="app-container"> <div class="status-timegraph-container" style="background: #f2f4f9; flex: 1">
<h1>设备状态时序图</h1> <el-row
<!-- 搜索工作栏 --> class=""
<SearchBar style="
:formConfigs="searchBarFormConfig" margin-bottom: 12px;
ref="search-bar" background: #fff;
@headBtnClick="handleSearchBarBtnClick" /> padding: 16px 16px 0;
border-radius: 8px;
">
<div class="blue-title">生产节拍时序图</div>
<!-- <h1>设备状态时序图</h1> -->
<!-- 搜索工作栏 -->
<SearchBar
:formConfigs="searchBarFormConfig"
ref="search-bar"
:remove-blue="true"
@headBtnClick="handleSearchBarBtnClick" />
</el-row>
<div class="main-area"> <el-row
<div class="legend-row"> class=""
<div class="legend"> style="
<div class="icon running"></div> margin-bottom: 12px;
<div>运行中</div> background: #fff;
</div> padding: 16px 16px 32px;
<!-- <div class="legend"> border-radius: 8px;
">
<el-row :gutter="20">
<el-col :span="6">
<div class="blue-title">设备状态时序图</div>
</el-col>
<el-col :span="18" class="legend-row">
<div class="legend">
<div class="icon running"></div>
<div>运行中</div>
</div>
<!-- <div class="legend">
<div class="icon waiting"></div> <div class="icon waiting"></div>
<div>待机</div> <div>待机</div>
</div> --> </div> -->
<div class="legend"> <div class="legend">
<div class="icon fault"></div> <div class="icon fault"></div>
<div>故障</div> <div>故障</div>
</div> </div>
<!-- <div class="legend"> <!-- <div class="legend">
<div class="icon lack"></div> <div class="icon lack"></div>
<div>缺料</div> <div>缺料</div>
</div> </div>
@ -36,83 +58,51 @@
<div class="icon full"></div> <div class="icon full"></div>
<div>满料</div> <div>满料</div>
</div> --> </div> -->
<div class="legend"> <div class="legend">
<div class="icon stop"></div> <div class="icon stop"></div>
<div>计划停机</div> <div>计划停机</div>
</div>
</el-col>
</el-row>
<div class="main-area">
<div class="graphs" v-if="graphList.length">
<div class="graph" v-for="eq in graphList" :key="eq.key">
<h2 class="graph-title">{{ eq.key }}</h2>
<div class="graph-content">
<el-popover
trigger="hover"
v-for="blc in eq"
:key="blc.startTime"
:title="
blc.status == 0
? '运行'
: blc.status == 2
? '故障'
: '计划停机'
"
placement="top"
:content="new Date(blc.startTime).toLocaleTimeString()">
<div
slot="reference"
:key="blc.startTime + '__div'"
class="graph-item-fixed tick"
:class="{
running: blc.status == 0,
fault: blc.status == 2,
stop: blc.status == 1,
}"
:style="{ height: '32px', width: blc.duration * 2 + 'px' }"
:data-time="
new Date(blc.startTime).toLocaleTimeString()
"></div>
</el-popover>
</div>
</div>
</div> </div>
<h2 v-else>请添加设备</h2>
</div> </div>
<div class="graphs" v-if="graphList.length"> </el-row>
<!-- <div class="graph">
<h2 class="graph-title">设备1</h2>
<div class="graph-item running tick" data-time="00:00"></div>
<div class="graph-item running"></div>
<div class="graph-item running"></div>
<div class="graph-item lack tick" data-time="03:00"></div>
<div class="graph-item full tick" data-time="04:00"></div>
<div class="graph-item waiting tick" data-time="05:00"></div>
<div class="graph-item running tick" data-time="06:00"></div>
<div class="graph-item running"></div>
<div class="graph-item fault tick" data-time="08:00"></div>
<div class="graph-item waiting tick" data-time="09:00"></div>
<div class="graph-item running tick" data-time="10:00"></div>
<div class="graph-item running"></div>
<div class="graph-item running"></div>
<div class="graph-item lack tick" data-time="13:00"></div>
<div class="graph-item full tick" data-time="14:00"></div>
<div class="graph-item running tick" data-time="15:00"></div>
<div class="graph-item running"></div>
<div class="graph-item running"></div>
<div class="graph-item fault tick" data-time="18:00"></div>
<div class="graph-item running tick" data-time="19:00"></div>
<div class="graph-item running"></div>
<div class="graph-item running"></div>
<div class="graph-item running"></div>
<div class="graph-item stop tick" data-time="23:00"></div>
</div> -->
<div class="graph" v-for="eq in graphList" :key="eq.key">
<h2 class="graph-title">{{ eq.key }}</h2>
<div
v-for="blc in eq"
:key="blc.startTime"
class="graph-item-fixed tick"
:class="{
running: blc.status == 0,
fault: blc.status == 2,
stop: blc.status == 1,
}"
:style="{ width: blc.duration * 2 + 'px' }"
:data-time="new Date(blc.startTime).toLocaleTimeString()"></div>
</div>
<!-- <div class="graph">
<h2 class="graph-title">设备3</h2>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
<div class="graph-item"></div>
</div> -->
</div>
<h2 v-else>请添加设备</h2>
</div>
<!-- 对话框(添加 / 修改) --> <!-- 对话框(添加 / 修改) -->
<base-dialog <base-dialog
@ -185,9 +175,9 @@ export default {
}, },
{ {
type: 'button', type: 'button',
btnName: '设备对比', btnName: '添加对比',
name: 'compare', name: 'compare',
color: 'warning', color: 'primary',
plain: true, plain: true,
}, },
], ],
@ -344,26 +334,52 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
.graph { .graph {
border: 1px solid #ccc; // border: 1px solid #ccc;
padding: 12px 12px 28px 12px; // padding: 12px 12px 28px 12px;
margin: 64px 0; // margin: 64px 0;
position: relative; position: relative;
display: flex; display: flex;
} }
.graph-title { .graph-title {
position: absolute; // position: absolute;
top: -64px; // top: -64px;
left: -1px; // left: -1px;
padding: 8px 18px; // padding: 8px 18px;
background: #ccc; padding: 0 12px;
font-size: 18px; font-size: 14px;
line-height: 1; line-height: 1;
} }
.graph-content {
display: flex;
flex: 1;
padding: 22px 12px;
border: 1px solid #ccc;
border-bottom-width: 2px;
border-top: none;
position: relative;
}
.graph-content::after,
.graph-content::before {
content: '';
position: absolute;
width: 3px;
height: 80%;
background: #fff;
right: -1px;
top: 0;
}
.graph-content::before {
right: unset;
left: -1px;
}
.graph-item, .graph-item,
.graph-item-fixed { .graph-item-fixed {
height: 88px; // height: 88px;
// width: 24px; // width: 24px;
flex: 1; flex: 1;
// border: 1px solid #ccc; // border: 1px solid #ccc;
@ -405,32 +421,40 @@ export default {
} }
.running { .running {
background-color: #84f04e; background-color: #5ad8a6;
// background-color: #84f04e;
} }
.waiting { .waiting {
background-color: #409eff; background-color: #5ad8a6;
// background-color: #409eff;
} }
.fault { .fault {
background-color: #ea5b5b; // background-color: #ea5b5b;
background-color: #fc9c91;
} }
.full { .full {
background-color: #e6a23c; // background-color: #e6a23c;
background-color: #598fff;
} }
.lack { .lack {
background-color: #a69c8d; // background-color: #a69c8d;
background-color: #7585a2;
} }
.stop { .stop {
background-color: #000c; background-color: #000;
} }
.legend-row { .legend-row {
margin: 12px 0; margin: 6px 0;
padding-right: 12px;
display: flex; display: flex;
justify-content: flex-end;
> .legend:not(:last-child) { > .legend:not(:last-child) {
margin-right: 12px; margin-right: 12px;
} }
@ -441,9 +465,32 @@ export default {
} }
.icon { .icon {
width: 16px; width: 8px;
height: 8px;
border-radius: 2px;
margin-right: 4px;
margin-top: 1px;
}
}
.blue-title {
position: relative;
padding: 4px 0;
padding-left: 12px;
font-size: 14px;
color: #606266;
font-weight: 700;
margin-bottom: 12px;
&::before {
content: '';
position: absolute;
left: 0;
top: 6px;
height: 16px; height: 16px;
margin-right: 8px; width: 4px;
border-radius: 1px;
background: #0b58ff;
} }
} }
</style> </style>

View File

@ -243,7 +243,11 @@ export default {
this.list = tempData this.list = tempData
} else { } else {
// 1, // 1,
autoSet().then(res => { // console.log(moment(this.startDay).format("YYYY-MM-DD"))
autoSet({
year: this.year,
month: moment(this.startDay).month() + 1
}).then(res => {
this.list = res.data || {} this.list = res.data || {}
}) })
} }

View File

@ -16,6 +16,7 @@
<method-btn <method-btn
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
:width="120"
label="操作" label="操作"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
@ -81,13 +82,12 @@ export default {
: undefined, : undefined,
].filter((v) => v), ].filter((v) => v),
tableProps: [ tableProps: [
{ width: 128, prop: 'productionLine', label: '产线', align: 'center' }, { width: 128, prop: 'productionLine', label: '产线' },
{ width: 128, prop: 'workshopSection', label: '工段', align: 'center' }, { width: 128, prop: 'workshopSection', label: '工段' },
{ width: 128, prop: 'equipment', label: '设备名称', align: 'center' }, { width: 128, prop: 'equipment', label: '设备名称' },
{ {
prop: 'alarmGrade', prop: 'alarmGrade',
label: '报警级别', label: '报警级别',
align: 'center',
filter: publicFormatter(this.DICT_TYPE.EQU_ALARM_LEVEL), filter: publicFormatter(this.DICT_TYPE.EQU_ALARM_LEVEL),
}, },
{ {
@ -96,11 +96,11 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'alarmContent', label: '报警内容', align: 'center' }, { prop: 'alarmContent', label: '报警内容' },
// { // {
// _action: 'equipment-group-show-alert', // _action: 'equipment-group-show-alert',
// label: '', // label: '',
// align: 'center', // ,
// subcomponent: { // subcomponent: {
// props: ['injectData'], // props: ['injectData'],
// render: function (h) { // render: function (h) {
@ -166,7 +166,7 @@ export default {
this.getList(); this.getList();
}, },
methods: { methods: {
/** 重写 basicPageMixin 里的 handleSearchBarBtnClick */ /** 重写 basicPageMixin 里的 handleSearchBarBtnClick */
handleSearchBarBtnClick(btn) { handleSearchBarBtnClick(btn) {
// //
this.queryParams.createTime = btn.createTime; this.queryParams.createTime = btn.createTime;

View File

@ -35,7 +35,9 @@
" /> " />
</div> </div>
</div> </div>
<div v-else style="margin-top: 20px; color: #c7c7c7; text-align: center;">暂无数据</div> <div v-else style="margin-top: 20px; color: #c7c7c7; text-align: center">
暂无数据
</div>
</div> </div>
</template> </template>
@ -139,19 +141,16 @@ export default {
width: 48, width: 48,
prop: 'index', prop: 'index',
label: '序号', label: '序号',
align: 'center',
}, },
{ {
width: 160, width: 160,
prop: 'time', prop: 'time',
label: '时间', label: '时间',
align: 'center',
}, },
{ {
width: 200, width: 200,
prop: 'plcCode', prop: 'plcCode',
label: 'PLC编码', label: 'PLC编码',
align: 'center',
}, },
]; ];
const firstLineData = { const firstLineData = {
@ -162,7 +161,6 @@ export default {
otherList.forEach((item) => { otherList.forEach((item) => {
props.push({ props.push({
label: item.name, label: item.name,
align: 'center',
prop: item.name, prop: item.name,
width: 128, width: 128,
}); });

View File

@ -17,15 +17,16 @@ import * as echarts from 'echarts';
export default { export default {
name: 'LineChartInEquipmentProcessAmount', name: 'LineChartInEquipmentProcessAmount',
components: {}, components: {},
props: ['equipmentList'], props: ['equipmentList', 'render'],
data() { data() {
return { return {
chart: null, chart: null,
option: { option: {
color: ['#288AFF'],
grid: { grid: {
top: 64, top: 64,
left: 56, left: 56,
right: 24, right: 64,
bottom: 56, bottom: 56,
}, },
title: { title: {
@ -64,6 +65,10 @@ export default {
color: '#ccc', color: '#ccc',
}, },
}, },
name: '数量',
nameTextStyle: {
fontSize: 14,
},
}, },
series: [ series: [
{ {
@ -85,16 +90,19 @@ export default {
}, },
}; };
}, },
mounted() { watch: {
// console.log('this.eq list', this.equipmentList); render: {
if (!this.chart) this.chart = echarts.init(this.$refs.chart); handler: function (newVal, oldVal) {
if (!this.chart) this.chart = echarts.init(this.$refs.chart);
this.$nextTick(() => { this.$nextTick(() => {
this.chart.setOption(this.updateConfig(this.option)); if (this.chart) this.chart.setOption(this.updateConfig(this.option));
}); });
},
deep: true,
},
}, },
beforeDestroy() { beforeDestroy() {
this.chart.dispose(); if (this.chart) this.chart.dispose();
}, },
methods: { methods: {
updateConfig(config) { updateConfig(config) {
@ -122,5 +130,6 @@ export default {
.chart { .chart {
height: 100%; height: 100%;
width: 100%; width: 100%;
// background: lightcoral;
} }
</style> </style>

View File

@ -9,15 +9,52 @@
<div style="flex: 1; display: flex; background: #f2f4f9"> <div style="flex: 1; display: flex; background: #f2f4f9">
<div <div
class="app-container" class="app-container"
style="margin-right: 12px; border-radius: 8px; background: #fff"> style="
<!-- side bar --> margin-right: 12px;
border-radius: 8px;
background: #fff;
padding: 0;
">
<div <div
class="side-bar__left" class="factory-list__selector"
style="width: 240px; padding: 12px; height: 100%"> style="margin: 12px"
title="点击切换工厂"
@mouseenter="factoryListOpen = true"
@mouseleave="factoryListOpen = false">
{{ currentFactory?.label || '工厂名称' }}
<div class="factory-list__wrapper" :class="{ open: factoryListOpen }">
<ul
class="factory-list"
v-if="sidebarContent.length"
@click.prevent="factoryChangeHandler">
<li
v-for="fc in sidebarContent"
:key="fc.id"
:data-value="fc.id"
class="factory-list__item"
:class="{ 'is-current': fc.id == currentFactory?.id }">
{{ fc.label }}
</li>
</ul>
<div v-else style="color: #0008; width: 128px; text-align: center">
- -
</div>
</div>
</div>
<!-- side bar -->
<div class="side-bar__left" style="width: 240px; height: 100%">
<el-tree <el-tree
:data="sidebarContent" class="custom-tree-class"
:data="currentFactory?.children"
:props="treeProps" :props="treeProps"
@node-click="handleSidebarItemClick" /> :empty-text="' - 暂无数据 - '"
icon-class="custom-icon-class"
@node-click="handleSidebarItemClick">
<!-- <div class="custom-tree-node" slot-scope="{ node, data }">
<span class="icon"></span>
<span>{{ node.label }}</span>
</div> -->
</el-tree>
</div> </div>
</div> </div>
<div <div
@ -30,28 +67,48 @@
ref="search-bar" ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" /> @headBtnClick="handleSearchBarBtnClick" />
<transition appear name="vvv" mode="out-in"> <el-row>
<base-table <el-col class="custom-tabs">
v-if="mode == 'table'" <el-tabs v-model="activeName" @tab-click="handleTabClick">
:table-props="tableProps" <el-tab-pane :label="'\u2002数据列表\u2002'" name="table">
:page="1" <base-table
:limit="999" v-if="mode == 'table'"
:table-data="list" :table-props="tableProps"
@emitFun="handleEmitFun"> :page="1"
<!-- <method-btn :limit="999"
:table-data="list"
@emitFun="handleEmitFun">
<!-- <method-btn
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> --> @clickBtn="handleTableBtnClick" /> -->
</base-table> </base-table>
</el-tab-pane>
<el-tab-pane :label="'\u3000柱状图\u3000'" name="graph">
<div class="graph" style="height: 56vh">
<!-- graph -->
<Graph
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>
</el-tab-pane>
</el-tabs>
</el-col>
</el-row>
<div class="graph" style="height: 56vh;" v-else> <!-- <transition appear name="vvv" mode="out-in"></transition> -->
<!-- graph -->
<Graph v-if="list.length" :equipment-list="list" />
<div v-else style="color: #c7c7c7; text-align: center; margin-top: 20px;">没有设备</div>
</div>
</transition>
</div> </div>
</div> </div>
</div> </div>
@ -66,6 +123,17 @@ export default {
props: {}, props: {},
data() { data() {
return { return {
renderKey: Math.random(),
factoryListOpen: false,
currentFactory: null,
factoryList: [
{ name: '1', value: 1 },
{ name: '2', value: 2 },
{ name: '3', value: 3 },
{ name: '4', value: 4 },
{ name: '5', value: 5 },
{ name: '6', value: 6 },
],
sidebarContent: [ sidebarContent: [
// { // {
// id: 'fc1', // id: 'fc1',
@ -152,6 +220,7 @@ export default {
// ], // ],
// }, // },
], ],
activeName: 'table',
searchBarFormConfig: [ searchBarFormConfig: [
{ {
type: 'datePicker', type: 'datePicker',
@ -172,23 +241,23 @@ export default {
name: 'search', name: 'search',
color: 'primary', color: 'primary',
}, },
{ // {
type: 'separate', // type: 'separate',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '表格', // btnName: '',
name: 'table', // name: 'table',
plain: true, // plain: true,
color: 'success', // color: 'success',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '图表', // btnName: '',
name: 'graph', // name: 'graph',
plain: true, // plain: true,
color: 'warning', // color: 'warning',
}, // },
// { // {
// type: this.$auth.hasPermi('base:equipment-group:export') ? 'button' : '', // type: this.$auth.hasPermi('base:equipment-group:export') ? 'button' : '',
// btnName: '', // btnName: '',
@ -197,11 +266,11 @@ export default {
// }, // },
], ],
tableProps: [ tableProps: [
{ prop: 'lineName', label: '产线', align: 'center' }, { prop: 'lineName', label: '产线' },
{ prop: 'sectionName', label: '工段', align: 'center' }, { prop: 'sectionName', label: '工段' },
{ prop: 'externalCode', label: '设备编码', align: 'center' }, { prop: 'externalCode', label: '设备编码' },
{ prop: 'equipmentName', label: '设备名称', align: 'center' }, { prop: 'equipmentName', label: '设备名称' },
{ prop: 'totalQuantity', label: '加工数量', align: 'center' }, { prop: 'totalQuantity', label: '加工数量' },
], ],
mode: 'table', // table | graph mode: 'table', // table | graph
queryParams: { queryParams: {
@ -230,20 +299,20 @@ export default {
this.$set(factory, 'label', factory.name); this.$set(factory, 'label', factory.name);
this.$set(factory, 'type', '工厂'); this.$set(factory, 'type', '工厂');
delete factory.name; delete factory.name;
factory.children = factory.lines; // factory.children = factory.lines;
delete factory.lines; // delete factory.lines;
factory.children?.forEach((line) => { factory.children?.forEach((line) => {
this.$set(line, 'label', line.name); this.$set(line, 'label', line.name);
this.$set(line, 'type', '产线'); this.$set(line, 'type', '产线');
delete line.name; delete line.name;
line.children = line.sections; // line.children = line.sections;
delete line.sections; // delete line.sections;
line.children?.forEach((ws) => { line.children?.forEach((ws) => {
this.$set(ws, 'label', ws.name); this.$set(ws, 'label', ws.name);
this.$set(ws, 'type', '工段'); this.$set(ws, 'type', '工段');
delete ws.name; delete ws.name;
ws.children = ws.equipments; // ws.children = ws.equipments;
delete ws.equipments; // delete ws.equipments;
ws.children?.forEach((eq) => { ws.children?.forEach((eq) => {
this.$set(eq, 'label', eq.name); this.$set(eq, 'label', eq.name);
this.$set(eq, 'type', '设备'); this.$set(eq, 'type', '设备');
@ -258,10 +327,22 @@ export default {
const { data } = await this.$axios('/base/factory/getTree'); const { data } = await this.$axios('/base/factory/getTree');
this.sidebarContent = data; this.sidebarContent = data;
this.buildTree(data); this.buildTree(data);
console.log('tree', this.sidebarContent);
},
handleTabClick(tab, event) {
if (tab.name == 'graph') this.renderKey = Math.random();
},
factoryChangeHandler(event) {
this.factoryListOpen = false;
const fcId = event.target.dataset.value;
this.handleSidebarItemClick({ id: fcId, type: '工厂' });
this.currentFactory = this.sidebarContent.find((item) => item.id == fcId);
}, },
handleSidebarItemClick({ label, id, type }) { handleSidebarItemClick({ label, id, type }) {
console.log('lable clicked!', label, id, type); console.log('label clicked!', label, id, type);
switch (type) { switch (type) {
case '设备': case '设备':
this.queryParams.equipmentId = id; this.queryParams.equipmentId = id;
@ -317,8 +398,10 @@ export default {
<style scoped> <style scoped>
.side-bar__left >>> .is-current { .side-bar__left >>> .is-current {
padding: 0;
color: #111; color: #111;
background: #f2f4f7; /* background: #f2f4f7; */
background: #e3efff !important;
} }
.vvv-enter, .vvv-enter,
@ -338,4 +421,149 @@ export default {
/* transform: translateY(0) scaleY(1); */ /* transform: translateY(0) scaleY(1); */
transform: translateY(0); transform: translateY(0);
} }
.custom-tabs >>> .el-tabs__header {
margin-bottom: 8px;
display: inline-block;
transform: translateY(-12px);
}
.custom-tabs >>> .el-tabs__item {
padding-left: 0px !important;
padding-right: 0px !important;
line-height: 36px !important;
height: 36px;
}
.factory-list__selector {
position: relative;
height: 36px;
font-size: 16px;
line-height: 36px;
padding-left: 28px;
background: url(../../../assets/images/factory-icon.png) 0 / 10% no-repeat;
}
.factory-list__selector:hover {
cursor: pointer;
color: #0008;
}
.factory-list__selector::after {
/* content: ''; */
position: absolute;
top: 16px;
right: 12px;
display: inline-block;
width: 8px;
height: 8px;
/* background: #5c5c5c; */
border-color: #000;
border-width: 4px;
border-style: solid;
border-top-color: transparent;
border-right-color: transparent;
border-bottom-color: transparent;
}
.factory-list__selector:hover::after {
/* cursor: pointer; */
border-left-color: #0008;
}
.factory-list__wrapper {
visibility: hidden;
position: absolute;
background: #fff;
box-shadow: 0 0 32px 10px #0002;
border-radius: 8px;
top: 36px;
left: 90px;
/* max-width: 128px; */
height: auto;
width: auto;
white-space: nowrap;
overflow: hidden;
/* transition: all 0.3s ease-out; */
z-index: 1000;
}
.factory-list__wrapper.open {
visibility: visible;
}
ul,
li {
margin: 0;
padding: 0;
list-style: none;
}
.factory-list {
color: #0008;
max-height: 240px;
overflow-y: auto;
}
.factory-list__item {
font-size: 16px;
line-height: 1;
padding: 8px 64px 8px 16px;
/* min-width: 64px; */
position: relative;
}
.factory-list__item:hover,
.factory-list__item.is-current {
background: #e3efff;
color: #0b58ff;
}
.factory-list__item.is-current::after {
content: '√';
position: absolute;
top: 8px;
right: 16px;
font-weight: bold;
}
.custom-tree-class >>> .el-tree-node__content {
height: auto !important;
padding: 8px 12px !important;
}
.custom-tree-class >>> .el-tree-node__content:hover {
background: #e3efff;
}
.custom-tree-class >>> .el-tree-node__children .el-tree-node__content {
padding: 8px 18px !important;
}
.custom-tree-class
>>> .el-tree-node__children
.el-tree-node__children
.el-tree-node__content {
padding: 8px 24px !important;
}
</style>
<style>
.custom-icon-class {
margin-right: 8px;
width: 20px;
height: 24px;
background: url('../../../assets/images/tree-icon-1.png') 100% / contain
no-repeat;
}
.custom-icon-class.el-tree-node__expand-icon.expanded {
transform: unset;
}
.el-tree-node__children .custom-icon-class {
background: url('../../../assets/images/tree-icon-2.png') 100% / contain
no-repeat;
}
.el-tree-node__children .el-tree-node__children .custom-icon-class {
background: unset;
}
</style> </style>

View File

@ -11,14 +11,16 @@
:formConfigs="[{ label: '设备近24小时生产记录', type: 'title' }]" :formConfigs="[{ label: '设备近24小时生产记录', type: 'title' }]"
ref="search-bar" /> ref="search-bar" />
<el-skeleton v-if="initing" :rows="6" animated /> <el-skeleton v-if="initing" :rows="6" animated />
<base-table <div v-else :class="{ 'no-data-bg': !list || list.length == 0 }">
v-else <base-table
:span-method="mergeColumnHandler" v-if="list && list.length > 0"
:table-props="tableProps" :span-method="mergeColumnHandler"
:table-data="list" :table-props="tableProps"
@emitFun="handleEmitFun"></base-table> :table-data="list"
<!-- :page="queryParams.pageNo" @emitFun="handleEmitFun"></base-table>
:limit="queryParams.pageSize" --> <!-- :page="queryParams.pageNo"
:limit="queryParams.pageSize" -->
</div>
</div> </div>
</template> </template>
@ -37,6 +39,7 @@ export default {
list: [], list: [],
tableProps: [], tableProps: [],
spanInfo: {}, spanInfo: {},
noData: true,
}; };
}, },
computed: {}, computed: {},
@ -51,24 +54,22 @@ export default {
} = item; } = item;
const props = [ const props = [
{ prop: 'productLine', label: '产线', align: 'center' }, { prop: 'productLine', label: '产线' },
{ prop: 'specification', label: '规格', align: 'center' }, { prop: 'specification', label: '规格' },
{ prop: 'equipmentName', label: '设备', align: 'center' }, { prop: 'equipmentName', label: '设备' },
{ prop: 'totalQuantity', label: '生产总数', align: 'center' }, { prop: 'totalQuantity', label: '生产总数' },
]; ];
for (const key of Object.keys(hourData).sort()) { for (const key of Object.keys(hourData).sort()) {
const subprop = { const subprop = {
label: key, label: key,
align: 'center',
children: [ children: [
{ prop: key + '__in', label: '进数据', align: 'center' }, { prop: key + '__in', label: '进数据' },
{ prop: key + '__out', label: '出数据', align: 'center' }, { prop: key + '__out', label: '出数据' },
{ prop: key + '__nok', label: '报废数据', align: 'center' }, { prop: key + '__nok', label: '报废数据' },
{ {
prop: key + '__ratio', prop: key + '__ratio',
label: '报废率', label: '报废率',
align: 'center',
filter: (val) => (val != null ? val + ' %' : '-'), filter: (val) => (val != null ? val + ' %' : '-'),
}, },
], ],
@ -138,6 +139,13 @@ export default {
// console.log('recent-24', data); // console.log('recent-24', data);
this.initing = true; this.initing = true;
if (!data || !data.length) {
this.initing = false;
this.noData = true;
return;
}
this.noData = false;
this.buildProps(data[0]); this.buildProps(data[0]);
this.buildData(data); this.buildData(data);
this.queryParams.pageSize = this.list.length; this.queryParams.pageSize = this.list.length;

View File

@ -18,6 +18,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
@ -74,28 +75,24 @@ export default {
width: 128, width: 128,
prop: 'equipmentName', prop: 'equipmentName',
label: '设备名称', label: '设备名称',
align: 'center',
}, },
{ {
width: 128, width: 128,
prop: 'equipmentCode', prop: 'equipmentCode',
label: '设备编码', label: '设备编码',
align: 'center',
}, },
{ width: 128, prop: 'inQuantity', label: '投入数', align: 'center' }, { width: 128, prop: 'inQuantity', label: '投入数' },
{ width: 128, prop: 'outQuantity', label: '产出数', align: 'center' }, { width: 128, prop: 'outQuantity', label: '产出数' },
{ {
width: 128, width: 128,
prop: 'run', prop: 'run',
label: '是否运行', label: '是否运行',
align: 'center',
filter: (val) => (val != null ? (val ? '是' : '否') : '-'), filter: (val) => (val != null ? (val ? '是' : '否') : '-'),
}, },
{ {
width: 128, width: 128,
prop: 'status', prop: 'status',
label: '状态', label: '状态',
align: 'center',
filter: (val) => filter: (val) =>
val != null ? ['正常', '计划停机', '故障'][val] : '-', val != null ? ['正常', '计划停机', '故障'][val] : '-',
}, },
@ -103,7 +100,6 @@ export default {
width: 128, width: 128,
prop: 'error', prop: 'error',
label: '是否故障', label: '是否故障',
align: 'center',
filter: (val) => (val != null ? (val ? '是' : '否') : '-'), filter: (val) => (val != null ? (val ? '是' : '否') : '-'),
}, },
{ {
@ -121,7 +117,6 @@ export default {
{ {
_action: 'params-monitor', _action: 'params-monitor',
label: '参数监控', label: '参数监控',
align: 'center',
subcomponent: { subcomponent: {
props: ['injectData'], props: ['injectData'],
render: function (h) { render: function (h) {
@ -129,7 +124,7 @@ export default {
return h( return h(
'el-button', 'el-button',
{ {
props: { type: 'text', size: 'mini' }, props: { type: 'text' },
on: { on: {
click: function () { click: function () {
console.log('inejctdata', _this.injectData); console.log('inejctdata', _this.injectData);

View File

@ -0,0 +1,238 @@
<!--
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>
<el-form-item
label="产线"
prop="productionId"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select
v-model="dataForm.productionId"
placeholder="请选择产线"
filterable
@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>
<el-form-item
label="工段"
prop="sectionId"
:rules="[{ required: true, message: '不能为空', trigger: 'blur' }]">
<el-select
v-model="dataForm.sectionId"
placeholder="请选择工段"
filterable
@change="$emit('update', dataForm)">
<el-option
v-for="opt in workshopSectionList"
:key="opt.value"
:label="opt.label"
:value="opt.value" />
</el-select>
</el-form-item>
</el-col>
<el-col>
<el-form-item
label="按钮盒识别码"
prop="buttonId"
:rules="[
{ required: true, message: '不能为空', trigger: 'blur' },
{
type: 'number',
message: '请输入整数',
trigger: 'blur',
transform: (val) => Number.isInteger(Number(val)) && Number(val),
},
]">
<el-input
v-model="dataForm.buttonId"
@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>
<el-form-item label="按钮盒模式" prop="model">
<el-input
v-model="dataForm.model"
@change="$emit('update', dataForm)"
placeholder="请输入按钮盒模式" />
</el-form-item>
</el-col>
<el-col>
<el-form-item
label="按钮值"
prop="keyValue"
:rules="[
{ required: true, message: '不能为空', trigger: 'blur' },
{
type: 'number',
message: '请输入100以内的整数',
trigger: 'blur',
transform: (val) =>
Number.isInteger(+val) &&
Number(val) >= 0 &&
Number(val) <= 100 &&
Number(val),
},
]">
<el-input
v-model="dataForm.keyValue"
type="number"
min="0"
max="100"
@change="$emit('update', dataForm)"
placeholder="请输入按钮盒模式" />
</el-form-item>
</el-col>
<el-col>
<el-form-item label="检测内容" prop="inspectionDetContent">
<el-input
type="textarea"
v-model="dataForm.inspectionDetContent"
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: () => ({}),
},
},
data() {
return {
formLoading: true,
productionLineList: [],
workshopSectionList: [],
};
},
mounted() {
this.getProductionLineList();
// this.getWorksectionList();
// this.getCode('/base/equipment-group-alarm/getCode').then((code) => {
// this.formLoading = false;
// this.$emit('update', {
// ...this.dataForm,
// code,
// });
// });
},
watch: {
'dataForm.productionId': {
handler(id) {
if (id != null) this.getWorksectionList(id);
},
immediate: true,
},
},
methods: {
/** 模拟透传 ref */
validate(cb) {
return this.$refs.form.validate(cb);
},
resetFields(args) {
return this.$refs.form.resetFields(args);
},
async getProductionLineList() {
this.formLoading = true;
const res = await this.$axios({
url: '/base/production-line/listAll',
method: 'get',
});
if (res.code == 0) {
this.productionLineList = res.data.map((item) => ({
label: item.name,
value: item.id,
}));
}
this.formLoading = false;
},
async getWorksectionList(id) {
this.formLoading = true;
const res = await this.$axios({
url: '/base/workshop-section/listByParentId',
method: 'get',
params: {
id,
},
});
if (res.code == 0) {
this.workshopSectionList = res.data.map((item) => ({
label: item.name,
value: item.id,
}));
}
this.formLoading = false;
},
async handleProductlineChange(id) {
await this.getWorksectionList(id);
this.dataForm.sectionId = null;
this.$emit('update', this.dataForm);
},
async getCode(url) {
const response = await this.$axios(url);
return response.data;
},
},
};
</script>
<style scoped lang="scss">
.el-date-editor,
.el-select {
width: 100%;
}
</style>

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
fixed="right" fixed="right"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
@ -38,7 +39,7 @@
@close="cancel" @close="cancel"
@cancel="cancel" @cancel="cancel"
@confirm="submitForm"> @confirm="submitForm">
<DialogForm v-if="open" ref="form" :dataForm="form" :rows="rows" /> <DialogForm v-if="open" ref="form" v-model="form" :rows="rows" />
</base-dialog> </base-dialog>
</div> </div>
</template> </template>
@ -54,11 +55,12 @@ import {
} from '@/api/base/qualityInspectionBoxBtn'; } from '@/api/base/qualityInspectionBoxBtn';
import basicPageMixin from '@/mixins/lb/basicPageMixin'; import basicPageMixin from '@/mixins/lb/basicPageMixin';
import moment from 'moment'; import moment from 'moment';
import DialogForm from './dialogForm.vue';
export default { export default {
name: 'QualityInspectionBoxBtn', name: 'QualityInspectionBoxBtn',
mixins: [basicPageMixin], mixins: [basicPageMixin],
components: {}, components: { DialogForm },
data() { data() {
return { return {
rows: [ rows: [
@ -81,6 +83,9 @@ export default {
url: '/base/workshop-section/listAll', url: '/base/workshop-section/listAll',
prop: 'sectionId', prop: 'sectionId',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
bind: {
filterable: true,
},
}, },
], ],
[ [
@ -91,9 +96,10 @@ export default {
rules: [ rules: [
{ {
type: 'number', type: 'number',
message: '请输入', message: '请输入数',
trigger: 'blur', trigger: 'blur',
transform: (val) => Number(val), transform: (val) =>
Number.isInteger(Number(val)) && Number(val),
}, },
], ],
}, },
@ -107,9 +113,10 @@ export default {
rules: [ rules: [
{ {
type: 'number', type: 'number',
message: '请输入100以内的', message: '请输入100以内的数',
trigger: 'blur', trigger: 'blur',
transform: (val) => Number(val) <= 100 && Number(val), transform: (val) =>
Number(val) <= 100 && Number.isInteger(+val) && Number(val),
}, },
], ],
bind: { type: 'number', min: 0, max: 100 }, bind: { type: 'number', min: 0, max: 100 },
@ -120,7 +127,7 @@ export default {
textarea: true, textarea: true,
label: '检测内容', label: '检测内容',
prop: 'inspectionDetContent', prop: 'inspectionDetContent',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], // rules: [{ required: true, message: '', trigger: 'blur' }],
}, },
], ],
], ],
@ -173,43 +180,36 @@ export default {
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ {
width: 128,
prop: 'productionName', prop: 'productionName',
label: '产线', label: '产线',
align: 'center',
}, },
{ {
width: 128,
prop: 'sectionName', prop: 'sectionName',
label: '工段', label: '工段',
align: 'center',
}, },
{ {
width: 128,
prop: 'inspectionDetContent', prop: 'inspectionDetContent',
label: '检测内容', label: '检测内容',
align: 'center',
}, },
{ {
width: 160, width: 160,
prop: 'buttonId', prop: 'buttonId',
label: '按钮盒识别码', label: '按钮盒识别码',
align: 'center',
}, },
// { // {
// width: 256, // width: 256,
// prop: 'productionId', // prop: 'productionId',
// label: '线ID', // label: '线ID',
// align: 'center', // ,
// }, // },
// { // {
// width: 256, // width: 256,
// prop: 'sectionId', // prop: 'sectionId',
// label: 'ID', // label: 'ID',
// align: 'center', // ,
// }, // },
{ width: 90, prop: 'keyValue', label: '按钮值', align: 'center' }, { width: 90, prop: 'keyValue', label: '按钮值' },
{ width: 128, prop: 'model', label: '按钮盒模式', align: 'center' }, { width: 128, prop: 'model', label: '按钮盒模式' },
], ],
// //
queryParams: { queryParams: {
@ -220,13 +220,13 @@ export default {
// keys, queryParams pageNo, pageSize key // keys, queryParams pageNo, pageSize key
searchBarKeys: ['inspectionDetContent'], searchBarKeys: ['inspectionDetContent'],
form: { form: {
id: undefined, id: null,
inspectionDetId: undefined, buttonId: null,
inspectionDetContent: undefined, inspectionDetContent: null,
productionId: undefined, productionId: null,
sectionId: undefined, sectionId: null,
model: undefined, model: null,
keyValue: undefined, keyValue: null,
}, },
}; };
}, },
@ -247,13 +247,13 @@ export default {
/** 表单重置 */ /** 表单重置 */
reset() { reset() {
this.form = { this.form = {
id: undefined, id: null,
inspectionDetId: undefined, buttonId: null,
inspectionDetContent: undefined, inspectionDetContent: null,
productionId: undefined, productionId: null,
sectionId: undefined, sectionId: null,
model: undefined, model: null,
keyValue: undefined, keyValue: null,
}; };
this.resetForm('form'); this.resetForm('form');
}, },
@ -300,7 +300,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm('是否确认删除安灯按钮16键对应编号为"' + id + '"的数据项?') .confirm('是否确认删除"' + row.sectionName + '"?')
.then(function () { .then(function () {
return deleteQualityInspectionBoxBtn(id); return deleteQualityInspectionBoxBtn(id);
}) })

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
fixed="right" fixed="right"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
@ -172,10 +173,10 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'typeName', label: '类型名称', align: 'center' }, { prop: 'typeName', label: '类型名称', },
{ prop: 'content', label: '检测内容', align: 'center' }, { prop: 'content', label: '检测内容', },
{ prop: 'code', label: '检测编码', align: 'center' }, { prop: 'code', label: '检测编码', },
{ prop: 'remark', label: '备注', align: 'center' }, { prop: 'remark', label: '备注', },
], ],
// //
queryParams: { queryParams: {
@ -275,7 +276,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm('是否确认删除质量检测信息基础编号为"' + id + '"的数据项?') .confirm('是否确认删除检测信息"' + row.content + '"?')
.then(function () { .then(function () {
return deleteQualityInspectionDet(id); return deleteQualityInspectionDet(id);
}) })

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
@ -106,9 +107,9 @@ export default {
width: 180, width: 180,
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'),
}, },
{ prop: 'name', label: '检测类型名称', align: 'center' }, { prop: 'name', label: '检测类型名称' },
{ prop: 'code', label: '检测类型编码', align: 'center' }, { prop: 'code', label: '检测类型编码' },
{ prop: 'remark', label: '备注', align: 'center' }, { prop: 'remark', label: '备注' },
// { // {
// label: '', // label: '',
// alignt: 'center', // alignt: 'center',
@ -321,7 +322,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm('是否确认删除质量检测类型基础编号为"' + id + '"的数据项?') .confirm('是否确认删除质量检测类型?')
.then(function () { .then(function () {
return deleteQualityInspectionType(id); return deleteQualityInspectionType(id);
}) })

View File

@ -17,6 +17,7 @@
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
label="操作" label="操作"
:width="120"
fixed="right" fixed="right"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
@ -68,7 +69,7 @@ export default {
[ [
{ {
select: true, select: true,
label: '检测内容ID', label: '检测内容',
url: '/base/quality-inspection-det/listAll', url: '/base/quality-inspection-det/listAll',
prop: 'inspectionDetId', prop: 'inspectionDetId',
labelKey: 'content', labelKey: 'content',
@ -97,6 +98,9 @@ export default {
label: '产线', label: '产线',
url: '/base/production-line/listAll', url: '/base/production-line/listAll',
prop: 'productionLineId', prop: 'productionLineId',
bind: {
filterable: true,
},
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
}, },
{ {
@ -105,6 +109,9 @@ export default {
url: '/base/workshop-section/listAll', // url options url: '/base/workshop-section/listAll', // url options
options: [], // options, options options: [], // options, options
prop: 'sectionId', prop: 'sectionId',
bind: {
filterable: true,
},
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
}, },
], ],
@ -118,6 +125,7 @@ export default {
datetime: true, datetime: true,
label: '检测时间', label: '检测时间',
prop: 'checkTime', prop: 'checkTime',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }],
bind: { bind: {
format: 'yyyy-MM-dd HH:mm:ss', format: 'yyyy-MM-dd HH:mm:ss',
'value-format': 'timestamp', 'value-format': 'timestamp',
@ -126,15 +134,15 @@ export default {
}, },
}, },
], ],
[ // [
{ // {
textarea: true, // textarea: true,
label: '检测内容', // label: '',
prop: 'inspectionDetContent', // prop: 'inspectionDetContent',
value: '', // value: '',
rules: [{ required: true, message: '不能为空', trigger: 'blur' }], // rules: [{ required: true, message: '', trigger: 'blur' }],
}, // },
], // ],
[{ textarea: true, label: '描述', prop: 'explainText' }], [{ textarea: true, label: '描述', prop: 'explainText' }],
[{ input: true, label: '备注', prop: 'remark' }], [{ input: true, label: '备注', prop: 'remark' }],
], ],
@ -213,32 +221,28 @@ export default {
width: 128, width: 128,
prop: 'inspectionDetContent', prop: 'inspectionDetContent',
label: '检测内容', label: '检测内容',
align: 'center',
}, },
{ {
// width: 128, // width: 128,
prop: 'lineName', prop: 'lineName',
label: '产线', label: '产线',
align: 'center',
}, },
{ {
// width: 128, // width: 128,
prop: 'checkPerson', prop: 'checkPerson',
label: '检测人员', label: '检测人员',
align: 'center',
}, },
{ {
// width: 160, // width: 160,
prop: 'checkTime', prop: 'checkTime',
label: '检测时间', label: '检测时间',
align: 'center', filter: (val) =>
filter: (val) => moment(val).format('yyyy-MM-DD HH:mm:ss'), val != null ? moment(val).format('yyyy-MM-DD HH:mm:ss') : '-',
}, },
{ {
width: 90, width: 90,
prop: 'source', prop: 'source',
label: '来源', label: '来源',
align: 'center',
filter: (val) => ['未知', '手动', '自动'][val], filter: (val) => ['未知', '手动', '自动'][val],
}, },
], ],
@ -394,7 +398,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const id = row.id; const id = row.id;
this.$modal this.$modal
.confirm('是否确认删除质量检查信息记录表编号为"' + id + '"的数据项?') .confirm('是否确认删除"' + row.inspectionDetContent + '"?')
.then(function () { .then(function () {
return deleteQualityInspectionRecord(id); return deleteQualityInspectionRecord(id);
}) })

View File

@ -13,18 +13,20 @@
<!-- <pre><code v-html="jsondemo"></code></pre> --> <!-- <pre><code v-html="jsondemo"></code></pre> -->
<el-skeleton v-if="initing" :rows="6" animated /> <el-skeleton v-if="initing" :rows="6" animated />
<base-table <div v-else :class="{ 'no-data-bg': !list || list.length == 0 }">
v-else <base-table
:table-props="tableProps" v-if="list && list.length > 0"
:page="queryParams.pageNo" :table-props="tableProps"
:limit="queryParams.pageSize" :page="queryParams.pageNo"
:table-data="list" :limit="queryParams.pageSize"
@emitFun="handleEmitFun"></base-table> :table-data="list"
@emitFun="handleEmitFun"></base-table>
</div>
</div> </div>
</template> </template>
<script> <script>
import response from './response.json'; // import response from './response.json';
import { handleNameData, handleDynamicData } from '@/utils/dynamicProps'; import { handleNameData, handleDynamicData } from '@/utils/dynamicProps';
// import hljs from 'highlight.js/lib/highlight'; // import hljs from 'highlight.js/lib/highlight';
// import json from 'highlight.js/lib/languages/json'; // import json from 'highlight.js/lib/languages/json';
@ -43,7 +45,6 @@ export default {
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
}, },
// jsondemo: '',
list: [ list: [
// { // {
// inspectionContent: '1', // inspectionContent: '1',
@ -56,7 +57,6 @@ export default {
{ {
prop: 'inspectionContent', prop: 'inspectionContent',
label: '检测内容', label: '检测内容',
align: 'center',
}, },
], ],
}; };
@ -67,20 +67,10 @@ export default {
}, },
methods: { methods: {
async getList() { async getList() {
// const response = await this.$axios({ const response = await this.$axios({
// url: '/monitoring/record-in-one-day/get', url: '/monitoring/record-in-one-day/get',
// method: 'get', method: 'get',
// }); });
console.log(response);
// const {
// // data: { nameData },
// code,
// } = response;
// this.jsondemo = hljs.highlight(
// 'json',
// JSON.stringify(response, null, 2),
// true
// ).value;
const { const {
data: { data: dyanmicData, nameData }, data: { data: dyanmicData, nameData },
} = response; } = response;

View File

@ -82,18 +82,22 @@ export default {
computed: { computed: {
config() { config() {
return { return {
color: ['#fde19a', '#8be2b9', '#288aff', '#7164ff'],
// title: { // title: {
// text: '线', // text: '线',
// }, // },
grid: { grid: {
top: '24%', top: '12%',
left: '3%', left: '2%',
right: '5%', right: '5%',
bottom: '5%', bottom: '5%',
containLabel: true, containLabel: true,
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: {
type: 'shadow',
},
}, },
legend: { legend: {
data: this.legend, data: this.legend,
@ -107,13 +111,145 @@ export default {
// }, // },
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: true,
onZero: false,
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
// color: 'red'
},
},
data: this.xProps, data: this.xProps,
// data: [
// '1',
// '2',
// '3',
// '4',
// '5',
// '6',
// '7',
// '8',
// '9',
// '10',
// '11',
// '12',
// '13',
// '14',
// '15',
// '16',
// '17',
// '18',
// '19',
// '20',
// ],
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
name: '单位/片',
nameTextStyle: {
color: '#999',
fontSize: 14,
align: 'right',
},
max: function (value) {
return value.max + Math.floor(value.max / 5);
},
},
// -
dataZoom: {
type: 'inside',
}, },
series: this.series, series: this.series,
// series: [
// {
// name: '',
// type: 'bar',
// // barWidth: 12,
// data: [
// 10,
// 12,
// 43,
// 4,
// 22,
// 32,
// 12,
// 8,
// 122,
// 0,
// ,
// 43,
// 4,
// 22,
// 32,
// 12,
// 8,
// 122,
// 77,
// 99,
// ],
// },
// {
// name: '',
// type: 'bar',
// // barWidth: 12,
// data: [
// 10,
// 12,
// 43,
// 4,
// 22,
// 32,
// 12,
// 8,
// 122,
// 0,
// 4,
// 22,
// 32,
// 12,
// 8,
// 122,
// 0,
// ,
// 43,
// 4,
// 22,
// 32,
// ],
// },
// {
// name: '',
// type: 'bar',
// // barWidth: 12,
// barCategoryGap: 12,
// data: [
// 10,
// 12,
// 43,
// 4,
// 22,
// 4,
// 22,
// 32,
// 12,
// 8,
// 122,
// 0,
// ,
// 43,
// 4,
// 22,
// 32,
// 32,
// 12,
// 8,
// 122,
// 0,
// ],
// },
// ],
}; };
}, },
}, },

View File

@ -7,9 +7,9 @@
<template> <template>
<!-- 列表 --> <!-- 列表 -->
<!-- height="35vh" -->
<base-table <base-table
:table-props="tableProps" :table-props="tableProps"
height="35vh"
:page="queryParams.pageNo" :page="queryParams.pageNo"
:limit="queryParams.pageSize" :limit="queryParams.pageSize"
:table-data="list"></base-table> :table-data="list"></base-table>
@ -35,27 +35,22 @@ export default {
{ {
prop: 'lineName', prop: 'lineName',
label: '产线', label: '产线',
align: 'center',
}, },
{ {
prop: 'sumUp', prop: 'sumUp',
label: '上片总数', label: '上片总数',
align: 'center',
}, },
{ {
prop: 'sumDown', prop: 'sumDown',
label: '下片总数', label: '下片总数',
align: 'center',
}, },
{ {
prop: 'sumCheck', prop: 'sumCheck',
label: '检测总数', label: '检测总数',
align: 'center',
}, },
{ {
prop: 'scrapRatio', prop: 'scrapRatio',
label: '比例(%)', label: '比例(%)',
align: 'center',
}, },
], ],
}; };

View File

@ -7,14 +7,20 @@
<template> <template>
<div class="graph-page"> <div class="graph-page">
<DetailGraph id="dg1" key="dg1" ref="dg1" title="数据总览"> <!-- <DetailGraph id="dg1" key="dg1" ref="dg1" title="数据总览">
<TotalGraph :summary-list="summaryList" /> <TotalGraph :summary-list="summaryList" />
</DetailGraph> </DetailGraph> -->
<DetailGraph id="dg2" key="dg2" ref="dg2" title="检测内容数据"> <!-- <DetailGraph id="dg2" key="dg2" ref="dg2" title="检测内容数据">
<LineGraph :x-props="lineData.xProps" :legend="legend" :series="series" /> <LineGraph :x-props="lineData.xProps" :legend="legend" :series="series" />
</DetailGraph> </DetailGraph> -->
<!-- <DetailGraph id="dg3" key="dg3" ref="dg3" /> <!-- <DetailGraph id="dg3" key="dg3" ref="dg3" />
<DetailGraph id="dg4" key="dg4" ref="dg4" /> --> <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>
<LineGraph
v-else
:x-props="lineData.xProps"
:legend="legend"
:series="series" />
</div> </div>
</template> </template>
@ -55,7 +61,8 @@ export default {
console.log('this.list', this.lineData.list, this.lineData.xProps); console.log('this.list', this.lineData.list, this.lineData.xProps);
const seriesItem = { const seriesItem = {
name: item.inspectionContent, name: item.inspectionContent,
type: 'line', type: 'bar',
barCategoryGap: 12,
data: [], data: [],
}; };

View File

@ -6,33 +6,84 @@
--> -->
<template> <template>
<div class="app-container"> <div class="quality-container" style="background: #f2f4f9; flex: 1">
<!-- 搜索工作栏 --> <el-row
<SearchBar class=""
:formConfigs="searchBarFormConfig" style="
ref="search-bar" margin-bottom: 12px;
@headBtnClick="handleSearchBarBtnClick" /> background: #fff;
padding: 16px 16px 0;
border-radius: 8px;
">
<!-- 搜索工作栏 -->
<SearchBar
:formConfigs="searchBarFormConfig"
ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" />
</el-row>
<transition mode="out-in" name="fade-down"> <el-row
<template v-if="mode == 'table'"> class=""
<!-- 列表 --> style="
<base-table margin-top: 0;
v-if="mode == 'table'" margin-bottom: 12px;
:table-props="tableProps" background: #fff;
:page="queryParams.pageNo" padding: 12px 16px 16px;
:limit="queryParams.pageSize" border-radius: 8px;
:table-data="list" ">
@emitFun="handleEmitFun"></base-table> <div class="blue-title">产线检测总览</div>
</template> <summaryTable :list="summaryList" />
</el-row>
<GraphPage <el-row
v-else class=""
:summary-list="summaryList" style="
:line-data="{ list: list, xProps: dynamicProps.map((v) => v.prop) }" /> margin-top: 0;
</transition> margin-bottom: 12px;
background: #fff;
padding: 12px 16px 16px;
border-radius: 8px;
">
<el-row style="display: flex; align-items: center">
<div class="blue-title">产线检测详细</div>
<div class="custom-tabs">
<el-tabs
v-model="activeName"
:stretch="true"
@tab-click="handleTabClick">
<el-tab-pane
:label="'\u2002数据列表\u2002'"
name="table"></el-tab-pane>
<el-tab-pane
:label="'\u3000柱状图\u3000'"
name="graph"></el-tab-pane>
</el-tabs>
</div>
</el-row>
<transition mode="out-in" name="fade-down">
<template v-if="mode == 'table'">
<base-table
v-if="mode == 'table'"
:table-props="tableProps"
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-data="list"
@emitFun="handleEmitFun"></base-table>
</template>
<GraphPage
v-else
:summary-list="summaryList"
:line-data="{
list: list,
xProps: dynamicProps.map((v) => v.prop),
}" />
</transition>
</el-row>
<!-- todo: 数据总览用弹窗包裹的 table 实现 --> <!-- todo: 数据总览用弹窗包裹的 table 实现 -->
<base-dialog <!-- <base-dialog
dialogTitle="数据总览" dialogTitle="数据总览"
:dialogVisible="summaryOpen" :dialogVisible="summaryOpen"
width="50%" width="50%"
@ -40,7 +91,7 @@
@cancel="handleSummaryClose" @cancel="handleSummaryClose"
@confirm="handleSummaryClose"> @confirm="handleSummaryClose">
<summaryTable :list="summaryList" /> <summaryTable :list="summaryList" />
</base-dialog> </base-dialog> -->
</div> </div>
</template> </template>
@ -48,13 +99,58 @@
import GraphPage from './graphPage.vue'; import GraphPage from './graphPage.vue';
import summaryTable from './components/summaryTable.vue'; import summaryTable from './components/summaryTable.vue';
class DateGetter {
constructor() {
this.today = new Date();
this.y = this.today.getFullYear();
this.m = this.today.getMonth();
this.d = this.today.getDate();
}
getWeekRange() {
const weekStart = new Date(this.y, this.m, this.d - this.today.getDay());
const weekEnd = new Date(
this.y,
this.m,
this.d + (6 - this.today.getDay()),
23,
59,
59
);
// console.log('week', [weekStart, weekEnd]);
return [weekStart, weekEnd];
}
getMonthRange() {
const monthStart = new Date(this.y, this.m, 1);
const monthEnd = new Date(this.y, this.m + 1, 0, 23, 59, 59);
// console.log('month', [monthStart, monthEnd]);
return [monthStart, monthEnd];
}
getQuarterRange() {
const quarterStart = new Date(this.y, this.m - (this.m % 3), 1);
const quarterEnd = new Date(
this.y,
this.m - (this.m % 3) + 3,
0,
23,
59,
59
);
// console.log('quarter', [quarterStart, quarterEnd]);
return [quarterStart, quarterEnd];
}
}
export default { export default {
name: 'QualityStatistics', name: 'QualityStatistics',
components: { GraphPage, summaryTable }, components: { GraphPage, summaryTable },
props: {}, props: {},
data() { data() {
const dateGetter = new DateGetter();
return { return {
mode: 'table', // defaults to 'table' // mode: 'table', // defaults to 'table'
activeName: 'table', // defaults to 'table'
searchBarFormConfig: [ searchBarFormConfig: [
{ {
type: 'datePicker', type: 'datePicker',
@ -70,6 +166,28 @@ export default {
defaultSelect: [], defaultSelect: [],
defaultTime: ['00:00:00', '23:59:59'], defaultTime: ['00:00:00', '23:59:59'],
param: 'timerange', param: 'timerange',
pickerOptions: {
shortcuts: [
{
text: '本周',
onClick(picker) {
picker.$emit('pick', dateGetter.getWeekRange());
},
},
{
text: '本月',
onClick(picker) {
picker.$emit('pick', dateGetter.getMonthRange());
},
},
{
text: '本季',
onClick(picker) {
picker.$emit('pick', dateGetter.getQuarterRange());
},
},
],
},
}, },
{ {
type: 'button', type: 'button',
@ -77,33 +195,33 @@ export default {
name: 'search', name: 'search',
color: 'primary', color: 'primary',
}, },
{ // {
type: 'separate', // type: 'separate',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '数据总览', // btnName: '',
name: 'summary', // name: 'summary',
color: 'text', // color: 'text',
}, // },
{ // {
type: 'separate', // type: 'separate',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '表格版', // btnName: '',
name: 'tableVersion', // name: 'tableVersion',
color: 'text btn-table', // color: 'text btn-table',
}, // },
{ // {
type: 'separate', // type: 'separate',
}, // },
{ // {
type: 'button', // type: 'button',
btnName: '图形版', // btnName: '',
name: 'graphVersion', // name: 'graphVersion',
color: 'text btn-graph', // color: 'text btn-graph',
}, // },
], ],
// props // props
dynamicProps: [], dynamicProps: [],
@ -166,26 +284,26 @@ export default {
}; };
}, },
computed: { computed: {
mode() {
return this.activeName;
},
tableProps() { tableProps() {
return [ return [
{ {
// width: 160, // width: 160,
prop: 'inspectionContent', prop: 'inspectionContent',
label: '检测内容', label: '检测内容',
align: 'center',
}, },
...this.dynamicProps, ...this.dynamicProps,
{ {
// width: 128, // width: 128,
prop: 'sumInput', prop: 'sumInput',
label: '检测类型总数', label: '检测类型总数',
align: 'center',
}, },
{ {
// width: 128, // width: 128,
prop: 'ratio', prop: 'ratio',
label: '比例', label: '比例',
align: 'center',
// subcomponent: { // subcomponent: {
// name: 'TextOnly', // name: 'TextOnly',
// props: { // props: {
@ -214,16 +332,51 @@ export default {
}, },
mounted() { mounted() {
if (this.$route.params.startTime && this.$route.params.endTime) { if (this.$route.params.startTime && this.$route.params.endTime) {
this.searchBarFormConfig[0].defaultSelect = [this.$route.params.startTime, this.$route.params.endTime] this.searchBarFormConfig[0].defaultSelect = [
this.$route.params.startTime,
this.$route.params.endTime,
];
this.queryParams.param = {}; this.queryParams.param = {};
this.$set(this.queryParams.param, 'startTime', this.$route.params.startTime); this.$set(
this.queryParams.param,
'startTime',
this.$route.params.startTime
);
this.$set(this.queryParams.param, 'endTime', this.$route.params.endTime); this.$set(this.queryParams.param, 'endTime', this.$route.params.endTime);
} else { } else {
this.searchBarFormConfig[0].defaultSelect = [] this.searchBarFormConfig[0].defaultSelect = [];
} }
this.getList(); this.getList();
}, },
watch: {
$route: 'initData',
},
methods: { methods: {
initData(to) {
// console.log(to)
if (to.name === 'QualityStatistics') {
if (this.$route.params.startTime && this.$route.params.endTime) {
this.searchBarFormConfig[0].defaultSelect = [
this.$route.params.startTime,
this.$route.params.endTime,
];
this.queryParams.param = {};
this.$set(
this.queryParams.param,
'startTime',
this.$route.params.startTime
);
this.$set(
this.queryParams.param,
'endTime',
this.$route.params.endTime
);
} else {
this.searchBarFormConfig[0].defaultSelect = [];
}
this.getList();
}
},
getList() { getList() {
this.getSummaryList(); this.getSummaryList();
this.getDetailedList(); this.getDetailedList();
@ -232,20 +385,17 @@ export default {
async getSummaryList() { async getSummaryList() {
const response = await this.$axios({ const response = await this.$axios({
url: '/monitoring/statistical-data/getUpPart', url: '/monitoring/statistical-data/getUpPart',
method: 'get', method: 'post',
params: this.queryParams.param data: this.queryParams.param
? { ? {
param: { // startTime: new Date(2022, 6, 1, 0, 0, 0).getTime(), // '2023-07-01 00:00:00',
// startTime: new Date(2022, 6, 1, 0, 0, 0).getTime(), // '2023-07-01 00:00:00', // endTime: new Date(2023, 7, 10, 0, 0, 0).getTime(), // '2023-08-10 00:00:00',
// endTime: new Date(2023, 7, 10, 0, 0, 0).getTime(), // '2023-08-10 00:00:00', startTime: this.queryParams.param.startTime,
startTime: this.queryParams.param.startTime, endTime: this.queryParams.param.endTime,
endTime: this.queryParams.param.endTime,
},
} }
: null, : {},
}); });
this.summaryList = response.data; this.summaryList = response.data;
console.log('summaryList', this.summaryList);
}, },
/** 获取 检测内容和产线关联 列表 */ /** 获取 检测内容和产线关联 列表 */
async getDetailedList() { async getDetailedList() {
@ -253,22 +403,21 @@ export default {
data: { data, otherList, otherMap, nameData }, data: { data, otherList, otherMap, nameData },
} = await this.$axios({ } = await this.$axios({
url: '/monitoring/statistical-data/getDownPart', url: '/monitoring/statistical-data/getDownPart',
params: this.queryParams.param method: 'post',
data: this.queryParams.param
? { ? {
param: { // startTime: new Date(2023, 6, 1).getTime(), // '2023-07-01 00:00:00',
// startTime: new Date(2023, 6, 1).getTime(), // '2023-07-01 00:00:00', // endTime: new Date(2023, 7, 22).getTime(), // '2023-08-10 00:00:00',
// endTime: new Date(2023, 7, 22).getTime(), // '2023-08-10 00:00:00', startTime: this.queryParams.param.startTime,
startTime: this.queryParams.param.startTime, endTime: this.queryParams.param.endTime,
endTime: this.queryParams.param.endTime,
},
} }
: null, : {},
}); });
// this.list = response.data; // this.list = response.data;
console.log('data', data); // console.log('data', data);
console.log('otherList', otherList); // console.log('otherList', otherList);
console.log('otherMap', otherMap); // console.log('otherMap', otherMap);
console.log('nameData', nameData); // console.log('nameData', nameData);
this.dynamicProps = this.filterNameData(nameData); this.dynamicProps = this.filterNameData(nameData);
this.list = this.filterData(data); this.list = this.filterData(data);
@ -299,6 +448,14 @@ export default {
}; };
}); });
}, },
handleTabClick(tab, event) {
const { name } = tab;
if (name == 'graph') {
//
} else {
//
}
},
/** 总览关闭 */ /** 总览关闭 */
handleSummaryClose() { handleSummaryClose() {
this.summaryOpen = false; this.summaryOpen = false;
@ -383,6 +540,42 @@ export default {
.fade-down-leave { .fade-down-leave {
transform: translateY(0); transform: translateY(0);
} }
.blue-title {
position: relative;
padding: 4px 0;
padding-left: 12px;
font-size: 14px;
color: #606266;
font-weight: 700;
margin-bottom: 12px;
&::before {
content: '';
position: absolute;
left: 0;
top: 6px;
height: 16px;
width: 4px;
border-radius: 1px;
background: #0b58ff;
}
}
:deep(.custom-tabs) {
.el-tabs__header {
margin-left: 12px;
margin-bottom: 8px;
display: inline-block;
transform: translateY(-6px);
}
.el-tabs__item {
padding-left: 0 !important;
padding-right: 0 !important;
height: 36px;
}
}
</style> </style>
<!-- <!--
@ -390,47 +583,47 @@ export default {
// width: 128, // width: 128,
// prop: 'line1', // prop: 'line1',
// label: '线1', // label: '线1',
// align: 'center', // ,
// }, // },
// { // {
// width: 128, // width: 128,
// prop: 'line2', // prop: 'line2',
// label: '线2', // label: '线2',
// align: 'center', // ,
// }, // },
// { // {
// width: 128, // width: 128,
// prop: 'line3', // prop: 'line3',
// label: '线3', // label: '线3',
// align: 'center', // ,
// }, // },
// { // {
// width: 128, // width: 128,
// prop: 'line4', // prop: 'line4',
// label: '线4', // label: '线4',
// align: 'center', // ,
// }, // },
// { // {
// width: 128, // width: 128,
// prop: 'line5', // prop: 'line5',
// label: '线5', // label: '线5',
// align: 'center', // ,
// }, // },
// { // {
// width: 128, // width: 128,
// prop: 'line6', // prop: 'line6',
// label: '线6', // label: '线6',
// align: 'center', // ,
// }, // },
// { // {
// width: 128, // width: 128,
// prop: 'line7', // prop: 'line7',
// label: '线7', // label: '线7',
// align: 'center', // ,
// }, // },
// { // {
// width: 128, // width: 128,
// prop: 'line8', // prop: 'line8',
// label: '线8', // label: '线8',
// align: 'center', // ,
// }, --> // }, -->