feature: collapsable and normalize panels (#372)

* feature: add panel component to normalize all panels
* feature: add expand function to panel component
* chore: remove not used computed value
* feature: add slot for override the icon in the panel
* chore: remove unused package (vue2-collapse)
* feature: change StatusPanel.vue to panel component
* feature: add prop to change toolbar color
* feature: change MinSettingsPanel.vue to panel component
* feature: change MoonrakerStatePanel.vue to panel component
* feature: change KlippyStatePanel.vue to panel component
* feature: change KlipperWarningsPanel.vue to panel component
* feature: change ControlPanel.vue to panel component
* feature: change PrintsettingsPanel.vue to panel component
* feature: change MiscellaneousPanel.vue to panel component
* feature: change ToolsPanel.vue to panel component
* feature: change MacrosPanel.vue to panel component
* feature: change MiniconsolePanel.vue to panel component
* feature: change FarmPrinterPanel.vue to panel component
* bugfix: add toolbar zindex to fix panel hover overlay
* bugfix: remove toolbar zindex and modify hover effect
* bugfix: farmprinter loading state (remove debug output)
* feature: add prop to disable margin botton of panel component
* feature: change HistoryListPanel.vue to panel component
* feature: move history statistics in HistoryStatisticsPanel.vue and use panel component
* feature: change ZoffsetPanel.vue to panel component
* feature: add slot after toolbar title in panel component
* feature: change Heightmap.vue to panel component
* feature: change Files.vue to panel component
* feature: change Viewer.vue to panel component
* feature: add buttons before toolbar in panel component
* feature: change ConfigFilesPanel.vue to panel component
* feature: change EndstopPanel.vue to panel component
* chore: fix typo in card class from ConfigFilesPanel.vue
* feature: change LimitsPanel.vue to panel component
* feature: change LogfilesPanel.vue to panel component
* feature: change SystemPanel.vue to panel component
* feature: change UpdatePanel.vue to panel component
This commit is contained in:
Stefan Dej
2021-10-09 23:39:06 +02:00
committed by GitHub
parent d147a92695
commit 3dae42edb0
33 changed files with 736 additions and 680 deletions

View File

@@ -39,15 +39,11 @@
<template>
<div>
<v-card>
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-video-3d</v-icon>{{ $t('GCodeViewer.Title') }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<panel :title="$t('GCodeViewer.Title')" icon="mdi-video-3d" card-class="gcode-viewer-panel">
<template v-slot:buttons>
<v-btn @click="reloadViewer" color="info" class="ml-3" v-show="reloadRequired" small>{{$t("GCodeViewer.ReloadRequired")}}</v-btn>
<v-btn @click="resetCamera" class="px-2 minwidth-0 ml-3" color="grey darken-3" small dense><v-icon small>mdi-camera-retake</v-icon></v-btn>
</v-toolbar>
</template>
<v-card-text>
<v-row>
<v-col>
@@ -123,7 +119,7 @@
</v-row>
<input :accept="'.g,.gcode,.gc,.gco,.nc,.ngc,.tap'" @change="fileSelected" hidden multiple ref="fileInput" type="file" />
</v-card-text>
</v-card>
</panel>
<v-snackbar v-model="loading" :timeout="-1" :value="true" fixed right bottom dark>
<div>
{{ $t('GCodeViewer.Rendering') }} - {{ loadingPercent }}%<br />
@@ -158,6 +154,7 @@ import BaseMixin from '../mixins/base'
import GCodeViewer from '@sindarius/gcodeviewer'
import axios from 'axios'
import {formatFilesize} from '@/plugins/helpers'
import Panel from '@/components/ui/Panel.vue'
interface downloadSnackbar {
status: boolean
@@ -173,8 +170,9 @@ interface downloadSnackbar {
}
let viewer: any = null
@Component
@Component({
components: {Panel}
})
export default class Viewer extends Mixins(BaseMixin) {
formatFilesize = formatFilesize
private isBusy = false

View File

@@ -21,19 +21,20 @@
</style>
<template>
<v-card class="mb-6" v-if="klipperReadyForGui && ['standby', 'paused', 'complete', 'cancelled', 'error'].includes(printer_state)">
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-gamepad</v-icon>{{ $t('Panels.ControlPanel.Headline') }}</span>
</v-toolbar-title>
</v-toolbar>
<panel
v-if="klipperReadyForGui && ['standby', 'paused', 'complete', 'cancelled', 'error'].includes(printer_state)"
icon="mdi-gamepad"
:title="$t('Panels.ControlPanel.Headline')"
:collapsible="true"
card-class="control-panel"
>
<v-container>
<control-panel-cross-control v-if="controlStyle === 'cross'"></control-panel-cross-control>
<control-panel-circle-control v-else-if="controlStyle === 'circle'"></control-panel-circle-control>
<control-panel-bars-control v-else></control-panel-bars-control>
<control-panel-extruder v-if="existsExtruder"></control-panel-extruder>
</v-container>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -43,8 +44,11 @@ import ControlPanelExtruder from '@/components/panels/ControlPanelExtruder.vue'
import ControlPanelCrossControl from '@/components/panels/ControlPanelCrossControl.vue'
import ControlPanelBarsControl from '@/components/panels/ControlPanelBarsControl.vue'
import ControlPanelCircleControl from '@/components/panels/ControlPanelCircleControl.vue'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {ControlPanelCircleControl, ControlPanelBarsControl, ControlPanelCrossControl, ControlPanelExtruder}
components: {
Panel,
ControlPanelCircleControl, ControlPanelBarsControl, ControlPanelCrossControl, ControlPanelExtruder}
})
export default class ControlPanel extends Mixins(BaseMixin) {
get controlStyle() {

View File

@@ -19,19 +19,22 @@
.webcamContainer .webcamFpsOutput {
display: none;
}
.v-overlay {
top: 48px;
}
</style>
<template>
<v-card
:class="(!printer.socket.isConnected && !printer.socket.isConnecting ? 'disabledPrinter' : '')"
<panel
icon="mdi-printer-3d"
:title="printer_name"
:card-class="'farmprinter-panel '+(!printer.socket.isConnected && !printer.socket.isConnecting ? 'disabledPrinter' : '')"
:loading="printer.socket.isConnecting"
:toolbar-color="isCurrentPrinter ? 'primary' : ''"
>
<v-toolbar flat dense :color="isCurrentPrinter ? 'primary' : ''" style="z-index: 5;">
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-printer-3d</v-icon>{{ printer_name }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-item-group v-if="printer.socket.isConnected && this.printer_webcams.length">
<template v-slot:buttons>
<v-item-group v-if="printer.socket.isConnected && printer_webcams.length">
<v-menu :offset-y="true" title="Webcam">
<template v-slot:activator="{ on, attrs }">
<v-btn small class="px-2 minwidth-0" color="grey darken-3" v-bind="attrs" v-on="on">
@@ -48,7 +51,7 @@
<v-list-item-title>{{ $t('Panels.FarmPrinterPanel.WebcamOff') }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item v-for="webcam of this.printer_webcams" v-bind:key="webcam.index" link @click="currentCamName = webcam.name">
<v-list-item v-for="webcam of printer_webcams" v-bind:key="webcam.index" link @click="currentCamName = webcam.name">
<v-list-item-icon class="mr-0">
<v-icon small>{{ webcam.icon }}</v-icon>
</v-list-item-icon>
@@ -59,7 +62,7 @@
</v-list>
</v-menu>
</v-item-group>
</v-toolbar>
</template>
<v-hover>
<template v-slot:default="{ hover }">
<div>
@@ -112,7 +115,7 @@
</div>
</template>
</v-hover>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -122,9 +125,11 @@ import { FarmPrinterState } from '@/store/farm/printer/types'
import Mjpegstreamer from '@/components/webcams/Mjpegstreamer.vue'
import MjpegstreamerAdaptive from '@/components/webcams/MjpegstreamerAdaptive.vue'
import MainsailLogo from '@/components/ui/MainsailLogo.vue'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {
Panel,
'webcam-mjpegstreamer': Mjpegstreamer,
'webcam-mjpegstreamer-adaptive': MjpegstreamerAdaptive,
'mainsail-logo': MainsailLogo

View File

@@ -1,11 +1,10 @@
<template>
<div>
<v-card>
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading align-baseline"><v-icon left>mdi-file-document-multiple-outline</v-icon>{{ $t('History.PrintHistory') }}</span>
</v-toolbar-title>
</v-toolbar>
<panel
icon="mdi-file-document-multiple-outline"
:title="$t('History.PrintHistory')"
card-class="history-list-panel"
>
<v-card-text>
<v-row>
<v-col class="col-4 d-flex align-center">
@@ -126,7 +125,7 @@
</tr>
</template>
</v-data-table>
</v-card>
</panel>
<v-menu v-model="contextMenu.shown" :position-x="contextMenu.x" :position-y="contextMenu.y" absolute offset-y>
<v-list>
<v-list-item @click="clickRow(contextMenu.item)">
@@ -140,144 +139,142 @@
</v-list-item>
</v-list>
</v-menu>
<v-dialog v-model="detailsDialog.boolShow" :max-width="600" :max-height="500" scrollable>
<v-card dark>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-update</v-icon>{{ $t('History.JobDetails') }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn small class="minwidth-0" color="grey darken-3" @click="detailsDialog.boolShow = false"><v-icon small>mdi-close-thick</v-icon></v-btn>
</v-toolbar>
<v-card-text class="pt-5" style="height: 350px;">
<v-row>
<v-col>{{ $t('History.Filename') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.filename }}</v-col>
</v-row>
<template v-if="'metadata' in detailsDialog.item && 'size' in detailsDialog.item.metadata">
<v-dialog v-model="detailsDialog.boolShow" :max-width="600" persistent>
<panel :title="$t('History.JobDetails')" icon="mdi-update" card-class="history-detail-dialog" :margin-bottom="false">
<template v-slot:buttons>
<v-btn small class="minwidth-0 px-2" color="grey darken-3" @click="detailsDialog.boolShow = false"><v-icon small>mdi-close-thick</v-icon></v-btn>
</template>
<v-card-text class="px-0">
<perfect-scrollbar style="height: 350px;" :options="{ suppressScrollX: true }" class="px-6">
<v-row>
<v-col>{{ $t('History.Filename') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.filename }}</v-col>
</v-row>
<template v-if="'metadata' in detailsDialog.item && 'size' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.Filesize') }}</v-col>
<v-col class="text-right">{{ formatFilesize(detailsDialog.item.metadata.size) }}</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'modified' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.LastModified') }}</v-col>
<v-col class="text-right">{{ formatDate(detailsDialog.item.metadata.modified) }}</v-col>
</v-row>
</template>
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.Filesize') }}</v-col>
<v-col class="text-right">{{ formatFilesize(detailsDialog.item.metadata.size) }}</v-col>
<v-col>{{ $t('History.Status') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.status }}</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'modified' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.LastModified') }}</v-col>
<v-col class="text-right">{{ formatDate(detailsDialog.item.metadata.modified) }}</v-col>
<v-col>{{ $t('History.StartTime') }}</v-col>
<v-col class="text-right">{{ formatDate(detailsDialog.item.start_time) }}</v-col>
</v-row>
</template>
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.Status') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.status }}</v-col>
</v-row>
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.StartTime') }}</v-col>
<v-col class="text-right">{{ formatDate(detailsDialog.item.start_time) }}</v-col>
</v-row>
<template v-if="'end_time' in detailsDialog.item && detailsDialog.item.end_time > 0">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.EndTime') }}</v-col>
<v-col class="text-right">{{ formatDate(detailsDialog.item.end_time) }}</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'estimated_time' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.EstimatedTime') }}</v-col>
<v-col class="text-right">{{ formatPrintTime(detailsDialog.item.metadata.estimated_time) }}</v-col>
</v-row>
</template>
<template v-if="detailsDialog.item.print_duration > 0">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.PrintDuration') }}</v-col>
<v-col class="text-right">{{ formatPrintTime(detailsDialog.item.print_duration) }}</v-col>
</v-row>
</template>
<template v-if="detailsDialog.item.total_duration > 0">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.TotalDuration') }}</v-col>
<v-col class="text-right">{{ formatPrintTime(detailsDialog.item.total_duration) }}</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'filament_total' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.EstimatedFilamentWeight') }}</v-col>
<v-col class="text-right">{{ Math.round(detailsDialog.item.metadata.filament_weight_total*100)/100 }} g</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'filament_total' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.EstimatedFilament') }}</v-col>
<v-col class="text-right">{{ Math.round(detailsDialog.item.metadata.filament_total) }} mm</v-col>
</v-row>
</template>
<template v-if="detailsDialog.item.filament_used > 0">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.FilamentUsed') }}</v-col>
<v-col class="text-right">{{ Math.round(detailsDialog.item.filament_used) }} mm</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'first_layer_extr_temp' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.FirstLayerExtTemp') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.first_layer_extr_temp }} °C</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'first_layer_bed_temp' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.FirstLayerBedTemp') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.first_layer_bed_temp }} °C</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'first_layer_height' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.FirstLayerHeight') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.first_layer_height }} mm</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'layer_height' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.LayerHeight') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.layer_height }} mm</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'object_height' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.ObjectHeight') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.object_height }} mm</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'slicer' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.Slicer') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.slicer }}</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'slicer_version' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.SlicerVersion') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.slicer_version }}</v-col>
</v-row>
</template>
<template v-if="'end_time' in detailsDialog.item && detailsDialog.item.end_time > 0">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.EndTime') }}</v-col>
<v-col class="text-right">{{ formatDate(detailsDialog.item.end_time) }}</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'estimated_time' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.EstimatedTime') }}</v-col>
<v-col class="text-right">{{ formatPrintTime(detailsDialog.item.metadata.estimated_time) }}</v-col>
</v-row>
</template>
<template v-if="detailsDialog.item.print_duration > 0">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.PrintDuration') }}</v-col>
<v-col class="text-right">{{ formatPrintTime(detailsDialog.item.print_duration) }}</v-col>
</v-row>
</template>
<template v-if="detailsDialog.item.total_duration > 0">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.TotalDuration') }}</v-col>
<v-col class="text-right">{{ formatPrintTime(detailsDialog.item.total_duration) }}</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'filament_total' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.EstimatedFilamentWeight') }}</v-col>
<v-col class="text-right">{{ Math.round(detailsDialog.item.metadata.filament_weight_total*100)/100 }} g</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'filament_total' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.EstimatedFilament') }}</v-col>
<v-col class="text-right">{{ Math.round(detailsDialog.item.metadata.filament_total) }} mm</v-col>
</v-row>
</template>
<template v-if="detailsDialog.item.filament_used > 0">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.FilamentUsed') }}</v-col>
<v-col class="text-right">{{ Math.round(detailsDialog.item.filament_used) }} mm</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'first_layer_extr_temp' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.FirstLayerExtTemp') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.first_layer_extr_temp }} °C</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'first_layer_bed_temp' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.FirstLayerBedTemp') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.first_layer_bed_temp }} °C</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'first_layer_height' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.FirstLayerHeight') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.first_layer_height }} mm</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'layer_height' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.LayerHeight') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.layer_height }} mm</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'object_height' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.ObjectHeight') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.object_height }} mm</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'slicer' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.Slicer') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.slicer }}</v-col>
</v-row>
</template>
<template v-if="'metadata' in detailsDialog.item && 'slicer_version' in detailsDialog.item.metadata">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.SlicerVersion') }}</v-col>
<v-col class="text-right">{{ detailsDialog.item.metadata.slicer_version }}</v-col>
</v-row>
</template>
</perfect-scrollbar>
</v-card-text>
</v-card>
</panel>
</v-dialog>
</div>
</template>
@@ -289,8 +286,10 @@ import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import {ServerHistoryStateJob} from '@/store/server/history/types'
import {caseInsensitiveSort, formatFilesize} from '@/plugins/helpers'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class HistoryListPanel extends Mixins(BaseMixin) {
formatFilesize = formatFilesize

View File

@@ -0,0 +1,119 @@
<template>
<panel
icon="mdi-chart-areaspline"
:title="$t('History.Statistics')"
card-class="history-statistics-panel"
:collapsible="true"
>
<v-card-text class="pa-0">
<v-row align="center">
<v-col class="col-12 col-sm-6 col-md-4">
<v-simple-table>
<tbody>
<tr>
<td>{{ $t('History.TotalPrinttime') }}</td>
<td class="text-right">{{ formatPrintTime(totalPrintTime) }}</td>
</tr>
<tr>
<td>{{ $t('History.LongestPrinttime') }}</td>
<td class="text-right">{{ formatPrintTime(longestPrintTime) }}</td>
</tr>
<tr>
<td>{{ $t('History.AvgPrinttime') }}</td>
<td class="text-right">{{ formatPrintTime(avgPrintTime) }}</td>
</tr>
<tr>
<td>{{ $t('History.TotalFilamentUsed') }}</td>
<td class="text-right">{{ Math.round(totalFilamentUsed / 100) / 10 }} m</td>
</tr>
<tr>
<td>{{ $t('History.TotalJobs') }}</td>
<td class="text-right">{{ totalJobsCount }}</td>
</tr>
</tbody>
</v-simple-table>
</v-col>
<v-col class="col-12 col-sm-6 col-md-4">
<history-all-print-status></history-all-print-status>
</v-col>
<v-col class="col-12 col-sm-12 col-md-4">
<history-filament-usage v-if="toggleChart === 'filament_usage'"></history-filament-usage>
<history-printtime-avg v-if="toggleChart === 'printtime_avg'"></history-printtime-avg>
<div class="text-center mt-3">
<v-btn-toggle v-model="toggleChart" small mandatory>
<v-btn small value="filament_usage">
{{ $t('History.FilamentUsage') }}
</v-btn>
<v-btn small value="printtime_avg">
{{ $t('History.PrinttimeAvg') }}
</v-btn>
</v-btn-toggle>
</div>
</v-col>
</v-row>
</v-card-text>
</panel>
</template>
<script lang="ts">
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import Panel from '@/components/ui/Panel.vue'
import HistoryFilamentUsage from '@/components/charts/HistoryFilamentUsage.vue'
import HistoryPrinttimeAvg from '@/components/charts/HistoryPrinttimeAvg.vue'
import HistoryAllPrintStatus from '@/components/charts/HistoryAllPrintStatus.vue'
@Component({
components: {Panel,HistoryFilamentUsage, HistoryPrinttimeAvg, HistoryAllPrintStatus}
})
export default class HistoryStatisticsPanel extends Mixins(BaseMixin) {
get totalPrintTime() {
return 'total_print_time' in this.$store.state.server.history.job_totals ? this.$store.state.server.history.job_totals.total_print_time : 0
}
get longestPrintTime() {
return 'longest_print' in this.$store.state.server.history.job_totals ? this.$store.state.server.history.job_totals.longest_print : 0
}
get avgPrintTime() {
if (this.totalJobsCount > 0 && this.totalPrintTime > 0) return Math.round(this.totalPrintTime / this.totalJobsCount)
return 0
}
get totalFilamentUsed() {
return 'total_filament_used' in this.$store.state.server.history.job_totals ? this.$store.state.server.history.job_totals.total_filament_used : 0
}
get totalJobsCount() {
return 'total_jobs' in this.$store.state.server.history.job_totals ? this.$store.state.server.history.job_totals.total_jobs : 0
}
get toggleChart () {
return this.$store.state.gui.history.toggleChartCol3
}
set toggleChart(newVal) {
this.$store.dispatch('gui/saveSetting', { name: 'history.toggleChartCol3', value: newVal })
}
formatPrintTime(totalSeconds: number) {
if (totalSeconds) {
let output = ''
const hours = Math.floor(totalSeconds / 3600)
totalSeconds %= 3600
if (hours) output += ' '+hours+'h'
const minutes = Math.floor(totalSeconds / 60)
if (minutes) output += ' '+minutes+'m'
const seconds = totalSeconds % 60
if (seconds) output += ' '+seconds.toFixed(0)+'s'
return output
}
return '--'
}
}
</script>

View File

@@ -1,12 +1,12 @@
<template>
<v-card class="mb-6" v-if="klipperReadyForGui && warnings.length">
<v-toolbar flat dense color="orange darken-2">
<v-toolbar-title>
<span class="subheading">
<v-icon class="mdi mdi-alert-circle" left></v-icon>{{ $t("Panels.KlipperWarningsPanel.KlipperWarnings") }} ({{ warnings.length }})
</span>
</v-toolbar-title>
</v-toolbar>
<panel
v-if="klipperReadyForGui && warnings.length"
icon="mdi-alert-circle"
:title="$t('Panels.KlipperWarningsPanel.KlipperWarnings')+' ('+warnings.length+')'"
:collapsible="true"
card-class="klipper-warnings-panel"
toolbar-color="orange darken-2"
>
<v-card-text :class="index > 0 ? 'py-0' : 'pt-3 pb-0'" v-for="(warning, index) in warnings" v-bind:key="index">
<v-divider class="my-2" v-if="index"></v-divider>
<v-row>
@@ -24,7 +24,7 @@
<v-card-actions class="px-4 pt-2 pb-4 text-center text-lg-left">
<v-btn small :href="apiUrl+'/server/files/klipper.log'" target="_blank" color="primary" class=""><v-icon class="mr-2" small>mdi-download</v-icon>{{ $t("Panels.KlipperWarningsPanel.DownloadLog") }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -32,8 +32,10 @@ import Component from 'vue-class-component'
import BaseMixin from '../mixins/base'
import {Mixins} from 'vue-property-decorator'
import {caseInsensitiveSort} from '@/plugins/helpers'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class KlipperWarningsPanelPanel extends Mixins(BaseMixin) {
get warnings() {

View File

@@ -1,10 +1,10 @@
<template>
<v-card v-if="klipperState !== 'ready' && socketIsConnected" class="mb-6">
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-alert-circle</v-icon>{{ $t('Panels.KlippyStatePanel.KlippyState')}}: {{ klipperState }}</span>
</v-toolbar-title>
</v-toolbar>
<panel
v-if="klipperState !== 'ready' && socketIsConnected"
icon="mdi-alert-circle"
:title="$t('Panels.KlippyStatePanel.KlippyState')+': '+klipperState"
card-class="klippy-state-panel"
>
<template v-if="klippyIsConnected">
<v-card-text class="py-1">
<pre style="white-space: pre-wrap;">{{ klippy_message }}</pre>
@@ -23,7 +23,7 @@
<p class="mt-2">{{ $t('Panels.KlippyStatePanel.KlipperCheck') }}</p>
</v-card-text>
</template>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -31,9 +31,10 @@ import Component from 'vue-class-component'
import {Mixins, Watch} from 'vue-property-decorator'
import BaseMixin from '../mixins/base'
import ConnectionStatus from '../ui/ConnectionStatus.vue'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {ConnectionStatus}
components: {Panel, ConnectionStatus}
})
export default class KlippyStatePanel extends Mixins(BaseMixin) {
private timer: number | null = null

View File

@@ -4,12 +4,7 @@
<template>
<div>
<v-card>
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading align-baseline"><v-icon left>mdi-information</v-icon>{{ $t('Machine.ConfigFilesPanel.ConfigFiles') }}</span>
</v-toolbar-title>
</v-toolbar>
<panel :title="$t('Machine.ConfigFilesPanel.ConfigFiles')" card-class="machine-configfiles-panel" icon="mdi-information">
<v-card-text>
<v-row>
<v-col class="col-12 col-lg pr-lg-0">
@@ -134,7 +129,7 @@
</tr>
</template>
</v-data-table>
</v-card>
</panel>
<v-menu v-model="contextMenu.shown" :position-x="contextMenu.x" :position-y="contextMenu.y" absolute offset-y>
<v-list>
<v-list-item @click="clickRow(contextMenu.item, true)" v-if="!contextMenu.item.isDirectory">
@@ -157,32 +152,21 @@
</v-list-item>
</v-list>
</v-menu>
<!-- <v-dialog v-model="editor.showLoader" hide-overlay persistent width="300">
<v-card color="primary" dark >
<v-card-text>
{{ $t('Machine.ConfigFilesPanel.PleaseStandBy') }}
<v-progress-linear indeterminate color="white" class="mb-0" ></v-progress-linear>
</v-card-text>
</v-card>
</v-dialog>-->
<v-dialog v-model="dialogImage.show" hide-overlay fullscreen @keydown.esc="dialogImage.show = false; dialogImage.item.url = null; dialogImage.item.svg = null;" class="fill-height">
<v-card style="position: relative;">
<v-toolbar dark color="primary">
<v-btn icon dark @click="dialogImage.show = false; dialogImage.item.url = null; dialogImage.item.svg = null;">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<div class="d-flex justify-center" style="max-height: calc(100vh - 64px); overflow: auto;">
<img v-if="dialogImage.item.url" :src="dialogImage.item.url" style="max-height: 100%; width: auto;" alt="image" />
<div v-else-if="dialogImage.item.svg"
class="fill-width"
v-html="dialogImage.item.svg"></div>
</div>
</v-card>
<panel :title="dialogImage.item.name" card-class="maschine-configfiles-imageviewer-dialog" toolbar-color="primary" style="position: relative;">
<template v-slot:buttons-left>
<v-btn icon dark @click="dialogImage.show = false; dialogImage.item.url = null; dialogImage.item.svg = null;">
<v-icon>mdi-close</v-icon>
</v-btn>
</template>
<div class="d-flex justify-center" style="max-height: calc(100vh - 64px); overflow: auto;">
<img v-if="dialogImage.item.url" :src="dialogImage.item.url" style="max-height: 100%; width: auto;" alt="image" />
<div v-else-if="dialogImage.item.svg" class="fill-width" v-html="dialogImage.item.svg"></div>
</div>
</panel>
</v-dialog>
<v-dialog v-model="dialogRenameFile.show" max-width="400">
<v-card>
<v-card-title class="headline">{{ $t('Machine.ConfigFilesPanel.RenameFile') }}</v-card-title>
<panel :title="$t('Machine.ConfigFilesPanel.RenameFile')" card-class="maschine-configfiles-rename-file-dialog" :margin-bottom="false">
<v-card-text>
<v-text-field :label="$t('Machine.ConfigFilesPanel.Name')" required v-model="dialogRenameFile.newName"></v-text-field>
</v-card-text>
@@ -191,11 +175,10 @@
<v-btn color="" text @click="dialogRenameFile.show = false">{{ $t('Machine.ConfigFilesPanel.Cancel') }}</v-btn>
<v-btn color="primary" text @click="renameFileAction">{{ $t('Machine.ConfigFilesPanel.Rename') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="dialogCreateFile.show" max-width="400">
<v-card>
<v-card-title class="headline">{{ $t('Machine.ConfigFilesPanel.CreateFile') }}</v-card-title>
<panel :title="$t('Machine.ConfigFilesPanel.CreateFile')" card-class="maschine-configfiles-create-file-dialog" :margin-bottom="false">
<v-card-text>
<v-text-field :label="$t('Machine.ConfigFilesPanel.Name')" required v-model="dialogCreateFile.name"></v-text-field>
</v-card-text>
@@ -204,11 +187,10 @@
<v-btn color="" text @click="dialogCreateFile.show = false">{{ $t('Machine.ConfigFilesPanel.Cancel') }}</v-btn>
<v-btn color="primary" text @click="createFileAction">{{ $t('Machine.ConfigFilesPanel.Create') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="dialogCreateDirectory.show" max-width="400">
<v-card>
<v-card-title class="headline">{{ $t('Machine.ConfigFilesPanel.CreateDirectory') }}</v-card-title>
<panel :title="$t('Machine.ConfigFilesPanel.CreateDirectory')" card-class="maschine-configfiles-create-directory-dialog" :margin-bottom="false">
<v-card-text>
<v-text-field :label="$t('Machine.ConfigFilesPanel.Name')" required v-model="dialogCreateDirectory.name"></v-text-field>
</v-card-text>
@@ -217,11 +199,10 @@
<v-btn color="" text @click="dialogCreateDirectory.show = false">{{ $t('Machine.ConfigFilesPanel.Cancel') }}</v-btn>
<v-btn color="primary" text @click="createDirectoryAction">{{ $t('Machine.ConfigFilesPanel.Create') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="dialogRenameDirectory.show" max-width="400">
<v-card>
<v-card-title class="headline">{{ $t('Machine.ConfigFilesPanel.RenameDirectory') }}</v-card-title>
<panel :title="$t('Machine.ConfigFilesPanel.RenameDirectory')" card-class="maschine-configfiles-rename-directory-dialog" :margin-bottom="false">
<v-card-text>
<v-text-field :label="$t('Machine.ConfigFilesPanel.Name')" required v-model="dialogRenameDirectory.newName"></v-text-field>
</v-card-text>
@@ -230,11 +211,10 @@
<v-btn color="" text @click="dialogRenameDirectory.show = false">{{ $t('Machine.ConfigFilesPanel.Cancel') }}</v-btn>
<v-btn color="primary" text @click="renameDirectoryAction">{{ $t('Machine.ConfigFilesPanel.Rename') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="dialogDeleteDirectory.show" max-width="400">
<v-card>
<v-card-title class="headline">{{ $t('Machine.ConfigFilesPanel.DeleteDirectory') }}</v-card-title>
<panel :title="$t('Machine.ConfigFilesPanel.DeleteDirectory')" card-class="maschine-configfiles-delete-directory-dialog" :margin-bottom="false">
<v-card-text>
<p class="mb-0">{{ $t('Machine.ConfigFilesPanel.DeleteDirectoryQuestion', { name: dialogDeleteDirectory.item.filename } )}}</p>
</v-card-text>
@@ -243,7 +223,7 @@
<v-btn color="" text @click="dialogDeleteDirectory.show = false">{{ $t('Machine.ConfigFilesPanel.Cancel') }}</v-btn>
<v-btn color="error" text @click="deleteDirectoryAction">{{ $t('Machine.ConfigFilesPanel.Delete') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-snackbar v-model="uploadSnackbar.status" :timeout="-1" :value="true" fixed right bottom dark>
<span v-if="uploadSnackbar.max > 1" class="mr-1">({{ uploadSnackbar.number }}/{{ uploadSnackbar.max }})</span><strong>{{ $t('Machine.ConfigFilesPanel.Uploading') }} {{ uploadSnackbar.filename }}</strong><br />
@@ -272,6 +252,7 @@ import {readOnlyRoots} from '@/store/variables'
import {findDirectory, formatDate, formatFilesize, sortFiles} from '@/plugins/helpers'
import {FileStateFile} from '@/store/files/types'
import axios from 'axios'
import Panel from '@/components/ui/Panel.vue'
interface contextMenu {
shown: boolean
@@ -286,6 +267,7 @@ interface contextMenu {
interface dialogImageObject {
show: boolean
item: {
name: string | null,
url: string | null,
svg: string | null
}
@@ -322,7 +304,9 @@ interface draggingFile {
item: FileStateFile
}
@Component
@Component({
components: {Panel}
})
export default class ConfigFilesPanel extends Mixins(BaseMixin) {
sortFiles = sortFiles
formatFilesize = formatFilesize
@@ -353,6 +337,7 @@ export default class ConfigFilesPanel extends Mixins(BaseMixin) {
private dialogImage: dialogImageObject = {
show: false,
item: {
name: null,
url: null,
svg: null
}
@@ -551,6 +536,7 @@ export default class ConfigFilesPanel extends Mixins(BaseMixin) {
if (!item.isDirectory) {
if (['png', 'jpeg', 'jpg', 'gif', 'bmp', 'tif', 'svg'].includes(item.filename.split('.').pop()?.toLowerCase() ?? '')) {
const url = `${this.apiUrl}/server/files${this.absolutePath}/${item.filename}?t=${Date.now()}`
this.dialogImage.item.name = item.filename
if (['svg'].includes(item.filename.split('.').pop()?.toLowerCase() ?? '')) {
fetch(url)
.then(res => res.text())

View File

@@ -1,10 +1,5 @@
<template>
<v-card>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-arrow-expand-vertical</v-icon>{{ $t('Machine.EndstopPanel.Endstops')}}</span>
</v-toolbar-title>
</v-toolbar>
<panel :title="$t('Machine.EndstopPanel.Endstops')" icon="mdi-arrow-expand-vertical" card-class="machine-endstop-panel" :collapsible="true">
<v-card-text class="pb-0">
<v-container px-0 py-0>
<template v-if="Object.keys(endstops).length">
@@ -49,16 +44,18 @@
<v-btn icon @click="syncEndstops" :loading="loadings.includes('queryEndstops')">
<v-icon>mdi-sync</v-icon>
</v-btn>
</v-card-actions>
</v-card>
</v-card-actions>
</panel>
</template>
<script lang="ts">
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '../../mixins/base'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class EndstopPanel extends Mixins(BaseMixin) {
public sortEndstops: any = {}

View File

@@ -1,10 +1,5 @@
<template>
<v-card class="mb-6" v-if="klipperReadyForGui">
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-speedometer</v-icon>{{ $t('Machine.LimitsPanel.MachineLimits')}}</span>
</v-toolbar-title>
</v-toolbar>
<panel :title="$t('Machine.LimitsPanel.MachineLimits')" v-if="klipperReadyForGui" icon="mdi-speedometer" card-class="machine-machinelimits-panel" :collapsible="true">
<v-card-text>
<v-row>
<v-col class="col-6">
@@ -51,7 +46,7 @@
</v-col>
</v-row>
</v-card-text>
</v-card>
</panel>
</template>
<script>
@@ -61,8 +56,9 @@ import { Component, Mixins } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base.ts'
import ToolSlider from '@/components/inputs/ToolSlider'
import MachineLimitsInput from '@/components/inputs/MachineLimitsInput'
import Panel from '@/components/ui/Panel'
@Component({
components: {MachineLimitsInput, ToolSlider}
components: {Panel, MachineLimitsInput, ToolSlider}
})
export default class LimitsPanel extends Mixins(BaseMixin) {

View File

@@ -1,10 +1,5 @@
<template>
<v-card>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-file-document-edit</v-icon>{{ $t("Machine.LogfilesPanel.Logfiles")}}</span>
</v-toolbar-title>
</v-toolbar>
<panel :title="$t('Machine.LogfilesPanel.Logfiles')" icon="mdi-file-document-edit" card-class="machine-logfiles-panel" :collapsible="true">
<v-card-text :class="'text-center text-lg-left py-0'">
<v-container pb-0 px-0>
<v-row>
@@ -17,15 +12,17 @@
</v-row>
</v-container>
</v-card-text>
</v-card>
</panel>
</template>
<script lang="ts">
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '../../mixins/base'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class LogfilesPanel extends Mixins(BaseMixin) {
downloadLog(event: any) {
event.preventDefault()

View File

@@ -6,12 +6,7 @@
<template>
<div>
<v-card class="mb-6">
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-memory</v-icon>{{ $t('Machine.SystemPanel.SystemLoad') }}</span>
</v-toolbar-title>
</v-toolbar>
<panel :title="$t('Machine.SystemPanel.SystemLoad')" icon="mdi-memory" card-class="machine-systemload-panel" :collapsible="true">
<v-card-text class="px-0 py-2">
<div v-for="(mcu, index) of mcus" v-bind:key="mcu.name">
<v-divider class="my-2" v-if="index" ></v-divider>
@@ -101,77 +96,73 @@
</v-row>
</div>
</v-card-text>
</v-card>
</panel>
<v-dialog v-model="mcuDetailsDialog.bool" :max-width="400" :max-height="500" scrollable>
<v-card dark>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-text-box-search-outline</v-icon>{{ mcuDetailsDialog.headline }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<panel :title="mcuDetailsDialog.headline" icon="mdi-text-box-search-outline" card-class="machine-systemload-mcu-details-dialog" :margin-bottom="false">
<template v-slot:buttons>
<v-btn small class="minwidth-0 px-2" color="grey darken-3" @click="mcuDetailsDialog.bool = false"><v-icon small>mdi-close-thick</v-icon></v-btn>
</v-toolbar>
<v-card-text class="pt-5" style="height: 350px;">
<template v-if="mcuDetailsDialog.mcu.mcu_constants">
<v-row>
<v-col><span class="headline">{{ $t('Machine.SystemPanel.Constants') }}</span></v-col>
</v-row>
<div v-for="(value, key, index) in mcuDetailsDialog.mcu.mcu_constants" :key="key">
<v-divider class="my-3" v-if="index"></v-divider>
</template>
<v-card-text class="pt-5 px-0">
<perfect-scrollbar style="height: 350px;" :options="{ suppressScrollX: true }" class="px-6">
<template v-if="mcuDetailsDialog.mcu.mcu_constants">
<v-row>
<v-col>{{ key }}</v-col>
<v-col class="text-right">{{ value }}</v-col>
<v-col><span class="headline">{{ $t('Machine.SystemPanel.Constants') }}</span></v-col>
</v-row>
</div>
</template>
<template v-if="mcuDetailsDialog.mcu.last_stats">
<v-row class="mt-5">
<v-col><span class="headline">{{ $t('Machine.SystemPanel.LastStats') }}</span></v-col>
</v-row>
<div v-for="(value, key, index) in mcuDetailsDialog.mcu.last_stats" :key="key">
<v-divider class="my-3" v-if="index"></v-divider>
<v-row>
<v-col>{{ key }}</v-col>
<v-col class="text-right">{{ value }}</v-col>
<div v-for="(value, key, index) in mcuDetailsDialog.mcu.mcu_constants" :key="key">
<v-divider class="my-3" v-if="index"></v-divider>
<v-row>
<v-col>{{ key }}</v-col>
<v-col class="text-right">{{ value }}</v-col>
</v-row>
</div>
</template>
<template v-if="mcuDetailsDialog.mcu.last_stats">
<v-row class="mt-5">
<v-col><span class="headline">{{ $t('Machine.SystemPanel.LastStats') }}</span></v-col>
</v-row>
</div>
</template>
<div v-for="(value, key, index) in mcuDetailsDialog.mcu.last_stats" :key="key">
<v-divider class="my-3" v-if="index"></v-divider>
<v-row>
<v-col>{{ key }}</v-col>
<v-col class="text-right">{{ value }}</v-col>
</v-row>
</div>
</template>
</perfect-scrollbar>
</v-card-text>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="hostDetailsDialog.bool" :max-width="600" :max-height="500" scrollable>
<v-card dark>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-text-box-search-outline</v-icon>{{ $t('Machine.SystemPanel.HostDetails') }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<panel :title="$t('Machine.SystemPanel.HostDetails')" icon="mdi-text-box-search-outline" card-class="machine-systemload-host-details-dialog" :margin-bottom="false">
<template v-slot:buttons>
<v-btn small class="minwidth-0 px-2" color="grey darken-3" @click="hostDetailsDialog.bool = false"><v-icon small>mdi-close-thick</v-icon></v-btn>
</v-toolbar>
<v-card-text class="pt-5" style="height: 350px;">
<template v-if="Object.keys(systemInfo).length">
<div v-for="(infoGroup, key, index) of systemInfo" v-bind:key="key">
<template v-if="key !== 'available_services'">
<v-row :class="index ? 'mt-5' : ''">
<v-col><span class="headline">{{ key }}</span></v-col>
</v-row>
<div v-for="(value, key, index) in infoGroup" :key="key">
<v-divider class="my-3" v-if="index"></v-divider>
<v-row>
<v-col>{{ key }}</v-col>
<v-col class="text-right">{{ value }}</v-col>
</template>
<v-card-text class="pt-5 px-0">
<perfect-scrollbar style="height: 350px;" :options="{ suppressScrollX: true }" class="px-6">
<template v-if="Object.keys(systemInfo).length">
<div v-for="(infoGroup, key, index) of systemInfo" v-bind:key="key">
<template v-if="key !== 'available_services'">
<v-row :class="index ? 'mt-5' : ''">
<v-col><span class="headline">{{ key }}</span></v-col>
</v-row>
</div>
</template>
</div>
</template>
<template v-else>
<v-row class="mt-5">
<v-col><p>No more Infos</p></v-col>
</v-row>
</template>
<div v-for="(value, key, index) in infoGroup" :key="key">
<v-divider class="my-3" v-if="index"></v-divider>
<v-row>
<v-col>{{ key }}</v-col>
<v-col class="text-right">{{ value }}</v-col>
</v-row>
</div>
</template>
</div>
</template>
<template v-else>
<v-row class="mt-5">
<v-col><p>No more Infos</p></v-col>
</v-row>
</template>
</perfect-scrollbar>
</v-card-text>
</v-card>
</panel>
</v-dialog>
</div>
</template>
@@ -180,8 +171,10 @@
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '../../mixins/base'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class SystemPanel extends Mixins(BaseMixin) {
private mcuDetailsDialog: { bool: boolean, headline: string, mcu: any } = {

View File

@@ -5,23 +5,18 @@
</style>
<template>
<div v-if="enableUpdateManager">
<v-card>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-update</v-icon>{{ $t('Machine.UpdatePanel.UpdateManager') }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<div>
<panel :title="$t('Machine.UpdatePanel.UpdateManager')" v-if="enableUpdateManager" icon="mdi-update" card-class="machine-update-panel" :collapsible="true">
<template v-slot:buttons>
<v-tooltip top>
<template v-slot:activator="{ on, attrs }">
<v-btn small class="px-2 minwidth-0" color="primary" :loading="loadings.includes('loadingBtnSyncUpdateManager')" :disabled="['printing', 'paused'].includes(printer_state)" @click="btnSync" v-bind="attrs" v-on="on"><v-icon small>mdi-refresh</v-icon></v-btn>
</template>
<span>{{ $t('Machine.UpdatePanel.CheckForUpdates') }}</span>
</v-tooltip>
</v-toolbar>
</template>
<v-card-text class="px-0 py-0">
<v-container py-0 px-0>
<div v-for="(value, key, index) of updateableSoftwares" v-bind:key="key">
<v-divider class="my-0" v-if="index" ></v-divider>
<v-row class="py-2">
@@ -111,16 +106,12 @@
</div>
</v-container>
</v-card-text>
</v-card>
</panel>
<v-dialog v-model="commitsOverlay.bool" persistent max-width="800">
<v-card dark>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-update</v-icon>{{ $t('Machine.UpdatePanel.Commits') }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<panel :title="$t('Machine.UpdatePanel.Commits')" icon="mdi-update" :margin-bottom="false" card-class="machine-update-commits-dialog">
<template v-slot:buttons>
<v-btn small class="minwidth-0 px-2" color="grey darken-3" @click="commitsOverlay.bool = false"><v-icon small>mdi-close-thick</v-icon></v-btn>
</v-toolbar>
</template>
<v-card-text class="py-0 px-0">
<perfect-scrollbar style="max-height: 400px;" :options="{ suppressScrollX: true }">
<v-row>
@@ -142,7 +133,7 @@
</v-row>
</perfect-scrollbar>
</v-card-text>
</v-card>
</panel>
</v-dialog>
</div>
</template>
@@ -153,8 +144,10 @@
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '../../mixins/base'
import semver from 'semver'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class UpdatePanel extends Mixins(BaseMixin) {
private commitsOverlay = {

View File

@@ -3,12 +3,13 @@
</style>
<template>
<v-card class="mb-6" v-if="klipperReadyForGui && macros.length > 0">
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-code-tags</v-icon>{{ $t('Panels.MacrosPanel.Headline') }}</span>
</v-toolbar-title>
</v-toolbar>
<panel
v-if="klipperReadyForGui && macros.length > 0"
icon="mdi-code-tags"
:title="$t('Panels.MacrosPanel.Headline')"
:collapsible="true"
card-class="macros-panel"
>
<v-container>
<v-row no-gutters v-if="macros.length">
<v-col class="text-center mr-fix-2 mb-fix-2">
@@ -22,14 +23,16 @@
</v-col>
</v-row>
</v-container>
</v-card>
</panel>
</template>
<script lang="ts">
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '../mixins/base'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class MacrosPanel extends Mixins(BaseMixin) {
get macros() {

View File

@@ -1,12 +1,12 @@
<template>
<v-card class="mb-6" v-if="klipperState === 'ready' && existsPrinterConfig && missingConfigs.length">
<v-toolbar flat dense color="orange darken-2">
<v-toolbar-title>
<span class="subheading">
<v-icon class="mdi mdi-alert-circle" left></v-icon>{{ $t("Panels.MinSettingsPanel.MissingConfiguration") }}
</span>
</v-toolbar-title>
</v-toolbar>
<panel
v-if="klipperState === 'ready' && existsPrinterConfig && missingConfigs.length"
icon="mdi-alert-circle"
:title="$t('Panels.MinSettingsPanel.MissingConfiguration')"
:collapsible="true"
card-class="min-settings-panel"
toolbar-color="orange darken-2"
>
<v-card-text>
<v-row>
<v-col>
@@ -32,7 +32,7 @@
<v-card-actions class="justify-center pb-3">
<v-btn href="https://docs.mainsail.xyz/necessary-configuration" target="_blank" color="white" outlined small><v-icon small class="mr-1">mdi-information</v-icon>{{ $t("Panels.MinSettingsPanel.MoreInformation") }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -40,8 +40,10 @@
import Component from 'vue-class-component'
import {Mixins} from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class MinSettingsPanel extends Mixins(BaseMixin) {
get existsPrinterConfig() {

View File

@@ -6,12 +6,13 @@
</style>
<template>
<v-card v-if="socketIsConnected" class="mb-6 d-flex flex-column">
<v-toolbar flat dense class="order-0">
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-console-line</v-icon>{{ $t("Panels.MiniconsolePanel.Headline") }}</span>
</v-toolbar-title>
</v-toolbar>
<panel
v-if="socketIsConnected"
icon="mdi-console-line"
:title="$t('Panels.MiniconsolePanel.Headline')"
:collapsible="true"
card-class="miniconsole-panel"
>
<v-card-text :class="consoleDirection === 'table' ? 'order-1' : 'order-2'">
<v-row>
<v-col>
@@ -69,7 +70,7 @@
</v-col>
</v-row>
</v-card-text>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -80,9 +81,11 @@ import {CommandHelp, VTextareaType} from '@/store/printer/types'
import ConsoleTable from '@/components/console/ConsoleTable.vue'
import CommandHelpModal from '@/components/CommandHelpModal.vue'
import Vue from 'vue'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {
Panel,
ConsoleTable,
CommandHelpModal
}

View File

@@ -3,12 +3,13 @@
</style>
<template>
<v-card class="mb-6" v-if="klipperReadyForGui && (miscellaneous.length || filamentSensors.length)">
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-dip-switch</v-icon>{{ $t("Panels.MiscellaneousPanel.Headline") }}</span>
</v-toolbar-title>
</v-toolbar>
<panel
v-if="klipperReadyForGui && (miscellaneous.length || filamentSensors.length)"
icon="mdi-dip-switch"
:title="$t('Panels.MiscellaneousPanel.Headline')"
:collapsible="true"
card-class="miscellaneous-panel"
>
<div v-for="(object, index) of miscellaneous" v-bind:key="index">
<v-divider v-if="index"></v-divider>
<miscellaneous-slider
@@ -31,7 +32,7 @@
:filament_detected="sensor.filament_detected"
></filament-sensor>
</div>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -40,8 +41,9 @@ import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import MiscellaneousSlider from '@/components/inputs/MiscellaneousSlider.vue'
import FilamentSensor from '@/components/inputs/FilamentSensor.vue'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {FilamentSensor, MiscellaneousSlider}
components: {Panel, FilamentSensor, MiscellaneousSlider}
})
export default class MiscellaneousPanel extends Mixins(BaseMixin) {
get miscellaneous() {

View File

@@ -1,12 +1,12 @@
<template>
<v-card class="mb-6" v-if="failedComponents.length || warnings.length">
<v-toolbar flat dense color="orange darken-2">
<v-toolbar-title>
<span class="subheading">
<v-icon class="mdi mdi-alert-circle" left></v-icon>{{ $t("Panels.MoonrakerStatePanel.MoonrakerWarnings") }}
</span>
</v-toolbar-title>
</v-toolbar>
<panel
v-if="failedComponents.length || warnings.length"
icon="mdi-alert-circle"
:title="$t('Panels.MoonrakerStatePanel.MoonrakerWarnings')"
:collapsible="true"
card-class="moonraker-state-panel"
toolbar-color="orange darken-2"
>
<v-card-text v-if="failedComponents.length">
<v-row>
<v-col>
@@ -27,15 +27,17 @@
<v-card-actions class="px-4 pt-2 pb-4 text-center text-lg-left">
<v-btn small :href="apiUrl+'/server/files/moonraker.log'" target="_blank" color="primary" class=""><v-icon class="mr-2" small>mdi-download</v-icon>{{ $t("Panels.MoonrakerStatePanel.DownloadLog") }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</template>
<script lang="ts">
import Component from 'vue-class-component'
import BaseMixin from '../mixins/base'
import {Mixins} from 'vue-property-decorator'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class MoonrakerStatePanel extends Mixins(BaseMixin) {
get failedComponents() {

View File

@@ -3,18 +3,19 @@
</style>
<template>
<v-card class="mb-6" v-if="klipperReadyForGui && ['printing', 'paused'].includes(printer_state)">
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon class="mdi mdi-printer-3d" left></v-icon>{{ $t("Panels.PrintsettingsPanel.Headline") }}</span>
</v-toolbar-title>
</v-toolbar>
<panel
v-if="klipperReadyForGui && ['printing', 'paused'].includes(printer_state)"
icon="mdi-printer-3d"
:title="$t('Panels.PrintsettingsPanel.Headline')"
:collapsible="true"
card-class="printsettings-panel"
>
<tool-slider :label="$t('Panels.PrintsettingsPanel.SpeedFactor')" :target="speed_factor" :max="200" :multi="100" :step="5" :dynamic-range="true" command="M220" attribute-name="S" ></tool-slider>
<template v-if="existsExtruder">
<v-divider></v-divider>
<tool-slider :label="$t('Panels.PrintsettingsPanel.ExtrusionFactor')" :target="extrude_factor" :max="200" :multi="100" :step="1" command="M221" attribute-name="S" ></tool-slider>
</template>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -22,8 +23,9 @@
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import ToolSlider from '@/components/inputs/ToolSlider.vue'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {ToolSlider}
components: {Panel, ToolSlider}
})
export default class PrintsettingsPanel extends Mixins(BaseMixin) {

View File

@@ -10,49 +10,43 @@
<moonraker-state-panel></moonraker-state-panel>
<klippy-state-panel></klippy-state-panel>
<klipper-warnings-panel></klipper-warnings-panel>
<v-card v-if="klipperState === 'ready'" class="mb-6">
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading align-baseline">
<v-progress-circular
:rotate="-90"
:size="30"
:width="5"
:value="printPercent"
v-if="['paused', 'printing'].includes(printer_state)"
color="primary"
class="mr-1"
>
</v-progress-circular>
<v-icon
v-if="!['paused', 'printing'].includes(printer_state)"
left
>
mdi-information
</v-icon>
{{ printerStateOutput }}
</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<template >
<v-btn
v-for="button in filteredToolbarButtons"
v-bind:key="button.loadingName"
class="px-2 minwidth-0 ml-3"
:color="button.color"
@click="button.click"
:loading="loadings.includes(button.loadingName)"
small
>
<v-tooltip top>
<template v-slot:activator="{ on, attrs }">
<v-icon v-bind="attrs" v-on="on" small>{{ button.icon }}</v-icon>
</template>
<span>{{ button.text }}</span>
</v-tooltip>
</v-btn>
</template>
</v-toolbar>
<panel
v-if="klipperState === 'ready'"
icon="mdi-information"
:title="printerStateOutput"
:collapsible="true"
card-class="status-panel"
>
<template v-slot:icon>
<v-progress-circular
:rotate="-90"
:size="30"
:width="5"
:value="printPercent"
color="primary"
class="mr-3"
v-if="['paused', 'printing'].includes(printer_state)"
>
</v-progress-circular>
</template>
<template v-slot:buttons >
<v-btn
v-for="button in filteredToolbarButtons"
v-bind:key="button.loadingName"
class="px-2 minwidth-0 ml-3"
:color="button.color"
@click="button.click"
:loading="loadings.includes(button.loadingName)"
small
>
<v-tooltip top>
<template v-slot:activator="{ on, attrs }">
<v-icon v-bind="attrs" v-on="on" small>{{ button.icon }}</v-icon>
</template>
<span>{{ button.text }}</span>
</v-tooltip>
</v-btn>
</template>
<v-card-text class="px-0 py-0 content">
<template v-if="boolBigThumbnail">
<v-img
@@ -270,7 +264,7 @@
</v-container>
</template>
</v-card-text>
</v-card>
</panel>
</div>
</template>
@@ -283,9 +277,11 @@ import MoonrakerStatePanel from '@/components/panels/MoonrakerStatePanel.vue'
import KlippyStatePanel from '@/components/panels/KlippyStatePanel.vue'
import KlipperWarningsPanel from '@/components/panels/KlipperWarningsPanel.vue'
import StatusPanelExcludeObject from '@/components/panels/StatusPanelExcludeObject.vue'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {
Panel,
StatusPanelExcludeObject, KlipperWarningsPanel, KlippyStatePanel, MoonrakerStatePanel, MinSettingsPanel}
})
export default class StatusPanel extends Mixins(BaseMixin) {

View File

@@ -23,12 +23,14 @@
</style>
<template>
<v-card v-if="klipperReadyForGui" class="mb-6">
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-thermometer-lines</v-icon>{{ $t("Panels.ToolsPanel.Headline") }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<panel
v-if="klipperReadyForGui"
icon="mdi-thermometer-lines"
:title="$t('Panels.ToolsPanel.Headline')"
:collapsible="true"
card-class="tools-panel"
>
<template v-slot:buttons>
<v-menu :offset-y="true" title="Preheat" v-if="presets.length">
<template v-slot:activator="{ on, attrs }">
<v-btn small class="px-2 minwidth-0" color="primary" v-bind="attrs" v-on="on" :disabled="['printing', 'paused'].includes(printer_state)">{{ $t("Panels.ToolsPanel.Presets") }} <v-icon small>mdi-menu-down</v-icon></v-btn>
@@ -69,7 +71,7 @@
</v-list-item>
</v-list>
</v-menu>
</v-toolbar>
</template>
<v-card-text class="pa-0 content">
<v-container class="px-0">
<v-row align="center">
@@ -243,7 +245,7 @@
</v-card-text>
</v-card>
</v-dialog>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -257,9 +259,10 @@ import TempChart from '@/components/charts/TempChart.vue'
import {datasetTypes} from '@/store/variables'
import {PrinterStateHeater, PrinterStateSensor, PrinterStateTemperatureFan} from '@/store/printer/types'
import {Debounce} from 'vue-debounce-decorator'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {TempChart, ToolInput}
components: {Panel, TempChart, ToolInput}
})
export default class ToolsPanel extends Mixins(BaseMixin) {
convertName = convertName

View File

@@ -3,15 +3,15 @@
</style>
<template>
<v-card class="mb-6" v-if="socketIsConnected">
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading">
<v-icon left>mdi-webcam</v-icon> {{ $t('Panels.WebcamPanel.Headline')}}
</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-item-group v-if="this.webcams.length > 1">
<panel
v-if="socketIsConnected"
icon="mdi-webcam"
:title="$t('Panels.WebcamPanel.Headline')"
:collapsible="true"
card-class="webcam-panel"
>
<template v-slot:buttons>
<v-item-group v-if="webcams.length > 1">
<v-menu :offset-y="true" title="Webcam">
<template v-slot:activator="{ on, attrs }">
<v-btn small class="px-2 minwidth-0" color="primary" v-bind="attrs" v-on="on">
@@ -29,7 +29,7 @@
<v-list-item-title>{{ $t('Panels.WebcamPanel.All') }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item v-for="webcam of this.webcams" v-bind:key="webcam.name" link @click="currentCamName = webcam.name">
<v-list-item v-for="webcam of webcams" v-bind:key="webcam.name" link @click="currentCamName = webcam.name">
<v-list-item-icon class="mr-0">
<v-icon small>{{ webcam.icon }}</v-icon>
</v-list-item-icon>
@@ -40,7 +40,7 @@
</v-list>
</v-menu>
</v-item-group>
</v-toolbar>
</template>
<v-card-text class="px-0 py-0 content d-inline-block">
<v-row>
<v-col class="pb-0" style="position: relative;">
@@ -65,7 +65,7 @@
</v-col>
</v-row>
</v-card-text>
</v-card>
</panel>
</template>
<script lang="ts">
@@ -78,9 +78,11 @@ import Component from 'vue-class-component'
import {Mixins} from 'vue-property-decorator'
import BaseMixin from '../mixins/base'
import {GuiStateWebcam} from '@/store/gui/types'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {
Panel,
'webcam-mjpegstreamer': Mjpegstreamer,
'webcam-mjpegstreamer-adaptive': MjpegstreamerAdaptive,
'webcam-ipstreamer': Ipstreamer,

View File

@@ -4,12 +4,14 @@
<template>
<div>
<v-card v-if="displayPanel" class="mb-6">
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon class="mdi mdi-arrow-collapse-vertical" left></v-icon>{{ $t("Panels.ZoffsetPanel.Headline") }}</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<panel
v-if="displayPanel"
icon="mdi-arrow-collapse-vertical"
:title="$t('Panels.ZoffsetPanel.Headline')"
:collapsible="true"
card-class="zoffset-panel"
>
<template v-slot:buttons>
<template v-if="z_gcode_offset !== 0">
<v-btn small class="px-2 minwidth-0" color="grey darken-3" @click="clearZOffset()" :loading="loadings.includes('babySteppingClear')"><v-icon small>mdi-broom</v-icon></v-btn>
<v-menu offset-y left v-if="existZOffsetApplyProbe && existZOffsetApplyEndstop">
@@ -38,8 +40,7 @@
<v-btn small class="px-2 minwidth-0" color="primary ml-3" v-else-if="existZOffsetApplyProbe && !existZOffsetApplyEndstop" @click="saveZOffsetToProbe"><v-icon small class="mr-1">mdi-content-save</v-icon>{{ $t("Panels.ZoffsetPanel.Save") }}</v-btn>
<v-btn small class="px-2 minwidth-0" color="primary ml-3" v-else-if="!existZOffsetApplyProbe && existZOffsetApplyEndstop" @click="saveZOffsetToEndstop"><v-icon small class="mr-1">mdi-content-save</v-icon>{{ $t("Panels.ZoffsetPanel.Save") }}</v-btn>
</template>
</v-toolbar>
</template>
<v-card-text class="px-0 py-0">
<v-container>
<v-row class="py-0">
@@ -61,14 +62,9 @@
</v-row>
</v-container>
</v-card-text>
</v-card>
</panel>
<v-dialog v-model="saveOffsetDialog" max-width="290">
<v-card>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon class="mdi mdi-information" left></v-icon>{{ $t("Panels.ZoffsetPanel.SaveInfoHeadline") }}</span>
</v-toolbar-title>
</v-toolbar>
<panel :title="$t('Panels.ZoffsetPanel.SaveInfoHeadline')" icon="mdi-information" card-class="zoffset-saveinfo-dialog" :margin-bottom="false">
<v-card-text class="mt-3" v-if="printerIsPrinting">{{ $t("Panels.ZoffsetPanel.SaveInfoDescriptionPrint") }}</v-card-text>
<v-card-text class="mt-3" v-else>{{ $t("Panels.ZoffsetPanel.SaveInfoDescription") }}</v-card-text>
<v-card-actions v-if="printerIsPrinting">
@@ -80,7 +76,7 @@
<v-btn color="primary" text @click="saveConfig">{{ $t("Panels.ZoffsetPanel.SAVE_CONFIG") }}</v-btn>
<v-btn text @click="saveOffsetDialog = false">{{ $t("Panels.ZoffsetPanel.Later") }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
</div>
</template>
@@ -90,8 +86,10 @@
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '../mixins/base'
import {CommandHelp} from '@/store/printer/types'
@Component
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class ZoffsetPanel extends Mixins(BaseMixin) {
private saveOffsetDialog = false

View File

@@ -0,0 +1,71 @@
<style lang="scss" scoped>
.expanded header.v-toolbar{
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.expand-button:focus:after {
opacity: 0 !important;
}
.icon-rotate-180 {
transform: rotate(180deg);
}
</style>
<template>
<v-card :class="cardClass+' '+(marginBottom ? 'mb-6' : '')+' '+(!expand ? 'expanded' : '')" :loading="loading">
<v-toolbar flat dense :color="toolbarColor" :class="toolbarClass" >
<slot name="buttons-left"></slot>
<v-toolbar-title class="d-flex align-center">
<slot name="icon" v-if="hasIconSlot"></slot>
<v-icon left v-if="icon !== null && !hasIconSlot">{{ icon }}</v-icon>
<span class="subheading" v-if="title">{{ title }}</span>
</v-toolbar-title>
<slot name="buttons-title"></slot>
<v-spacer></v-spacer>
<slot name="buttons"></slot>
<v-icon
v-if="collapsible"
@click="expand = !expand"
:class="'ml-3 expand-button '+(!expand ? 'icon-rotate-180' : '')"
>mdi-chevron-down</v-icon>
</v-toolbar>
<v-expand-transition>
<div v-show="expand || !collapsible">
<slot></slot>
</div>
</v-expand-transition>
</v-card>
</template>
<script lang="ts">
import Component from 'vue-class-component'
import {Mixins, Prop} from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
@Component
export default class Panel extends Mixins(BaseMixin) {
@Prop({ default: null }) readonly icon!: string | null
@Prop({ required: true, default: '' }) readonly title!: string
@Prop({ default: false }) readonly collapsible!: boolean
@Prop({ required: true }) readonly cardClass!: string
@Prop({ default: '' }) readonly toolbarColor!: string
@Prop({ default: '' }) readonly toolbarClass!: string
@Prop({ default: false }) readonly loading!: boolean
@Prop({ default: true }) readonly marginBottom!: boolean
get expand() {
return this.$store.getters['gui/getPanelExpand'](this.cardClass)
}
set expand(newVal) {
this.$store.dispatch('gui/saveExpandPanel', { name: this.cardClass, value: newVal })
}
get hasIconSlot() {
return !! this.$slots.icon
}
}
</script>

View File

@@ -178,6 +178,7 @@
"Name": "Name",
"Abort": "abort",
"Rename": "rename",
"BedMeshRemove": "Bed Mesh Remove",
"BedMeshCalibrate": "Bed Mesh Calibrate",
"DoYouReallyWantToCalibrate": "Do you really want to start the bed calibration?",
"DeleteBedMeshProfile": "Delete Bed Mesh Profile",

View File

@@ -45,12 +45,7 @@
<template>
<div>
<v-card class="fileupload-card mb-3" @dragover="dragOverUpload" @dragleave="dragLeaveUpload" @drop.prevent.stop="dragDropUpload">
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading align-baseline"><v-icon left>mdi-file-document-multiple-outline</v-icon>{{ $t("Files.GCodeFiles")}}</span>
</v-toolbar-title>
</v-toolbar>
<panel :title="$t('Files.GCodeFiles')" icon="mdi-file-document-multiple-outline" card-class="gcode-files-panel fileupload-card" @dragover="dragOverUpload" @dragleave="dragLeaveUpload" @drop.prevent.stop="dragDropUpload">
<v-card-text>
<v-row>
<v-col class="col-12 d-flex align-center">
@@ -218,7 +213,7 @@
<div class="dragzone" :style="'visibility: '+dropzone.visibility+'; opacity: '+dropzone.hidden">
<div class="textnode">{{ $t('Files.DropFilesToAddGcode')}}</div>
</div>
</v-card>
</panel>
<v-snackbar
:timeout="-1"
:value="true"
@@ -300,9 +295,8 @@
</v-list-item>
</v-list>
</v-menu>
<v-dialog v-model="dialogCreateDirectory.show" max-width="400">
<v-card>
<v-card-title class="headline">{{ $t('Files.NewDirectory') }}</v-card-title>
<v-dialog v-model="dialogCreateDirectory.show" :max-width="400">
<panel :title="$t('Files.NewDirectory')" card-class="gcode-files-new-directory-dialog" :margin-bottom="false">
<v-card-text>
{{ $t('Files.PleaseEnterANewDirectoryName') }}
<v-text-field label="Name" :rules="input_rules" @keypress.enter="createDirectoryAction" required v-model="dialogCreateDirectory.name" ref="inputFieldCreateDirectory"></v-text-field>
@@ -312,11 +306,10 @@
<v-btn color="" text @click="dialogCreateDirectory.show = false">{{ $t('Files.Cancel') }}</v-btn>
<v-btn color="primary" text @click="createDirectoryAction">{{ $t('Files.Create') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="dialogRenameFile.show" max-width="400">
<v-card>
<v-card-title class="headline">{{ $t('Files.RenameFile')}}</v-card-title>
<v-dialog v-model="dialogRenameFile.show" :max-width="400">
<panel :title="$t('Files.RenameFile')" card-class="gcode-files-rename-file-dialog" :margin-bottom="false">
<v-card-text>
<v-text-field :label="$t('Files.Name')" required v-model="dialogRenameFile.newName" ref="inputFieldRenameFile"></v-text-field>
</v-card-text>
@@ -325,11 +318,10 @@
<v-btn color="" text @click="dialogRenameFile.show = false">{{ $t('Files.Cancel') }}</v-btn>
<v-btn color="primary" text @click="renameFileAction">{{ $t('Files.Rename') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="dialogRenameDirectory.show" max-width="400">
<v-card>
<v-card-title class="headline">{{ $t('Files.RenameDirectory') }}</v-card-title>
<panel :title="$t('Files.RenameDirectory')" card-class="gcode-files-rename-directory-dialog" :margin-bottom="false">
<v-card-text>
<v-text-field label="Name" required v-model="dialogRenameDirectory.newName" ref="inputFieldRenameDirectory"></v-text-field>
</v-card-text>
@@ -338,11 +330,10 @@
<v-btn color="" text @click="dialogRenameDirectory.show = false">{{ $t('Files.Cancel') }}</v-btn>
<v-btn color="primary" text @click="renameDirectoryAction">{{ $t('Files.Rename') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="dialogDeleteDirectory.show" max-width="400">
<v-card>
<v-card-title class="headline">{{ $t('Files.DeleteDirectory') }}</v-card-title>
<panel :title="$t('Files.DeleteDirectory')" card-class="gcode-files-delete-directory-dialog" :margin-bottom="false">
<v-card-text>
<p class="mb-0">{{ $t('Files.DeleteDirectoryQuestion', { name: dialogDeleteDirectory.item.filename } )}}</p>
</v-card-text>
@@ -351,7 +342,7 @@
<v-btn color="" text @click="dialogDeleteDirectory.show = false">{{ $t('Files.Cancel') }}</v-btn>
<v-btn color="error" text @click="deleteDirectoryAction">{{ $t('Files.Delete') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
</div>
</template>
@@ -362,6 +353,7 @@ import axios from 'axios'
import { validGcodeExtensions } from '@/store/variables'
import {findDirectory, formatFilesize, formatDate, sortFiles} from '@/plugins/helpers'
import {FileStateFile} from '@/store/files/types'
import Panel from '@/components/ui/Panel.vue'
interface draggingFile {
status: boolean
@@ -394,7 +386,9 @@ interface dialogRenameObject {
item: FileStateFile
}
@Component
@Component({
components: {Panel}
})
export default class PageFiles extends Mixins(BaseMixin) {
validGcodeExtensions = validGcodeExtensions
formatDate = formatDate

View File

@@ -6,32 +6,16 @@
<div>
<v-row v-if="klipperReadyForGui">
<v-col class="col-12 col-md-8">
<v-card>
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading">
<v-icon left>mdi-grid</v-icon>
{{ $t('Heightmap.Heightmap') }}
</span>
<v-btn
text
color="primary"
class="ml-1 d-none d-sm-inline-flex"
v-if="bed_mesh"
@click="openRenameProfile()">{{ bed_mesh ? bed_mesh.profile_name : "" }}</v-btn>
</v-toolbar-title>
<v-spacer class=""></v-spacer>
<v-btn
text
color="primary"
class=" d-sm-none"
@click="openRenameProfile()">{{ bed_mesh ? bed_mesh.profile_name : "" }}</v-btn>
<v-item-group class="v-btn-toggle d-none d-sm-flex" name="controllers">
<v-btn small class="px-2 minwidth-0" color="primary" @click="homePrinter" :loading="loadings.includes('homeAll')" :title="$t('Heightmap.TitleHomeAll')"><v-icon small>mdi-home</v-icon></v-btn>
<v-btn small class="px-2 minwidth-0" color="primary" @click="clearBedMesh" :loading="loadings.includes('bedMeshClear')" v-if="bed_mesh" :title="$t('Heightmap.TitleClear')">{{ $t('Heightmap.Clear') }}</v-btn>
<v-btn small class="px-2 minwidth-0" color="primary" @click="calibrateDialog = true" :loading="loadings.includes('bedMeshCalibrate')" :disabled="printerIsPrinting" :title="$t('Heightmap.TitleCalibrate')">{{ $t('Heightmap.Calibrate') }}</v-btn>
</v-item-group>
</v-toolbar>
<panel card-class="heightmap-map-panel" :title="$t('Heightmap.Heightmap')" icon="mdi-grid">
<template v-slot:buttons-title>
<v-btn text color="primary" class="ml-1 d-none d-sm-inline-flex" v-if="bed_mesh" @click="openRenameProfile()">{{ bed_mesh ? bed_mesh.profile_name : "" }}</v-btn>
</template>
<template v-slot:buttons>
<v-btn text color="primary" class=" d-sm-none" @click="openRenameProfile()">{{ bed_mesh ? bed_mesh.profile_name : "" }}</v-btn>
<v-btn small class="px-2 minwidth-0 ml-3" color="primary" @click="homePrinter" :loading="loadings.includes('homeAll')" :title="$t('Heightmap.TitleHomeAll')"><v-icon small>mdi-home</v-icon></v-btn>
<v-btn small class="px-2 minwidth-0 ml-3" color="primary" @click="clearBedMesh" :loading="loadings.includes('bedMeshClear')" v-if="bed_mesh" :title="$t('Heightmap.TitleClear')">{{ $t('Heightmap.Clear') }}</v-btn>
<v-btn small class="px-2 minwidth-0 ml-3" color="primary" @click="calibrateDialog = true" :loading="loadings.includes('bedMeshCalibrate')" :disabled="printerIsPrinting" :title="$t('Heightmap.TitleCalibrate')">{{ $t('Heightmap.Calibrate') }}</v-btn>
</template>
<v-card-text class="d-sm-none text-center pb-0">
<v-item-group class="v-btn-toggle" name="controllers">
<v-btn small class="px-2 minwidth-0" color="primary" @click="homePrinter" :loading="loadings.includes('homeAll')" :title="$t('Heightmap.TitleHomeAll')"><v-icon small>mdi-home</v-icon></v-btn>
@@ -79,15 +63,10 @@
</v-row>
</v-card-text>
</template>
</v-card>
</panel>
</v-col>
<v-col class="col-12 col-md-4">
<v-card>
<v-toolbar flat dense>
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-stack-overflow</v-icon>{{ $t('Heightmap.Profiles') }}</span>
</v-toolbar-title>
</v-toolbar>
<panel :title="$t('Heightmap.Profiles')" card-class="heightmap-profiles-panel" icon="mdi-stack-overflow">
<v-card-text class="py-0 px-0" v-if="profiles.length">
<v-simple-table>
<template v-slot:default>
@@ -116,7 +95,7 @@
<v-card-text v-else>
<p>{{ $t('Heightmap.NoProfile') }}</p>
</v-card-text>
</v-card>
</panel>
</v-col>
</v-row>
<v-row v-else>
@@ -130,11 +109,8 @@
icon="mdi-lock-outline"
>{{ $t('Heightmap.ErrorKlipperNotReady') }}</v-alert>
</v-row>
<v-dialog v-model="renameDialog" persistent max-width="600px">
<v-card>
<v-card-title>
<span class="headline">{{ $t('Heightmap.RenameBedMeshProfile') }}</span>
</v-card-title>
<v-dialog v-model="renameDialog" persistent :max-width="400">
<panel :title="$t('Heightmap.RenameBedMeshProfile')" icon="mdi-grid" card-class="heightmap-rename-dialog" :margin-bottom="false">
<v-card-text>
<v-row>
<v-col cols="12">
@@ -147,47 +123,31 @@
<v-btn text @click="renameDialog = false">{{ $t('Heightmap.Abort') }}</v-btn>
<v-btn color="primary" text @click="renameProfile">{{ $t('Heightmap.Rename') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="calibrateDialog" persistent max-width="600px">
<v-card>
<v-card-title>
<span class="headline">{{ $t('Heightmap.BedMeshCalibrate') }}</span>
</v-card-title>
<v-dialog v-model="calibrateDialog" persistent :max-width="400">
<panel :title="$t('Heightmap.BedMeshCalibrate')" icon="mdi-grid" card-class="heightmap-calibrate-dialog" :margin-bottom="false">
<v-card-text>
<v-row>
<v-col>
<p>{{ $t('Heightmap.DoYouReallyWantToCalibrate') }}</p>
</v-col>
</v-row>
<p>{{ $t('Heightmap.DoYouReallyWantToCalibrate') }}</p>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text @click="calibrateDialog = false">{{ $t('Heightmap.Abort') }}</v-btn>
<v-btn color="primary" text @click="calibrateMesh">{{ $t('Heightmap.Calibrate') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
<v-dialog v-model="removeDialog" persistent max-width="600px">
<v-card>
<v-card-title>
<span class="headline"></span>
</v-card-title>
<v-dialog v-model="removeDialog" persistent :max-width="400">
<panel :title="$t('Heightmap.BedMeshRemove')" icon="mdi-grid" card-class="heightmap-calibrate-dialog" :margin-bottom="false">
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<p>{{ $t('Heightmap.DoYouReallyWantToDelete', { name: removeDialogProfile }) }}</p>
</v-col>
</v-row>
</v-container>
<p>{{ $t('Heightmap.DoYouReallyWantToDelete', { name: removeDialogProfile }) }}</p>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text @click="removeDialog = false">{{ $t('Heightmap.Abort') }}</v-btn>
<v-btn color="error" text @click="removeProfile">{{ $t('Heightmap.Remove') }}</v-btn>
</v-card-actions>
</v-card>
</panel>
</v-dialog>
</div>
</template>
@@ -199,6 +159,7 @@ import { createComponent } from 'echarts-for-vue'
import * as echarts from 'echarts'
import {ECharts} from 'echarts/core'
import 'echarts-gl'
import Panel from '@/components/ui/Panel.vue'
interface HeightmapSerie {
type: string
@@ -216,6 +177,7 @@ interface HeightmapSerie {
@Component({
components: {
Panel,
ECharts: createComponent({ echarts }),
}
})

View File

@@ -6,63 +6,10 @@
<div>
<v-row>
<v-col>
<v-card>
<v-toolbar flat dense >
<v-toolbar-title>
<span class="subheading"><v-icon left>mdi-chart-areaspline</v-icon>{{ $t('History.Statistics') }}</span>
</v-toolbar-title>
</v-toolbar>
<v-card-text class="pa-0">
<v-row align="center">
<v-col class="col-12 col-sm-6 col-md-4">
<v-simple-table>
<tbody>
<tr>
<td>{{ $t('History.TotalPrinttime') }}</td>
<td class="text-right">{{ formatPrintTime(totalPrintTime) }}</td>
</tr>
<tr>
<td>{{ $t('History.LongestPrinttime') }}</td>
<td class="text-right">{{ formatPrintTime(longestPrintTime) }}</td>
</tr>
<tr>
<td>{{ $t('History.AvgPrinttime') }}</td>
<td class="text-right">{{ formatPrintTime(avgPrintTime) }}</td>
</tr>
<tr>
<td>{{ $t('History.TotalFilamentUsed') }}</td>
<td class="text-right">{{ Math.round(totalFilamentUsed / 100) / 10 }} m</td>
</tr>
<tr>
<td>{{ $t('History.TotalJobs') }}</td>
<td class="text-right">{{ totalJobsCount }}</td>
</tr>
</tbody>
</v-simple-table>
</v-col>
<v-col class="col-12 col-sm-6 col-md-4">
<history-all-print-status></history-all-print-status>
</v-col>
<v-col class="col-12 col-sm-12 col-md-4">
<history-filament-usage v-if="toggleChart === 'filament_usage'"></history-filament-usage>
<history-printtime-avg v-if="toggleChart === 'printtime_avg'"></history-printtime-avg>
<div class="text-center mt-3">
<v-btn-toggle v-model="toggleChart" small mandatory>
<v-btn small value="filament_usage">
{{ $t('History.FilamentUsage') }}
</v-btn>
<v-btn small value="printtime_avg">
{{ $t('History.PrinttimeAvg') }}
</v-btn>
</v-btn-toggle>
</div>
</v-col>
</v-row>
</v-card-text>
</v-card>
<history-statistics-panel></history-statistics-panel>
</v-col>
</v-row>
<v-row class="mt-6">
<v-row class="mt-0">
<v-col>
<history-list-panel></history-list-panel>
</v-col>
@@ -74,63 +21,15 @@
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import HistoryAllPrintStatus from '@/components/charts/HistoryAllPrintStatus.vue'
import HistoryPrinttimeAvg from '@/components/charts/HistoryPrinttimeAvg.vue'
import HistoryFilamentUsage from '@/components/charts/HistoryFilamentUsage.vue'
import HistoryListPanel from '@/components/panels/HistoryListPanel.vue'
import HistoryStatisticsPanel from '@/components/panels/HistoryStatisticsPanel.vue'
@Component({
components: {HistoryListPanel, HistoryFilamentUsage, HistoryPrinttimeAvg, HistoryAllPrintStatus}
components: {
HistoryStatisticsPanel,
HistoryListPanel
}
})
export default class PageHistory extends Mixins(BaseMixin) {
get totalPrintTime() {
return 'total_print_time' in this.$store.state.server.history.job_totals ? this.$store.state.server.history.job_totals.total_print_time : 0
}
get longestPrintTime() {
return 'longest_print' in this.$store.state.server.history.job_totals ? this.$store.state.server.history.job_totals.longest_print : 0
}
get avgPrintTime() {
if (this.totalJobsCount > 0 && this.totalPrintTime > 0) return Math.round(this.totalPrintTime / this.totalJobsCount)
return 0
}
get totalFilamentUsed() {
return 'total_filament_used' in this.$store.state.server.history.job_totals ? this.$store.state.server.history.job_totals.total_filament_used : 0
}
get totalJobsCount() {
return 'total_jobs' in this.$store.state.server.history.job_totals ? this.$store.state.server.history.job_totals.total_jobs : 0
}
get toggleChart () {
return this.$store.state.gui.history.toggleChartCol3
}
set toggleChart(newVal) {
this.$store.dispatch('gui/saveSetting', { name: 'history.toggleChartCol3', value: newVal })
}
formatPrintTime(totalSeconds: number) {
if (totalSeconds) {
let output = ''
const hours = Math.floor(totalSeconds / 3600)
totalSeconds %= 3600
if (hours) output += ' '+hours+'h'
const minutes = Math.floor(totalSeconds / 60)
if (minutes) output += ' '+minutes+'m'
const seconds = totalSeconds % 60
if (seconds) output += ' '+seconds.toFixed(0)+'s'
return output
}
return '--'
}
}
</script>

View File

@@ -191,6 +191,16 @@ export const actions: ActionTree<GuiState, RootState> = {
}
},
saveExpandPanel({ commit, dispatch, state }, payload) {
if (!payload.value) commit('addClosePanel', { name: payload.name })
else commit('removeClosePanel', { name: payload.name })
dispatch('updateSettings', {
keyName: 'dashboard.nonExpandPanels',
newVal: state.dashboard.nonExpandPanels
})
},
showStatusInHistoryList({ commit, dispatch, state }, name) {
const array: string[] = [...state.history.hidePrintStatus]

View File

@@ -102,5 +102,9 @@ export const getters: GetterTree<GuiState, any> = {
return 0
})
},
getPanelExpand: (state) => (name: string) => {
return !state.dashboard.nonExpandPanels?.includes(name) ?? true
}
}

View File

@@ -89,7 +89,8 @@ export const getDefaultState = (): GuiState => {
widescreenLayout3: [
{ 'name': 'webcam', visable: true },
{ 'name': 'miniconsole', visable: true },
]
],
nonExpandPanels: []
},
webcam: {
selectedCam: '',

View File

@@ -152,5 +152,15 @@ export const mutations: MutationTree<GuiState> = {
setHistoryHidePrintStatus(state, payload) {
Vue.set(state.history, 'hidePrintStatus', payload)
},
addClosePanel(state, payload) {
const exist = state.dashboard.nonExpandPanels?.includes(payload.name) ?? false
if (!exist) state.dashboard.nonExpandPanels.push(payload.name)
},
removeClosePanel(state, payload) {
const index = state.dashboard.nonExpandPanels.indexOf(payload.name)
if (index > -1) state.dashboard.nonExpandPanels.splice(index, 1)
}
}

View File

@@ -62,7 +62,8 @@ export const actions: ActionTree<ServerState, RootState> = {
},
initProcStats({ commit }, payload) {
commit('setThrottledState', payload.throttled_state)
if (payload.throttled_state !== null)
commit('setThrottledState', payload.throttled_state)
},
setKlippyReady({ dispatch }) {