Merge pull request #98 from Hufe921/feature/content-block
Feature/content block
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import Editor from '../../../src/editor'
|
||||
|
||||
describe('菜单-内容块', () => {
|
||||
const url = 'http://localhost:3000/canvas-editor/'
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit(url)
|
||||
|
||||
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||
})
|
||||
|
||||
|
||||
it('内容块', () => {
|
||||
cy.getEditor().then((editor: Editor) => {
|
||||
editor.listener.saved = function (payload) {
|
||||
const data = payload.data
|
||||
|
||||
expect(data[0].type).to.eq('block')
|
||||
|
||||
expect(data[0].block?.iframeBlock?.src).to.eq(url)
|
||||
}
|
||||
|
||||
editor.command.executeSelectAll()
|
||||
|
||||
editor.command.executeBackspace()
|
||||
|
||||
cy.get('.menu-item__block').click()
|
||||
|
||||
cy.get('.dialog-option__item [name="width"]').type('500')
|
||||
|
||||
cy.get('.dialog-option__item [name="height"]').type('300')
|
||||
|
||||
cy.get('.dialog-option__item [name="value"]').type(url)
|
||||
|
||||
cy.get('.dialog-menu button').eq(1).click()
|
||||
|
||||
cy.get('@canvas').type('{ctrl}s')
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -188,6 +188,9 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-item__block" title="内容块">
|
||||
<i></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-divider"></div>
|
||||
<div class="menu-item">
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757"><path d="M8.923 11v1h-2A2 2 0 015 10.55c.328-.15.638-.335.923-.55a1 1 0 001 1h2zm0-6h-2a1 1 0 00-1 1A4.997 4.997 0 005 5.45 2 2 0 016.923 4h2v1z"/><path d="M4 10a2 2 0 100-4 2 2 0 000 4zm0 1a3 3 0 110-6 3 3 0 010 6zm6-9a1 1 0 00-1 1v3a1 1 0 001 1h3a1 1 0 001-1V3a1 1 0 00-1-1h-3zm0-1h3a2 2 0 012 2v3a2 2 0 01-2 2h-3a2 2 0 01-2-2V3a2 2 0 012-2zm0 10a1 1 0 00-1 1v1a1 1 0 001 1h3a1 1 0 001-1v-1a1 1 0 00-1-1h-3zm0-1h3a2 2 0 012 2v1a2 2 0 01-2 2h-3a2 2 0 01-2-2v-1a2 2 0 012-2z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 568 B |
@@ -6,9 +6,11 @@ export interface IDialogData {
|
||||
label?: string;
|
||||
name: string;
|
||||
value?: string;
|
||||
options?: { label: string; value: string; }[];
|
||||
placeholder?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export interface IDialogConfirm {
|
||||
@@ -29,7 +31,7 @@ export class Dialog {
|
||||
private options: IDialogOptions
|
||||
private mask: HTMLDivElement | null
|
||||
private container: HTMLDivElement | null
|
||||
private inputList: (HTMLInputElement | HTMLTextAreaElement)[]
|
||||
private inputList: (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)[]
|
||||
|
||||
constructor(options: IDialogOptions) {
|
||||
this.options = options
|
||||
@@ -83,10 +85,21 @@ export class Dialog {
|
||||
const optionName = document.createElement('span')
|
||||
optionName.append(document.createTextNode(option.label))
|
||||
optionItemContainer.append(optionName)
|
||||
if (option.required) {
|
||||
optionName.classList.add('dialog-option__item--require')
|
||||
}
|
||||
}
|
||||
// 选项输入框
|
||||
let optionInput: HTMLInputElement | HTMLTextAreaElement
|
||||
if (option.type === 'textarea') {
|
||||
let optionInput: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||
if (option.type === 'select') {
|
||||
optionInput = document.createElement('select')
|
||||
option.options?.forEach(item => {
|
||||
const optionItem = document.createElement('option')
|
||||
optionItem.value = item.value
|
||||
optionItem.label = item.label
|
||||
optionInput.append(optionItem)
|
||||
})
|
||||
} else if (option.type === 'textarea') {
|
||||
optionInput = document.createElement('textarea')
|
||||
} else {
|
||||
optionInput = document.createElement('input')
|
||||
@@ -100,7 +113,9 @@ export class Dialog {
|
||||
}
|
||||
optionInput.name = option.name
|
||||
optionInput.value = option.value || ''
|
||||
optionInput.placeholder = option.placeholder || ''
|
||||
if (!(optionInput instanceof HTMLSelectElement)) {
|
||||
optionInput.placeholder = option.placeholder || ''
|
||||
}
|
||||
optionItemContainer.append(optionInput)
|
||||
optionContainer.append(optionItemContainer)
|
||||
this.inputList.push(optionInput)
|
||||
|
||||
@@ -61,10 +61,12 @@
|
||||
margin-right: 12px;
|
||||
font-size: 14px;
|
||||
color: #3d4757;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dialog-option__item input,
|
||||
.dialog-option__item textarea {
|
||||
.dialog-option__item textarea,
|
||||
.dialog-option__item select {
|
||||
width: 276px;
|
||||
height: 30px;
|
||||
border-radius: 2px;
|
||||
@@ -83,6 +85,14 @@
|
||||
border-color: #4991f2;
|
||||
}
|
||||
|
||||
.dialog-option__item--require::before {
|
||||
content: "*";
|
||||
color: #f56c6c;
|
||||
margin-right: 4px;
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
}
|
||||
|
||||
.dialog-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.block-item {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid rgb(235 236 240);
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
color: #606266;
|
||||
background: #ffffff;
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@import './control/select.css';
|
||||
@import './date/datePicker.css';
|
||||
@import './block/block.css';
|
||||
|
||||
.inputarea {
|
||||
width: 0;
|
||||
@@ -442,6 +443,7 @@
|
||||
padding: 16px;
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -430,7 +430,9 @@ export class CommandAdapt {
|
||||
trList
|
||||
}
|
||||
// 格式化element
|
||||
formatElementList([element])
|
||||
formatElementList([element], {
|
||||
editorOptions: this.options
|
||||
})
|
||||
const curIndex = startIndex + 1
|
||||
if (startIndex === endIndex) {
|
||||
elementList.splice(curIndex, 0, element)
|
||||
|
||||
@@ -46,6 +46,7 @@ import { WorkerManager } from '../worker/WorkerManager'
|
||||
import { Previewer } from './particle/previewer/Previewer'
|
||||
import { DateParticle } from './particle/date/DateParticle'
|
||||
import { IMargin } from '../../interface/Margin'
|
||||
import { BlockParticle } from './particle/block/BlockParticle'
|
||||
|
||||
export class Draw {
|
||||
|
||||
@@ -86,6 +87,7 @@ export class Draw {
|
||||
private superscriptParticle: SuperscriptParticle
|
||||
private subscriptParticle: SubscriptParticle
|
||||
private checkboxParticle: CheckboxParticle
|
||||
private blockParticle: BlockParticle
|
||||
private control: Control
|
||||
private workerManager: WorkerManager
|
||||
|
||||
@@ -138,6 +140,7 @@ export class Draw {
|
||||
this.superscriptParticle = new SuperscriptParticle()
|
||||
this.subscriptParticle = new SubscriptParticle()
|
||||
this.checkboxParticle = new CheckboxParticle(this)
|
||||
this.blockParticle = new BlockParticle(this)
|
||||
this.control = new Control(this)
|
||||
|
||||
new ScrollObserver(this)
|
||||
@@ -707,6 +710,11 @@ export class Draw {
|
||||
metrics.height = defaultSize * scale
|
||||
metrics.boundingBoxDescent = 0
|
||||
metrics.boundingBoxAscent = metrics.height
|
||||
} else if (element.type === ElementType.BLOCK) {
|
||||
metrics.width = element.width! * scale
|
||||
metrics.height = element.height! * scale
|
||||
metrics.boundingBoxDescent = metrics.height
|
||||
metrics.boundingBoxAscent = 0
|
||||
} else {
|
||||
// 设置上下标真实字体尺寸
|
||||
const size = element.size || this.options.defaultSize
|
||||
@@ -739,6 +747,7 @@ export class Draw {
|
||||
const preElement = elementList[i - 1]
|
||||
if (
|
||||
preElement?.type === ElementType.TABLE
|
||||
|| preElement?.type === ElementType.BLOCK
|
||||
|| preElement?.imgDisplay === ImageDisplay.INLINE
|
||||
|| element.imgDisplay === ImageDisplay.INLINE
|
||||
|| curRow.width + metrics.width > innerWidth
|
||||
@@ -880,6 +889,9 @@ export class Draw {
|
||||
// 如果是两端对齐,因canvas目前不支持letterSpacing需单独绘制文本
|
||||
this.textParticle.record(ctx, element, x, y + offsetY)
|
||||
this._drawRichText(ctx)
|
||||
} else if (element.type === ElementType.BLOCK) {
|
||||
this._drawRichText(ctx)
|
||||
this.blockParticle.render(pageNo, element, x, y)
|
||||
} else {
|
||||
this.textParticle.record(ctx, element, x, y + offsetY)
|
||||
}
|
||||
@@ -972,13 +984,19 @@ export class Draw {
|
||||
return { x, y, index }
|
||||
}
|
||||
|
||||
private _clearPage(pageNo: number) {
|
||||
const ctx = this.ctxList[pageNo]
|
||||
const pageDom = this.pageList[pageNo]
|
||||
ctx.clearRect(0, 0, pageDom.width, pageDom.height)
|
||||
this.blockParticle.clear()
|
||||
}
|
||||
|
||||
private _drawPage(positionList: IElementPosition[], rowList: IRow[], pageNo: number) {
|
||||
const { pageMode } = this.options
|
||||
const margins = this.getMargins()
|
||||
const innerWidth = this.getInnerWidth()
|
||||
const ctx = this.ctxList[pageNo]
|
||||
const pageDom = this.pageList[pageNo]
|
||||
ctx.clearRect(0, 0, pageDom.width, pageDom.height)
|
||||
this._clearPage(pageNo)
|
||||
// 绘制背景
|
||||
this.background.render(ctx)
|
||||
// 绘制页边距
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { ElementType } from '../../../../dataset/enum/Element'
|
||||
import { IRowElement } from '../../../../interface/Row'
|
||||
import { Draw } from '../../Draw'
|
||||
import { BaseBlock } from './modules/BaseBlock'
|
||||
|
||||
export class BlockParticle {
|
||||
|
||||
private draw: Draw
|
||||
private container: HTMLDivElement
|
||||
private blockContainer: HTMLDivElement
|
||||
private blockMap: Map<string, BaseBlock>
|
||||
|
||||
constructor(draw: Draw) {
|
||||
this.draw = draw
|
||||
this.container = draw.getContainer()
|
||||
this.blockMap = new Map()
|
||||
this.blockContainer = this._createBlockContainer()
|
||||
this.container.append(this.blockContainer)
|
||||
}
|
||||
|
||||
private _createBlockContainer(): HTMLDivElement {
|
||||
const blockContainer = document.createElement('div')
|
||||
blockContainer.classList.add('block-container')
|
||||
return blockContainer
|
||||
}
|
||||
|
||||
public getDraw(): Draw {
|
||||
return this.draw
|
||||
}
|
||||
|
||||
public getBlockContainer(): HTMLDivElement {
|
||||
return this.blockContainer
|
||||
}
|
||||
|
||||
public render(pageNo: number, element: IRowElement, x: number, y: number) {
|
||||
const id = element.id!
|
||||
const cacheBlock = this.blockMap.get(id)
|
||||
if (cacheBlock) {
|
||||
cacheBlock.setClientRects(pageNo, x, y)
|
||||
} else {
|
||||
const newBlock = new BaseBlock(this, element)
|
||||
newBlock.render()
|
||||
newBlock.setClientRects(pageNo, x, y)
|
||||
this.blockMap.set(id, newBlock)
|
||||
}
|
||||
}
|
||||
|
||||
public clear() {
|
||||
if (!this.blockMap.size) return
|
||||
const elementList = this.draw.getElementList()
|
||||
const blockElementIds: string[] = []
|
||||
for (let e = 0; e < elementList.length; e++) {
|
||||
const element = elementList[e]
|
||||
if (element.type === ElementType.BLOCK) {
|
||||
blockElementIds.push(element.id!)
|
||||
}
|
||||
}
|
||||
this.blockMap.forEach(block => {
|
||||
const id = block.getBlockElement().id!
|
||||
if (!blockElementIds.includes(id)) {
|
||||
block.remove()
|
||||
this.blockMap.delete(id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { BlockType } from '../../../../../dataset/enum/Block'
|
||||
import { IRowElement } from '../../../../../interface/Row'
|
||||
import { Draw } from '../../../Draw'
|
||||
import { BlockParticle } from '../BlockParticle'
|
||||
import { IFrameBlock } from './IFrameBlock'
|
||||
import { VideoBlock } from './VideoBlock'
|
||||
|
||||
export class BaseBlock {
|
||||
|
||||
private draw: Draw
|
||||
private element: IRowElement
|
||||
private block: IFrameBlock | VideoBlock | null
|
||||
private blockContainer: HTMLDivElement
|
||||
private blockItem: HTMLDivElement
|
||||
|
||||
constructor(blockParticle: BlockParticle, element: IRowElement) {
|
||||
this.draw = blockParticle.getDraw()
|
||||
this.blockContainer = blockParticle.getBlockContainer()
|
||||
this.element = element
|
||||
this.block = null
|
||||
this.blockItem = this._createBlockItem()
|
||||
this.blockContainer.append(this.blockItem)
|
||||
}
|
||||
|
||||
public getBlockElement(): IRowElement {
|
||||
return this.element
|
||||
}
|
||||
|
||||
private _createBlockItem(): HTMLDivElement {
|
||||
const blockItem = document.createElement('div')
|
||||
blockItem.classList.add('block-item')
|
||||
return blockItem
|
||||
}
|
||||
|
||||
public render() {
|
||||
const block = this.element.block!
|
||||
if (block.type === BlockType.IFRAME) {
|
||||
this.block = new IFrameBlock(this.element)
|
||||
this.block.render(this.blockItem)
|
||||
} else if (block.type === BlockType.VIDEO) {
|
||||
this.block = new VideoBlock(this.element)
|
||||
this.block.render(this.blockItem)
|
||||
}
|
||||
}
|
||||
|
||||
public setClientRects(pageNo: number, x: number, y: number) {
|
||||
const scale = this.draw.getOptions().scale
|
||||
const height = this.draw.getHeight()
|
||||
const pageGap = this.draw.getPageGap()
|
||||
const preY = pageNo * (height + pageGap)
|
||||
// 尺寸
|
||||
this.blockItem.style.width = `${this.element.width! * scale}px`
|
||||
this.blockItem.style.height = `${this.element.height! * scale}px`
|
||||
// 位置
|
||||
this.blockItem.style.left = `${x}px`
|
||||
this.blockItem.style.top = `${preY + y}px`
|
||||
}
|
||||
|
||||
public remove() {
|
||||
this.blockItem.remove()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { IRowElement } from '../../../../../interface/Row'
|
||||
|
||||
export class IFrameBlock {
|
||||
|
||||
private static readonly sandbox = [
|
||||
'allow-forms',
|
||||
'allow-scripts',
|
||||
'allow-same-origin',
|
||||
'allow-popups'
|
||||
]
|
||||
private element: IRowElement
|
||||
|
||||
constructor(element: IRowElement) {
|
||||
this.element = element
|
||||
}
|
||||
|
||||
public render(blockItemContainer: HTMLDivElement) {
|
||||
const block = this.element.block!
|
||||
const iframe = document.createElement('iframe')
|
||||
iframe.sandbox.add(...IFrameBlock.sandbox)
|
||||
iframe.style.border = 'none'
|
||||
iframe.style.width = '100%'
|
||||
iframe.style.height = '100%'
|
||||
iframe.src = block.iframeBlock?.src || ''
|
||||
blockItemContainer.append(iframe)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { IRowElement } from '../../../../../interface/Row'
|
||||
|
||||
export class VideoBlock {
|
||||
|
||||
private element: IRowElement
|
||||
|
||||
constructor(element: IRowElement) {
|
||||
this.element = element
|
||||
}
|
||||
|
||||
public render(blockItemContainer: HTMLDivElement) {
|
||||
const block = this.element.block!
|
||||
const video = document.createElement('video')
|
||||
video.style.width = '100%'
|
||||
video.style.height = '100%'
|
||||
video.style.objectFit = 'contain'
|
||||
video.src = block.videoBlock?.src || ''
|
||||
video.controls = true
|
||||
video.crossOrigin = 'anonymous'
|
||||
blockItemContainer.append(video)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,7 +50,8 @@ export const EDITOR_ELEMENT_ZIP_ATTR: Array<keyof IElement> = [
|
||||
'valueList',
|
||||
'control',
|
||||
'checkbox',
|
||||
'dateFormat'
|
||||
'dateFormat',
|
||||
'block'
|
||||
]
|
||||
|
||||
export const TEXTLIKE_ELEMENT_TYPE: ElementType[] = [
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum BlockType {
|
||||
IFRAME = 'iframe',
|
||||
VIDEO = 'video'
|
||||
}
|
||||
@@ -11,5 +11,6 @@ export enum ElementType {
|
||||
CHECKBOX = 'checkbox',
|
||||
LATEX = 'latex',
|
||||
TAB = 'tab',
|
||||
DATE = 'date'
|
||||
DATE = 'date',
|
||||
BLOCK = 'block'
|
||||
}
|
||||
+6
-2
@@ -26,6 +26,8 @@ import { DeepRequired } from './interface/Common'
|
||||
import { INavigateInfo } from './core/draw/interactive/Search'
|
||||
import { Shortcut } from './core/shortcut/Shortcut'
|
||||
import { KeyMap } from './dataset/enum/KeyMap'
|
||||
import { BlockType } from './dataset/enum/Block'
|
||||
import { IBlock } from './interface/Block'
|
||||
|
||||
export default class Editor {
|
||||
|
||||
@@ -125,7 +127,8 @@ export {
|
||||
PageMode,
|
||||
ImageDisplay,
|
||||
Command,
|
||||
KeyMap
|
||||
KeyMap,
|
||||
BlockType
|
||||
}
|
||||
|
||||
// 对外类型
|
||||
@@ -136,5 +139,6 @@ export type {
|
||||
IContextMenuContext,
|
||||
IRegisterContextMenu,
|
||||
IWatermark,
|
||||
INavigateInfo
|
||||
INavigateInfo,
|
||||
IBlock
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { BlockType } from '../dataset/enum/Block'
|
||||
|
||||
export interface IIFrameBlock {
|
||||
src: string;
|
||||
}
|
||||
|
||||
export interface IVideoBlock {
|
||||
src: string;
|
||||
}
|
||||
|
||||
export interface IBlock {
|
||||
type: BlockType;
|
||||
iframeBlock?: IIFrameBlock;
|
||||
videoBlock?: IVideoBlock;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ControlComponent, ImageDisplay } from '../dataset/enum/Control'
|
||||
import { ElementType } from '../dataset/enum/Element'
|
||||
import { RowFlex } from '../dataset/enum/Row'
|
||||
import { IBlock } from './Block'
|
||||
import { ICheckbox } from './Checkbox'
|
||||
import { IControl } from './Control'
|
||||
import { IColgroup } from './table/Colgroup'
|
||||
@@ -78,6 +79,10 @@ export interface IImageElement {
|
||||
imgDisplay?: ImageDisplay
|
||||
}
|
||||
|
||||
export interface IBlockElement {
|
||||
block?: IBlock;
|
||||
}
|
||||
|
||||
export type IElement = IElementBasic
|
||||
& IElementStyle
|
||||
& ITable
|
||||
@@ -89,6 +94,7 @@ export type IElement = IElementBasic
|
||||
& ILaTexElement
|
||||
& IDateElement
|
||||
& IImageElement
|
||||
& IBlockElement
|
||||
|
||||
export interface IElementMetrics {
|
||||
width: number;
|
||||
|
||||
@@ -9,10 +9,10 @@ import { ControlComponent, ControlType } from '../dataset/enum/Control'
|
||||
|
||||
interface IFormatElementListOption {
|
||||
isHandleFirstElement?: boolean;
|
||||
editorOptions?: Required<IEditorOption>;
|
||||
editorOptions: Required<IEditorOption>;
|
||||
}
|
||||
|
||||
export function formatElementList(elementList: IElement[], options: IFormatElementListOption = {}) {
|
||||
export function formatElementList(elementList: IElement[], options: IFormatElementListOption) {
|
||||
const { isHandleFirstElement, editorOptions } = <IFormatElementListOption>{
|
||||
isHandleFirstElement: true,
|
||||
...options
|
||||
@@ -240,6 +240,13 @@ export function formatElementList(elementList: IElement[], options: IFormatEleme
|
||||
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
|
||||
|
||||
+85
-1
@@ -1,7 +1,7 @@
|
||||
import { data, options } from './mock'
|
||||
import './style.css'
|
||||
import prism from 'prismjs'
|
||||
import Editor, { Command, ControlType, EditorMode, ElementType, IElement, KeyMap, PageMode } from './editor'
|
||||
import Editor, { BlockType, Command, ControlType, EditorMode, ElementType, IBlock, IElement, KeyMap, PageMode } from './editor'
|
||||
import { Dialog } from './components/dialog/Dialog'
|
||||
import { formatPrismToken } from './utils/prism'
|
||||
import { Signature } from './components/signature/Signature'
|
||||
@@ -273,11 +273,13 @@ window.onload = function () {
|
||||
type: 'text',
|
||||
label: '文本',
|
||||
name: 'name',
|
||||
required: true,
|
||||
placeholder: '请输入文本'
|
||||
}, {
|
||||
type: 'text',
|
||||
label: '链接',
|
||||
name: 'url',
|
||||
required: true,
|
||||
placeholder: '请输入链接'
|
||||
}],
|
||||
onConfirm: (payload) => {
|
||||
@@ -340,16 +342,19 @@ window.onload = function () {
|
||||
type: 'text',
|
||||
label: '内容',
|
||||
name: 'data',
|
||||
required: true,
|
||||
placeholder: '请输入内容'
|
||||
}, {
|
||||
type: 'color',
|
||||
label: '颜色',
|
||||
name: 'color',
|
||||
required: true,
|
||||
value: '#AEB5C0'
|
||||
}, {
|
||||
type: 'number',
|
||||
label: '字体大小',
|
||||
name: 'size',
|
||||
required: true,
|
||||
value: '120'
|
||||
}],
|
||||
onConfirm: (payload) => {
|
||||
@@ -435,6 +440,7 @@ window.onload = function () {
|
||||
type: 'text',
|
||||
label: '占位符',
|
||||
name: 'placeholder',
|
||||
required: true,
|
||||
placeholder: '请输入占位符'
|
||||
}, {
|
||||
type: 'text',
|
||||
@@ -469,6 +475,7 @@ window.onload = function () {
|
||||
type: 'text',
|
||||
label: '占位符',
|
||||
name: 'placeholder',
|
||||
required: true,
|
||||
placeholder: '请输入占位符'
|
||||
}, {
|
||||
type: 'text',
|
||||
@@ -479,6 +486,7 @@ window.onload = function () {
|
||||
type: 'textarea',
|
||||
label: '值集',
|
||||
name: 'valueSets',
|
||||
required: true,
|
||||
height: 100,
|
||||
placeholder: `请输入值集JSON,例:\n[{\n"value":"有",\n"code":"98175"\n}]`
|
||||
}],
|
||||
@@ -514,6 +522,7 @@ window.onload = function () {
|
||||
type: 'textarea',
|
||||
label: '值集',
|
||||
name: 'valueSets',
|
||||
required: true,
|
||||
height: 100,
|
||||
placeholder: `请输入值集JSON,例:\n[{\n"value":"有",\n"code":"98175"\n}]`
|
||||
}],
|
||||
@@ -612,6 +621,77 @@ window.onload = function () {
|
||||
}])
|
||||
}
|
||||
|
||||
const blockDom = document.querySelector<HTMLDivElement>('.menu-item__block')!
|
||||
blockDom.onclick = function () {
|
||||
console.log('block')
|
||||
new Dialog({
|
||||
title: '内容块',
|
||||
data: [{
|
||||
type: 'select',
|
||||
label: '类型',
|
||||
name: 'type',
|
||||
value: 'iframe',
|
||||
required: true,
|
||||
options: [{
|
||||
label: '网址',
|
||||
value: 'iframe'
|
||||
}, {
|
||||
label: '视频',
|
||||
value: 'video'
|
||||
}]
|
||||
}, {
|
||||
type: 'number',
|
||||
label: '宽度',
|
||||
name: 'width',
|
||||
placeholder: '请输入宽度(默认页面内宽度)'
|
||||
}, {
|
||||
type: 'number',
|
||||
label: '高度',
|
||||
name: 'height',
|
||||
required: true,
|
||||
placeholder: '请输入高度'
|
||||
}, {
|
||||
type: 'textarea',
|
||||
label: '地址',
|
||||
height: 100,
|
||||
name: 'value',
|
||||
required: true,
|
||||
placeholder: '请输入地址'
|
||||
}],
|
||||
onConfirm: (payload) => {
|
||||
const type = payload.find(p => p.name === 'type')?.value
|
||||
if (!type) return
|
||||
const value = payload.find(p => p.name === 'value')?.value
|
||||
if (!value) return
|
||||
const width = payload.find(p => p.name === 'width')?.value
|
||||
const height = payload.find(p => p.name === 'height')?.value
|
||||
if (!height) return
|
||||
const block: IBlock = {
|
||||
type: <BlockType>type
|
||||
}
|
||||
if (block.type === BlockType.IFRAME) {
|
||||
block.iframeBlock = {
|
||||
src: value
|
||||
}
|
||||
} else if (block.type === BlockType.VIDEO) {
|
||||
block.videoBlock = {
|
||||
src: value
|
||||
}
|
||||
}
|
||||
const blockElement: IElement = {
|
||||
type: ElementType.BLOCK,
|
||||
value: '',
|
||||
height: Number(height),
|
||||
block
|
||||
}
|
||||
if (width) {
|
||||
blockElement.width = Number(width)
|
||||
}
|
||||
instance.command.executeInsertElementList([blockElement])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 5. | 搜索&替换 | 打印 |
|
||||
const searchCollapseDom = document.querySelector<HTMLDivElement>('.menu-item__search__collapse')!
|
||||
const searchInputDom = document.querySelector<HTMLInputElement>('.menu-item__search__collapse__search input')!
|
||||
@@ -732,24 +812,28 @@ window.onload = function () {
|
||||
type: 'text',
|
||||
label: '上边距',
|
||||
name: 'top',
|
||||
required: true,
|
||||
value: `${topMargin}`,
|
||||
placeholder: '请输入上边距'
|
||||
}, {
|
||||
type: 'text',
|
||||
label: '下边距',
|
||||
name: 'bottom',
|
||||
required: true,
|
||||
value: `${bottomMargin}`,
|
||||
placeholder: '请输入下边距'
|
||||
}, {
|
||||
type: 'text',
|
||||
label: '左边距',
|
||||
name: 'left',
|
||||
required: true,
|
||||
value: `${leftMargin}`,
|
||||
placeholder: '请输入左边距'
|
||||
}, {
|
||||
type: 'text',
|
||||
label: '右边距',
|
||||
name: 'right',
|
||||
required: true,
|
||||
value: `${rightMargin}`,
|
||||
placeholder: '请输入右边距'
|
||||
}],
|
||||
|
||||
@@ -463,6 +463,10 @@ ul {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.menu-item__block i {
|
||||
background-image: url('./assets/images/block.svg');
|
||||
}
|
||||
|
||||
.menu-item .menu-item__control .options {
|
||||
width: 55px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user