diff --git a/packages/devui-vue/devui/code-review/src/code-review.scss b/packages/devui-vue/devui/code-review/src/code-review.scss
index 47a1c36bff..ef00494816 100644
--- a/packages/devui-vue/devui/code-review/src/code-review.scss
+++ b/packages/devui-vue/devui/code-review/src/code-review.scss
@@ -183,6 +183,10 @@
content: '';
}
+ .d2h-code-linenumber {
+ border-right: 2px solid transparent;
+ }
+
.d2h-code-line-ctn {
word-break: break-all;
word-wrap: break-word !important;
@@ -222,6 +226,11 @@
position: static;
display: table-cell;
}
+
+ .d-code-left:nth-of-type(2) > .d2h-code-side-line,
+ .d-code-right:nth-of-type(4) > .d2h-code-side-line {
+ border-left: 2px solid transparent;
+ }
}
.d2h-file-diff {
@@ -269,6 +278,15 @@
background-color: #daf4ae; // 增加行中的number
}
}
+
+ &.d2h-code-linenumber {
+ border-right: 2px solid #fe7300;
+ }
+
+ &.d-code-left:nth-of-type(2) > .d2h-code-side-line,
+ &.d-code-right:nth-of-type(4) > .d2h-code-side-line {
+ border-left: 2px solid #fe7300;
+ }
}
}
diff --git a/packages/devui-vue/devui/code-review/src/code-review.tsx b/packages/devui-vue/devui/code-review/src/code-review.tsx
index c47e6bbef3..c1ea435294 100644
--- a/packages/devui-vue/devui/code-review/src/code-review.tsx
+++ b/packages/devui-vue/devui/code-review/src/code-review.tsx
@@ -1,5 +1,5 @@
/* @jsxImportSource vue */
-import { defineComponent, onMounted, provide, toRefs, onBeforeUnmount } from 'vue';
+import { defineComponent, onMounted, provide, toRefs } from 'vue';
import type { SetupContext } from 'vue';
import CodeReviewHeader from './components/code-review-header';
import { CommentIcon } from './components/code-review-icons';
@@ -14,27 +14,40 @@ import './code-review.scss';
export default defineComponent({
name: 'DCodeReview',
props: codeReviewProps,
- emits: ['foldChange', 'addComment', 'afterViewInit', 'contentRefresh'],
+ emits: ['foldChange', 'addComment', 'afterViewInit', 'contentRefresh', 'afterCheckLines'],
setup(props: CodeReviewProps, ctx: SetupContext) {
const ns = useNamespace('code-review');
const { diffType } = toRefs(props);
const { renderHtml, reviewContentRef, diffFile, onContentClick } = useCodeReview(props, ctx);
const { isFold, toggleFold } = useCodeReviewFold(props, ctx);
- const { commentLeft, commentTop,
- mouseEvent, onCommentMouseLeave,
- onCommentIconClick, onCommentKeyDown,
- unCommentKeyDown, insertComment,
- removeComment, updateCheckedLineClass, clearCheckedLines } = useCodeReviewComment(reviewContentRef, props, ctx);
+ const {
+ commentLeft,
+ commentTop,
+ mouseEvent,
+ onCommentMouseLeave,
+ onCommentIconClick,
+ insertComment,
+ removeComment,
+ updateCheckedLineClass,
+ clearCheckedLines,
+ } = useCodeReviewComment(reviewContentRef, props, ctx);
onMounted(() => {
- ctx.emit('afterViewInit', { toggleFold, insertComment, removeComment, updateCheckedLineClass, clearCheckedLines });
- onCommentKeyDown();
+ ctx.emit('afterViewInit', {
+ toggleFold,
+ insertComment,
+ removeComment,
+ updateCheckedLineClass,
+ clearCheckedLines,
+ });
});
- // 销毁
- onBeforeUnmount(() => {
- unCommentKeyDown();
+ provide(CodeReviewInjectionKey, {
+ diffType,
+ reviewContentRef,
+ diffInfo: diffFile.value[0],
+ isFold,
+ rootCtx: ctx,
});
- provide(CodeReviewInjectionKey, { diffType, reviewContentRef, diffInfo: diffFile.value[0], isFold, rootCtx: ctx });
return () => (
@@ -59,8 +72,7 @@ export default defineComponent({
class="comment-icon"
style={{ left: commentLeft.value + 'px', top: commentTop.value + 'px' }}
onClick={onCommentIconClick}
- onMouseleave={onCommentMouseLeave}
- >
+ onMouseleave={onCommentMouseLeave}>
)}
diff --git a/packages/devui-vue/devui/code-review/src/composables/use-code-review-comment.ts b/packages/devui-vue/devui/code-review/src/composables/use-code-review-comment.ts
index 62846b36ba..f9ceeb0d5e 100644
--- a/packages/devui-vue/devui/code-review/src/composables/use-code-review-comment.ts
+++ b/packages/devui-vue/devui/code-review/src/composables/use-code-review-comment.ts
@@ -1,5 +1,6 @@
import { ref, toRefs, onUnmounted, watch } from 'vue';
import type { SetupContext, Ref } from 'vue';
+import { useCodeReviewLineSelection } from './use-code-review-line-selection';
import type { LineSide, CodeReviewProps } from '../code-review-types';
import { useNamespace } from '../../../shared/hooks/use-namespace';
import {
@@ -13,23 +14,28 @@ import {
export function useCodeReviewComment(reviewContentRef: Ref, props: CodeReviewProps, ctx: SetupContext) {
const { outputFormat, allowComment, allowChecked } = toRefs(props);
const ns = useNamespace('code-review');
+ const { onMousedown } = useCodeReviewLineSelection(reviewContentRef, props, updateLineNumbers, afterCheckLines);
const commentLeft = ref(-100);
const commentTop = ref(-100);
let currentLeftLineNumber = -1;
let currentRightLineNumber = -1;
let lastLineNumberContainer: HTMLElement | null;
let checkedLineNumberContainer: Array = [];
- let isShift = false;
let currentLeftLineNumbers: Array = [];
let currentRightLineNumbers: Array = [];
let checkedLineCodeString: Array | Record> = {};
- watch(() => outputFormat.value, () => {
- // 如果出现单栏双栏切换则需要重置选中
- checkedLineNumberContainer = [];
- currentLeftLineNumbers = [];
- currentRightLineNumbers = [];
- checkedLineCodeString = [];
- });
+ let allTrNodes: NodeListOf = [];
+ let afterCheckLinesEmitData: Record;
+ watch(
+ () => outputFormat.value,
+ () => {
+ // 如果出现单栏双栏切换则需要重置选中
+ checkedLineNumberContainer = [];
+ currentLeftLineNumbers = [];
+ currentRightLineNumbers = [];
+ checkedLineCodeString = [];
+ }
+ );
const resetLeftTop = () => {
commentLeft.value = -100;
commentTop.value = -100;
@@ -111,35 +117,10 @@ export function useCodeReviewComment(reviewContentRef: Ref, props:
resetLeftTop();
}
};
- function commentKeyDown(e: any) {
- // keyCode已经被废弃了 用e.key代替 或者e.code代替
- switch (e.key) {
- case 'Shift':
- isShift = true;
- break;
- }
- }
- function commentKeyUp(e: any) {
- e.preventDefault();
- switch (e.key) {
- case 'Shift':
- isShift = false;
- break;
- }
- }
- // 销毁键盘事件
- const unCommentKeyDown = () => {
- document.removeEventListener('keydown', commentKeyDown);
- document.removeEventListener('keyup', commentKeyUp);
- };
- // 键盘事件
- const onCommentKeyDown = () => {
- document.addEventListener('keydown', commentKeyDown);
- document.addEventListener('keyup', commentKeyUp);
- };
// 获代码行 取值方法
- const getLineNumbers = (currentNumber: number, currentNumbers: Array, e: Event) => {
- if (currentNumber === -1) { // 当前行没数据不代表之前选中的没数据,此时返回原来的
+ const getLineNumbers = (currentNumber: number, currentNumbers: Array) => {
+ if (currentNumber === -1) {
+ // 当前行没数据不代表之前选中的没数据,此时返回原来的
return currentNumbers;
}
if (currentNumbers.length === 0) {
@@ -147,69 +128,70 @@ export function useCodeReviewComment(reviewContentRef: Ref, props:
}
const numbers = [...currentNumbers];
let max = Math.max(...numbers);
- const min = Math.min(...numbers);
- if (currentNumber > max) { // 限制规则只能从小选到大。
+ let min = Math.min(...numbers);
+ if (currentNumber < min) {
+ min = currentNumber;
+ }
+ if (currentNumber > max) {
max = currentNumber;
}
return Array.from({ length: max - min + 1 }, (_, i) => i + min);
};
// 获取一些公共类和判断
- const getCommonClassAndJudge = (side: string) => {
- const lineClassName = side === 'line-by-line' ? '.d2h-code-linenumber' : '.d2h-code-side-linenumber';
- const linenumberDom = reviewContentRef.value.querySelectorAll(lineClassName);
+ const getCommonClassAndJudge = () => {
const checkedLine = [currentLeftLineNumbers, currentRightLineNumbers];
return {
- linenumberDom,
- checkedLine
+ linenumberDom: allTrNodes,
+ checkedLine,
};
};
// 之前每次都先移出所有选中的方法过于浪费性能,增加具体dom节点选中方法(防重复添加)
const addCommentCheckedClass = (Dom: Element) => {
- !Dom.classList.contains('comment-checked') && Dom.classList.add('comment-checked');
+ !Dom.classList.contains('comment-checked') && Dom.classList.add('comment-checked');
};
- // 选中(单栏)
- const addCommentClassSingle = (side: string) => {
- const { linenumberDom, checkedLine } = getCommonClassAndJudge(side);
+ // 单栏
+ function getSingleCheckedLineCode(shouldRenderClass: boolean) {
+ const { linenumberDom, checkedLine } = getCommonClassAndJudge();
const checkedCodeContent = [];
- // resetCommentClass();
for (let i = 0; i < linenumberDom.length; i++) {
const lineNumberDomLeft = linenumberDom[i].children[0];
const lineNumberDomRight = linenumberDom[i].children[1];
if (lineNumberDomLeft || lineNumberDomRight) {
- const codeLineNumberLeft = parseInt((lineNumberDomLeft as HTMLElement)?.innerText);
- const codeLineNumberRight = parseInt((lineNumberDomRight as HTMLElement)?.innerText);
+ const codeLineNumberLeft = parseInt((lineNumberDomLeft as HTMLElement)?.innerText);
+ const codeLineNumberRight = parseInt((lineNumberDomRight as HTMLElement)?.innerText);
// 因为存在左边或者右边为空的num所以两边都要循环,但是同一个dom已经过就不需要再赋予
if (checkedLine[0].includes(codeLineNumberLeft) || checkedLine[1].includes(codeLineNumberRight)) {
checkedLineNumberContainer.push(linenumberDom[i]);
// 两个节点之间可能间隔文本节点
- const codeNode = (linenumberDom[i].nextSibling as HTMLElement).nodeName === '#text'
- ? (linenumberDom[i].nextSibling as HTMLElement).nextSibling
- : linenumberDom[i].nextSibling;
- checkedCodeContent.push((codeNode as HTMLElement)?.innerText);
- addCommentCheckedClass(linenumberDom[i]);
- addCommentCheckedClass(codeNode as HTMLElement);
+ const codeNode = linenumberDom[i].nextElementSibling as HTMLElement;
+ checkedCodeContent.push(codeNode?.innerText);
+ if (shouldRenderClass) {
+ addCommentCheckedClass(linenumberDom[i]);
+ addCommentCheckedClass(codeNode);
+ }
}
}
}
checkedLineCodeString = checkedCodeContent;
- };
- // 选中(双栏)
- const addCommentClassDouble = (side: string) => {
- const { linenumberDom, checkedLine } = getCommonClassAndJudge(side);
+ }
+ // 双栏
+ function getDoubleCheckedLineCode(shouldRenderClass: boolean) {
+ const { linenumberDom, checkedLine } = getCommonClassAndJudge();
const checkedCodeContentLeft = [];
const checkedCodeContentRight = [];
function checkedFunc(Dom: Element) {
checkedLineNumberContainer.push(Dom);
- const codeNode = (Dom.nextSibling as HTMLElement).nodeName === '#text'
- ? (Dom.nextSibling as HTMLElement).nextSibling
- : Dom.nextSibling;
- addCommentCheckedClass(Dom);
- addCommentCheckedClass(codeNode as HTMLElement);
- return (codeNode as HTMLElement)?.innerText;
+ const codeNode = Dom.nextElementSibling as HTMLElement;
+ if (shouldRenderClass) {
+ addCommentCheckedClass(Dom);
+ addCommentCheckedClass(codeNode);
+ }
+ return codeNode?.innerText;
}
- for (let i = 0; i < linenumberDom.length; i++) { // 左右双栏一起遍历
+ for (let i = 0; i < linenumberDom.length; i++) {
+ // 左右双栏一起遍历
const codeLineNumber = parseInt(linenumberDom[i]?.innerHTML);
if (linenumberDom[i].classList.contains('d-code-left') && checkedLine[0].includes(codeLineNumber)) {
const lineNumText = checkedFunc(linenumberDom[i]);
@@ -222,37 +204,43 @@ export function useCodeReviewComment(reviewContentRef: Ref, props:
}
}
checkedLineCodeString = { leftCode: checkedCodeContentLeft, rightCode: checkedCodeContentRight };
- };
- const updateCheckedLineClass = () => {
- if (outputFormat.value === 'line-by-line') {
- addCommentClassSingle(outputFormat.value);
- return;
+ }
+ function getCheckedLineCode(shouldRenderClass: boolean) {
+ if (props.outputFormat === 'line-by-line') {
+ return getSingleCheckedLineCode(shouldRenderClass);
}
- addCommentClassDouble(outputFormat.value);
+ getDoubleCheckedLineCode(shouldRenderClass);
+ }
+ function updateLineNumbers() {
+ currentLeftLineNumbers =
+ currentLeftLineNumber === -1 ? currentLeftLineNumbers : getLineNumbers(currentLeftLineNumber, currentLeftLineNumbers);
+ currentRightLineNumbers =
+ currentRightLineNumber === -1 ? currentRightLineNumbers : getLineNumbers(currentRightLineNumber, currentRightLineNumbers);
+ getCheckedLineCode(false);
+ afterCheckLinesEmitData = {
+ left: currentLeftLineNumber,
+ right: currentRightLineNumber,
+ details: {
+ lefts: currentLeftLineNumbers,
+ rights: currentRightLineNumbers,
+ codes: checkedLineCodeString,
+ },
+ };
+ }
+ const updateCheckedLineClass = () => {
+ getCheckedLineCode(true);
};
// 还原样式
const resetCommentClass = () => {
for (let i = 0; i < checkedLineNumberContainer.length; i++) {
checkedLineNumberContainer[i].classList.remove('comment-checked');
- const codeNode = (checkedLineNumberContainer[i].nextSibling as HTMLElement).nodeName === '#text'
- ? (checkedLineNumberContainer[i].nextSibling as HTMLElement).nextSibling
- : checkedLineNumberContainer[i].nextSibling;
+ const codeNode = checkedLineNumberContainer[i].nextElementSibling;
(codeNode as HTMLElement)?.classList.remove('comment-checked');
}
checkedLineNumberContainer = [];
};
- // 按住shift键点击
- const commentShiftClick = (e: Event) => {
- currentLeftLineNumbers = currentLeftLineNumber === -1
- ? currentLeftLineNumbers
- : getLineNumbers(currentLeftLineNumber, currentLeftLineNumbers, e);
- currentRightLineNumbers = currentRightLineNumber === -1
- ? currentRightLineNumbers
- : getLineNumbers(currentRightLineNumber, currentRightLineNumbers, e);
- updateCheckedLineClass();
- };
// 点击
- const commentClick = (e: Event) => {
+ const commentClick = () => {
interface recordType {
left: number;
right: number;
@@ -263,15 +251,22 @@ export function useCodeReviewComment(reviewContentRef: Ref, props:
};
}
let obj: recordType = { left: currentLeftLineNumber, right: currentRightLineNumber };
- if (currentLeftLineNumbers.length >= 1 || currentRightLineNumbers.length >= 1 && allowChecked.value) { // 选中模式
+ if ((currentLeftLineNumbers.length >= 1 || currentRightLineNumbers.length >= 1) && allowChecked.value) {
+ // 选中模式
const maxCurrentLeftLineNumber = currentLeftLineNumbers[currentLeftLineNumbers.length - 1];
const maxCurrentRightLineNumber = currentRightLineNumbers[currentRightLineNumbers.length - 1];
if (maxCurrentLeftLineNumber === currentLeftLineNumber || maxCurrentRightLineNumber === currentRightLineNumber) {
// 点击添加评论图标触发的事件
- obj = { left: currentLeftLineNumber, right: currentRightLineNumber, details: {
- lefts: currentLeftLineNumbers, rights: currentRightLineNumbers, codes: checkedLineCodeString
- }};
- } else{
+ obj = {
+ left: currentLeftLineNumber,
+ right: currentRightLineNumber,
+ details: {
+ lefts: currentLeftLineNumbers,
+ rights: currentRightLineNumbers,
+ codes: checkedLineCodeString,
+ },
+ };
+ } else {
currentLeftLineNumbers = [];
currentRightLineNumbers = [];
resetCommentClass();
@@ -280,9 +275,13 @@ export function useCodeReviewComment(reviewContentRef: Ref, props:
// 点击添加评论图标触发的事件
ctx.emit('addComment', obj);
};
+ function afterCheckLines() {
+ ctx.emit('afterCheckLines', afterCheckLinesEmitData);
+ }
// 图标或者单行的点击
const onCommentIconClick = (e: Event) => {
- if (e) { // 根据时间反回的dom判断是否点击中的制定区域
+ if (e) {
+ // 根据时间反回的dom判断是否点击中的制定区域
const composedPath = e.composedPath() as HTMLElement[];
const lineNumberBox = composedPath.find(
(item) => item.classList?.contains('comment-icon-hover') || item.classList?.contains('comment-icon')
@@ -291,12 +290,7 @@ export function useCodeReviewComment(reviewContentRef: Ref, props:
return;
}
}
- // 按住shift键选中
- if (isShift && allowChecked.value) {
- commentShiftClick(e);
- return;
- }
- commentClick(e);
+ commentClick();
};
const insertComment = (lineNumber: number, lineSide: LineSide, commentDom: HTMLElement) => {
if (outputFormat.value === 'line-by-line') {
@@ -345,7 +339,20 @@ export function useCodeReviewComment(reviewContentRef: Ref, props:
resetCommentClass();
};
- const mouseEvent = allowComment.value ? { onMousemove: onMouseMove, onMouseleave: onMouseleave } : {};
+ const handleMouseDown = (e: MouseEvent) => {
+ const lineClassName = props.outputFormat === 'line-by-line' ? '.d2h-code-linenumber' : '.d2h-code-side-linenumber';
+ allTrNodes = reviewContentRef.value.querySelectorAll(lineClassName);
+ onMousedown(e);
+ };
+
+ const mouseEvent: Record void> = {};
+ if (allowComment.value) {
+ mouseEvent.onMousemove = onMouseMove;
+ mouseEvent.onMouseleave = onMouseleave;
+ }
+ if (props.allowChecked) {
+ mouseEvent.onMousedown = handleMouseDown;
+ }
window.addEventListener('scroll', resetLeftTop);
@@ -357,14 +364,10 @@ export function useCodeReviewComment(reviewContentRef: Ref, props:
commentLeft,
commentTop,
mouseEvent,
- // currentLeftLineNumbers,
- // currentRightLineNumbers,
updateCheckedLineClass,
clearCheckedLines,
onCommentMouseLeave,
onCommentIconClick,
- onCommentKeyDown,
- unCommentKeyDown,
insertComment,
removeComment,
};
diff --git a/packages/devui-vue/devui/code-review/src/composables/use-code-review-line-selection.ts b/packages/devui-vue/devui/code-review/src/composables/use-code-review-line-selection.ts
new file mode 100644
index 0000000000..6d0592bb25
--- /dev/null
+++ b/packages/devui-vue/devui/code-review/src/composables/use-code-review-line-selection.ts
@@ -0,0 +1,140 @@
+import type { Ref } from 'vue';
+import type { CodeReviewProps } from '../code-review-types';
+import { useNamespace } from '../../../shared/hooks/use-namespace';
+import { findParentTrNode } from '../utils';
+
+export function useCodeReviewLineSelection(
+ reviewContentRef: Ref,
+ props: CodeReviewProps,
+ mouseMoveCb: () => void,
+ mouseupCb: () => void
+) {
+ const ns = useNamespace('code-review');
+ let dragging = false;
+ let startTrNode: HTMLElement;
+ let trNodes: HTMLElement[];
+ let isClickedLeft: boolean | undefined;
+ let shouldClear: boolean;
+ let isMouseMoved: boolean;
+
+ const onMousedown = (e: MouseEvent) => {
+ // 鼠标左键按下
+ if (e.button === 0) {
+ const composedPath = e.composedPath() as HTMLElement[];
+ const lineNumberBox = composedPath.find(
+ (item) => item.classList?.contains('comment-icon-hover') || item.classList?.contains('comment-icon')
+ );
+ trNodes = Array.from(reviewContentRef.value.querySelectorAll('tr')).filter((item) => !item.classList?.contains('expand-line'));
+ // 根据事件返回的dom判断是否点击的行号
+ if (!lineNumberBox) {
+ return;
+ }
+ const parentTrNode = findParentTrNode(e.target as HTMLElement);
+ // 判断点击的是否是展开图标
+ if (parentTrNode && parentTrNode?.classList.contains('expand-line')) {
+ return;
+ }
+ startTrNode = parentTrNode as HTMLElement;
+ if (props.outputFormat === 'side-by-side') {
+ isClickedLeft = composedPath.some((item) => item.classList?.contains('d-code-left'));
+ } else {
+ isClickedLeft = undefined;
+ }
+
+ dragging = true;
+ shouldClear = true;
+ isMouseMoved = false;
+ e.preventDefault();
+ e.stopPropagation();
+ document.addEventListener('mousemove', onMousemove);
+ document.addEventListener('mouseup', onMouseup);
+ }
+ };
+
+ function onMousemove(e: MouseEvent) {
+ if (!dragging) {
+ return;
+ }
+ isMouseMoved = true;
+ if (shouldClear) {
+ clearCommentChecked();
+ shouldClear = false;
+ }
+ const composedPath = e.composedPath() as HTMLElement[];
+ const inReviewContent = composedPath.some((item) => item.classList?.contains(ns.e('content')));
+ if (!inReviewContent) {
+ return;
+ }
+ const endTrNode = findParentTrNode(e.target as HTMLElement);
+ if (!endTrNode) {
+ return;
+ }
+ let startIndex = trNodes.indexOf(startTrNode);
+ let endIndex = trNodes.indexOf(endTrNode);
+ if (endIndex === -1) {
+ return;
+ }
+ mouseMoveCb();
+ if (startIndex > endIndex) {
+ [startIndex, endIndex] = [endIndex, startIndex];
+ }
+
+ let position: 'left' | 'right' | 'all';
+ if (isClickedLeft === undefined) {
+ position = 'all';
+ } else if (isClickedLeft) {
+ position = 'left';
+ } else {
+ position = 'right';
+ }
+ for (let i = 0; i < trNodes.length; i++) {
+ if (i >= startIndex && i <= endIndex) {
+ toggleCommentCheckedClass(trNodes[i], true, position);
+ } else {
+ toggleCommentCheckedClass(trNodes[i], false, position);
+ }
+ }
+ }
+
+ function onMouseup() {
+ dragging = false;
+ if (isMouseMoved) {
+ mouseupCb();
+ }
+ document.removeEventListener('mouseup', onMouseup);
+ document.removeEventListener('mousemove', onMousemove);
+ }
+
+ // 清除上次的选中
+ function clearCommentChecked() {
+ for (let i = 0; i < trNodes.length; i++) {
+ toggleCommentCheckedClass(trNodes[i], false, 'all');
+ }
+ }
+
+ function toggleCommentCheckedClass(trNode: HTMLElement, isAddClass: boolean, position: 'left' | 'right' | 'all') {
+ const tdNodes = Array.from(trNode.children);
+ let toDoNodes;
+ if (position === 'all') {
+ toDoNodes = tdNodes;
+ } else if (position === 'left') {
+ toDoNodes = tdNodes.slice(0, 2);
+ } else {
+ toDoNodes = tdNodes.slice(2);
+ }
+ if ((position === 'left' || position === 'right') && isNaN(parseInt(toDoNodes[0]?.innerHTML))) {
+ return;
+ }
+ toDoNodes.forEach((item) => {
+ if (item.tagName === 'TD') {
+ if (isAddClass) {
+ item.classList.add('comment-checked');
+ } else {
+ item.classList.remove('comment-checked');
+ }
+ }
+ });
+ }
+
+ return { onMousedown };
+}
diff --git a/packages/devui-vue/devui/code-review/src/utils.ts b/packages/devui-vue/devui/code-review/src/utils.ts
index 8e3559b3c6..74180edba5 100644
--- a/packages/devui-vue/devui/code-review/src/utils.ts
+++ b/packages/devui-vue/devui/code-review/src/utils.ts
@@ -517,3 +517,14 @@ export function findReferenceDomForDoubleColumn(parentNode: HTMLElement, lineNum
}
}
}
+
+/* 多行选中,根据当前dom节点,向上寻找tr节点 */
+export function findParentTrNode(node: HTMLElement | null) {
+ if (!node) {
+ return null;
+ }
+ if (node.tagName === 'TR') {
+ return node;
+ }
+ return findParentTrNode(node.parentElement);
+}
diff --git a/packages/devui-vue/docs/components/code-review/index.md b/packages/devui-vue/docs/components/code-review/index.md
index 87f4214c39..831b38849b 100644
--- a/packages/devui-vue/docs/components/code-review/index.md
+++ b/packages/devui-vue/docs/components/code-review/index.md
@@ -580,7 +580,7 @@ export default defineComponent({
| diff | `string` | '' | 必选,diff 内容 |
| fold | `boolean` | false | 可选,是否折叠显示 |
| allow-comment | `boolean` | true | 可选,是否支持评论 |
-| allow-checked | `boolean` | false | 可选,是否支持代码行选中,开启后可以按住 shift 点击鼠标选中多行代码,只能按照从小到大的顺序选择,可以跨行选择,开启后add-comment事件的反回值会发生变化。参数内容详见[CommentPosition](#commentposition) |
+| allow-checked | `boolean` | false | 可选,是否支持代码行选中,开启后可以通过拖拽选中多行代码,可以跨行选择,开启后add-comment事件的返回值会发生变化。参数内容详见[CommentPosition](#commentposition) |
| show-blob | `boolean` | false | 可选,是否展示缩略内容,一般大文件或二进制文件等需要展示缩略内容时使用 |
| output-format | [OutputFormat](#outputformat) | 'line-by-line' | 可选,diff 展示格式,单栏展示或者分栏展示 |
| diff-type | [DiffType](#difftype) | 'modify' | 可选,文件 diff 类型 |
@@ -597,6 +597,7 @@ export default defineComponent({
| add-comment | `Function(position: CommentPosition)` | 点击添加评论图标时触发的事件,参数内容详见[CommentPosition](#commentposition) |
| after-view-init | `Function(methods: CodeReviewMethods)` | 初始化完成后触发的事件,返回相关操作方法,参数内容详见[CodeReviewMethods](#codereviewmethods) |
| content-refresh | `Function(diffFile: DiffFile)` | 内容刷新后触发的事件,返回解析后的相关文件信息,参数内容详见[DiffFile](https://github.com/rtfpessoa/diff2html/blob/master/src/types.ts#L49) |
+|after-check-lines|`Function(position: CommentPosition)`|多行选中后触发的事件,参数内容详见[CommentPosition](#commentposition)|
### CodeReview 插槽
diff --git a/packages/devui-vue/package.json b/packages/devui-vue/package.json
index 1fd40fc688..fa6b791d3a 100644
--- a/packages/devui-vue/package.json
+++ b/packages/devui-vue/package.json
@@ -1,6 +1,6 @@
{
"name": "vue-devui",
- "version": "1.6.23",
+ "version": "1.6.24",
"license": "MIT",
"description": "DevUI components based on Vite and Vue3",
"keywords": [