feat: add radio element #494

Co-authored-by: jeffycai <caixiaobing@live.cn>
Co-authored-by: Hufe921 <huangyunfeihufe@hotmail.com>
pr675
huaworld 2 years ago committed by GitHub
parent 2e1403507f
commit c6d9cffc27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -61,7 +61,8 @@ interface IEditorOption {
wordBreak?: WordBreak // Word and punctuation breaks: No punctuation in the first line of the BREAK_WORD &The word is not split, and the line is folded after BREAK_ALL full according to the width of the character. default: BREAK_WORD
watermark?: IWatermark // Watermark{data:string; color?:string; opacity?:number; size?:number; font?:string;}
control?: IControlOption // Control {placeholderColor?:string; bracketColor?:string; prefix?:string; postfix?:string; borderWidth?: number; borderColor?: string;}
checkbox?: ICheckboxOption // Checkbox {width?:number; height?:number; gap?:number; lineWidth?:number; fillStyle?:string; fontStyle?: string;}
checkbox?: ICheckboxOption // Checkbox {width?:number; height?:number; gap?:number; lineWidth?:number; fillStyle?:string; strokeStyle?: string;}
radio?: IRadioOption // Radio {width?:number; height?:number; gap?:number; lineWidth?:number; fillStyle?:string; strokeStyle?: string;}
cursor?: ICursorOption // Cursor style. {width?: number; color?: string; dragWidth?: number; dragColor?: string;}
title?: ITitleOption // Title configuration.{ defaultFirstSize?: number; defaultSecondSize?: number; defaultThirdSize?: number defaultFourthSize?: number; defaultFifthSize?: number; defaultSixthSize?: number;}
placeholder?: IPlaceholder // Placeholder text

@ -15,6 +15,7 @@ interface IElement {
PAGE_BREAK = 'pageBreak',
CONTROL = 'control',
CHECKBOX = 'checkbox',
RADIO = 'radio',
LATEX = 'latex',
TAB = 'tab',
DATE = 'date',
@ -76,7 +77,8 @@ interface IElement {
type: {
TEXT = 'text',
SELECT = 'select',
CHECKBOX = 'checkbox'
CHECKBOX = 'checkbox',
RADIO = 'radio'
};
value: IElement[] | null;
placeholder?: string;
@ -98,6 +100,13 @@ interface IElement {
code: string;
}[];
checkbox?: {
value: boolean | null;
code?: string;
min?: number;
max?: number;
disabled?: boolean;
};
radio?: {
value: boolean | null;
code?: string;
disabled?: boolean;
@ -115,7 +124,8 @@ interface IElement {
POSTFIX = 'postfix',
PLACEHOLDER = 'placeholder',
VALUE = 'value',
CHECKBOX = 'checkbox'
CHECKBOX = 'checkbox',
RADIO = 'radio'
};
// checkbox
checkbox?: {
@ -123,6 +133,12 @@ interface IElement {
code?: string;
disabled?: boolean;
};
// radio
radio?: {
value: boolean | null;
code?: string;
disabled?: boolean;
};
// LaTeX
laTexSVG?: string;
// date

@ -61,7 +61,8 @@ interface IEditorOption {
wordBreak?: WordBreak // 单词与标点断行BREAK_WORD首行不出现标点&单词不拆分、BREAK_ALL按字符宽度撑满后折行。默认BREAK_WORD
watermark?: IWatermark // 水印信息。{data:string; color?:string; opacity?:number; size?:number; font?:string;}
control?: IControlOption // 控件信息。 {placeholderColor?:string; bracketColor?:string; prefix?:string; postfix?:string; borderWidth?: number; borderColor?: string;}
checkbox?: ICheckboxOption // 复选框信息。{width?:number; height?:number; gap?:number; lineWidth?:number; fillStyle?:string; fontStyle?: string;}
checkbox?: ICheckboxOption // 复选框信息。{width?:number; height?:number; gap?:number; lineWidth?:number; fillStyle?:string; strokeStyle?: string;}
radio?: IRadioOption // 单选框信息。{width?:number; height?:number; gap?:number; lineWidth?:number; fillStyle?:string; strokeStyle?: string;}
cursor?: ICursorOption // 光标样式。{width?: number; color?: string; dragWidth?: number; dragColor?: string;}
title?: ITitleOption // 标题配置。{ defaultFirstSize?: number; defaultSecondSize?: number; defaultThirdSize?: number defaultFourthSize?: number; defaultFifthSize?: number; defaultSixthSize?: number;}
placeholder?: IPlaceholder // 编辑器空白占位文本

@ -15,6 +15,7 @@ interface IElement {
PAGE_BREAK = 'pageBreak',
CONTROL = 'control',
CHECKBOX = 'checkbox',
RADIO = 'radio',
LATEX = 'latex',
TAB = 'tab',
DATE = 'date',
@ -76,7 +77,8 @@ interface IElement {
type: {
TEXT = 'text',
SELECT = 'select',
CHECKBOX = 'checkbox'
CHECKBOX = 'checkbox',
RADIO = 'radio'
};
value: IElement[] | null;
placeholder?: string;
@ -98,6 +100,13 @@ interface IElement {
code: string;
}[];
checkbox?: {
value: boolean | null;
code?: string;
min?: number;
max?: number;
disabled?: boolean;
};
radio?: {
value: boolean | null;
code?: string;
disabled?: boolean;
@ -115,7 +124,8 @@ interface IElement {
POSTFIX = 'postfix',
PLACEHOLDER = 'placeholder',
VALUE = 'value',
CHECKBOX = 'checkbox'
CHECKBOX = 'checkbox',
RADIO = 'radio'
};
// 复选框
checkbox?: {
@ -123,6 +133,12 @@ interface IElement {
code?: string;
disabled?: boolean;
};
// 单选框
radio?: {
value: boolean | null;
code?: string;
disabled?: boolean;
};
// LaTeX
laTexSVG?: string;
// 日期

@ -280,12 +280,16 @@
<li data-control='text'>文本</li>
<li data-control="select">列举</li>
<li data-control="checkbox">复选框</li>
<li data-control="radio">单选框</li>
</ul>
</div>
</div>
<div class="menu-item__checkbox" title="复选框">
<i></i>
</div>
<div class="menu-item__radio" title="单选框">
<i></i>
</div>
<div class="menu-item__latex" title="LateX">
<i></i>
</div>

@ -0,0 +1,4 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" stroke="#3d4757" stroke-width="2" fill="none" />
<circle cx="12" cy="12" r="4" fill="#3d4757" />
</svg>

After

Width:  |  Height:  |  Size: 236 B

@ -65,6 +65,7 @@ import {
import { Control } from './control/Control'
import { getSlimCloneElementList, zipElementList } from '../../utils/element'
import { CheckboxParticle } from './particle/CheckboxParticle'
import { RadioParticle } from './particle/RadioParticle'
import { DeepRequired, IPadding } from '../../interface/Common'
import {
ControlComponent,
@ -142,6 +143,7 @@ export class Draw {
private superscriptParticle: SuperscriptParticle
private subscriptParticle: SubscriptParticle
private checkboxParticle: CheckboxParticle
private radioParticle: RadioParticle
private blockParticle: BlockParticle
private listParticle: ListParticle
private lineBreakParticle: LineBreakParticle
@ -216,6 +218,7 @@ export class Draw {
this.superscriptParticle = new SuperscriptParticle()
this.subscriptParticle = new SubscriptParticle()
this.checkboxParticle = new CheckboxParticle(this)
this.radioParticle = new RadioParticle(this)
this.blockParticle = new BlockParticle(this)
this.listParticle = new ListParticle(this)
this.lineBreakParticle = new LineBreakParticle(this)
@ -751,6 +754,10 @@ export class Draw {
return this.checkboxParticle
}
public getRadioParticle(): RadioParticle {
return this.radioParticle
}
public getControl(): Control {
return this.control
}
@ -1414,6 +1421,15 @@ export class Draw {
element.width = availableWidth / scale
metrics.width = availableWidth
metrics.height = defaultSize
} else if (
element.type === ElementType.RADIO ||
element.controlComponent === ControlComponent.RADIO
) {
const { width, height, gap } = this.options.radio
const elementWidth = width + gap * 2
element.width = elementWidth
metrics.width = elementWidth * scale
metrics.height = height * scale
} else if (
element.type === ElementType.CHECKBOX ||
element.controlComponent === ControlComponent.CHECKBOX
@ -1805,6 +1821,12 @@ export class Draw {
) {
this.textParticle.complete()
this.checkboxParticle.render(ctx, element, x, y + offsetY)
} else if (
element.type === ElementType.RADIO ||
element.controlComponent === ControlComponent.RADIO
) {
this.textParticle.complete()
this.radioParticle.render(ctx, element, x, y + offsetY)
} else if (element.type === ElementType.TAB) {
this.textParticle.complete()
} else if (element.rowFlex === RowFlex.ALIGNMENT) {

@ -32,6 +32,7 @@ import { Listener } from '../../listener/Listener'
import { RangeManager } from '../../range/RangeManager'
import { Draw } from '../Draw'
import { CheckboxControl } from './checkbox/CheckboxControl'
import { RadioControl } from './radio/RadioControl'
import { ControlSearch } from './interactive/ControlSearch'
import { ControlBorder } from './richtext/Border'
import { SelectControl } from './select/SelectControl'
@ -236,6 +237,8 @@ export class Control {
selectControl.awake()
} else if (control.type === ControlType.CHECKBOX) {
this.activeControl = new CheckboxControl(element, this)
} else if (control.type === ControlType.RADIO) {
this.activeControl = new RadioControl(element, this)
}
// 激活控件回调
nextTick(() => {
@ -522,7 +525,8 @@ export class Control {
})
} else if (
type === ControlType.SELECT ||
type === ControlType.CHECKBOX
type === ControlType.CHECKBOX ||
type === ControlType.RADIO
) {
const innerText = code
?.split(',')
@ -629,6 +633,10 @@ export class Control {
const checkbox = new CheckboxControl(element, this)
const codes = value?.split(',') || []
checkbox.setSelect(codes, controlContext, controlRule)
} else if (type === ControlType.RADIO) {
const radio = new RadioControl(element, this)
const codes = value ? [value] : []
radio.setSelect(codes, controlContext, controlRule)
}
// 修改后控件结束索引
let newEndIndex = i

@ -9,8 +9,8 @@ import { IElement } from '../../../../interface/Element'
import { Control } from '../Control'
export class CheckboxControl implements IControlInstance {
private element: IElement
private control: Control
protected element: IElement
protected control: Control
constructor(element: IElement, control: Control) {
this.element = element

@ -0,0 +1,57 @@
import { ControlComponent } from '../../../../dataset/enum/Control'
import {
IControlContext,
IControlRuleOption
} from '../../../../interface/Control'
import { CheckboxControl } from '../checkbox/CheckboxControl'
export class RadioControl extends CheckboxControl {
public setSelect(
codes: string[],
context: IControlContext = {},
options: IControlRuleOption = {}
) {
// 校验是否可以设置
if (!options.isIgnoreDisabledRule && this.control.getIsDisabledControl()) {
return
}
const { control } = this.element
const elementList = context.elementList || this.control.getElementList()
const { startIndex } = context.range || this.control.getRange()
const startElement = elementList[startIndex]
// 向左查找
let preIndex = startIndex
while (preIndex > 0) {
const preElement = elementList[preIndex]
if (
preElement.controlId !== startElement.controlId ||
preElement.controlComponent === ControlComponent.PREFIX
) {
break
}
if (preElement.controlComponent === ControlComponent.RADIO) {
const radio = preElement.radio!
radio.value = codes.includes(radio.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.RADIO) {
const radio = nextElement.radio!
radio.value = codes.includes(radio.code!)
}
nextIndex++
}
control!.code = codes.join(',')
this.control.repaintControl()
}
}

@ -35,7 +35,7 @@ export class CheckboxParticle {
y: number
) {
const {
checkbox: { gap, lineWidth, fillStyle, fontStyle },
checkbox: { gap, lineWidth, fillStyle, strokeStyle },
scale
} = this.options
const { metrics, checkbox } = element
@ -60,7 +60,7 @@ export class CheckboxParticle {
ctx.fillRect(left, top, width, height)
// 勾选对号
ctx.beginPath()
ctx.strokeStyle = fontStyle
ctx.strokeStyle = strokeStyle
ctx.lineWidth = lineWidth * 2 * scale
ctx.moveTo(left + 2 * scale, top + height / 2)
ctx.lineTo(left + width / 2, top + height - 3 * scale)

@ -0,0 +1,65 @@
import { DeepRequired } from '../../../interface/Common'
import { IEditorOption } from '../../../interface/Editor'
import { IElement } from '../../../interface/Element'
import { IRowElement } from '../../../interface/Row'
import { Draw } from '../Draw'
export class RadioParticle {
private draw: Draw
private options: DeepRequired<IEditorOption>
constructor(draw: Draw) {
this.draw = draw
this.options = draw.getOptions()
}
public setSelect(element: IElement) {
const { radio } = element
if (radio) {
radio.value = !radio.value
} else {
element.radio = {
value: true
}
}
this.draw.render({
isCompute: false,
isSetCursor: false
})
}
public render(
ctx: CanvasRenderingContext2D,
element: IRowElement,
x: number,
y: number
) {
const {
radio: { gap, lineWidth, fillStyle, strokeStyle },
scale
} = this.options
const { metrics, radio } = element
// left top 四舍五入避免1像素问题
const left = Math.round(x + gap * scale)
const top = Math.round(y - metrics.height + lineWidth)
const width = metrics.width - gap * 2 * scale
const height = metrics.height
ctx.save()
ctx.beginPath()
ctx.translate(0.5, 0.5)
// 边框
ctx.strokeStyle = radio?.value ? fillStyle : strokeStyle
ctx.lineWidth = lineWidth
ctx.arc(left + width / 2, top + height / 2, width / 2, 0, Math.PI * 2)
ctx.stroke()
// 填充选中色
if (radio?.value) {
ctx.beginPath()
ctx.fillStyle = fillStyle
ctx.arc(left + width / 2, top + height / 2, width / 3, 0, Math.PI * 2)
ctx.fill()
}
ctx.closePath()
ctx.restore()
}
}

@ -124,8 +124,13 @@ function dblclick(host: CanvasEvent, evt: MouseEvent) {
return
}
}
// 复选框双击时是切换选择状态,禁用扩选
if (positionContext.isCheckbox && positionContext.isDirectHit) return
// 复选/单选框双击时是切换选择状态,禁用扩选
if (
(positionContext.isCheckbox || positionContext.isRadio) &&
positionContext.isDirectHit
) {
return
}
// 自动扩选文字-分词处理,优先使用分词器否则降级使用光标所在位置
const rangeManager = draw.getRange()
const segmenterRange =

@ -4,6 +4,7 @@ import { MouseEventButton } from '../../../dataset/enum/Event'
import { deepClone } from '../../../utils'
import { isMod } from '../../../utils/hotkey'
import { CheckboxControl } from '../../draw/control/checkbox/CheckboxControl'
import { RadioControl } from '../../draw/control/radio/RadioControl'
import { CanvasEvent } from '../CanvasEvent'
export function setRangeCache(host: CanvasEvent) {
@ -54,6 +55,7 @@ export function mousedown(evt: MouseEvent, host: CanvasEvent) {
index,
isDirectHit,
isCheckbox,
isRadio,
isImage,
isTable,
tdValueIndex,
@ -73,11 +75,14 @@ export function mousedown(evt: MouseEvent, host: CanvasEvent) {
// 绘制
const isDirectHitImage = !!(isDirectHit && isImage)
const isDirectHitCheckbox = !!(isDirectHit && isCheckbox)
const isDirectHitRadio = !!(isDirectHit && isRadio)
if (~index) {
rangeManager.setRange(curIndex, curIndex)
position.setCursorPosition(positionList[curIndex])
// 复选框
const isSetCheckbox = isDirectHitCheckbox && !isReadonly
// 单选框
const isSetRadio = isDirectHitRadio && !isReadonly
if (isSetCheckbox) {
const { checkbox, control } = curElement
// 复选框不在控件内独立控制
@ -98,12 +103,25 @@ export function mousedown(evt: MouseEvent, host: CanvasEvent) {
activeControl.setSelect(codes)
}
}
} else if (isSetRadio) {
const { control, radio } = curElement
// 单选框不在控件内独立控制
if (!control) {
draw.getRadioParticle().setSelect(curElement)
} else {
const codes = radio?.code ? [radio.code] : []
const activeControl = draw.getControl().getActiveControl()
if (activeControl instanceof RadioControl) {
activeControl.setSelect(codes)
}
}
} else {
draw.render({
curIndex,
isCompute: false,
isSubmitHistory: false,
isSetCursor: !isDirectHitImage && !isDirectHitCheckbox
isSetCursor:
!isDirectHitImage && !isDirectHitCheckbox && !isDirectHitRadio
})
}
// 首字需定位到行首,非上一行最后一个字后

@ -394,6 +394,9 @@ export class Position {
tdValueElement.type === ElementType.CHECKBOX ||
tdValueElement.controlComponent ===
ControlComponent.CHECKBOX,
isRadio:
tdValueElement.type === ElementType.RADIO ||
tdValueElement.controlComponent === ControlComponent.RADIO,
isControl: !!tdValueElement.controlId,
isImage: tablePosition.isImage,
isDirectHit: tablePosition.isDirectHit,
@ -431,6 +434,16 @@ export class Position {
isCheckbox: true
}
}
if (
element.type === ElementType.RADIO ||
element.controlComponent === ControlComponent.RADIO
) {
return {
index: curPositionIndex,
isDirectHit: true,
isRadio: true
}
}
let hitLineStartIndex: number | undefined
// 判断是否在文字中间前后
if (elementList[index].value !== ZERO) {
@ -651,6 +664,7 @@ export class Position {
const {
index,
isCheckbox,
isRadio,
isControl,
isTable,
trIndex,
@ -663,6 +677,7 @@ export class Position {
this.setPositionContext({
isTable: isTable || false,
isCheckbox: isCheckbox || false,
isRadio: isRadio || false,
isControl: isControl || false,
index,
trIndex,

@ -12,6 +12,7 @@ enum ElementType {
PAGE_BREAK = 'pageBreak',
CONTROL = 'control',
CHECKBOX = 'checkbox',
RADIO = 'radio',
LATEX = 'latex',
TAB = 'tab',
DATE = 'date',

@ -6,5 +6,5 @@ export const defaultCheckboxOption: Readonly<Required<ICheckboxOption>> = {
gap: 5,
lineWidth: 1,
fillStyle: '#5175f4',
fontStyle: '#ffffff'
strokeStyle: '#ffffff'
}

@ -60,6 +60,7 @@ export const EDITOR_ELEMENT_ZIP_ATTR: Array<keyof IElement> = [
'valueList',
'control',
'checkbox',
'radio',
'dateFormat',
'block',
'level',

@ -0,0 +1,10 @@
import { IRadioOption } from '../../interface/Radio'
export const defaultRadioOption: Readonly<Required<IRadioOption>> = {
width: 14,
height: 14,
gap: 5,
lineWidth: 1,
fillStyle: '#5175f4',
strokeStyle: '#000000'
}

@ -1,7 +1,8 @@
export enum ControlType {
TEXT = 'text',
SELECT = 'select',
CHECKBOX = 'checkbox'
CHECKBOX = 'checkbox',
RADIO = 'radio'
}
export enum ControlComponent {
@ -9,7 +10,8 @@ export enum ControlComponent {
POSTFIX = 'postfix',
PLACEHOLDER = 'placeholder',
VALUE = 'value',
CHECKBOX = 'checkbox'
CHECKBOX = 'checkbox',
RADIO = 'radio'
}
// 控件内容缩进方式

@ -9,6 +9,7 @@ export enum ElementType {
PAGE_BREAK = 'pageBreak',
CONTROL = 'control',
CHECKBOX = 'checkbox',
RADIO = 'radio',
LATEX = 'latex',
TAB = 'tab',
DATE = 'date',

@ -32,7 +32,9 @@ import { ControlIndentation, ControlType } from './dataset/enum/Control'
import { defaultControlOption } from './dataset/constant/Control'
import { IControlOption } from './interface/Control'
import { ICheckboxOption } from './interface/Checkbox'
import { IRadioOption } from './interface/Radio'
import { defaultCheckboxOption } from './dataset/constant/Checkbox'
import { defaultRadioOption } from './dataset/constant/Radio'
import { DeepRequired } from './interface/Common'
import { INavigateInfo } from './core/draw/interactive/Search'
import { Shortcut } from './core/shortcut/Shortcut'
@ -117,6 +119,10 @@ export default class Editor {
...defaultCheckboxOption,
...options.checkbox
}
const radioOptions: Required<IRadioOption> = {
...defaultRadioOption,
...options.radio
}
const cursorOptions: Required<ICursorOption> = {
...defaultCursorOption,
...options.cursor
@ -200,6 +206,7 @@ export default class Editor {
watermark: waterMarkOptions,
control: controlOptions,
checkbox: checkboxOptions,
radio: radioOptions,
cursor: cursorOptions,
title: titleOptions,
placeholder: placeholderOptions,

@ -10,5 +10,5 @@ export interface ICheckboxOption {
gap?: number
lineWidth?: number
fillStyle?: string
fontStyle?: string
strokeStyle?: string
}

@ -2,6 +2,7 @@ import { ControlType, ControlIndentation } from '../dataset/enum/Control'
import { EditorZone } from '../dataset/enum/Editor'
import { ICheckbox } from './Checkbox'
import { IElement } from './Element'
import { IRadio } from './Radio'
import { IRange } from './Range'
export interface IValueSet {
@ -22,6 +23,12 @@ export interface IControlCheckbox {
checkbox?: ICheckbox
}
export interface IControlRadio {
code: string | null
valueSets: IValueSet[]
radio?: IRadio
}
export interface IControlHighlightRule {
keyword: string
alpha?: number
@ -65,6 +72,7 @@ export type IControl = IControlBasic &
IControlRule &
Partial<IControlSelect> &
Partial<IControlCheckbox> &
Partial<IControlRadio> &
Partial<IControlStyle>
export interface IControlOption {

@ -7,6 +7,7 @@ import {
} from '../dataset/enum/Editor'
import { IBackgroundOption } from './Background'
import { ICheckboxOption } from './Checkbox'
import { IRadioOption } from './Radio'
import { IPadding } from './Common'
import { IControlOption } from './Control'
import { ICursorOption } from './Cursor'
@ -77,6 +78,7 @@ export interface IEditorOption {
watermark?: IWatermark
control?: IControlOption
checkbox?: ICheckboxOption
radio?: IRadioOption
cursor?: ICursorOption
title?: ITitleOption
placeholder?: IPlaceholder

@ -8,6 +8,7 @@ import { TableBorder } from '../dataset/enum/table/Table'
import { IBlock } from './Block'
import { ICheckbox } from './Checkbox'
import { IControl } from './Control'
import { IRadio } from './Radio'
import { ITextDecoration } from './Text'
import { IColgroup } from './table/Colgroup'
import { ITr } from './table/Tr'
@ -94,6 +95,10 @@ export interface ICheckboxElement {
checkbox?: ICheckbox
}
export interface IRadioElement {
radio?: IRadio
}
export interface ILaTexElement {
laTexSVG?: string
}
@ -124,6 +129,7 @@ export type IElement = IElementBasic &
ISeparator &
IControlElement &
ICheckboxElement &
IRadioElement &
ILaTexElement &
IDateElement &
IImageElement &

@ -9,6 +9,7 @@ export interface ICurrentPosition {
x?: number
y?: number
isCheckbox?: boolean
isRadio?: boolean
isControl?: boolean
isImage?: boolean
isTable?: boolean
@ -41,6 +42,7 @@ export type IGetFloatPositionByXYPayload = IGetPositionByXYPayload & {
export interface IPositionContext {
isTable: boolean
isCheckbox?: boolean
isRadio?: boolean
isControl?: boolean
index?: number
trIndex?: number

@ -0,0 +1,14 @@
export interface IRadio {
value: boolean | null
code?: string
disabled?: boolean
}
export interface IRadioOption {
width?: number
height?: number
gap?: number
lineWidth?: number
fillStyle?: string
strokeStyle?: string
}

@ -222,7 +222,11 @@ export function formatElementList(
const { prefix, postfix, value, placeholder, code, type, valueSets } =
el.control
const {
editorOptions: { control: controlOption, checkbox: checkboxOption }
editorOptions: {
control: controlOption,
checkbox: checkboxOption,
radio: radioOption
}
} = options
const controlId = getUUID()
// 移除父节点
@ -258,6 +262,7 @@ export function formatElementList(
if (
(value && value.length) ||
type === ControlType.CHECKBOX ||
type === ControlType.RADIO ||
(type === ControlType.SELECT && code && (!value || !value.length))
) {
let valueList: IElement[] = value || []
@ -309,6 +314,53 @@ export function formatElementList(
}
}
}
} else if (type === ControlType.RADIO) {
if (Array.isArray(valueSets) && valueSets.length) {
// 拆分valueList优先使用其属性
const valueStyleList = valueList.reduce(
(pre, cur) =>
pre.concat(
cur.value.split('').map(v => ({ ...cur, value: v }))
),
[] as IElement[]
)
let valueStyleIndex = 0
for (let v = 0; v < valueSets.length; v++) {
const valueSet = valueSets[v]
// radio组件
elementList.splice(i, 0, {
...controlContext,
controlId,
value: '',
type: el.type,
control: el.control,
controlComponent: ControlComponent.RADIO,
radio: {
code: valueSet.code,
value: code === 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, {
...controlContext,
...controlDefaultStyle,
...valueStyleList[valueStyleIndex],
controlId,
value: value === '\n' ? ZERO : value,
letterSpacing: isLastLetter ? radioOption.gap : 0,
control: el.control,
controlComponent: ControlComponent.VALUE
})
valueStyleIndex++
i++
}
}
}
} else {
if (!value || !value.length) {
if (Array.isArray(valueSets) && valueSets.length) {
@ -955,6 +1007,13 @@ export function createDomFromElementList(
checkbox.setAttribute('checked', 'true')
}
clipboardDom.append(checkbox)
} else if (element.type === ElementType.RADIO) {
const radio = document.createElement('input')
radio.type = 'radio'
if (element.radio?.value) {
radio.setAttribute('checked', 'true')
}
clipboardDom.append(radio)
} else if (element.type === ElementType.TAB) {
const tab = document.createElement('span')
tab.innerHTML = `${NON_BREAKING_SPACE}${NON_BREAKING_SPACE}`
@ -1200,6 +1259,17 @@ export function getElementListByHTML(
value: (<HTMLInputElement>node).checked
}
})
} else if (
node.nodeName === 'INPUT' &&
(<HTMLInputElement>node).type === ControlComponent.RADIO
) {
elementList.push({
type: ElementType.RADIO,
value: '',
radio: {
value: (<HTMLInputElement>node).checked
}
})
} else {
findTextNode(node)
if (node.nodeType === 1 && n !== childNodes.length - 1) {
@ -1275,6 +1345,8 @@ export function getTextFromElementList(elementList: IElement[]) {
})
} else if (element.type === ElementType.CHECKBOX) {
text += element.checkbox?.value ? `` : ``
} else if (element.type === ElementType.RADIO) {
text += element.radio?.value ? `` : ``
} else if (
!element.type ||
element.type === ElementType.LATEX ||

@ -768,6 +768,44 @@ window.onload = function () {
}
})
break
case ControlType.RADIO:
new Dialog({
title: '单选框控件',
data: [
{
type: 'text',
label: '默认值',
name: 'code',
placeholder: '请输入默认值'
},
{
type: 'textarea',
label: '值集',
name: 'valueSets',
required: true,
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
}
@ -789,6 +827,20 @@ window.onload = function () {
])
}
const radioDom = document.querySelector<HTMLDivElement>('.menu-item__radio')!
radioDom.onclick = function () {
console.log('radio')
instance.command.executeInsertElementList([
{
type: ElementType.RADIO,
checkbox: {
value: false
},
value: ''
}
])
}
const latexDom = document.querySelector<HTMLDivElement>('.menu-item__latex')!
latexDom.onclick = function () {
console.log('LaTeX')

@ -540,6 +540,10 @@ ul {
background-image: url('./assets/images/checkbox.svg');
}
.menu-item__radio i {
background-image: url('./assets/images/radio.svg');
}
.menu-item__latex i {
background-image: url('./assets/images/latex.svg');
}

Loading…
Cancel
Save