mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-06 11:57:16 +08:00
Add validation to label template code [SCI-7210]
This commit is contained in:
parent
e1ce81280e
commit
17bf490fef
5 changed files with 128 additions and 21 deletions
|
@ -29,6 +29,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-preview__error {
|
||||||
|
background-color: $brand-danger-light;
|
||||||
|
color: $brand-danger-hover;
|
||||||
|
margin-top: .5em;
|
||||||
|
padding: 1.5em 2em;
|
||||||
|
}
|
||||||
|
|
||||||
.label-preview__controls {
|
.label-preview__controls {
|
||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -55,6 +55,24 @@
|
||||||
|
|
||||||
.label-textarea-container {
|
.label-textarea-container {
|
||||||
height: calc(100% - 6em);
|
height: calc(100% - 6em);
|
||||||
|
|
||||||
|
.label-textarea {
|
||||||
|
height: 100%;
|
||||||
|
margin-top: .5em;
|
||||||
|
padding: .5em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
.label-textarea {
|
||||||
|
border: 1px solid $brand-danger;
|
||||||
|
height: calc(100% - 2em);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: $brand-danger;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-edit-header {
|
.label-edit-header {
|
||||||
|
@ -115,13 +133,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-textarea {
|
|
||||||
height: 100%;
|
|
||||||
margin-top: .5em;
|
|
||||||
padding: .5em;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inser-field-dropdown {
|
.inser-field-dropdown {
|
||||||
.open-dropdown-button:not(.collapsed) {
|
.open-dropdown-button:not(.collapsed) {
|
||||||
.fas {
|
.fas {
|
||||||
|
|
|
@ -51,6 +51,10 @@
|
||||||
<div v-if="base64Image" class="label-preview__image">
|
<div v-if="base64Image" class="label-preview__image">
|
||||||
<img :src="`data:image/png;base64,${base64Image}`" />
|
<img :src="`data:image/png;base64,${base64Image}`" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="label-preview__error" v-html="i18n.t('label_templates.label_preview.error_html')"
|
||||||
|
v-else-if="base64Image != null && base64Image.length === 0">
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -113,6 +117,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setDefaults();
|
this.setDefaults();
|
||||||
|
},
|
||||||
|
zpl() {
|
||||||
|
this.refreshPreview();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -123,6 +130,8 @@
|
||||||
!this.height && (this.height = this.unit === 'in' ? 1 : 25.4);
|
!this.height && (this.height = this.unit === 'in' ? 1 : 25.4);
|
||||||
},
|
},
|
||||||
refreshPreview() {
|
refreshPreview() {
|
||||||
|
if (this.zpl.length === 0) return;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: this.previewUrl,
|
url: this.previewUrl,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
|
@ -134,7 +143,17 @@
|
||||||
},
|
},
|
||||||
success: (result) => {
|
success: (result) => {
|
||||||
this.base64Image = result.base64_preview;
|
this.base64Image = result.base64_preview;
|
||||||
|
if (this.base64Image.length > 0) {
|
||||||
|
this.$emit('preview:valid');
|
||||||
|
} else {
|
||||||
|
this.$emit('preview:invalid');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (result) => {
|
||||||
|
this.base64Image = '';
|
||||||
|
this.$emit('preview:invalid');
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateUnit(unit) {
|
updateUnit(unit) {
|
||||||
|
|
|
@ -55,23 +55,26 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="editingContent">
|
<template v-if="editingContent">
|
||||||
<div class="label-textarea-container">
|
<div class="label-textarea-container" :class="{'error': hasError }">
|
||||||
<textarea
|
<textarea
|
||||||
ref="contentInput"
|
ref="contentInput"
|
||||||
v-model="newContent"
|
v-model="newContent"
|
||||||
class="label-textarea"
|
class="label-textarea"
|
||||||
@blur="updateContent"
|
@blur="saveCursorPosition"
|
||||||
></textarea>
|
></textarea>
|
||||||
|
<div class="error-message">
|
||||||
|
{{ codeErrorMessage }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<div class="btn btn-secondary refresh-preview">
|
<div class="btn btn-secondary refresh-preview" @click="generatePreview(true)">
|
||||||
<i class="fas fa-sync"></i>
|
<i class="fas fa-sync"></i>
|
||||||
{{ i18n.t('label_templates.show.buttons.refresh') }}
|
{{ i18n.t('label_templates.show.buttons.refresh') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="btn btn-secondary" @mousedown="disableContentEdit">
|
<div class="btn btn-secondary" @mousedown="disableContentEdit">
|
||||||
{{ i18n.t('general.cancel') }}
|
{{ i18n.t('general.cancel') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="btn btn-primary save-template" @click="updateContent">
|
<div class="btn btn-primary save-template" :disabled="hasError && previewValid" @click="generatePreview(false)">
|
||||||
<i class="fas fa-save"></i>
|
<i class="fas fa-save"></i>
|
||||||
{{ i18n.t('label_templates.show.buttons.save') }}
|
{{ i18n.t('label_templates.show.buttons.save') }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,7 +86,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="label-preview-container">
|
<div class="label-preview-container">
|
||||||
<LabelPreview :zpl='labelTemplate.attributes.content' :previewUrl="previewUrl" />
|
<LabelPreview :zpl='previewContent' :previewUrl="previewUrl" @preview:valid="updateContent" @preview:invalid="invalidPreview" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,14 +115,32 @@
|
||||||
editingDescription: false,
|
editingDescription: false,
|
||||||
editingContent: false,
|
editingContent: false,
|
||||||
newContent: '',
|
newContent: '',
|
||||||
|
previewContent: '',
|
||||||
|
previewValid: false,
|
||||||
|
skipSave: false,
|
||||||
|
codeErrorMessage: '',
|
||||||
cursorPos: 0
|
cursorPos: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
newContent() {
|
||||||
|
this.showErrors();
|
||||||
|
},
|
||||||
|
previewValid() {
|
||||||
|
this.showErrors();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasError() {
|
||||||
|
return this.codeErrorMessage.length > 0
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {InlineEdit, InsertFieldDropdown, LabelPreview},
|
components: {InlineEdit, InsertFieldDropdown, LabelPreview},
|
||||||
created() {
|
created() {
|
||||||
$.get(this.labelTemplateUrl, (result) => {
|
$.get(this.labelTemplateUrl, (result) => {
|
||||||
this.labelTemplate = result.data
|
this.labelTemplate = result.data
|
||||||
this.newContent = this.labelTemplate.attributes.content
|
this.newContent = this.labelTemplate.attributes.content
|
||||||
|
this.previewContent = this.labelTemplate.attributes.content
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -134,6 +155,7 @@
|
||||||
disableContentEdit() {
|
disableContentEdit() {
|
||||||
this.editingContent = false;
|
this.editingContent = false;
|
||||||
this.newContent = this.labelTemplate.attributes.content;
|
this.newContent = this.labelTemplate.attributes.content;
|
||||||
|
this.previewContent = this.labelTemplate.attributes.content;
|
||||||
},
|
},
|
||||||
updateName(newName) {
|
updateName(newName) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
@ -156,23 +178,66 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateContent() {
|
updateContent() {
|
||||||
this.cursorPos = $(this.$refs.contentInput).prop('selectionStart');
|
this.previewValid = true;
|
||||||
$.ajax({
|
|
||||||
url: this.labelTemplate.attributes.urls.update,
|
if (!this.editingContent) return;
|
||||||
type: 'PATCH',
|
|
||||||
data: {label_template: {content: this.newContent}},
|
if (this.skipSave) {
|
||||||
success: (result) => {
|
this.skipSave = false;
|
||||||
this.labelTemplate.attributes.content = result.data.attributes.content;
|
return;
|
||||||
this.editingContent = false;
|
}
|
||||||
}
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.hasError) return;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: this.labelTemplate.attributes.urls.update,
|
||||||
|
type: 'PATCH',
|
||||||
|
data: {label_template: {content: this.newContent}},
|
||||||
|
success: (result) => {
|
||||||
|
this.labelTemplate.attributes.content = result.data.attributes.content;
|
||||||
|
this.editingContent = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
generatePreview(skipSave = false) {
|
||||||
|
this.skipSave = skipSave;
|
||||||
|
if (!skipSave && this.previewContent === this.newContent && this.previewValid) {
|
||||||
|
this.updateContent();
|
||||||
|
} else {
|
||||||
|
this.previewContent = this.newContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
invalidPreview() {
|
||||||
|
this.previewValid = false;
|
||||||
|
this.skipSave = false;
|
||||||
|
},
|
||||||
|
saveCursorPosition() {
|
||||||
|
this.cursorPos = $(this.$refs.contentInput).prop('selectionStart');
|
||||||
|
},
|
||||||
insertField(field) {
|
insertField(field) {
|
||||||
this.enableContentEdit();
|
this.enableContentEdit();
|
||||||
let textBefore = this.newContent.substring(0, this.cursorPos);
|
let textBefore = this.newContent.substring(0, this.cursorPos);
|
||||||
let textAfter = this.newContent.substring(this.cursorPos, this.newContent.length);
|
let textAfter = this.newContent.substring(this.cursorPos, this.newContent.length);
|
||||||
this.newContent = textBefore + field + textAfter;
|
this.newContent = textBefore + field + textAfter;
|
||||||
this.cursorPos = this.cursorPos + field.length;
|
this.cursorPos = this.cursorPos + field.length;
|
||||||
|
},
|
||||||
|
showErrors() {
|
||||||
|
if (this.editingContent) {
|
||||||
|
if (this.newContent.length === 0) {
|
||||||
|
this.codeErrorMessage = this.i18n.t('label_templates.show.code_errors.empty')
|
||||||
|
} else if (this.newContent.length > 10000) {
|
||||||
|
this.codeErrorMessage = this.i18n.t('label_templates.show.code_errors.too_long')
|
||||||
|
} else if (!this.previewValid) {
|
||||||
|
this.codeErrorMessage = this.i18n.t('label_templates.show.code_errors.invalid')
|
||||||
|
} else {
|
||||||
|
this.codeErrorMessage = ''
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.codeErrorMessage = ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -872,6 +872,10 @@ en:
|
||||||
content_title: '%{format} template code'
|
content_title: '%{format} template code'
|
||||||
preview_title: 'Template preview'
|
preview_title: 'Template preview'
|
||||||
view_content_tooltip: 'Click to edit template code'
|
view_content_tooltip: 'Click to edit template code'
|
||||||
|
code_errors:
|
||||||
|
empty: 'ZPL template code cannot be empty'
|
||||||
|
too_long: 'ZPL template code has exceeded the maximum of 10.000 characters'
|
||||||
|
invalid: 'ZPL template code invalid'
|
||||||
insert_dropdown:
|
insert_dropdown:
|
||||||
button: 'Insert field code'
|
button: 'Insert field code'
|
||||||
common_fields: 'Common custom fields'
|
common_fields: 'Common custom fields'
|
||||||
|
@ -896,6 +900,7 @@ en:
|
||||||
refresh_preview: 'Refresh preview'
|
refresh_preview: 'Refresh preview'
|
||||||
mm: 'Millimeters (mm) 1 cm = 10 mm'
|
mm: 'Millimeters (mm) 1 cm = 10 mm'
|
||||||
in: 'Inches (in)'
|
in: 'Inches (in)'
|
||||||
|
error_html: 'Label cannot be previewed:<br>the ZPL code might be invalid, or the preview generator is down. Please double-check your code, save it and reload the page.'
|
||||||
promo:
|
promo:
|
||||||
head_title: 'Label templates'
|
head_title: 'Label templates'
|
||||||
promo_title: 'This feature is disabled by default in open source SciNote'
|
promo_title: 'This feature is disabled by default in open source SciNote'
|
||||||
|
|
Loading…
Add table
Reference in a new issue