From 126d7b77791aef14c38724d93415298797a32ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=8C=E8=A8=80?= <2311595895@qq.com> Date: Sun, 24 Nov 2024 20:12:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=9A=E9=80=89=E8=A1=8C=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E7=94=B1shift=E9=80=89=E4=B8=AD=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=8B=96=E6=8B=BD=E9=80=89=E4=B8=AD=20(#1914)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(CodeReview): 支持传入options&暴露清除选中行样式的方法 * chore: 更新版本号 * feat: 多选行交互由shift选中改为拖拽选中 * feat: 更新文档 * feat(CodeReview): 添加边框线 * feat(CodeReview): 抛出选中事件 * feat(CodeReview): 优化边框线 * fix(CodeReview): 修复单行点击抛出数据错误问题 --------- Co-authored-by: GreatZP --- .../devui/code-review/src/code-review.scss | 18 ++ .../devui/code-review/src/code-review.tsx | 42 ++-- .../composables/use-code-review-comment.ts | 211 +++++++++--------- .../use-code-review-line-selection.ts | 140 ++++++++++++ .../devui-vue/devui/code-review/src/utils.ts | 11 + .../docs/components/code-review/index.md | 3 +- packages/devui-vue/package.json | 2 +- 7 files changed, 306 insertions(+), 121 deletions(-) create mode 100644 packages/devui-vue/devui/code-review/src/composables/use-code-review-line-selection.ts 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": [