Skip to content

Commit 2eea00a

Browse files
authored
Merge pull request #1921 from DevCloudFE/xingyan/fix-code-review
fix(code-review): 优化选中行 & fix(md): 修复快捷键功能
2 parents 89c26fa + 33bc408 commit 2eea00a

File tree

10 files changed

+434
-261
lines changed

10 files changed

+434
-261
lines changed

packages/devui-vue/devui/code-review/src/code-review-types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ export interface CommentPosition {
1010
left: number;
1111
right: number;
1212
}
13+
export type ILineNumberTdMap = Record<number, HTMLElement[]>;
14+
export interface IExpandLineNumberInfo {
15+
nextL: string;
16+
nextR: string;
17+
prevL: string;
18+
prevR: string;
19+
}
20+
export interface ICheckedLineDetails {
21+
lefts: number[];
22+
rights: number[];
23+
codes: Record<string, string[]> | string[];
24+
}
1325
export interface CodeReviewMethods {
1426
toggleFold: (status?: boolean) => void;
1527
insertComment: (lineNumber: number, lineSide: LineSide, commentDom: HTMLElement) => void;

packages/devui-vue/devui/code-review/src/code-review.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* @jsxImportSource vue */
2-
import { defineComponent, onMounted, provide, toRefs } from 'vue';
2+
import { defineComponent, onMounted, provide, toRefs, ref } from 'vue';
33
import type { SetupContext } from 'vue';
44
import CodeReviewHeader from './components/code-review-header';
55
import { CommentIcon } from './components/code-review-icons';
@@ -18,8 +18,7 @@ export default defineComponent({
1818
setup(props: CodeReviewProps, ctx: SetupContext) {
1919
const ns = useNamespace('code-review');
2020
const { diffType } = toRefs(props);
21-
const { renderHtml, reviewContentRef, diffFile, onContentClick } = useCodeReview(props, ctx);
22-
const { isFold, toggleFold } = useCodeReviewFold(props, ctx);
21+
const reviewContentRef = ref();
2322
const {
2423
commentLeft,
2524
commentTop,
@@ -28,16 +27,18 @@ export default defineComponent({
2827
onCommentIconClick,
2928
insertComment,
3029
removeComment,
31-
updateCheckedLineClass,
3230
clearCheckedLines,
31+
updateLineNumberMap,
32+
updateCheckedLine,
3333
} = useCodeReviewComment(reviewContentRef, props, ctx);
34+
const { renderHtml, diffFile, onContentClick } = useCodeReview(props, ctx, reviewContentRef, updateLineNumberMap, updateCheckedLine);
35+
const { isFold, toggleFold } = useCodeReviewFold(props, ctx);
3436

3537
onMounted(() => {
3638
ctx.emit('afterViewInit', {
3739
toggleFold,
3840
insertComment,
3941
removeComment,
40-
updateCheckedLineClass,
4142
clearCheckedLines,
4243
});
4344
});
Lines changed: 29 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { ref, toRefs, onUnmounted, watch } from 'vue';
1+
import { ref, toRefs, onUnmounted } from 'vue';
22
import type { SetupContext, Ref } from 'vue';
33
import { useCodeReviewLineSelection } from './use-code-review-line-selection';
4-
import type { LineSide, CodeReviewProps } from '../code-review-types';
4+
import type { LineSide, CodeReviewProps, ICheckedLineDetails } from '../code-review-types';
55
import { useNamespace } from '../../../shared/hooks/use-namespace';
66
import {
77
notEmptyNode,
@@ -14,28 +14,18 @@ import {
1414
export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props: CodeReviewProps, ctx: SetupContext) {
1515
const { outputFormat, allowComment, allowChecked } = toRefs(props);
1616
const ns = useNamespace('code-review');
17-
const { onMousedown } = useCodeReviewLineSelection(reviewContentRef, props, afterMouseup);
17+
const { onMousedown, updateLineNumberMap, getCheckedLineDetails, clearCommentClass, updateCheckedLine } = useCodeReviewLineSelection(
18+
reviewContentRef,
19+
props,
20+
afterMouseup
21+
);
1822
const commentLeft = ref(-100);
1923
const commentTop = ref(-100);
2024
let currentLeftLineNumber = -1;
2125
let currentRightLineNumber = -1;
26+
let currentPosition: 'left' | 'right';
2227
let lastLineNumberContainer: HTMLElement | null;
23-
let checkedLineNumberContainer: Array<Element> = [];
24-
let currentLeftLineNumbers: Array<number> = [];
25-
let currentRightLineNumbers: Array<number> = [];
26-
let checkedLineCodeString: Array<string> | Record<string, Array<string>> = {};
27-
let allTrNodes: NodeListOf<Element> = [];
28-
let afterCheckLinesEmitData: Record<string, any>;
29-
watch(
30-
() => outputFormat.value,
31-
() => {
32-
// 如果出现单栏双栏切换则需要重置选中
33-
checkedLineNumberContainer = [];
34-
currentLeftLineNumbers = [];
35-
currentRightLineNumbers = [];
36-
checkedLineCodeString = [];
37-
}
38-
);
28+
3929
const resetLeftTop = () => {
4030
commentLeft.value = -100;
4131
commentTop.value = -100;
@@ -85,6 +75,8 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
8575
commentLeft.value = left;
8676
commentTop.value = top;
8777
currentLeftLineNumber = parseInt(leftLineNumberContainer.innerText);
78+
currentRightLineNumber = parseInt(rightLineNumberContainer.innerText || '-1');
79+
currentPosition = 'left';
8880
} else {
8981
resetLeftTop();
9082
}
@@ -98,7 +90,9 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
9890
const { top, left } = rightLineNumberContainer.getBoundingClientRect();
9991
commentLeft.value = left;
10092
commentTop.value = top;
93+
currentLeftLineNumber = parseInt(leftLineNumberContainer.innerText || '-1');
10194
currentRightLineNumber = parseInt(rightLineNumberContainer.innerText);
95+
currentPosition = 'right';
10296
} else {
10397
resetLeftTop();
10498
}
@@ -117,150 +111,27 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
117111
resetLeftTop();
118112
}
119113
};
120-
// 获取一些公共类和判断
121-
const getCommonClassAndJudge = () => {
122-
const checkedLine = [currentLeftLineNumbers, currentRightLineNumbers];
123-
return {
124-
linenumberDom: allTrNodes,
125-
checkedLine,
126-
};
127-
};
128-
// 之前每次都先移出所有选中的方法过于浪费性能,增加具体dom节点选中方法(防重复添加)
129-
const addCommentCheckedClass = (Dom: Element) => {
130-
!Dom.classList.contains('comment-checked') && Dom.classList.add('comment-checked');
131-
};
132-
// 单栏
133-
function getSingleCheckedLineCode(shouldRenderClass: boolean) {
134-
const { linenumberDom, checkedLine } = getCommonClassAndJudge();
135-
const checkedCodeContent = [];
136-
for (let i = 0; i < linenumberDom.length; i++) {
137-
const lineNumberDomLeft = linenumberDom[i].children[0];
138-
const lineNumberDomRight = linenumberDom[i].children[1];
139-
if (lineNumberDomLeft || lineNumberDomRight) {
140-
const codeLineNumberLeft = parseInt((lineNumberDomLeft as HTMLElement)?.innerText);
141-
const codeLineNumberRight = parseInt((lineNumberDomRight as HTMLElement)?.innerText);
142-
// 因为存在左边或者右边为空的num所以两边都要循环,但是同一个dom已经过就不需要再赋予
143-
if (checkedLine[0].includes(codeLineNumberLeft) || checkedLine[1].includes(codeLineNumberRight)) {
144-
checkedLineNumberContainer.push(linenumberDom[i]);
145-
// 两个节点之间可能间隔文本节点
146-
const codeNode = linenumberDom[i].nextElementSibling as HTMLElement;
147-
checkedCodeContent.push(codeNode?.innerText);
148-
if (shouldRenderClass) {
149-
addCommentCheckedClass(linenumberDom[i]);
150-
addCommentCheckedClass(codeNode);
151-
}
152-
}
153-
}
154-
}
155-
checkedLineCodeString = checkedCodeContent;
156-
}
157-
// 双栏
158-
function getDoubleCheckedLineCode(shouldRenderClass: boolean) {
159-
const { linenumberDom, checkedLine } = getCommonClassAndJudge();
160-
const checkedCodeContentLeft = [];
161-
const checkedCodeContentRight = [];
162-
163-
function checkedFunc(Dom: Element) {
164-
checkedLineNumberContainer.push(Dom);
165-
const codeNode = Dom.nextElementSibling as HTMLElement;
166-
if (shouldRenderClass) {
167-
addCommentCheckedClass(Dom);
168-
addCommentCheckedClass(codeNode);
169-
}
170-
return codeNode?.innerText;
171-
}
172114

173-
for (let i = 0; i < linenumberDom.length; i++) {
174-
// 左右双栏一起遍历
175-
const codeLineNumber = parseInt(linenumberDom[i]?.innerHTML);
176-
if (linenumberDom[i].classList.contains('d-code-left') && checkedLine[0].includes(codeLineNumber)) {
177-
const lineNumText = checkedFunc(linenumberDom[i]);
178-
checkedCodeContentLeft.push(lineNumText);
179-
continue;
180-
}
181-
if (linenumberDom[i].classList.contains('d-code-right') && checkedLine[1].includes(codeLineNumber)) {
182-
const lineNumText = checkedFunc(linenumberDom[i]);
183-
checkedCodeContentRight.push(lineNumText);
184-
}
185-
}
186-
checkedLineCodeString = { leftCode: checkedCodeContentLeft, rightCode: checkedCodeContentRight };
187-
}
188-
function getCheckedLineCode(shouldRenderClass: boolean) {
189-
if (props.outputFormat === 'line-by-line') {
190-
return getSingleCheckedLineCode(shouldRenderClass);
191-
}
192-
getDoubleCheckedLineCode(shouldRenderClass);
193-
}
194-
function updateLineNumbers({ lefts, rights }: { lefts: number[]; rights: number[] }) {
195-
currentLeftLineNumbers = lefts;
196-
currentRightLineNumbers = rights;
197-
getCheckedLineCode(false);
198-
afterCheckLinesEmitData = {
199-
left: currentLeftLineNumber,
200-
right: currentRightLineNumber,
201-
details: {
202-
lefts: currentLeftLineNumbers,
203-
rights: currentRightLineNumbers,
204-
codes: checkedLineCodeString,
205-
},
206-
};
207-
}
208-
const updateCheckedLineClass = () => {
209-
const lineClassName = props.outputFormat === 'line-by-line' ? '.d2h-code-linenumber' : '.d2h-code-side-linenumber';
210-
allTrNodes = reviewContentRef.value.querySelectorAll(lineClassName);
211-
getCheckedLineCode(true);
212-
};
213-
// 还原样式
214-
const resetCommentClass = () => {
215-
for (let i = 0; i < checkedLineNumberContainer.length; i++) {
216-
checkedLineNumberContainer[i].classList.remove('comment-checked');
217-
const codeNode = checkedLineNumberContainer[i].nextElementSibling;
218-
(codeNode as HTMLElement)?.classList.remove('comment-checked');
219-
}
220-
checkedLineNumberContainer = [];
221-
};
222115
// 点击
223116
const commentClick = () => {
224-
interface recordType {
225-
left: number;
226-
right: number;
227-
details?: {
228-
lefts: Array<number>;
229-
rights: Array<number>;
230-
codes: Record<string, Array<string>> | Record<string, Array<number>>;
231-
};
232-
}
233-
let obj: recordType = { left: currentLeftLineNumber, right: currentRightLineNumber };
234-
if ((currentLeftLineNumbers.length >= 1 || currentRightLineNumbers.length >= 1) && allowChecked.value) {
235-
// 选中模式
236-
const maxCurrentLeftLineNumber = currentLeftLineNumbers[currentLeftLineNumbers.length - 1];
237-
const maxCurrentRightLineNumber = currentRightLineNumbers[currentRightLineNumbers.length - 1];
238-
if (maxCurrentLeftLineNumber === currentLeftLineNumber || maxCurrentRightLineNumber === currentRightLineNumber) {
239-
// 点击添加评论图标触发的事件
240-
obj = {
241-
left: currentLeftLineNumber,
242-
right: currentRightLineNumber,
243-
details: {
244-
lefts: currentLeftLineNumbers,
245-
rights: currentRightLineNumbers,
246-
codes: checkedLineCodeString,
247-
},
248-
};
117+
let obj = { left: currentLeftLineNumber, right: currentRightLineNumber, position: currentPosition };
118+
const checkedLineDetails = getCheckedLineDetails();
119+
// 多行选中
120+
if (checkedLineDetails && allowChecked.value) {
121+
const { lefts, rights } = checkedLineDetails;
122+
const maxCheckedLeftLineNumber = lefts[lefts.length - 1];
123+
const maxCheckedRightLineNumber = rights[rights.length - 1];
124+
if (maxCheckedLeftLineNumber === currentLeftLineNumber || maxCheckedRightLineNumber === currentRightLineNumber) {
125+
obj.details = checkedLineDetails;
249126
} else {
250-
currentLeftLineNumbers = [];
251-
currentRightLineNumbers = [];
252-
resetCommentClass();
127+
clearCommentClass();
253128
}
254129
}
255130
// 点击添加评论图标触发的事件
256131
ctx.emit('addComment', obj);
257132
};
258-
function afterCheckLines() {
259-
ctx.emit('afterCheckLines', afterCheckLinesEmitData);
260-
}
261-
function afterMouseup(lineNumbers: { lefts: number[]; rights: number[] }) {
262-
updateLineNumbers(lineNumbers);
263-
afterCheckLines();
133+
function afterMouseup(details: ICheckedLineDetails) {
134+
ctx.emit('afterCheckLines', { left: currentLeftLineNumber, right: currentRightLineNumber, position: currentPosition, details });
264135
}
265136
// 图标或者单行的点击
266137
const onCommentIconClick = (e: Event) => {
@@ -317,16 +188,7 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
317188
};
318189

319190
const clearCheckedLines = () => {
320-
currentLeftLineNumbers = [];
321-
currentRightLineNumbers = [];
322-
checkedLineCodeString = [];
323-
resetCommentClass();
324-
};
325-
326-
const handleMouseDown = (e: MouseEvent) => {
327-
const lineClassName = props.outputFormat === 'line-by-line' ? '.d2h-code-linenumber' : '.d2h-code-side-linenumber';
328-
allTrNodes = reviewContentRef.value.querySelectorAll(lineClassName);
329-
onMousedown(e);
191+
clearCommentClass();
330192
};
331193

332194
const mouseEvent: Record<string, (e: MouseEvent) => void> = {};
@@ -335,7 +197,7 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
335197
mouseEvent.onMouseleave = onMouseleave;
336198
}
337199
if (props.allowChecked) {
338-
mouseEvent.onMousedown = handleMouseDown;
200+
mouseEvent.onMousedown = onMousedown;
339201
}
340202

341203
window.addEventListener('scroll', resetLeftTop);
@@ -348,11 +210,12 @@ export function useCodeReviewComment(reviewContentRef: Ref<HTMLElement>, props:
348210
commentLeft,
349211
commentTop,
350212
mouseEvent,
351-
updateCheckedLineClass,
352213
clearCheckedLines,
353214
onCommentMouseLeave,
354215
onCommentIconClick,
355216
insertComment,
356217
removeComment,
218+
updateLineNumberMap,
219+
updateCheckedLine,
357220
};
358221
}

packages/devui-vue/devui/code-review/src/composables/use-code-review-expand.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { toRefs } from 'vue';
22
import type { Ref } from 'vue';
3-
import type { CodeReviewProps, ExpandDirection } from '../code-review-types';
3+
import type { CodeReviewProps, ExpandDirection, IExpandLineNumberInfo } from '../code-review-types';
44
import { ExpandLineReg, FirstLineReg } from '../const';
55
import {
66
attachExpandUpDownButton,
@@ -14,7 +14,12 @@ import {
1414
ifRemoveExpandLineForDoubleColumn,
1515
} from '../utils';
1616

17-
export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, props: CodeReviewProps) {
17+
export function useCodeReviewExpand(
18+
reviewContentRef: Ref<HTMLElement>,
19+
props: CodeReviewProps,
20+
updateLineNumberMap: (expandLineNumberInfo: IExpandLineNumberInfo, newCode: string, direction: 'up' | 'down') => void,
21+
updateCheckedLine: (expandLineNumberInfo: IExpandLineNumberInfo, direction: 'up' | 'down') => void
22+
) {
1823
const { outputFormat, expandThreshold, expandLoader } = toRefs(props);
1924

2025
const processSideBySide = () => {
@@ -85,8 +90,12 @@ export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, props: C
8590

8691
// 过滤有效行
8792
const trNodesToBeInserted = trNodes.filter((element) => element !== expandLine);
93+
/* 更新左右行号映射关系 */
94+
updateLineNumberMap(referenceDom.dataset as unknown as IExpandLineNumberInfo, prefix + code, direction);
8895
// 将有效代码行插入页面
8996
insertIncrementLineToPage(referenceDom, trNodesToBeInserted, direction);
97+
/* 若新增行在选中区间,则将新增行高亮 */
98+
updateCheckedLine(referenceDom.dataset as unknown as IExpandLineNumberInfo, direction);
9099

91100
// 判断是否需要移除展开行,代码若已全部展开,不再需要展开行
92101
const removedExpandLine = ifRemoveExpandLineForDoubleColumn(referenceDom, expandLine, direction);
@@ -192,6 +201,8 @@ export function useCodeReviewExpand(reviewContentRef: Ref<HTMLElement>, props: C
192201
const trNodesToBeInserted = trNodes.filter((element) => element.children[0].children.length === 2);
193202
// 将有效代码行插入页面
194203
insertIncrementLineToPage(referenceDom, trNodesToBeInserted, direction);
204+
/* 若新增行在选中区间,则将新增行高亮 */
205+
updateCheckedLine(referenceDom.dataset as unknown as IExpandLineNumberInfo, direction);
195206

196207
// 判断是否需要移除展开行,代码若已全部展开,不再需要展开行
197208
const removedExpandLine = ifRemoveExpandLine(referenceDom, expandLine, direction);

0 commit comments

Comments
 (0)