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>
<div class="options">
<ul>
<li data-control='text'>文本型</li>
<li data-control="select">列举型</li>
<li data-control='text'>文本</li>
<li data-control="select">列举</li>
<li data-control="checkbox">复选框</li>
</ul>
</div>
</div>

@ -39,6 +39,7 @@ import { Control } from './control/Control'
import { zipElementList } from '../../utils/element'
import { CheckboxParticle } from './particle/CheckboxParticle'
import { DeepRequired } from '../../interface/Common'
import { ControlComponent } from '../../dataset/enum/Control'
export class Draw {
@ -567,7 +568,10 @@ export class Draw {
element.width = innerWidth
metrics.width = innerWidth
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 elementWidth = (width + gap * 2) * scale
element.width = elementWidth
@ -707,7 +711,10 @@ export class Draw {
if (this.mode !== EditorMode.CLEAN) {
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.checkboxParticle.render(ctx, element, x, y + offsetY)
} else {

@ -2,11 +2,13 @@ import { ControlComponent, ControlType } from '../../../dataset/enum/Control'
import { ElementType } from '../../../dataset/enum/Element'
import { IControl, IControlInitOption, IControlInstance, IControlOption } from '../../../interface/Control'
import { IElement, IElementPosition } from '../../../interface/Element'
import { IRange } from '../../../interface/Range'
import { deepClone } from '../../../utils'
import { pickElementAttr, zipElementList } from '../../../utils/element'
import { Listener } from '../../listener/Listener'
import { RangeManager } from '../../range/RangeManager'
import { Draw } from '../Draw'
import { CheckboxControl } from './checkbox/CheckboxControl'
import { SelectControl } from './select/SelectControl'
import { TextControl } from './text/TextControl'
@ -66,7 +68,7 @@ export class Control {
return this.draw.getPageNo() * (height + pageGap)
}
public getRange() {
public getRange(): IRange {
return this.range.getRange()
}
@ -103,6 +105,8 @@ export class Control {
const selectControl = new SelectControl(element, this)
this.activeControl = selectControl
selectControl.awake()
} else if (control.type === ControlType.CHECKBOX) {
this.activeControl = new CheckboxControl(element, this)
}
// 激活控件回调
setTimeout(() => {
@ -270,6 +274,7 @@ export class Control {
const elementList = this.getElementList()
const startElement = elementList[startIndex]
const control = startElement.control!
if (!control.placeholder) return
const placeholderStrList = control.placeholder.split('')
for (let p = 0; p < placeholderStrList.length; 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 { LETTER_REG, NUMBER_LIKE_REG } from '../../dataset/constant/Regular'
import { Control } from '../draw/control/Control'
import { CheckboxControl } from '../draw/control/checkbox/CheckboxControl'
export class CanvasEvent {
@ -202,6 +203,7 @@ export class CanvasEvent {
// 设置位置上下文
this.position.setPositionContext({
isTable: isTable || false,
isCheckbox: isCheckbox || false,
isControl: isControl || false,
index,
trIndex,
@ -235,6 +237,10 @@ export class CanvasEvent {
value: true
}
}
const activeControl = this.control.getActiveControl()
if (activeControl instanceof CheckboxControl) {
activeControl.setSelect()
}
}
this.draw.render({
curIndex,

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

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

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

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

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

@ -97,9 +97,44 @@ export function formatElementList(elementList: IElement[], options: IFormatEleme
// 值
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 = 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 {
if (!value || !value.length) {
if (Array.isArray(valueSets) && valueSets.length) {
const valueSet = valueSets.find(v => v.code === code)
@ -126,7 +161,8 @@ export function formatElementList(elementList: IElement[], options: IFormatEleme
i++
}
}
} else {
}
} else if (placeholder) {
// placeholder
const thePlaceholderArgs: Pick<IElement, 'color'> = {}
if (editorOptions && editorOptions.control) {

@ -415,7 +415,7 @@ window.onload = function () {
switch (type) {
case ControlType.TEXT:
new Dialog({
title: '文本控件',
title: '文本控件',
data: [{
type: 'text',
label: '占位符',
@ -449,7 +449,7 @@ window.onload = function () {
break
case ControlType.SELECT:
new Dialog({
title: '列举控件',
title: '列举控件',
data: [{
type: 'text',
label: '占位符',
@ -487,6 +487,38 @@ window.onload = function () {
}
})
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:
break
}

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

Loading…
Cancel
Save