feat:增加全局搜索
This commit is contained in:
@@ -59,6 +59,12 @@
|
|||||||
<div class="menu-item__search">
|
<div class="menu-item__search">
|
||||||
<i></i>
|
<i></i>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="menu-item__search__collapse">
|
||||||
|
<div class="menu-item__search__collapse__search">
|
||||||
|
<input type="text" />
|
||||||
|
<span>×</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="menu-item__print">
|
<div class="menu-item__print">
|
||||||
<i></i>
|
<i></i>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,15 +7,17 @@ export class Command {
|
|||||||
private static painter: Function
|
private static painter: Function
|
||||||
private static format: Function
|
private static format: Function
|
||||||
private static bold: Function
|
private static bold: Function
|
||||||
|
private static search: Function
|
||||||
private static print: Function
|
private static print: Function
|
||||||
|
|
||||||
constructor(adapt: CommandAdapt) {
|
constructor(adapt: CommandAdapt) {
|
||||||
Command.undo = adapt.undo.bind(adapt)
|
Command.undo = adapt.undo.bind(adapt)
|
||||||
Command.redo = adapt.redo.bind(adapt)
|
Command.redo = adapt.redo.bind(adapt)
|
||||||
Command.painter = adapt.painter.bind(adapt)
|
Command.painter = adapt.painter.bind(adapt)
|
||||||
Command.print = adapt.print.bind(adapt)
|
|
||||||
Command.format = adapt.format.bind(adapt)
|
Command.format = adapt.format.bind(adapt)
|
||||||
Command.bold = adapt.bold.bind(adapt)
|
Command.bold = adapt.bold.bind(adapt)
|
||||||
|
Command.search = adapt.search.bind(adapt)
|
||||||
|
Command.print = adapt.print.bind(adapt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 撤销、重做、格式刷、清除格式
|
// 撤销、重做、格式刷、清除格式
|
||||||
@@ -41,6 +43,10 @@ export class Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 搜索、打印
|
// 搜索、打印
|
||||||
|
public executeSearch(payload: string | null) {
|
||||||
|
return Command.search(payload)
|
||||||
|
}
|
||||||
|
|
||||||
public executePrint() {
|
public executePrint() {
|
||||||
return Command.print()
|
return Command.print()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,27 @@ export class CommandAdapt {
|
|||||||
this.draw.render({ isSetCursor: false })
|
this.draw.render({ isSetCursor: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public search(payload: string | null) {
|
||||||
|
if (payload) {
|
||||||
|
const elementList = this.draw.getElementList()
|
||||||
|
const text = elementList.map(e => !e.type || e.type === 'TEXT' ? e.value : null)
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('')
|
||||||
|
const matchStartIndexList = []
|
||||||
|
let index = text.indexOf(payload)
|
||||||
|
while (index !== -1) {
|
||||||
|
matchStartIndexList.push(index)
|
||||||
|
index = text.indexOf(payload, index + 1)
|
||||||
|
}
|
||||||
|
const searchMatch: number[][] = matchStartIndexList
|
||||||
|
.map(i => Array(payload.length).fill(i).map((_, j) => i + j))
|
||||||
|
this.draw.setSearchMatch(searchMatch.length ? searchMatch : null)
|
||||||
|
} else {
|
||||||
|
this.draw.setSearchMatch(null)
|
||||||
|
}
|
||||||
|
this.draw.render({ isSetCursor: false, isSubmitHistory: false })
|
||||||
|
}
|
||||||
|
|
||||||
public print() {
|
public print() {
|
||||||
return printImageBase64(this.draw.getDataURL())
|
return printImageBase64(this.draw.getDataURL())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Position } from "../position/Position"
|
|||||||
import { RangeManager } from "../range/RangeManager"
|
import { RangeManager } from "../range/RangeManager"
|
||||||
import { Background } from "./Background"
|
import { Background } from "./Background"
|
||||||
import { Margin } from "./Margin"
|
import { Margin } from "./Margin"
|
||||||
|
import { Search } from "./Search"
|
||||||
|
|
||||||
export class Draw {
|
export class Draw {
|
||||||
|
|
||||||
@@ -25,10 +26,12 @@ export class Draw {
|
|||||||
private range: RangeManager
|
private range: RangeManager
|
||||||
private margin: Margin
|
private margin: Margin
|
||||||
private background: Background
|
private background: Background
|
||||||
|
private search: Search
|
||||||
private historyManager: HistoryManager
|
private historyManager: HistoryManager
|
||||||
|
|
||||||
private rowCount: number
|
private rowCount: number
|
||||||
private painterStyle: IElementStyle | null
|
private painterStyle: IElementStyle | null
|
||||||
|
private searchMatchList: number[][] | null
|
||||||
|
|
||||||
constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, elementList: IElement[]) {
|
constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, elementList: IElement[]) {
|
||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
@@ -41,6 +44,7 @@ export class Draw {
|
|||||||
this.range = new RangeManager(ctx, elementList, options)
|
this.range = new RangeManager(ctx, elementList, options)
|
||||||
this.margin = new Margin(ctx, options)
|
this.margin = new Margin(ctx, options)
|
||||||
this.background = new Background(ctx)
|
this.background = new Background(ctx)
|
||||||
|
this.search = new Search(ctx, options, this)
|
||||||
|
|
||||||
const canvasEvent = new CanvasEvent(canvas, this)
|
const canvasEvent = new CanvasEvent(canvas, this)
|
||||||
this.cursor = new Cursor(canvas, this, canvasEvent)
|
this.cursor = new Cursor(canvas, this, canvasEvent)
|
||||||
@@ -50,6 +54,7 @@ export class Draw {
|
|||||||
|
|
||||||
this.rowCount = 0
|
this.rowCount = 0
|
||||||
this.painterStyle = null
|
this.painterStyle = null
|
||||||
|
this.searchMatchList = null
|
||||||
}
|
}
|
||||||
|
|
||||||
public getHistoryManager(): HistoryManager {
|
public getHistoryManager(): HistoryManager {
|
||||||
@@ -91,6 +96,14 @@ export class Draw {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getSearchMathch(): number[][] | null {
|
||||||
|
return this.searchMatchList
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSearchMatch(payload: number[][] | null) {
|
||||||
|
this.searchMatchList = payload
|
||||||
|
}
|
||||||
|
|
||||||
public render(payload?: IDrawOption) {
|
public render(payload?: IDrawOption) {
|
||||||
let { curIndex, isSubmitHistory = true, isSetCursor = true } = payload || {}
|
let { curIndex, isSubmitHistory = true, isSetCursor = true } = payload || {}
|
||||||
// 清除光标
|
// 清除光标
|
||||||
@@ -189,6 +202,10 @@ export class Draw {
|
|||||||
x = leftTopPoint[0]
|
x = leftTopPoint[0]
|
||||||
y += curRow.height
|
y += curRow.height
|
||||||
}
|
}
|
||||||
|
// 搜索匹配绘制
|
||||||
|
if (this.searchMatchList) {
|
||||||
|
this.search.render()
|
||||||
|
}
|
||||||
// 光标重绘
|
// 光标重绘
|
||||||
if (curIndex === undefined) {
|
if (curIndex === undefined) {
|
||||||
curIndex = positionList.length - 1
|
curIndex = positionList.length - 1
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { IEditorOption } from "../../interface/Editor"
|
||||||
|
import { Position } from "../position/Position"
|
||||||
|
import { Draw } from "./Draw"
|
||||||
|
|
||||||
|
export class Search {
|
||||||
|
|
||||||
|
private ctx: CanvasRenderingContext2D
|
||||||
|
private options: Required<IEditorOption>
|
||||||
|
private draw: Draw
|
||||||
|
private position: Position
|
||||||
|
|
||||||
|
constructor(ctx: CanvasRenderingContext2D, options: Required<IEditorOption>, draw: Draw) {
|
||||||
|
this.ctx = ctx
|
||||||
|
this.options = options
|
||||||
|
this.draw = draw
|
||||||
|
this.position = draw.getPosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const searchMatch = this.draw.getSearchMathch()
|
||||||
|
if (!searchMatch || !searchMatch.length) return
|
||||||
|
const searchMatchList = searchMatch.flat()
|
||||||
|
const positionList = this.position.getPositionList()
|
||||||
|
this.ctx.save()
|
||||||
|
this.ctx.globalAlpha = this.options.searchMatchAlpha
|
||||||
|
this.ctx.fillStyle = this.options.searchMatchColor
|
||||||
|
searchMatchList.forEach(s => {
|
||||||
|
const position = positionList[s]
|
||||||
|
const { leftTop, leftBottom, rightTop } = position.coordinate
|
||||||
|
const x = leftTop[0]
|
||||||
|
const y = leftTop[1]
|
||||||
|
const width = rightTop[0] - leftTop[0]
|
||||||
|
const height = leftBottom[1] - leftTop[1]
|
||||||
|
this.ctx.fillRect(x, y, width, height)
|
||||||
|
})
|
||||||
|
this.ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ export class RangeManager {
|
|||||||
private options: Required<IEditorOption>
|
private options: Required<IEditorOption>
|
||||||
private range: IRange
|
private range: IRange
|
||||||
|
|
||||||
constructor(ctx: CanvasRenderingContext2D, elementList: IElement[], options: Required<IEditorOption>,) {
|
constructor(ctx: CanvasRenderingContext2D, elementList: IElement[], options: Required<IEditorOption>) {
|
||||||
this.ctx = ctx
|
this.ctx = ctx
|
||||||
this.elementList = elementList
|
this.elementList = elementList
|
||||||
this.options = options
|
this.options = options
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export default class Editor {
|
|||||||
defaultSize: 16,
|
defaultSize: 16,
|
||||||
rangeAlpha: 0.6,
|
rangeAlpha: 0.6,
|
||||||
rangeColor: '#AECBFA',
|
rangeColor: '#AECBFA',
|
||||||
|
searchMatchAlpha: 0.6,
|
||||||
|
searchMatchColor: '#FFFF00',
|
||||||
marginIndicatorSize: 35,
|
marginIndicatorSize: 35,
|
||||||
marginIndicatorColor: '#BABABA',
|
marginIndicatorColor: '#BABABA',
|
||||||
margins: [100, 120, 100, 120],
|
margins: [100, 120, 100, 120],
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ export interface IEditorOption {
|
|||||||
defaultSize?: number;
|
defaultSize?: number;
|
||||||
rangeColor?: string;
|
rangeColor?: string;
|
||||||
rangeAlpha?: number;
|
rangeAlpha?: number;
|
||||||
|
searchMatchColor?: string;
|
||||||
|
searchMatchAlpha?: number;
|
||||||
marginIndicatorSize?: number;
|
marginIndicatorSize?: number;
|
||||||
marginIndicatorColor?: string,
|
marginIndicatorColor?: string,
|
||||||
margins?: [top: number, right: number, bootom: number, left: number]
|
margins?: [top: number, right: number, bootom: number, left: number]
|
||||||
|
|||||||
+19
@@ -70,6 +70,25 @@ window.onload = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 搜索、打印
|
// 搜索、打印
|
||||||
|
const collspanDom = document.querySelector<HTMLDivElement>('.menu-item__search__collapse')
|
||||||
|
const searchInputDom = document.querySelector<HTMLInputElement>('.menu-item__search__collapse__search input')
|
||||||
|
document.querySelector<HTMLDivElement>('.menu-item__search')!.onclick = function () {
|
||||||
|
console.log('search')
|
||||||
|
collspanDom!.style.display = 'block'
|
||||||
|
}
|
||||||
|
document.querySelector<HTMLDivElement>('.menu-item__search__collapse span')!.onclick = function () {
|
||||||
|
collspanDom!.style.display = 'none'
|
||||||
|
searchInputDom!.value = ''
|
||||||
|
instance.command.executeSearch(null)
|
||||||
|
}
|
||||||
|
searchInputDom!.oninput = function () {
|
||||||
|
instance.command.executeSearch(searchInputDom?.value || null)
|
||||||
|
}
|
||||||
|
searchInputDom!.onkeydown = function (evt) {
|
||||||
|
if (evt.key === 'Enter') {
|
||||||
|
instance.command.executeSearch(searchInputDom?.value || null)
|
||||||
|
}
|
||||||
|
}
|
||||||
document.querySelector<HTMLDivElement>('.menu-item__print')!.onclick = function () {
|
document.querySelector<HTMLDivElement>('.menu-item__print')!.onclick = function () {
|
||||||
console.log('print')
|
console.log('print')
|
||||||
instance.command.executePrint()
|
instance.command.executePrint()
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ body {
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item>div {
|
.menu-item>div {
|
||||||
@@ -125,10 +126,59 @@ body {
|
|||||||
background-color: #ffff00;
|
background-color: #ffff00;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-item__search {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.menu-item__search i {
|
.menu-item__search i {
|
||||||
background-image: url('./assets/images/search.svg');
|
background-image: url('./assets/images/search.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-item .menu-item__search__collapse {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
z-index: 99;
|
||||||
|
top: 25px;
|
||||||
|
left: 0;
|
||||||
|
box-shadow: 0px 5px 5px #e3dfdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item .menu-item__search__collapse__search {
|
||||||
|
width: 205px;
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 5px;
|
||||||
|
line-height: 36px;
|
||||||
|
background: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item .menu-item__search__collapse__search input {
|
||||||
|
height: 27px;
|
||||||
|
appearance: none;
|
||||||
|
background-color: #fff;
|
||||||
|
background-image: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ebebeb;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #606266;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 27px;
|
||||||
|
outline: none;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item .menu-item__search__collapse__search span {
|
||||||
|
height: 100%;
|
||||||
|
color: #dcdfe6;
|
||||||
|
font-size: 25px;
|
||||||
|
display: inline-block;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.menu-item__print i {
|
.menu-item__print i {
|
||||||
background-image: url('./assets/images/print.svg');
|
background-image: url('./assets/images/print.svg');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user