import { deepClone, getUUID, splitText } from '.' import { ElementType, IEditorOption, IElement } from '..' import { LaTexParticle } from '../core/draw/particle/latex/LaTexParticle' import { defaultCheckboxOption } from '../dataset/constant/Checkbox' import { ZERO } from '../dataset/constant/Common' import { defaultControlOption } from '../dataset/constant/Control' import { EDITOR_ELEMENT_ZIP_ATTR } from '../dataset/constant/Element' import { ControlComponent, ControlType } from '../dataset/enum/Control' interface IFormatElementListOption { isHandleFirstElement?: boolean; editorOptions: Required; } export function formatElementList(elementList: IElement[], options: IFormatElementListOption) { const { isHandleFirstElement, editorOptions } = { isHandleFirstElement: true, ...options } if (isHandleFirstElement && elementList[0]?.value !== ZERO) { elementList.unshift({ value: ZERO }) } let i = 0 while (i < elementList.length) { let el = elementList[i] if (el.type === ElementType.TABLE) { const tableId = getUUID() el.id = tableId if (el.trList) { for (let t = 0; t < el.trList.length; t++) { const tr = el.trList[t] const trId = getUUID() tr.id = trId for (let d = 0; d < tr.tdList.length; d++) { const td = tr.tdList[d] const tdId = getUUID() td.id = tdId formatElementList(td.value, options) for (let v = 0; v < td.value.length; v++) { const value = td.value[v] value.tdId = tdId value.trId = trId value.tableId = tableId } } } } } else if (el.type === ElementType.HYPERLINK) { const valueList = el.valueList || [] // 移除父节点 elementList.splice(i, 1) // 追加字节点 if (valueList.length) { // 元素展开 if (valueList[0].value.length > 1) { const deleteValue = valueList.splice(0, 1)[0] const deleteTextList = splitText(deleteValue.value) for (let d = 0; d < deleteTextList.length; d++) { valueList.splice(d, 0, { ...deleteValue, value: deleteTextList[d] }) } } const hyperlinkId = getUUID() for (let v = 0; v < valueList.length; v++) { const value = valueList[v] value.type = el.type value.url = el.url value.hyperlinkId = hyperlinkId elementList.splice(i, 0, value) i++ } } i-- } else if (el.type === ElementType.DATE) { const valueList = el.valueList || [] // 移除父节点 elementList.splice(i, 1) // 追加字节点 if (valueList.length) { // 元素展开 if (valueList[0].value.length > 1) { const deleteValue = valueList.splice(0, 1)[0] const deleteTextList = splitText(deleteValue.value) for (let d = 0; d < deleteTextList.length; d++) { valueList.splice(d, 0, { ...deleteValue, value: deleteTextList[d] }) } } const dateId = getUUID() for (let v = 0; v < valueList.length; v++) { const value = valueList[v] value.type = el.type value.dateFormat = el.dateFormat value.dateId = dateId elementList.splice(i, 0, value) i++ } } i-- } else if (el.type === ElementType.CONTROL) { const { prefix, postfix, value, placeholder, code, type, valueSets } = el.control! const controlId = getUUID() // 移除父节点 elementList.splice(i, 1) // 前后缀个性化设置 const thePrePostfixArgs: Pick = {} if (editorOptions && editorOptions.control) { thePrePostfixArgs.color = editorOptions.control.bracketColor } // 前缀 const prefixStrList = splitText(prefix || defaultControlOption.prefix) for (let p = 0; p < prefixStrList.length; p++) { const value = prefixStrList[p] elementList.splice(i, 0, { controlId, value, type: el.type, control: el.control, controlComponent: ControlComponent.PREFIX, ...thePrePostfixArgs }) i++ } // 值 if ( (value && value.length) || type === ControlType.CHECKBOX || (type === ControlType.SELECT && code && (!value || !value.length)) ) { let valueList: IElement[] = value || [] if (type === ControlType.CHECKBOX) { const codeList = code ? code.split(',') : [] if (Array.isArray(valueSets) && valueSets.length) { for (let v = 0; v < valueSets.length; v++) { const valueSet = valueSets[v] // checkbox组件 elementList.splice(i, 0, { controlId, value: '', type: el.type, control: el.control, controlComponent: ControlComponent.CHECKBOX, checkbox: { code: valueSet.code, value: codeList.includes(valueSet.code) } }) i++ // 文本 const valueStrList = splitText(valueSet.value) for (let e = 0; e < valueStrList.length; e++) { const value = valueStrList[e] const isLastLetter = e === valueStrList.length - 1 elementList.splice(i, 0, { controlId, value, type: el.type, letterSpacing: isLastLetter ? defaultCheckboxOption.gap : 0, control: el.control, controlComponent: ControlComponent.VALUE }) i++ } } } } else { if (!value || !value.length) { if (Array.isArray(valueSets) && valueSets.length) { const valueSet = valueSets.find(v => v.code === code) if (valueSet) { valueList = [{ value: valueSet.value }] } } } for (let v = 0; v < valueList.length; v++) { const element = valueList[v] const valueStrList = splitText(element.value) for (let e = 0; e < valueStrList.length; e++) { const value = valueStrList[e] elementList.splice(i, 0, { ...element, controlId, value, type: el.type, control: el.control, controlComponent: ControlComponent.VALUE }) i++ } } } } else if (placeholder) { // placeholder const thePlaceholderArgs: Pick = {} if (editorOptions && editorOptions.control) { thePlaceholderArgs.color = editorOptions.control.placeholderColor } const placeholderStrList = splitText(placeholder) for (let p = 0; p < placeholderStrList.length; p++) { const value = placeholderStrList[p] elementList.splice(i, 0, { controlId, value, type: el.type, control: el.control, controlComponent: ControlComponent.PLACEHOLDER, ...thePlaceholderArgs }) i++ } } // 后缀 const postfixStrList = splitText(postfix || defaultControlOption.postfix) for (let p = 0; p < postfixStrList.length; p++) { const value = postfixStrList[p] elementList.splice(i, 0, { controlId, value, type: el.type, control: el.control, controlComponent: ControlComponent.POSTFIX, ...thePrePostfixArgs }) i++ } i-- } else if ((!el.type || el.type === ElementType.TEXT) && el.value.length > 1) { elementList.splice(i, 1) const valueList = splitText(el.value) for (let v = 0; v < valueList.length; v++) { elementList.splice(i + v, 0, { ...el, value: valueList[v] }) } el = elementList[i] } if (el.value === '\n') { el.value = ZERO } if (el.type === ElementType.IMAGE) { el.id = getUUID() } if (el.type === ElementType.BLOCK) { el.id = getUUID() if (!el.width) { const { editorOptions: { width, margins } } = options el.width = width - margins[1] - margins[3] } } if (el.type === ElementType.LATEX) { const { svg, width, height } = LaTexParticle.convertLaTextToSVG(el.value) el.width = el.width || width el.height = el.height || height el.laTexSVG = svg el.id = getUUID() } i++ } } export function isSameElementExceptValue(source: IElement, target: IElement): boolean { const sourceKeys = Object.keys(source) const targetKeys = Object.keys(target) if (sourceKeys.length !== targetKeys.length) return false for (let s = 0; s < sourceKeys.length; s++) { const key = sourceKeys[s] as never if (key === 'value') continue if (source[key] !== target[key]) { return false } } return true } export function pickElementAttr(payload: IElement): IElement { const element: IElement = { value: payload.value === ZERO ? `\n` : payload.value, } EDITOR_ELEMENT_ZIP_ATTR.forEach(attr => { const value = payload[attr] as never if (value !== undefined) { element[attr] = value } }) return element } export function zipElementList(payload: IElement[]): IElement[] { const elementList = deepClone(payload) const zipElementListData: IElement[] = [] let e = 0 while (e < elementList.length) { let element = elementList[e] // 筛选所需项 if (e === 0 && element.value === ZERO) { e++ continue } // 表格、超链接递归处理 if (element.type === ElementType.TABLE) { if (element.trList) { for (let t = 0; t < element.trList.length; t++) { const tr = element.trList[t] delete tr.id for (let d = 0; d < tr.tdList.length; d++) { const td = tr.tdList[d] tr.tdList[d] = { colspan: td.colspan, rowspan: td.rowspan, value: zipElementList(td.value) } } } } } else if (element.type === ElementType.HYPERLINK) { // 超链接处理 const hyperlinkId = element.hyperlinkId const hyperlinkElement: IElement = { type: ElementType.HYPERLINK, value: '', url: element.url } const valueList: IElement[] = [] while (e < elementList.length) { const hyperlinkE = elementList[e] if (hyperlinkId !== hyperlinkE.hyperlinkId) { e-- break } delete hyperlinkE.type delete hyperlinkE.url valueList.push(hyperlinkE) e++ } hyperlinkElement.valueList = zipElementList(valueList) element = hyperlinkElement } else if (element.type === ElementType.DATE) { const dateId = element.dateId const dateElement: IElement = { type: ElementType.DATE, value: '', dateFormat: element.dateFormat } const valueList: IElement[] = [] while (e < elementList.length) { const dateE = elementList[e] if (dateId !== dateE.dateId) { e-- break } delete dateE.type delete dateE.dateFormat valueList.push(dateE) e++ } dateElement.valueList = zipElementList(valueList) element = dateElement } else if (element.type === ElementType.CONTROL) { // 控件处理 const controlId = element.controlId const control = element.control! const controlElement: IElement = { type: ElementType.CONTROL, value: '', control } const valueList: IElement[] = [] while (e < elementList.length) { const controlE = elementList[e] if (controlId !== controlE.controlId) { e-- break } if (controlE.controlComponent === ControlComponent.VALUE) { delete controlE.type delete controlE.control valueList.push(controlE) } e++ } controlElement.control!.value = zipElementList(valueList) element = controlElement } // 组合元素 const pickElement = pickElementAttr(element) if (!element.type || element.type === ElementType.TEXT) { while (e < elementList.length) { const nextElement = elementList[e + 1] e++ if ( nextElement && isSameElementExceptValue(pickElement, pickElementAttr(nextElement)) ) { const nextValue = nextElement.value === ZERO ? '\n' : nextElement.value pickElement.value += nextValue } else { break } } } else { e++ } zipElementListData.push(pickElement) } return zipElementListData }