From b6fe21230b34afa6ac53eee146e1c291a22da04e Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Sat, 16 Dec 2023 18:32:52 +0800 Subject: [PATCH] feat: set control highlight rule #332 --- docs/en/guide/command-execute.md | 10 ++ docs/guide/command-execute.md | 10 ++ src/editor/core/command/Command.ts | 2 + src/editor/core/command/CommandAdapt.ts | 5 + src/editor/core/draw/Draw.ts | 4 + src/editor/core/draw/control/Control.ts | 35 ++++- .../draw/control/interactive/ControlSearch.ts | 145 ++++++++++++++++++ src/editor/core/draw/interactive/Search.ts | 11 +- src/editor/core/range/RangeManager.ts | 4 +- src/editor/interface/Control.ts | 13 ++ 10 files changed, 231 insertions(+), 8 deletions(-) create mode 100644 src/editor/core/draw/control/interactive/ControlSearch.ts diff --git a/docs/en/guide/command-execute.md b/docs/en/guide/command-execute.md index 33186ea..c85a78c 100644 --- a/docs/en/guide/command-execute.md +++ b/docs/en/guide/command-execute.md @@ -891,3 +891,13 @@ Usage: ```javascript instance.command.executeSetControlExtension(payload: ISetControlExtensionOption) ``` + +## executeSetControlHighlight + +Feature: Set control highlight (by keyword) + +Usage: + +```javascript +instance.command.executeSetControlHighlight(payload: ISetControlHighlightOption) +``` diff --git a/docs/guide/command-execute.md b/docs/guide/command-execute.md index 52d13fe..9465072 100644 --- a/docs/guide/command-execute.md +++ b/docs/guide/command-execute.md @@ -891,3 +891,13 @@ instance.command.executeSetControlValue(payload: ISetControlValueOption) ```javascript instance.command.executeSetControlExtension(payload: ISetControlExtensionOption) ``` + +## executeSetControlHighlight + +功能:设置控件高亮(根据关键词) + +用法: + +```javascript +instance.command.executeSetControlHighlight(payload: ISetControlHighlightOption) +``` diff --git a/src/editor/core/command/Command.ts b/src/editor/core/command/Command.ts index 7c50c67..3a495d0 100644 --- a/src/editor/core/command/Command.ts +++ b/src/editor/core/command/Command.ts @@ -88,6 +88,7 @@ export class Command { public executeSetZone: CommandAdapt['setZone'] public executeSetControlValue: CommandAdapt['setControlValue'] public executeSetControlExtension: CommandAdapt['setControlExtension'] + public executeSetControlHighlight: CommandAdapt['setControlHighlight'] public getCatalog: CommandAdapt['getCatalog'] public getImage: CommandAdapt['getImage'] public getOptions: CommandAdapt['getOptions'] @@ -221,6 +222,7 @@ export class Command { // 控件 this.executeSetControlValue = adapt.setControlValue.bind(adapt) this.executeSetControlExtension = adapt.setControlExtension.bind(adapt) + this.executeSetControlHighlight = adapt.setControlHighlight.bind(adapt) this.getControlValue = adapt.getControlValue.bind(adapt) } } diff --git a/src/editor/core/command/CommandAdapt.ts b/src/editor/core/command/CommandAdapt.ts index d0e2387..83e6ef7 100644 --- a/src/editor/core/command/CommandAdapt.ts +++ b/src/editor/core/command/CommandAdapt.ts @@ -23,6 +23,7 @@ import { IGetControlValueOption, IGetControlValueResult, ISetControlExtensionOption, + ISetControlHighlightOption, ISetControlValueOption } from '../../interface/Control' import { @@ -2197,6 +2198,10 @@ export class CommandAdapt { this.draw.getControl().setExtensionByConceptId(payload) } + public setControlHighlight(payload: ISetControlHighlightOption) { + this.draw.getControl().setHighlightList(payload) + } + public getContainer(): HTMLDivElement { return this.draw.getContainer() } diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index a3616bc..4ccbfd6 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -1821,6 +1821,8 @@ export class Draw { if (this.mode !== EditorMode.PRINT) { this.margin.render(ctx, pageNo) } + // 控件高亮 + this.control.renderHighlightList(ctx, pageNo) // 渲染元素 const index = rowList[0].startIndex this.drawRow(ctx, { @@ -1935,6 +1937,8 @@ export class Draw { if (searchKeyword) { this.search.compute(searchKeyword) } + // 控件关键词高亮 + this.control.computeHighlightList() } // 清除光标等副作用 this.imageObserver.clearAll() diff --git a/src/editor/core/draw/control/Control.ts b/src/editor/core/draw/control/Control.ts index f2bc4c8..2cae2a7 100644 --- a/src/editor/core/draw/control/Control.ts +++ b/src/editor/core/draw/control/Control.ts @@ -1,9 +1,11 @@ import { ControlComponent, ControlType } from '../../../dataset/enum/Control' import { EditorZone } from '../../../dataset/enum/Editor' import { ElementType } from '../../../dataset/enum/Element' +import { DeepRequired } from '../../../interface/Common' import { IControl, IControlContext, + IControlHighlight, IControlInitOption, IControlInstance, IControlOption, @@ -13,6 +15,7 @@ import { ISetControlExtensionOption, ISetControlValueOption } from '../../../interface/Control' +import { IEditorOption } from '../../../interface/Editor' import { IElement, IElementPosition } from '../../../interface/Element' import { EventBusMap } from '../../../interface/EventBus' import { IRange } from '../../../interface/Range' @@ -28,6 +31,7 @@ import { Listener } from '../../listener/Listener' import { RangeManager } from '../../range/RangeManager' import { Draw } from '../Draw' import { CheckboxControl } from './checkbox/CheckboxControl' +import { ControlSearch } from './interactive/ControlSearch' import { SelectControl } from './select/SelectControl' import { TextControl } from './text/TextControl' @@ -40,7 +44,9 @@ export class Control { private range: RangeManager private listener: Listener private eventBus: EventBus - private options: IControlOption + private controlSearch: ControlSearch + private options: DeepRequired + private controlOptions: IControlOption private activeControl: IControlInstance | null constructor(draw: Draw) { @@ -48,11 +54,32 @@ export class Control { this.range = draw.getRange() this.listener = draw.getListener() this.eventBus = draw.getEventBus() + this.controlSearch = new ControlSearch(this) - this.options = draw.getOptions().control + this.options = draw.getOptions() + this.controlOptions = this.options.control this.activeControl = null } + // 搜索高亮匹配 + public setHighlightList(payload: IControlHighlight[]) { + this.controlSearch.setHighlightList(payload) + } + + public computeHighlightList() { + const highlightList = this.controlSearch.getHighlightList() + if (highlightList.length) { + this.controlSearch.computeHighlightList() + } + } + + public renderHighlightList(ctx: CanvasRenderingContext2D, pageNo: number) { + const highlightMatchResult = this.controlSearch.getHighlightMatchResult() + if (highlightMatchResult.length) { + this.controlSearch.renderHighlightList(ctx, pageNo) + } + } + public getDraw(): Draw { return this.draw } @@ -395,7 +422,7 @@ export class Control { type: ElementType.CONTROL, control: startElement.control, controlComponent: ControlComponent.PLACEHOLDER, - color: this.options.placeholderColor + color: this.controlOptions.placeholderColor } formatElementContext(elementList, [newElement], startIndex) this.draw.spliceElementList( @@ -561,7 +588,7 @@ export class Control { const formatValue = [{ value }] formatElementList(formatValue, { isHandleFirstElement: false, - editorOptions: this.draw.getOptions() + editorOptions: this.options }) const text = new TextControl(element, this) if (value) { diff --git a/src/editor/core/draw/control/interactive/ControlSearch.ts b/src/editor/core/draw/control/interactive/ControlSearch.ts new file mode 100644 index 0000000..9d57287 --- /dev/null +++ b/src/editor/core/draw/control/interactive/ControlSearch.ts @@ -0,0 +1,145 @@ +import { ElementType } from '../../../../dataset/enum/Element' +import { DeepRequired } from '../../../../interface/Common' +import { + IControlHighlight, + IControlHighlightRule +} from '../../../../interface/Control' +import { IEditorOption } from '../../../../interface/Editor' +import { IElement, IElementPosition } from '../../../../interface/Element' +import { + ISearchResult, + ISearchResultRestArgs +} from '../../../../interface/Search' +import { Draw } from '../../Draw' +import { Control } from '../Control' + +type IHighlightMatchResult = (ISearchResult & IControlHighlightRule)[] + +export class ControlSearch { + private draw: Draw + private options: DeepRequired + private highlightList: IControlHighlight[] + private highlightMatchResult: IHighlightMatchResult + + constructor(control: Control) { + this.draw = control.getDraw() + this.options = this.draw.getOptions() + + this.highlightList = [] + this.highlightMatchResult = [] + } + + public getHighlightMatchResult(): IHighlightMatchResult { + return this.highlightMatchResult + } + + public getHighlightList(): IControlHighlight[] { + return this.highlightList + } + + public setHighlightList(payload: IControlHighlight[]) { + this.highlightList = payload + } + + public computeHighlightList() { + const search = this.draw.getSearch() + const computeHighlight = ( + elementList: IElement[], + restArgs?: ISearchResultRestArgs + ) => { + let i = 0 + while (i < elementList.length) { + const element = elementList[i] + i++ + // 表格下钻处理 + if (element.type === ElementType.TABLE) { + const trList = element.trList! + for (let r = 0; r < trList.length; r++) { + const tr = trList[r] + for (let d = 0; d < tr.tdList.length; d++) { + const td = tr.tdList[d] + const restArgs: ISearchResultRestArgs = { + tableId: element.id, + tableIndex: i - 1, + trIndex: r, + tdIndex: d, + tdId: td.id + } + computeHighlight(td.value, restArgs) + } + } + } + const controlConceptId = element?.control?.conceptId + if (!controlConceptId) continue + const highlightIndex = this.highlightList.findIndex( + highlight => highlight.conceptId === controlConceptId + ) + if (!~highlightIndex) continue + // 搜索后控件结束索引 + const startIndex = i + let newEndIndex = i + while (newEndIndex < elementList.length) { + const nextElement = elementList[newEndIndex] + if (nextElement.controlId !== element.controlId) break + newEndIndex++ + } + i = newEndIndex + // 高亮信息 + const controlElementList = elementList.slice(startIndex, newEndIndex) + const highlight = this.highlightList[highlightIndex] + const { ruleList } = highlight + for (let r = 0; r < ruleList.length; r++) { + const rule = ruleList[r] + const searchResult = search.getMatchList( + rule.keyword, + controlElementList + ) + this.highlightMatchResult.push( + ...searchResult.map(result => ({ + ...result, + ...rule, + ...restArgs, + index: result.index + startIndex // 实际索引 + })) + ) + } + } + } + this.highlightMatchResult = [] + computeHighlight(this.draw.getOriginalMainElementList()) + } + + public renderHighlightList(ctx: CanvasRenderingContext2D, pageIndex: number) { + if (!this.highlightMatchResult?.length) return + const { searchMatchAlpha, searchMatchColor } = this.options + const positionList = this.draw.getPosition().getOriginalPositionList() + const elementList = this.draw.getOriginalElementList() + ctx.save() + for (let s = 0; s < this.highlightMatchResult.length; s++) { + const searchMatch = this.highlightMatchResult[s] + let position: IElementPosition | null = null + if (searchMatch.tableId) { + const { tableIndex, trIndex, tdIndex, index } = searchMatch + position = + elementList[tableIndex!]?.trList![trIndex!].tdList[tdIndex!] + ?.positionList![index] + } else { + position = positionList[searchMatch.index] + } + if (!position) continue + const { + coordinate: { leftTop, leftBottom, rightTop }, + pageNo + } = position + if (pageNo !== pageIndex) continue + ctx.fillStyle = searchMatch.backgroundColor || searchMatchColor + ctx.globalAlpha = searchMatch.alpha || searchMatchAlpha + const x = leftTop[0] + const y = leftTop[1] + const width = rightTop[0] - leftTop[0] + const height = leftBottom[1] - leftTop[1] + ctx.fillRect(x, y, width, height) + } + ctx.restore() + } +} diff --git a/src/editor/core/draw/interactive/Search.ts b/src/editor/core/draw/interactive/Search.ts index fff7976..7c3be83 100644 --- a/src/editor/core/draw/interactive/Search.ts +++ b/src/editor/core/draw/interactive/Search.ts @@ -153,7 +153,10 @@ export class Search { } } - public getMatchList(payload: string): ISearchResult[] { + public getMatchList( + payload: string, + originalElementList: IElement[] + ): ISearchResult[] { const keyword = payload.toLocaleLowerCase() const searchMatchList: ISearchResult[] = [] // 分组 @@ -162,7 +165,6 @@ export class Search { elementList: IElement[] index: number }[] = [] - const originalElementList = this.draw.getOriginalElementList() const originalElementListLength = originalElementList.length // 查找表格所在位置 const tableIndexList = [] @@ -264,7 +266,10 @@ export class Search { } public compute(payload: string) { - this.searchMatchList = this.getMatchList(payload) + this.searchMatchList = this.getMatchList( + payload, + this.draw.getOriginalElementList() + ) } public render(ctx: CanvasRenderingContext2D, pageIndex: number) { diff --git a/src/editor/core/range/RangeManager.ts b/src/editor/core/range/RangeManager.ts index d32b97a..c45b19d 100644 --- a/src/editor/core/range/RangeManager.ts +++ b/src/editor/core/range/RangeManager.ts @@ -249,7 +249,9 @@ export class RangeManager { } public getKeywordRangeList(payload: string): IRange[] { - const searchMatchList = this.draw.getSearch().getMatchList(payload) + const searchMatchList = this.draw + .getSearch() + .getMatchList(payload, this.draw.getOriginalElementList()) const searchRangeMap: Map = new Map() for (const searchMatch of searchMatchList) { const searchRange = searchRangeMap.get(searchMatch.groupId) diff --git a/src/editor/interface/Control.ts b/src/editor/interface/Control.ts index fa23ea8..0041e92 100644 --- a/src/editor/interface/Control.ts +++ b/src/editor/interface/Control.ts @@ -22,6 +22,17 @@ export interface IControlCheckbox { checkbox?: ICheckbox } +export interface IControlHighlightRule { + keyword: string + alpha?: number + backgroundColor?: string +} + +export interface IControlHighlight { + ruleList: IControlHighlightRule[] + conceptId: string +} + export interface IControlRule { deletable?: boolean disabled?: boolean @@ -108,3 +119,5 @@ export interface ISetControlExtensionOption { conceptId: string extension: unknown } + +export type ISetControlHighlightOption = IControlHighlight[]