diff --git a/src/components/panels/GcodefilesPanel.vue b/src/components/panels/GcodefilesPanel.vue index f9433391..aae54289 100644 --- a/src/components/panels/GcodefilesPanel.vue +++ b/src/components/panels/GcodefilesPanel.vue @@ -324,6 +324,13 @@ {{ mdiPlaylistPlus }} {{ $t('Files.AddToQueue') }} + + {{ mdiPlaylistPlus }} + {{ $t('Files.AddBatchToQueue') }} + + + + + + + + + + + + + {{ $t('Files.Cancel') }} + {{ $t('Files.AddToQueue') }} + + + @@ -509,6 +563,8 @@ import Panel from '@/components/ui/Panel.vue' import SettingsRow from '@/components/settings/SettingsRow.vue' import draggable from 'vuedraggable' import { + mdiChevronDown, + mdiChevronUp, mdiDragVertical, mdiCheckboxBlankOutline, mdiCheckboxMarked, @@ -552,6 +608,12 @@ interface dialogPrintFile { item: FileStateGcodefile } +interface dialogAddBatchToQueue { + show: boolean + count: number + item: FileStateGcodefile +} + interface dialogRenameObject { show: boolean newName: string @@ -572,6 +634,8 @@ interface tableColumnSetting { components: { StartPrintDialog, Panel, SettingsRow, draggable }, }) export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { + mdiChevronDown = mdiChevronDown + mdiChevronUp = mdiChevronUp mdiFile = mdiFile mdiFileDocumentMultipleOutline = mdiFileDocumentMultipleOutline mdiMagnify = mdiMagnify @@ -646,6 +710,12 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { item: { ...this.contextMenu.item }, } + private dialogAddBatchToQueue: dialogAddBatchToQueue = { + show: false, + count: 1, + item: { ...this.contextMenu.item }, + } + private dialogRenameFile: dialogRenameObject = { show: false, newName: '', @@ -671,6 +741,10 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { (value: string) => !!value || this.$t('Files.InvalidNameEmpty'), (value: string) => !this.existsFilename(value) || this.$t('Files.InvalidNameAlreadyExists'), ] + private countInputRules = [ + (value: string) => !!value || this.$t('JobQueue.InvalidCountEmpty'), + (value: string) => parseInt(value) > 0 || this.$t('JobQueue.InvalidCountGreaterZero'), + ] existsFilename(name: string) { return this.files.findIndex((file: FileStateFile) => file.filename === name) >= 0 @@ -1097,11 +1171,31 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { this.currentPath = this.currentPath.slice(0, this.currentPath.lastIndexOf('/')) } - addToQueue(item: FileStateGcodefile | FileStateFile) { + async addToQueue(item: FileStateGcodefile) { let filename = [this.currentPath, item.filename].join('/') if (filename.startsWith('/')) filename = filename.slice(1) - this.$store.dispatch('server/jobQueue/addToQueue', [filename]) + await this.$store.dispatch('server/jobQueue/addToQueue', [filename]) + } + + openAddBatchToQueueDialog(item: FileStateGcodefile) { + this.dialogAddBatchToQueue.show = true + this.dialogAddBatchToQueue.count = 1 + this.dialogAddBatchToQueue.item = item + } + + async addBatchToQueueAction() { + let filename = [this.currentPath, this.dialogAddBatchToQueue.item.filename].join('/') + if (filename.startsWith('/')) filename = filename.slice(1) + + const array: string[] = [] + for (let i = 0; i < this.dialogAddBatchToQueue.count; i++) { + array.push(filename) + } + + await this.$store.dispatch('server/jobQueue/addToQueue', array) + + this.dialogAddBatchToQueue.show = false } changeMetadataVisible(name: string, value: boolean) { @@ -1336,6 +1430,15 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { } + + diff --git a/src/components/panels/Status/Jobqueue.vue b/src/components/panels/Status/Jobqueue.vue index 4e8a76f9..9c7e92c1 100644 --- a/src/components/panels/Status/Jobqueue.vue +++ b/src/components/panels/Status/Jobqueue.vue @@ -5,77 +5,27 @@ hide-default-footer class="dashboard-jobqueue-table" sort-by="time_added" - mobile-breakpoint="0" - @current-items="setFirst"> + mobile-breakpoint="0"> - - @@ -101,24 +43,15 @@ import Component from 'vue-class-component' import { Mixins } from 'vue-property-decorator' import BaseMixin from '@/components/mixins/base' import { ServerJobQueueStateJob } from '@/store/server/jobQueue/types' -import { mdiFile, mdiPlay, mdiFileMultiple, mdiPlaylistRemove } from '@mdi/js' +import { mdiFileMultiple } from '@mdi/js' +import JobqueueEntry from '@/components/panels/Status/JobqueueEntry.vue' @Component({ - components: {}, + components: { JobqueueEntry }, }) export default class StatusPanelJobqueue extends Mixins(BaseMixin) { - mdiFile = mdiFile - mdiPlay = mdiPlay mdiFileMultiple = mdiFileMultiple - mdiPlaylistRemove = mdiPlaylistRemove private contentTdWidth = 100 - private contextMenu = { - shown: false, - touchTimer: undefined, - x: 0, - y: 0, - item: {}, - } declare $refs: { filesJobqueue: any @@ -136,15 +69,27 @@ export default class StatusPanelJobqueue extends Mixins(BaseMixin) { return this.jobs.slice(5) } + get restJobsLength() { + let count = 0 + + this.jobsRest.forEach((item: ServerJobQueueStateJob) => { + count += (item.combinedIds?.length ?? 0) + 1 + }) + + return count + } + get descriptionRestJobs() { let filamentLength = 0 let filamentWeight = 0 let printTime = 0 this.jobsRest.forEach((item: ServerJobQueueStateJob) => { - if (item.metadata?.filament_total) filamentLength += item.metadata?.filament_total - if (item.metadata?.filament_weight_total) filamentWeight += item.metadata?.filament_weight_total - if (item.metadata?.estimated_time) printTime = item.metadata.estimated_time + const count = (item.combinedIds?.length ?? 0) + 1 + + if (item.metadata?.filament_total) filamentLength += item.metadata?.filament_total * count + if (item.metadata?.filament_weight_total) filamentWeight += item.metadata?.filament_weight_total * count + if (item.metadata?.estimated_time) printTime = item.metadata.estimated_time * count }) let output = '' @@ -163,49 +108,6 @@ export default class StatusPanelJobqueue extends Mixins(BaseMixin) { return output } - get styleContentTdWidth() { - return `width: ${this.contentTdWidth}px;` - } - - getSmallThumbnail(item: ServerJobQueueStateJob) { - return this.$store.getters['server/jobQueue/getSmallThumbnail'](item) - } - - getBigThumbnail(item: ServerJobQueueStateJob) { - return this.$store.getters['server/jobQueue/getBigThumbnail'](item) - } - - getDescription(item: ServerJobQueueStateJob) { - let output = '' - - output += this.$t('Panels.StatusPanel.Filament') + ': ' - if (item.metadata?.filament_total || item.metadata.filament_weight_total) { - if (item.metadata?.filament_total) output += item.metadata.filament_total.toFixed() + ' mm' - if (item.metadata?.filament_total && item.metadata.filament_weight_total) output += ' / ' - if (item.metadata?.filament_weight_total) output += item.metadata.filament_weight_total.toFixed(2) + ' g' - } else output += '--' - - output += ', ' + this.$t('Panels.StatusPanel.PrintTime') + ': ' - if (item.metadata?.estimated_time) output += this.formatPrintTime(item.metadata.estimated_time) - else output += '--' - - return output - } - - existMetadata(item: ServerJobQueueStateJob) { - return item?.metadata?.metadataPulled - } - - setFirst(currItems: ServerJobQueueStateJob[]) { - // first check that actually exists values - if (currItems.length) { - // toggle all to false - currItems.forEach((x: ServerJobQueueStateJob) => (x.isFirst = false)) - // just set first to true - currItems[0].isFirst = true - } - } - formatPrintTime(totalSeconds: number) { if (totalSeconds) { let output = '' @@ -232,27 +134,10 @@ export default class StatusPanelJobqueue extends Mixins(BaseMixin) { return '--' } - showContextMenu(e: any, item: ServerJobQueueStateJob) { - if (!this.contextMenu.shown) { - e?.preventDefault() - this.contextMenu.shown = true - this.contextMenu.x = e?.clientX || e?.pageX || window.screenX / 2 - this.contextMenu.y = e?.clientY || e?.pageY || window.screenY / 2 - this.contextMenu.item = item - this.$nextTick(() => { - this.contextMenu.shown = true - }) - } - } - startJobqueue() { this.$store.dispatch('server/jobQueue/start') } - removeFromJobqueue(item: ServerJobQueueStateJob) { - this.$store.dispatch('server/jobQueue/deleteFromQueue', [item.job_id]) - } - mounted() { setTimeout(() => { this.calcContentTdWidth() diff --git a/src/components/panels/Status/JobqueueEntry.vue b/src/components/panels/Status/JobqueueEntry.vue new file mode 100644 index 00000000..4ba7e0ac --- /dev/null +++ b/src/components/panels/Status/JobqueueEntry.vue @@ -0,0 +1,271 @@ + + + + + diff --git a/src/components/panels/StatusPanel.vue b/src/components/panels/StatusPanel.vue index 4303996f..f80c524f 100644 --- a/src/components/panels/StatusPanel.vue +++ b/src/components/panels/StatusPanel.vue @@ -133,7 +133,7 @@ export default class StatusPanel extends Mixins(BaseMixin) { } get jobsCount() { - return this.jobs?.length ?? 0 + return this.$store.getters['server/jobQueue/getJobsCount'] ?? 0 } get current_filename() { diff --git a/src/locales/en.json b/src/locales/en.json index c2f9b94d..23923fbc 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -164,11 +164,13 @@ "Yes": "Yes" }, "Files": { + "AddBatchToQueue": "Add batch to Queue", "AddToQueue": "Add to Queue", "AllFiles": "All", "BedTemp": "Bed Temp.", "Cancel": "Cancel", "ChamberTemp": "Chamber Temp.", + "Count": "Count", "Create": "Create", "CreateNewDirectory": "Create new Directory", "CurrentPath": "Current path", @@ -369,7 +371,12 @@ }, "JobQueue": { "AllJobs": "All Jobs", + "ChangeCount": "Change count", + "Cancel": "Cancel", + "Count": "Count", "Empty": "Empty", + "InvalidCountEmpty": "Input must not be empty!", + "InvalidCountGreaterZero": "Input must be greater than 0!", "JobQueue": "Job Queue", "Jobs": "Jobs", "Pause": "Pause", diff --git a/src/store/server/jobQueue/actions.ts b/src/store/server/jobQueue/actions.ts index 5902c849..705eb09c 100644 --- a/src/store/server/jobQueue/actions.ts +++ b/src/store/server/jobQueue/actions.ts @@ -1,7 +1,7 @@ import Vue from 'vue' import { ActionTree } from 'vuex' import { RootState } from '@/store/types' -import { ServerJobQueueState } from '@/store/server/jobQueue/types' +import { ServerJobQueueState, ServerJobQueueStateJob } from '@/store/server/jobQueue/types' export const actions: ActionTree = { reset({ commit }) { @@ -28,6 +28,31 @@ export const actions: ActionTree = { Vue.$socket.emit('server.job_queue.post_job', { filenames: filenames }) }, + changeCount({ getters }, payload: { job_id: string; count: number }) { + const filenames: string[] = [] + const jobs = getters['getJobs'] + + jobs.forEach((job: ServerJobQueueStateJob) => { + if (job.job_id === payload.job_id) { + for (let i = 0; i < payload.count; i++) { + filenames.push(job.filename) + } + + return + } + + const count = (job.combinedIds?.length ?? 0) + 1 + for (let i = 0; i < count; i++) { + filenames.push(job.filename) + } + }) + + Vue.$socket.emit('server.job_queue.post_job', { + filenames, + reset: true, + }) + }, + deleteFromQueue(_, job_ids: string[]) { Vue.$socket.emit('server.job_queue.delete_job', { job_ids }) }, diff --git a/src/store/server/jobQueue/getters.ts b/src/store/server/jobQueue/getters.ts index 62265776..3bd0b1c8 100644 --- a/src/store/server/jobQueue/getters.ts +++ b/src/store/server/jobQueue/getters.ts @@ -10,10 +10,17 @@ export const getters: GetterTree = { state.queued_jobs.forEach((queuedJob) => { const job = { ...queuedJob } + + if (jobs.length && jobs[jobs.length - 1].filename === job.filename) { + jobs[jobs.length - 1].combinedIds?.push(job.job_id) + return + } + const file = rootGetters['files/getFile']('gcodes/' + job.filename) if (!file?.metadataPulled) Vue.$socket.emit('server.files.metadata', { filename: job.filename }, { action: 'files/getMetadata' }) - job['metadata'] = file + job.metadata = file + job.combinedIds = [] jobs.push(job) }) @@ -21,6 +28,10 @@ export const getters: GetterTree = { return jobs }, + getJobsCount: (state) => { + return state.queued_jobs.length + }, + getSmallThumbnail: (state, getters, rootState, rootGetters) => (item: ServerJobQueueStateJob) => { if (item?.metadata?.thumbnails?.length) { const thumbnail = item?.metadata?.thumbnails.find( diff --git a/src/store/server/jobQueue/types.ts b/src/store/server/jobQueue/types.ts index 1b007256..2d162b45 100644 --- a/src/store/server/jobQueue/types.ts +++ b/src/store/server/jobQueue/types.ts @@ -10,4 +10,5 @@ export interface ServerJobQueueStateJob { time_in_queue: number metadata?: any isFirst?: boolean + combinedIds?: string[] }