Skip to content

Commit 14c24b0

Browse files
committed
feat: merge Hufe921#768
2 parents 3254acc + 6ac8020 commit 14c24b0

File tree

39 files changed

+2356
-1472
lines changed

39 files changed

+2356
-1472
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"@typescript-eslint/parser": "5.62.0",
5656
"cypress": "13.6.0",
5757
"cypress-file-upload": "^5.0.8",
58+
"prismjs": "^1.29.0",
5859
"eslint": "7.32.0",
5960
"simple-git-hooks": "^2.8.1",
6061
"typescript": "4.9.5",

src/editor/assets/css/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@import './contextmenu/contextmenu.css';
88
@import './hyperlink/hyperlink.css';
99
@import './zone/zone.css';
10+
@import './track/track.css';
1011

1112
.ce-inputarea {
1213
width: 100px;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.ce-track-popup {
2+
background: #fff;
3+
box-shadow: 0 2px 12px 0 rgb(98 107 132 / 20%);
4+
border-radius: 2px;
5+
color: #3d4757;
6+
padding: 8px 16px;
7+
position: absolute;
8+
z-index: 1;
9+
text-align: center;
10+
display: none;
11+
}
12+
13+
.ce-track-popup div {
14+
min-width: 100px;
15+
max-width: 300px;
16+
font-size: 12px;
17+
display: inline-block;
18+
white-space: nowrap;
19+
overflow: hidden;
20+
text-overflow: ellipsis;
21+
cursor: pointer;
22+
text-decoration: none;
23+
}

src/editor/core/command/Command.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export class Command {
119119
public getContainer: CommandAdapt['getContainer']
120120
public getTitleValue: CommandAdapt['getTitleValue']
121121
public getPositionContextByEvent: CommandAdapt['getPositionContextByEvent']
122+
public execHideTracks: CommandAdapt['hideTracks']
122123

123124
constructor(adapt: CommandAdapt) {
124125
// 全局命令
@@ -248,5 +249,7 @@ export class Command {
248249
this.getControlList = adapt.getControlList.bind(adapt)
249250
this.executeLocationControl = adapt.locationControl.bind(adapt)
250251
this.executeInsertControl = adapt.insertControl.bind(adapt)
252+
// 审核
253+
this.execHideTracks = adapt.hideTracks.bind(adapt)
251254
}
252255
}

src/editor/core/command/CommandAdapt.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,6 +2058,20 @@ export class CommandAdapt {
20582058
this.draw.insertElementList([cloneElement])
20592059
}
20602060

2061+
public hideTracks (hide:boolean) {
2062+
2063+
if(hide) {
2064+
this.draw.hideReview()
2065+
} else {
2066+
this.draw.showReview()
2067+
}
2068+
this.draw.render({
2069+
isSetCursor: false,
2070+
isSubmitHistory: false
2071+
})
2072+
2073+
}
2074+
20612075
public focus(payload?: IFocusOption) {
20622076
const { position = LocationPosition.AFTER } = payload || {}
20632077
const curIndex =

src/editor/core/contextmenu/ContextMenu.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { globalMenus } from './menus/globalMenus'
2020
import { hyperlinkMenus } from './menus/hyperlinkMenus'
2121
import { imageMenus } from './menus/imageMenus'
2222
import { tableMenus } from './menus/tableMenus'
23+
import {reviewMenus} from './menus/reviewMenus'
2324

2425
interface IRenderPayload {
2526
contextMenuList: IRegisterContextMenu[]
@@ -56,7 +57,8 @@ export class ContextMenu {
5657
...tableMenus,
5758
...imageMenus,
5859
...controlMenus,
59-
...hyperlinkMenus
60+
...hyperlinkMenus,
61+
...reviewMenus
6062
]
6163
this.contextMenuContainerList = []
6264
this.contextMenuRelationShip = new Map()
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {IRegisterContextMenu} from '../../../interface/contextmenu/ContextMenu'
2+
import {Command} from '../../command/Command'
3+
4+
export const reviewMenus: IRegisterContextMenu[] = [
5+
{
6+
name: '隐藏痕迹',
7+
when: ()=>true,
8+
callback: (command: Command) => {
9+
command.execHideTracks(true)
10+
}
11+
},
12+
{
13+
name: '显示痕迹',
14+
when: ()=>true,
15+
callback: (command: Command) => {
16+
command.execHideTracks(false)
17+
}
18+
}
19+
]

src/editor/core/draw/Draw.ts

Lines changed: 147 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ import {
2020
} from '../../interface/Editor'
2121
import {
2222
IElement,
23-
IElementMetrics,
2423
IElementFillRect,
24+
IElementMetrics,
2525
IElementStyle
2626
} from '../../interface/Element'
2727
import { IRow, IRowElement } from '../../interface/Row'
28-
import { deepClone, getUUID, nextTick } from '../../utils'
28+
import { deepClone, getCurrentTimeString, getUUID, nextTick } from '../../utils'
2929
import { Cursor } from '../cursor/Cursor'
3030
import { CanvasEvent } from '../event/CanvasEvent'
3131
import { GlobalEvent } from '../event/GlobalEvent'
@@ -65,6 +65,7 @@ import {
6565
} from '../../dataset/enum/Editor'
6666
import { Control } from './control/Control'
6767
import {
68+
formatElementList,
6869
deleteSurroundElementList,
6970
getIsBlockElement,
7071
getSlimCloneElementList,
@@ -78,7 +79,6 @@ import {
7879
ControlComponent,
7980
ControlIndentation
8081
} from '../../dataset/enum/Control'
81-
import { formatElementList } from '../../utils/element'
8282
import { WorkerManager } from '../worker/WorkerManager'
8383
import { Previewer } from './particle/previewer/Previewer'
8484
import { DateParticle } from './particle/date/DateParticle'
@@ -108,6 +108,8 @@ import { ITd } from '../../interface/table/Td'
108108
import { PageBorder } from './frame/PageBorder'
109109
import { Actuator } from '../actuator/Actuator'
110110
import { TableOperate } from './particle/table/TableOperate'
111+
import {TrackType} from '../../dataset/enum/Track'
112+
import {Track} from './interactive/Track'
111113

112114
export class Draw {
113115
private container: HTMLDivElement
@@ -134,6 +136,7 @@ export class Draw {
134136
private background: Background
135137
private search: Search
136138
private group: Group
139+
private track: Track
137140
private underline: Underline
138141
private strikeout: Strikeout
139142
private highlight: Highlight
@@ -179,6 +182,7 @@ export class Draw {
179182
private intersectionPageNo: number
180183
private lazyRenderIntersectionObserver: IntersectionObserver | null
181184
private printModeData: Required<IEditorData> | null
185+
private hideTrack: boolean
182186

183187
constructor(
184188
rootContainer: HTMLElement,
@@ -213,6 +217,7 @@ export class Draw {
213217
this.background = new Background(this)
214218
this.search = new Search(this)
215219
this.group = new Group(this)
220+
this.track = new Track(this)
216221
this.underline = new Underline(this)
217222
this.strikeout = new Strikeout(this)
218223
this.highlight = new Highlight(this)
@@ -270,6 +275,7 @@ export class Draw {
270275
this.intersectionPageNo = 0
271276
this.lazyRenderIntersectionObserver = null
272277
this.printModeData = null
278+
this.hideTrack = false
273279

274280
this.render({
275281
isInit: true,
@@ -353,6 +359,79 @@ export class Draw {
353359
element => element.title?.disabled || element.control?.disabled
354360
)
355361
}
362+
// 添加痕迹信息
363+
public addReviewInformation(elementList: IElement[], type: TrackType) {
364+
if(this.mode !== EditorMode.REVIEW) return
365+
const trackId = getUUID()
366+
const len = elementList.length
367+
for(let i = 0; i < len; i++){
368+
const element = elementList[i]
369+
element.trackId = trackId
370+
element.trackType = type
371+
element.track = {
372+
author: this.options.user.name,
373+
date: getCurrentTimeString()
374+
}
375+
if(this.hideTrack && type === TrackType.DELETE) element.hide = true
376+
}
377+
}
378+
// 隐藏、显示痕迹
379+
public hideReview() {
380+
this.hideTrack = true
381+
const len = this.elementList.length
382+
for(let i = 0; i < len; i++){
383+
const el = this.elementList[i]
384+
if(el.type === ElementType.TABLE) {
385+
const trList = el.trList!
386+
trList.forEach(tr => {
387+
tr.tdList.forEach(td => {
388+
td.value.forEach((el, index) => {
389+
if (el.trackId && el.trackType === TrackType.DELETE) {
390+
td.value[index].hide = true
391+
}
392+
})
393+
})
394+
})
395+
} else if(el.trackId && el.trackType === TrackType.DELETE) {
396+
el.hide = true
397+
}
398+
}
399+
this.clearSideEffect()
400+
this.render({
401+
isSetCursor: false,
402+
isSubmitHistory: false
403+
})
404+
}
405+
public showReview() {
406+
this.hideTrack = false
407+
const len = this.elementList.length
408+
for(let i = 0; i < len; i++){
409+
const el = this.elementList[i]
410+
if(el.type === ElementType.TABLE) {
411+
const trList = el.trList!
412+
trList.forEach(tr => {
413+
tr.tdList.forEach(td => {
414+
td.value.forEach((el, index) => {
415+
if (el.trackId && el.trackType === TrackType.DELETE && el.hide) {
416+
delete td.value[index].hide
417+
}
418+
})
419+
})
420+
})
421+
} else if(el.trackId && el.trackType === TrackType.DELETE && el.hide) {
422+
delete el.hide
423+
}
424+
}
425+
this.clearSideEffect()
426+
this.render({
427+
isSetCursor: false,
428+
isSubmitHistory: false
429+
})
430+
}
431+
432+
public getHideTrackMode(): boolean {
433+
return this.hideTrack
434+
}
356435

357436
public isDesignMode() {
358437
return this.mode === EditorMode.DESIGN
@@ -848,6 +927,10 @@ export class Draw {
848927
return this.radioParticle
849928
}
850929

930+
public getTrack(): Track {
931+
return this.track
932+
}
933+
851934
public getControl(): Control {
852935
return this.control
853936
}
@@ -1296,7 +1379,12 @@ export class Draw {
12961379
const availableWidth = innerWidth - offsetX
12971380
// 增加起始位置坐标偏移量
12981381
x += curRow.elementList.length === 1 ? offsetX : 0
1299-
if (
1382+
if(element.hide) {
1383+
metrics.width = 0
1384+
metrics.height = 0
1385+
metrics.boundingBoxDescent = 0
1386+
metrics.boundingBoxAscent = 0
1387+
} else if (
13001388
element.type === ElementType.IMAGE ||
13011389
element.type === ElementType.LATEX
13021390
) {
@@ -1993,6 +2081,13 @@ export class Draw {
19932081
const isWrap = isForceBreak || isWidthNotEnough
19942082
// 新行数据处理
19952083
if (isWrap) {
2084+
// 整行宽度为0 隐藏行不保留高度
2085+
if(curRow.width === 0) {
2086+
const emptyPara = curRow.elementList.length === 1 && curRow.elementList[0].value === ZERO
2087+
if(!emptyPara) {
2088+
curRow.height = 0
2089+
}
2090+
}
19962091
const row: IRow = {
19972092
width: metrics.width,
19982093
height,
@@ -2254,7 +2349,9 @@ export class Draw {
22542349
} = positionList[curRow.startIndex + j]
22552350
const preElement = curRow.elementList[j - 1]
22562351
// 元素绘制
2257-
if (element.type === ElementType.IMAGE) {
2352+
if(element.hide) {
2353+
this.textParticle.complete()
2354+
} else if (element.type === ElementType.IMAGE) {
22582355
this.textParticle.complete()
22592356
// 浮动图片单独绘制
22602357
if (
@@ -2501,6 +2598,44 @@ export class Draw {
25012598
if (!group.disabled && element.groupIds) {
25022599
this.group.recordFillInfo(element, x, y, metrics.width, curRow.height)
25032600
}
2601+
// todo : 留痕信息记录
2602+
if(element.trackId) {
2603+
// 如果前后类型不一致
2604+
if(preElement?.trackId && preElement?.trackType && element?.trackType && element.trackType !== preElement.trackType) {
2605+
if(!this.hideTrack) {
2606+
this.track.render(ctx)
2607+
} else {
2608+
this.track.clearRectInfo()
2609+
}
2610+
}
2611+
// 基线文字测量信息
2612+
const standardMetrics = this.textParticle.measureBasisWord(
2613+
ctx,
2614+
this.getElementFont(element)
2615+
)
2616+
const rowMargin = this.getElementRowMargin(element)
2617+
2618+
if(element.trackType === TrackType.INSERT) {
2619+
const offsetX = element.left || 0
2620+
const coordinateX = x - offsetX
2621+
const coordinateY = y + curRow.height - rowMargin + 1
2622+
const width = metrics.width + offsetX
2623+
const height = curRow.height
2624+
this.track.recordInsertRectInfo(element, coordinateX, coordinateY, width, height)
2625+
} else if(element.trackType === TrackType.DELETE) {
2626+
let adjustY = y + offsetY + standardMetrics.actualBoundingBoxDescent * scale - metrics.height / 2
2627+
if(element.type === ElementType.IMAGE || element.type === ElementType.LATEX || element.type === ElementType.TABLE) {
2628+
adjustY = y + (element.height! / 2) + offsetY
2629+
}
2630+
this.track.recordDeleteRectInfo(element, x, adjustY, metrics.width, curRow.height)
2631+
}
2632+
} else if(preElement?.trackId) {
2633+
if(!this.hideTrack) {
2634+
this.track.render(ctx)
2635+
} else {
2636+
this.track.clearRectInfo()
2637+
}
2638+
}
25042639
index++
25052640
// 绘制表格内元素
25062641
if (element.type === ElementType.TABLE) {
@@ -2538,6 +2673,11 @@ export class Draw {
25382673
this.strikeout.render(ctx)
25392674
// 绘制批注样式
25402675
this.group.render(ctx)
2676+
if(!this.hideTrack) {
2677+
this.track.render(ctx)
2678+
} else {
2679+
this.track.clearRectInfo()
2680+
}
25412681
// 绘制选区
25422682
if (!isPrintMode) {
25432683
if (rangeRecord.width && rangeRecord.height) {
@@ -2944,5 +3084,7 @@ export class Draw {
29443084
this.getHyperlinkParticle().clearHyperlinkPopup()
29453085
// 日期控件
29463086
this.getDateParticle().clearDatePicker()
3087+
// 痕迹显示
3088+
this.getTrack().clearTrackPopup()
29473089
}
29483090
}

0 commit comments

Comments
 (0)