|
|
|
@ -381,6 +381,9 @@
|
|
|
|
:document="document"
|
|
|
|
:document="document"
|
|
|
|
:is-drag="!!dragField"
|
|
|
|
:is-drag="!!dragField"
|
|
|
|
:input-mode="inputMode"
|
|
|
|
:input-mode="inputMode"
|
|
|
|
|
|
|
|
:conditional-field-index="conditionalFieldIndex"
|
|
|
|
|
|
|
|
:formula-values-index="formulaValuesIndex"
|
|
|
|
|
|
|
|
:page-preview-format="pagePreviewFormat"
|
|
|
|
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
|
|
|
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
|
|
|
:allow-draw="!onlyDefinedFields || drawField || drawCustomField"
|
|
|
|
:allow-draw="!onlyDefinedFields || drawField || drawCustomField"
|
|
|
|
:with-signature-id="withSignatureId"
|
|
|
|
:with-signature-id="withSignatureId"
|
|
|
|
@ -504,11 +507,14 @@
|
|
|
|
:with-custom-fields="withCustomFields"
|
|
|
|
:with-custom-fields="withCustomFields"
|
|
|
|
:with-fields-search="withFieldsSearch"
|
|
|
|
:with-fields-search="withFieldsSearch"
|
|
|
|
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
|
|
|
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
|
|
|
|
|
|
|
:with-custom-fields-tab="withCustomFieldsTab"
|
|
|
|
:template="template"
|
|
|
|
:template="template"
|
|
|
|
:default-required-fields="defaultRequiredFields"
|
|
|
|
:default-required-fields="defaultRequiredFields"
|
|
|
|
|
|
|
|
:detect-custom-fields-index="detectCustomFieldsIndex"
|
|
|
|
:field-types="fieldTypes"
|
|
|
|
:field-types="fieldTypes"
|
|
|
|
:with-sticky-submitters="withStickySubmitters"
|
|
|
|
:with-sticky-submitters="withStickySubmitters"
|
|
|
|
:with-fields-detection="withFieldsDetection"
|
|
|
|
:with-fields-detection="withFieldsDetection"
|
|
|
|
|
|
|
|
:with-detect-existing-fields="withDetectExistingFields"
|
|
|
|
:with-signature-id="withSignatureId"
|
|
|
|
:with-signature-id="withSignatureId"
|
|
|
|
:with-prefillable="withPrefillable"
|
|
|
|
:with-prefillable="withPrefillable"
|
|
|
|
:only-defined-fields="onlyDefinedFields"
|
|
|
|
:only-defined-fields="onlyDefinedFields"
|
|
|
|
@ -617,6 +623,16 @@ import { v4 } from 'uuid'
|
|
|
|
import { ref, computed, toRaw, defineAsyncComponent } from 'vue'
|
|
|
|
import { ref, computed, toRaw, defineAsyncComponent } from 'vue'
|
|
|
|
import * as i18n from './i18n'
|
|
|
|
import * as i18n from './i18n'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const isEmpty = (obj) => {
|
|
|
|
|
|
|
|
if (obj == null) return true
|
|
|
|
|
|
|
|
if (Array.isArray(obj)) return obj.length === 0
|
|
|
|
|
|
|
|
if (typeof obj === 'string') return obj.trim().length === 0
|
|
|
|
|
|
|
|
if (typeof obj === 'object') return Object.keys(obj).length === 0
|
|
|
|
|
|
|
|
if (obj === false) return true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
export default {
|
|
|
|
name: 'TemplateBuilder',
|
|
|
|
name: 'TemplateBuilder',
|
|
|
|
components: {
|
|
|
|
components: {
|
|
|
|
@ -654,6 +670,7 @@ export default {
|
|
|
|
locale: this.locale,
|
|
|
|
locale: this.locale,
|
|
|
|
baseFetch: this.baseFetch,
|
|
|
|
baseFetch: this.baseFetch,
|
|
|
|
fieldTypes: this.fieldTypes,
|
|
|
|
fieldTypes: this.fieldTypes,
|
|
|
|
|
|
|
|
dateFormats: this.dateFormats,
|
|
|
|
backgroundColor: this.backgroundColor,
|
|
|
|
backgroundColor: this.backgroundColor,
|
|
|
|
withPhone: this.withPhone,
|
|
|
|
withPhone: this.withPhone,
|
|
|
|
withVerification: this.withVerification,
|
|
|
|
withVerification: this.withVerification,
|
|
|
|
@ -738,6 +755,11 @@ export default {
|
|
|
|
required: false,
|
|
|
|
required: false,
|
|
|
|
default: false
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
withDetectExistingFields: {
|
|
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
|
|
required: false,
|
|
|
|
|
|
|
|
default: false
|
|
|
|
|
|
|
|
},
|
|
|
|
withCustomFields: {
|
|
|
|
withCustomFields: {
|
|
|
|
type: Boolean,
|
|
|
|
type: Boolean,
|
|
|
|
required: false,
|
|
|
|
required: false,
|
|
|
|
@ -778,6 +800,11 @@ export default {
|
|
|
|
required: false,
|
|
|
|
required: false,
|
|
|
|
default: () => []
|
|
|
|
default: () => []
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
withCustomFieldsTab: {
|
|
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
|
|
required: false,
|
|
|
|
|
|
|
|
default: false
|
|
|
|
|
|
|
|
},
|
|
|
|
withSelectedFieldType: {
|
|
|
|
withSelectedFieldType: {
|
|
|
|
type: Boolean,
|
|
|
|
type: Boolean,
|
|
|
|
required: false,
|
|
|
|
required: false,
|
|
|
|
@ -798,6 +825,11 @@ export default {
|
|
|
|
required: false,
|
|
|
|
required: false,
|
|
|
|
default: () => []
|
|
|
|
default: () => []
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
dateFormats: {
|
|
|
|
|
|
|
|
type: Array,
|
|
|
|
|
|
|
|
required: false,
|
|
|
|
|
|
|
|
default: () => []
|
|
|
|
|
|
|
|
},
|
|
|
|
defaultSubmitters: {
|
|
|
|
defaultSubmitters: {
|
|
|
|
type: Array,
|
|
|
|
type: Array,
|
|
|
|
required: false,
|
|
|
|
required: false,
|
|
|
|
@ -808,6 +840,11 @@ export default {
|
|
|
|
required: false,
|
|
|
|
required: false,
|
|
|
|
default: () => []
|
|
|
|
default: () => []
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
pagePreviewFormat: {
|
|
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
|
|
required: false,
|
|
|
|
|
|
|
|
default: '.jpg'
|
|
|
|
|
|
|
|
},
|
|
|
|
acceptFileTypes: {
|
|
|
|
acceptFileTypes: {
|
|
|
|
type: String,
|
|
|
|
type: String,
|
|
|
|
required: false,
|
|
|
|
required: false,
|
|
|
|
@ -953,6 +990,7 @@ export default {
|
|
|
|
isLoadingBlankPage: false,
|
|
|
|
isLoadingBlankPage: false,
|
|
|
|
isSaving: false,
|
|
|
|
isSaving: false,
|
|
|
|
isDetectingPageFields: false,
|
|
|
|
isDetectingPageFields: false,
|
|
|
|
|
|
|
|
detectFieldsQueue: [],
|
|
|
|
detectingAnalyzingProgress: null,
|
|
|
|
detectingAnalyzingProgress: null,
|
|
|
|
detectingFieldsAddedCount: null,
|
|
|
|
detectingFieldsAddedCount: null,
|
|
|
|
selectedSubmitter: null,
|
|
|
|
selectedSubmitter: null,
|
|
|
|
@ -963,7 +1001,8 @@ export default {
|
|
|
|
drawCustomField: null,
|
|
|
|
drawCustomField: null,
|
|
|
|
drawOption: null,
|
|
|
|
drawOption: null,
|
|
|
|
dragField: null,
|
|
|
|
dragField: null,
|
|
|
|
isDragFile: false
|
|
|
|
isDragFile: false,
|
|
|
|
|
|
|
|
isMathLoaded: false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
computed: {
|
|
|
|
@ -1003,6 +1042,8 @@ export default {
|
|
|
|
return isMobileSafariIos || /android|iphone|ipad/i.test(navigator.userAgent)
|
|
|
|
return isMobileSafariIos || /android|iphone|ipad/i.test(navigator.userAgent)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
defaultDateFormat () {
|
|
|
|
defaultDateFormat () {
|
|
|
|
|
|
|
|
if (this.dateFormats.length) return this.dateFormats[0]
|
|
|
|
|
|
|
|
|
|
|
|
const isUsBrowser = Intl.DateTimeFormat().resolvedOptions().locale.endsWith('-US')
|
|
|
|
const isUsBrowser = Intl.DateTimeFormat().resolvedOptions().locale.endsWith('-US')
|
|
|
|
const isUsTimezone = new Intl.DateTimeFormat('en-US', { timeZoneName: 'short' }).format(new Date()).match(/\s(?:CST|CDT|PST|PDT|EST|EDT)$/)
|
|
|
|
const isUsTimezone = new Intl.DateTimeFormat('en-US', { timeZoneName: 'short' }).format(new Date()).match(/\s(?:CST|CDT|PST|PDT|EST|EDT)$/)
|
|
|
|
|
|
|
|
|
|
|
|
@ -1044,6 +1085,43 @@ export default {
|
|
|
|
|
|
|
|
|
|
|
|
return map
|
|
|
|
return map
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
fieldsUuidIndex () {
|
|
|
|
|
|
|
|
return this.template.fields.reduce((acc, f) => {
|
|
|
|
|
|
|
|
acc[f.uuid] = f
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return acc
|
|
|
|
|
|
|
|
}, {})
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
conditionalFieldIndex () {
|
|
|
|
|
|
|
|
if (!this.inputMode) return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const cache = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this.template.fields.reduce((acc, f) => {
|
|
|
|
|
|
|
|
acc[f.uuid] = this.checkFieldConditions(f, cache)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return acc
|
|
|
|
|
|
|
|
}, {})
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
formulaValuesIndex () {
|
|
|
|
|
|
|
|
const formulaFields = this.template.fields.filter((f) => f.preferences?.formula && f.type !== 'payment' && this.hasFormulaDependencyValue(f))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!formulaFields.length) return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!this.isMathLoaded) {
|
|
|
|
|
|
|
|
this.loadCalculator()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return formulaFields.reduce((acc, f) => {
|
|
|
|
|
|
|
|
if (this.conditionalFieldIndex[f.uuid] !== false) {
|
|
|
|
|
|
|
|
acc[f.uuid] = this.calculateFormula(f)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return acc
|
|
|
|
|
|
|
|
}, {})
|
|
|
|
|
|
|
|
},
|
|
|
|
isAllRequiredFieldsAdded () {
|
|
|
|
isAllRequiredFieldsAdded () {
|
|
|
|
return !this.defaultRequiredFields?.some((f) => {
|
|
|
|
return !this.defaultRequiredFields?.some((f) => {
|
|
|
|
return !this.template.fields?.some((field) => field.name === f.name)
|
|
|
|
return !this.template.fields?.some((field) => field.name === f.name)
|
|
|
|
@ -1052,6 +1130,39 @@ export default {
|
|
|
|
selectedField () {
|
|
|
|
selectedField () {
|
|
|
|
return this.template.fields.find((f) => f.areas?.includes(this.lastSelectedArea))
|
|
|
|
return this.template.fields.find((f) => f.areas?.includes(this.lastSelectedArea))
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
detectFieldsIndex () {
|
|
|
|
|
|
|
|
const submittersByUuid = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.template.submitters.forEach((s) => {
|
|
|
|
|
|
|
|
submittersByUuid[s.uuid] = s
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const index = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.template.fields.forEach((f) => {
|
|
|
|
|
|
|
|
if (!f.name) return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const role = submittersByUuid[f.submitter_uuid]?.name
|
|
|
|
|
|
|
|
const key = [f.name, role].filter(Boolean).join(':').toLowerCase()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!index[key]) index[key] = f
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return index
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
detectCustomFieldsIndex () {
|
|
|
|
|
|
|
|
const index = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;[...this.customFields, ...this.defaultRequiredFields, ...this.defaultFields].forEach((c) => {
|
|
|
|
|
|
|
|
if (!c.name) return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const key = [c.name, c.role].filter(Boolean).join(':').toLowerCase()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!index[key]) index[key] = c
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return index
|
|
|
|
|
|
|
|
},
|
|
|
|
sortedDocuments () {
|
|
|
|
sortedDocuments () {
|
|
|
|
return this.template.schema.map((item) => {
|
|
|
|
return this.template.schema.map((item) => {
|
|
|
|
return this.template.documents.find(doc => doc.uuid === item.attachment_uuid)
|
|
|
|
return this.template.documents.find(doc => doc.uuid === item.attachment_uuid)
|
|
|
|
@ -1147,6 +1258,132 @@ export default {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
methods: {
|
|
|
|
toRaw,
|
|
|
|
toRaw,
|
|
|
|
|
|
|
|
applyCustomFieldAttributes: Fields.methods.applyCustomFieldAttributes,
|
|
|
|
|
|
|
|
buildExistingFields: Fields.methods.buildExistingFields,
|
|
|
|
|
|
|
|
async loadCalculator () {
|
|
|
|
|
|
|
|
if (this.math) return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { Calculator } = await import('../submission_form/calculator')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.math = new Calculator()
|
|
|
|
|
|
|
|
this.isMathLoaded = true
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
optionValue (option, index) {
|
|
|
|
|
|
|
|
if (option.value) {
|
|
|
|
|
|
|
|
return option.value
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return `${this.t('option')} ${index + 1}`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
checkFieldConditions (field, cache = {}) {
|
|
|
|
|
|
|
|
const cacheKey = field.uuid || field.attachment_uuid
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (cache[cacheKey] !== undefined) {
|
|
|
|
|
|
|
|
return cache[cacheKey]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (field.conditions?.length) {
|
|
|
|
|
|
|
|
const result = field.conditions.reduce((acc, cond) => {
|
|
|
|
|
|
|
|
if (cond.operation === 'or') {
|
|
|
|
|
|
|
|
acc.push(acc.pop() || this.checkFieldCondition(cond, cache))
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
acc.push(this.checkFieldCondition(cond, cache))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return acc
|
|
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cache[cacheKey] = !result.includes(false)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
cache[cacheKey] = true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return cache[cacheKey]
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
checkFieldCondition (condition, cache = {}) {
|
|
|
|
|
|
|
|
const field = this.fieldsUuidIndex[condition.field_uuid]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (['not_empty', 'checked', 'equal', 'contains', 'greater_than', 'less_than'].includes(condition.action) && field && !this.checkFieldConditions(field, cache)) {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const defaultValue = !field || isEmpty(field.default_value) ? null : field.default_value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (['empty', 'unchecked'].includes(condition.action)) {
|
|
|
|
|
|
|
|
return isEmpty(defaultValue)
|
|
|
|
|
|
|
|
} else if (['not_empty', 'checked'].includes(condition.action)) {
|
|
|
|
|
|
|
|
return !isEmpty(defaultValue)
|
|
|
|
|
|
|
|
} else if (field?.type === 'number' && ['equal', 'not_equal', 'greater_than', 'less_than'].includes(condition.action)) {
|
|
|
|
|
|
|
|
const value = defaultValue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isEmpty(value) || isEmpty(condition.value)) return false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const actual = parseFloat(value)
|
|
|
|
|
|
|
|
const expected = parseFloat(condition.value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (Number.isNaN(actual) || Number.isNaN(expected)) return false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (condition.action === 'equal') return Math.abs(actual - expected) < Number.EPSILON
|
|
|
|
|
|
|
|
if (condition.action === 'not_equal') return Math.abs(actual - expected) > Number.EPSILON
|
|
|
|
|
|
|
|
if (condition.action === 'greater_than') return actual > expected
|
|
|
|
|
|
|
|
if (condition.action === 'less_than') return actual < expected
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
} else if (['equal', 'contains'].includes(condition.action) && field) {
|
|
|
|
|
|
|
|
if (field.options) {
|
|
|
|
|
|
|
|
const option = field.options.find((o) => o.uuid === condition.value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (option) {
|
|
|
|
|
|
|
|
const values = [defaultValue].flat()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return values.includes(this.optionValue(option, field.options.indexOf(option)))
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return [defaultValue].flat().includes(condition.value)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (['not_equal', 'does_not_contain'].includes(condition.action) && field) {
|
|
|
|
|
|
|
|
if (field.options) {
|
|
|
|
|
|
|
|
const option = field.options.find((o) => o.uuid === condition.value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (option) {
|
|
|
|
|
|
|
|
const values = [defaultValue].flat()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return !values.includes(this.optionValue(option, field.options.indexOf(option)))
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
normalizeFormula (formula, depth = 0) {
|
|
|
|
|
|
|
|
if (depth > 10) return formula
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return formula.replace(/{{(.*?)}}/g, (match, uuid) => {
|
|
|
|
|
|
|
|
if (this.fieldsUuidIndex[uuid]?.preferences?.formula) {
|
|
|
|
|
|
|
|
return `(${this.normalizeFormula(this.fieldsUuidIndex[uuid].preferences.formula, depth + 1)})`
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return match
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
calculateFormula (field) {
|
|
|
|
|
|
|
|
const transformedFormula = this.normalizeFormula(field.preferences.formula).replace(/{{(.*?)}}/g, (match, uuid) => {
|
|
|
|
|
|
|
|
return this.fieldsUuidIndex[uuid]?.default_value || 0.0
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this.math.evaluate(transformedFormula.toLowerCase())
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
hasFormulaDependencyValue (field) {
|
|
|
|
|
|
|
|
const normalized = this.normalizeFormula(field.preferences.formula)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return [...normalized.matchAll(/{{(.*?)}}/g)].some(([, uuid]) => !isEmpty(this.fieldsUuidIndex[uuid]?.default_value))
|
|
|
|
|
|
|
|
},
|
|
|
|
addCustomField (field) {
|
|
|
|
addCustomField (field) {
|
|
|
|
return this.$refs.fields.addCustomField(field)
|
|
|
|
return this.$refs.fields.addCustomField(field)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
@ -1492,6 +1729,30 @@ export default {
|
|
|
|
this.template.fields.push(field)
|
|
|
|
this.template.fields.push(field)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
insertDetectedField (field) {
|
|
|
|
|
|
|
|
if (!this.withDetectExistingFields || !field.name) {
|
|
|
|
|
|
|
|
this.insertField(field)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const role = this.template.submitters.find((s) => s.uuid === field.submitter_uuid)?.name
|
|
|
|
|
|
|
|
const nameKey = field.name.toLowerCase()
|
|
|
|
|
|
|
|
const indexKey = [field.name, role].filter(Boolean).join(':').toLowerCase()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const existingField = this.detectFieldsIndex[indexKey]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (existingField) {
|
|
|
|
|
|
|
|
existingField.areas = existingField.areas || []
|
|
|
|
|
|
|
|
existingField.areas.push(...(field.areas || []))
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
const customField = this.detectCustomFieldsIndex[indexKey] || this.detectCustomFieldsIndex[nameKey]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (customField) this.applyCustomFieldAttributes(field, customField)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.insertField(field)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
closeDropdown () {
|
|
|
|
closeDropdown () {
|
|
|
|
document.activeElement.blur()
|
|
|
|
document.activeElement.blur()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
@ -2783,6 +3044,12 @@ export default {
|
|
|
|
})
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
detectFieldsForPage ({ page, attachmentUuid }) {
|
|
|
|
detectFieldsForPage ({ page, attachmentUuid }) {
|
|
|
|
|
|
|
|
if (this.isDetectingPageFields) {
|
|
|
|
|
|
|
|
this.detectFieldsQueue.push({ page, attachmentUuid })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.isDetectingPageFields = true
|
|
|
|
this.isDetectingPageFields = true
|
|
|
|
this.detectingAnalyzingProgress = null
|
|
|
|
this.detectingAnalyzingProgress = null
|
|
|
|
this.detectingFieldsAddedCount = null
|
|
|
|
this.detectingFieldsAddedCount = null
|
|
|
|
@ -2821,7 +3088,11 @@ export default {
|
|
|
|
this.baseFetch(`/templates/${this.template.id}/detect_fields`, {
|
|
|
|
this.baseFetch(`/templates/${this.template.id}/detect_fields`, {
|
|
|
|
method: 'POST',
|
|
|
|
method: 'POST',
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
body: JSON.stringify({ attachment_uuid: attachmentUuid, page })
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
|
|
attachment_uuid: attachmentUuid,
|
|
|
|
|
|
|
|
page,
|
|
|
|
|
|
|
|
...(this.withDetectExistingFields ? { fields: this.buildExistingFields() } : {})
|
|
|
|
|
|
|
|
})
|
|
|
|
}).then(async (response) => {
|
|
|
|
}).then(async (response) => {
|
|
|
|
const reader = response.body.getReader()
|
|
|
|
const reader = response.body.getReader()
|
|
|
|
const decoder = new TextDecoder('utf-8')
|
|
|
|
const decoder = new TextDecoder('utf-8')
|
|
|
|
@ -2850,7 +3121,7 @@ export default {
|
|
|
|
if (!f.submitter_uuid) {
|
|
|
|
if (!f.submitter_uuid) {
|
|
|
|
f.submitter_uuid = this.template.submitters[0].uuid
|
|
|
|
f.submitter_uuid = this.template.submitters[0].uuid
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.insertField(f)
|
|
|
|
this.insertDetectedField(f)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
totalFieldsAdded += errorFields.length
|
|
|
|
totalFieldsAdded += errorFields.length
|
|
|
|
@ -2879,7 +3150,7 @@ export default {
|
|
|
|
|
|
|
|
|
|
|
|
const nonOverlappingFields = filterNonOverlappingFields(finalFields)
|
|
|
|
const nonOverlappingFields = filterNonOverlappingFields(finalFields)
|
|
|
|
|
|
|
|
|
|
|
|
nonOverlappingFields.forEach((f) => this.insertField(f))
|
|
|
|
nonOverlappingFields.forEach((f) => this.insertDetectedField(f))
|
|
|
|
totalFieldsAdded += nonOverlappingFields.length
|
|
|
|
totalFieldsAdded += nonOverlappingFields.length
|
|
|
|
|
|
|
|
|
|
|
|
if (nonOverlappingFields.length) {
|
|
|
|
if (nonOverlappingFields.length) {
|
|
|
|
@ -2917,7 +3188,7 @@ export default {
|
|
|
|
|
|
|
|
|
|
|
|
const nonOverlappingFields = filterNonOverlappingFields(finalFields)
|
|
|
|
const nonOverlappingFields = filterNonOverlappingFields(finalFields)
|
|
|
|
|
|
|
|
|
|
|
|
nonOverlappingFields.forEach((f) => this.insertField(f))
|
|
|
|
nonOverlappingFields.forEach((f) => this.insertDetectedField(f))
|
|
|
|
totalFieldsAdded += nonOverlappingFields.length
|
|
|
|
totalFieldsAdded += nonOverlappingFields.length
|
|
|
|
|
|
|
|
|
|
|
|
if (nonOverlappingFields.length) {
|
|
|
|
if (nonOverlappingFields.length) {
|
|
|
|
@ -2935,7 +3206,7 @@ export default {
|
|
|
|
|
|
|
|
|
|
|
|
const nonOverlappingFields = filterNonOverlappingFields(finalFields)
|
|
|
|
const nonOverlappingFields = filterNonOverlappingFields(finalFields)
|
|
|
|
|
|
|
|
|
|
|
|
nonOverlappingFields.forEach((f) => this.insertField(f))
|
|
|
|
nonOverlappingFields.forEach((f) => this.insertDetectedField(f))
|
|
|
|
totalFieldsAdded += nonOverlappingFields.length
|
|
|
|
totalFieldsAdded += nonOverlappingFields.length
|
|
|
|
|
|
|
|
|
|
|
|
if (nonOverlappingFields.length) {
|
|
|
|
if (nonOverlappingFields.length) {
|
|
|
|
@ -2968,6 +3239,10 @@ export default {
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
this.detectingFieldsAddedCount = null
|
|
|
|
this.detectingFieldsAddedCount = null
|
|
|
|
}, 1000)
|
|
|
|
}, 1000)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.detectFieldsQueue.length) {
|
|
|
|
|
|
|
|
this.detectFieldsForPage(this.detectFieldsQueue.shift())
|
|
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
save ({ force } = { force: false }) {
|
|
|
|
save ({ force } = { force: false }) {
|
|
|
|
|