Merge pull request #39 from Hufe921/feature/control-checkbox

Feature/control checkbox
pr675
Hufe 4 years ago committed by GitHub
commit 7ae8f0775a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,47 @@
import Editor, { ControlType, ElementType } from '../../../src/editor'
describe('控件-复选框', () => {
beforeEach(() => {
cy.visit('http://localhost:3000/canvas-editor/')
cy.get('canvas').first().as('canvas').should('have.length', 1)
})
const elementType: ElementType = <ElementType>'control'
const controlType: ControlType = <ControlType>'checkbox'
it('复选框', () => {
cy.getEditor().then((editor: Editor) => {
editor.listener.saved = function (payload) {
const data = payload.data[0]
expect(data.control!.code).to.be.eq('98175')
}
editor.command.executeSelectAll()
editor.command.executeBackspace()
editor.command.executeInsertElementList([{
type: elementType,
value: '',
control: {
code: '98175',
type: controlType,
value: null,
valueSets: [{
value: '有',
code: '98175'
}, {
value: '无',
code: '98176'
}]
}
}])
cy.get('@canvas').type('{ctrl}s')
})
})
})

@ -164,8 +164,9 @@
<i></i> <i></i>
<div class="options"> <div class="options">
<ul> <ul>
<li data-control='text'>文本型</li> <li data-control='text'>文本</li>
<li data-control="select">列举型</li> <li data-control="select">列举</li>
<li data-control="checkbox">复选框</li>
</ul> </ul>
</div> </div>
</div> </div>

@ -39,6 +39,7 @@ import { Control } from './control/Control'
import { zipElementList } from '../../utils/element' import { zipElementList } from '../../utils/element'
import { CheckboxParticle } from './particle/CheckboxParticle' import { CheckboxParticle } from './particle/CheckboxParticle'
import { DeepRequired } from '../../interface/Common' import { DeepRequired } from '../../interface/Common'
import { ControlComponent } from '../../dataset/enum/Control'
export class Draw { export class Draw {
@ -567,7 +568,10 @@ export class Draw {
element.width = innerWidth element.width = innerWidth
metrics.width = innerWidth metrics.width = innerWidth
metrics.height = this.options.defaultSize metrics.height = this.options.defaultSize
} else if (element.type === ElementType.CHECKBOX) { } else if (
element.type === ElementType.CHECKBOX ||
element.controlComponent === ControlComponent.CHECKBOX
) {
const { width, height, gap } = this.options.checkbox const { width, height, gap } = this.options.checkbox
const elementWidth = (width + gap * 2) * scale const elementWidth = (width + gap * 2) * scale
element.width = elementWidth element.width = elementWidth
@ -707,7 +711,10 @@ export class Draw {
if (this.mode !== EditorMode.CLEAN) { if (this.mode !== EditorMode.CLEAN) {
this.pageBreakParticle.render(ctx, element, x, y) this.pageBreakParticle.render(ctx, element, x, y)
} }
} else if (element.type === ElementType.CHECKBOX) { } else if (
element.type === ElementType.CHECKBOX ||
element.controlComponent === ControlComponent.CHECKBOX
) {
this.textParticle.complete() this.textParticle.complete()
this.checkboxParticle.render(ctx, element, x, y + offsetY) this.checkboxParticle.render(ctx, element, x, y + offsetY)
} else { } else {

@ -2,11 +2,13 @@ import { ControlComponent, ControlType } from '../../../dataset/enum/Control'
import { ElementType } from '../../../dataset/enum/Element' import { ElementType } from '../../../dataset/enum/Element'
import { IControl, IControlInitOption, IControlInstance, IControlOption } from '../../../interface/Control' import { IControl, IControlInitOption, IControlInstance, IControlOption } from '../../../interface/Control'
import { IElement, IElementPosition } from '../../../interface/Element' import { IElement, IElementPosition } from '../../../interface/Element'
import { IRange } from '../../../interface/Range'
import { deepClone } from '../../../utils' import { deepClone } from '../../../utils'
import { pickElementAttr, zipElementList } from '../../../utils/element' import { pickElementAttr, zipElementList } from '../../../utils/element'
import { Listener } from '../../listener/Listener' import { Listener } from '../../listener/Listener'
import { RangeManager } from '../../range/RangeManager' import { RangeManager } from '../../range/RangeManager'
import { Draw } from '../Draw' import { Draw } from '../Draw'
import { CheckboxControl } from './checkbox/CheckboxControl'
import { SelectControl } from './select/SelectControl' import { SelectControl } from './select/SelectControl'
import { TextControl } from './text/TextControl' import { TextControl } from './text/TextControl'
@ -66,7 +68,7 @@ export class Control {
return this.draw.getPageNo() * (height + pageGap) return this.draw.getPageNo() * (height + pageGap)
} }
public getRange() { public getRange(): IRange {
return this.range.getRange() return this.range.getRange()
} }
@ -103,6 +105,8 @@ export class Control {
const selectControl = new SelectControl(element, this) const selectControl = new SelectControl(element, this)
this.activeControl = selectControl this.activeControl = selectControl
selectControl.awake() selectControl.awake()
} else if (control.type === ControlType.CHECKBOX) {
this.activeControl = new CheckboxControl(element, this)
} }
// 激活控件回调 // 激活控件回调
setTimeout(() => { setTimeout(() => {
@ -270,6 +274,7 @@ export class Control {
const elementList = this.getElementList() const elementList = this.getElementList()
const startElement = elementList[startIndex] const startElement = elementList[startIndex]
const control = startElement.control! const control = startElement.control!
if (!control.placeholder) return
const placeholderStrList = control.placeholder.split('') const placeholderStrList = control.placeholder.split('')
for (let p = 0; p < placeholderStrList.length; p++) { for (let p = 0; p < placeholderStrList.length; p++) {
const value = placeholderStrList[p] const value = placeholderStrList[p]

@ -0,0 +1,130 @@
import { ControlComponent } from '../../../../dataset/enum/Control'
import { KeyMap } from '../../../../dataset/enum/Keymap'
import { IControlInstance } from '../../../../interface/Control'
import { IElement } from '../../../../interface/Element'
import { Control } from '../Control'
export class CheckboxControl implements IControlInstance {
private element: IElement
private control: Control
constructor(element: IElement, control: Control) {
this.element = element
this.control = control
}
public getElement(): IElement {
return this.element
}
public getCode(): string | null {
return this.element.control?.code || null
}
public getValue(): IElement[] {
const elementList = this.control.getElementList()
const { startIndex } = this.control.getRange()
const startElement = elementList[startIndex]
const data: IElement[] = []
// 向左查找
let preIndex = startIndex
while (preIndex > 0) {
const preElement = elementList[preIndex]
if (
preElement.controlId !== startElement.controlId ||
preElement.controlComponent === ControlComponent.PREFIX
) {
break
}
if (preElement.controlComponent === ControlComponent.VALUE) {
data.unshift(preElement)
}
preIndex--
}
// 向右查找
let nextIndex = startIndex + 1
while (nextIndex < elementList.length) {
const nextElement = elementList[nextIndex]
if (
nextElement.controlId !== startElement.controlId ||
nextElement.controlComponent === ControlComponent.POSTFIX
) {
break
}
if (nextElement.controlComponent === ControlComponent.VALUE) {
data.push(nextElement)
}
nextIndex++
}
return data
}
public setValue(): number {
const { endIndex } = this.control.getRange()
return endIndex
}
public setSelect() {
const { control } = this.element
const elementList = this.control.getElementList()
const { startIndex } = this.control.getRange()
const startElement = elementList[startIndex]
const data: string[] = []
// 向左查找
let preIndex = startIndex
while (preIndex > 0) {
const preElement = elementList[preIndex]
if (
preElement.controlId !== startElement.controlId ||
preElement.controlComponent === ControlComponent.PREFIX
) {
break
}
if (preElement.controlComponent === ControlComponent.CHECKBOX) {
const checkbox = preElement.checkbox
if (checkbox && checkbox.value && checkbox.code) {
data.unshift(checkbox.code)
}
}
preIndex--
}
// 向右查找
let nextIndex = startIndex + 1
while (nextIndex < elementList.length) {
const nextElement = elementList[nextIndex]
if (
nextElement.controlId !== startElement.controlId ||
nextElement.controlComponent === ControlComponent.POSTFIX
) {
break
}
if (nextElement.controlComponent === ControlComponent.CHECKBOX) {
const checkbox = nextElement.checkbox
if (checkbox && checkbox.value && checkbox.code) {
data.push(checkbox.code)
}
}
nextIndex++
}
control!.code = data.join(',')
}
public keydown(evt: KeyboardEvent): number {
const range = this.control.getRange()
// 收缩边界到Value内
this.control.shrinkBoundary()
const { startIndex, endIndex } = range
// 删除
if (evt.key === KeyMap.Backspace || evt.key === KeyMap.Delete) {
return this.control.removeControl(startIndex)
}
return endIndex
}
public cut(): number {
const { endIndex } = this.control.getRange()
return endIndex
}
}

@ -18,6 +18,7 @@ import { Position } from '../position/Position'
import { RangeManager } from '../range/RangeManager' import { RangeManager } from '../range/RangeManager'
import { LETTER_REG, NUMBER_LIKE_REG } from '../../dataset/constant/Regular' import { LETTER_REG, NUMBER_LIKE_REG } from '../../dataset/constant/Regular'
import { Control } from '../draw/control/Control' import { Control } from '../draw/control/Control'
import { CheckboxControl } from '../draw/control/checkbox/CheckboxControl'
export class CanvasEvent { export class CanvasEvent {
@ -202,6 +203,7 @@ export class CanvasEvent {
// 设置位置上下文 // 设置位置上下文
this.position.setPositionContext({ this.position.setPositionContext({
isTable: isTable || false, isTable: isTable || false,
isCheckbox: isCheckbox || false,
isControl: isControl || false, isControl: isControl || false,
index, index,
trIndex, trIndex,
@ -235,6 +237,10 @@ export class CanvasEvent {
value: true value: true
} }
} }
const activeControl = this.control.getActiveControl()
if (activeControl instanceof CheckboxControl) {
activeControl.setSelect()
}
} }
this.draw.render({ this.draw.render({
curIndex, curIndex,

@ -1,5 +1,6 @@
import { ElementType } from '../..' import { ElementType } from '../..'
import { ZERO } from '../../dataset/constant/Common' import { ZERO } from '../../dataset/constant/Common'
import { ControlComponent } from '../../dataset/enum/Control'
import { IEditorOption } from '../../interface/Editor' import { IEditorOption } from '../../interface/Editor'
import { IElementPosition } from '../../interface/Element' import { IElementPosition } from '../../interface/Element'
import { ICurrentPosition, IGetPositionByXYPayload, IPositionContext } from '../../interface/Position' import { ICurrentPosition, IGetPositionByXYPayload, IPositionContext } from '../../interface/Position'
@ -97,7 +98,8 @@ export class Position {
const tdValueElement = td.value[tdValueIndex] const tdValueElement = td.value[tdValueIndex]
return { return {
index, index,
isCheckbox: tdValueElement.type === ElementType.CHECKBOX, isCheckbox: tdValueElement.type === ElementType.CHECKBOX ||
tdValueElement.controlComponent === ControlComponent.CHECKBOX,
isControl: tdValueElement.type === ElementType.CONTROL, isControl: tdValueElement.type === ElementType.CONTROL,
isImage: tablePosition.isImage, isImage: tablePosition.isImage,
isDirectHit: tablePosition.isDirectHit, isDirectHit: tablePosition.isDirectHit,
@ -121,7 +123,10 @@ export class Position {
isImage: true isImage: true
} }
} }
if (element.type === ElementType.CHECKBOX) { if (
element.type === ElementType.CHECKBOX ||
element.controlComponent === ControlComponent.CHECKBOX
) {
return { return {
index: curPositionIndex, index: curPositionIndex,
isDirectHit: true, isDirectHit: true,

@ -1,11 +1,13 @@
export enum ControlType { export enum ControlType {
TEXT = 'text', TEXT = 'text',
SELECT = 'select' SELECT = 'select',
CHECKBOX = 'checkbox'
} }
export enum ControlComponent { export enum ControlComponent {
PREFIX = 'prefix', PREFIX = 'prefix',
POSTFIX = 'postfix', POSTFIX = 'postfix',
PLACEHOLDER = 'placeholder', PLACEHOLDER = 'placeholder',
VALUE = 'value' VALUE = 'value',
CHECKBOX = 'checkbox'
} }

@ -1,6 +1,7 @@
export interface ICheckbox { export interface ICheckbox {
disabled?: boolean;
value: boolean | null; value: boolean | null;
code?: string;
disabled?: boolean;
} }
export interface ICheckboxOption { export interface ICheckboxOption {

@ -1,4 +1,5 @@
import { ControlType } from '../dataset/enum/Control' import { ControlType } from '../dataset/enum/Control'
import { ICheckbox } from './Checkbox'
import { IElement } from './Element' import { IElement } from './Element'
export interface IValueSet { export interface IValueSet {
@ -11,16 +12,27 @@ export interface IControlSelect {
valueSets: IValueSet[]; valueSets: IValueSet[];
} }
export interface IControlCheckbox {
code: string | null;
min?: number;
max?: number;
valueSets: IValueSet[];
checkbox?: ICheckbox;
}
export interface IControlBasic { export interface IControlBasic {
type: ControlType; type: ControlType;
value: IElement[] | null; value: IElement[] | null;
placeholder: string; placeholder?: string;
conceptId?: string; conceptId?: string;
prefix?: string; prefix?: string;
postfix?: string; postfix?: string;
} }
export type IControl = IControlBasic & Partial<IControlSelect> export type IControl = IControlBasic
& Partial<IControlSelect>
& Partial<IControlCheckbox>
export interface IControlOption { export interface IControlOption {
placeholderColor?: string; placeholderColor?: string;

@ -29,6 +29,7 @@ export interface IGetPositionByXYPayload {
export interface IPositionContext { export interface IPositionContext {
isTable: boolean; isTable: boolean;
isCheckbox?: boolean;
isControl?: boolean; isControl?: boolean;
index?: number; index?: number;
trIndex?: number; trIndex?: number;

@ -97,36 +97,72 @@ export function formatElementList(elementList: IElement[], options: IFormatEleme
// 值 // 值
if ( if (
(value && value.length) || (value && value.length) ||
type === ControlType.CHECKBOX ||
(type === ControlType.SELECT && code && (!value || !value.length)) (type === ControlType.SELECT && code && (!value || !value.length))
) { ) {
let valueList: IElement[] = value || [] let valueList: IElement[] = value || []
if (!value || !value.length) { if (type === ControlType.CHECKBOX) {
const codeList = code ? code.split(',') : []
if (Array.isArray(valueSets) && valueSets.length) { if (Array.isArray(valueSets) && valueSets.length) {
const valueSet = valueSets.find(v => v.code === code) for (let v = 0; v < valueSets.length; v++) {
if (valueSet) { const valueSet = valueSets[v]
valueList = [{ // checkbox组件
value: valueSet.value 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 = valueSet.value.split('')
for (let e = 0; e < valueStrList.length; e++) {
const value = valueStrList[e]
elementList.splice(i, 0, {
controlId,
value,
type: el.type,
control: el.control,
controlComponent: ControlComponent.VALUE
})
i++
}
} }
} }
} } else {
for (let v = 0; v < valueList.length; v++) { if (!value || !value.length) {
const element = valueList[v] if (Array.isArray(valueSets) && valueSets.length) {
const valueStrList = element.value.split('') const valueSet = valueSets.find(v => v.code === code)
for (let e = 0; e < valueStrList.length; e++) { if (valueSet) {
const value = valueStrList[e] valueList = [{
elementList.splice(i, 0, { value: valueSet.value
...element, }]
controlId, }
value, }
type: el.type, }
control: el.control, for (let v = 0; v < valueList.length; v++) {
controlComponent: ControlComponent.VALUE const element = valueList[v]
}) const valueStrList = element.value.split('')
i++ 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 { } else if (placeholder) {
// placeholder // placeholder
const thePlaceholderArgs: Pick<IElement, 'color'> = {} const thePlaceholderArgs: Pick<IElement, 'color'> = {}
if (editorOptions && editorOptions.control) { if (editorOptions && editorOptions.control) {

@ -415,7 +415,7 @@ window.onload = function () {
switch (type) { switch (type) {
case ControlType.TEXT: case ControlType.TEXT:
new Dialog({ new Dialog({
title: '文本控件', title: '文本控件',
data: [{ data: [{
type: 'text', type: 'text',
label: '占位符', label: '占位符',
@ -449,7 +449,7 @@ window.onload = function () {
break break
case ControlType.SELECT: case ControlType.SELECT:
new Dialog({ new Dialog({
title: '列举控件', title: '列举控件',
data: [{ data: [{
type: 'text', type: 'text',
label: '占位符', label: '占位符',
@ -487,6 +487,38 @@ window.onload = function () {
} }
}) })
break break
case ControlType.CHECKBOX:
new Dialog({
title: '复选框控件',
data: [{
type: 'text',
label: '默认值',
name: 'code',
placeholder: '请输入默认值,多个值以英文逗号分割'
}, {
type: 'textarea',
label: '值集',
name: 'valueSets',
height: 100,
placeholder: `请输入值集JSON\n[{\n"value":"有",\n"code":"98175"\n}]`
}],
onConfirm: (payload) => {
const valueSets = payload.find(p => p.name === 'valueSets')?.value
if (!valueSets) return
const code = payload.find(p => p.name === 'code')?.value
instance.command.executeInsertElementList([{
type: ElementType.CONTROL,
value: '',
control: {
type,
code,
value: null,
valueSets: JSON.parse(valueSets)
}
}])
}
})
break
default: default:
break break
} }

@ -250,18 +250,20 @@ elementList.push({
elementList.push(...<IElement[]>[{ elementList.push(...<IElement[]>[{
value: '是否同意以上内容:' value: '是否同意以上内容:'
}, { }, {
type: ElementType.CHECKBOX, type: ElementType.CONTROL,
checkbox: { control: {
value: true type: ControlType.CHECKBOX,
code: '98175',
value: '',
valueSets: [{
value: '同意',
code: '98175'
}, {
value: '否定',
code: '98176'
}]
}, },
value: '' value: ''
}, {
value: '同意'
}, {
type: ElementType.CHECKBOX,
value: ''
}, {
value: '否定'
}, { }, {
value: '\n' value: '\n'
}]) }])

Loading…
Cancel
Save