Showing
1 changed file
with
86 additions
and
27 deletions
| ... | @@ -84,9 +84,12 @@ | ... | @@ -84,9 +84,12 @@ |
| 84 | 84 | ||
| 85 | <!-- Dropdown thủ công --> | 85 | <!-- Dropdown thủ công --> |
| 86 | <select v-if="selectBox.showDropdown" | 86 | <select v-if="selectBox.showDropdown" |
| 87 | - :style="{ left: selectBox.x + 'px', top: (selectBox.y + selectBox.height) + 'px' }" | 87 | + class="manual-select" |
| 88 | + :style="{ left: selectBox.x + 'px', top: (selectBox.y + selectBox.height) + 'px', zIndex: 10000, position: 'absolute' }" | ||
| 88 | v-model="manualField" | 89 | v-model="manualField" |
| 89 | @change="applyManualMapping" | 90 | @change="applyManualMapping" |
| 91 | + @mousedown.stop | ||
| 92 | + @mouseup.stop | ||
| 90 | @click.stop | 93 | @click.stop |
| 91 | > | 94 | > |
| 92 | <option disabled value="">-- Chọn trường dữ liệu --</option> | 95 | <option disabled value="">-- Chọn trường dữ liệu --</option> |
| ... | @@ -127,7 +130,8 @@ | ... | @@ -127,7 +130,8 @@ |
| 127 | hasCustomerNameXY: false, | 130 | hasCustomerNameXY: false, |
| 128 | ocrData: [], | 131 | ocrData: [], |
| 129 | selectBox: { show: false, showDropdown: false, x: 0, y: 0, width: 0, height: 0, startX: 0, startY: 0 }, | 132 | selectBox: { show: false, showDropdown: false, x: 0, y: 0, width: 0, height: 0, startX: 0, startY: 0 }, |
| 130 | - manualIndex: null | 133 | + manualIndex: null, |
| 134 | + suppressNextDocumentClick: false | ||
| 131 | } | 135 | } |
| 132 | }, | 136 | }, |
| 133 | created() { | 137 | created() { |
| ... | @@ -138,14 +142,35 @@ | ... | @@ -138,14 +142,35 @@ |
| 138 | }, | 142 | }, |
| 139 | mounted() { | 143 | mounted() { |
| 140 | this.loadOCRData(); | 144 | this.loadOCRData(); |
| 141 | - //Thêm event listener để xóa focus khi click ra ngoài | 145 | + // Thêm event listener để xử lý click ra ngoài |
| 142 | document.addEventListener('click', (e) => { | 146 | document.addEventListener('click', (e) => { |
| 143 | - if ( | 147 | + // Bỏ qua click ngay sau khi thả chuột tạo box |
| 144 | - !e.target.closest('.right-panel') && | 148 | + if (this.suppressNextDocumentClick) { |
| 145 | - !e.target.closest('.bbox') && | 149 | + this.suppressNextDocumentClick = false; |
| 146 | - !e.target.closest('select') | 150 | + return; |
| 147 | - ) { | 151 | + } |
| 148 | - this.removeAllFocus(); | 152 | + // Nếu đang có box thủ công mới quét và chưa chọn field |
| 153 | + if (this.manualIndex !== null) { | ||
| 154 | + const currentIdx = this.manualIndex; | ||
| 155 | + const currentBox = this.ocrData[currentIdx]; | ||
| 156 | + if (currentBox && currentBox.isManual && !currentBox.field && !currentBox.isDeleted) { | ||
| 157 | + const bboxEl = e.target.closest('.bbox'); | ||
| 158 | + const manualSelectEl = e.target.closest('.manual-select'); | ||
| 159 | + const clickedInsideCurrentBox = bboxEl && String(bboxEl.getAttribute('data-index')) === String(currentIdx); | ||
| 160 | + const clickedInsideManualSelect = !!manualSelectEl; | ||
| 161 | + if (!clickedInsideCurrentBox && !clickedInsideManualSelect) { | ||
| 162 | + // Click ra ngoài box/ select → xóa box thủ công vừa quét | ||
| 163 | + this.deleteBox(currentIdx); | ||
| 164 | + // Đóng dropdown thủ công nếu còn mở | ||
| 165 | + this.selectBox.show = false; | ||
| 166 | + this.selectBox.showDropdown = false; | ||
| 167 | + this.isMappingManually = false; | ||
| 168 | + this.manualField = ""; | ||
| 169 | + this.manualIndex = null; | ||
| 170 | + this.activeIndex = null; | ||
| 171 | + this.selectingIndex = null; | ||
| 172 | + } | ||
| 173 | + } | ||
| 149 | } | 174 | } |
| 150 | }); | 175 | }); |
| 151 | }, | 176 | }, |
| ... | @@ -283,6 +308,7 @@ | ... | @@ -283,6 +308,7 @@ |
| 283 | this.manualIndex = null; | 308 | this.manualIndex = null; |
| 284 | } | 309 | } |
| 285 | this.selectingIndex = null; | 310 | this.selectingIndex = null; |
| 311 | + this.activeIndex = null; | ||
| 286 | } | 312 | } |
| 287 | }, | 313 | }, |
| 288 | // Xử lý data sau khi image đã load | 314 | // Xử lý data sau khi image đã load |
| ... | @@ -426,6 +452,9 @@ | ... | @@ -426,6 +452,9 @@ |
| 426 | if (fieldValue && fieldValue.trim()) { | 452 | if (fieldValue && fieldValue.trim()) { |
| 427 | // Nếu có data từ DB, highlight và focus vào box tương ứng | 453 | // Nếu có data từ DB, highlight và focus vào box tương ứng |
| 428 | this.highlightField(fieldName); | 454 | this.highlightField(fieldName); |
| 455 | + | ||
| 456 | + // Không ẩn nút xóa các box khác để tránh nhầm lẫn | ||
| 457 | + // Chỉ highlight box tương ứng | ||
| 429 | } | 458 | } |
| 430 | }, | 459 | }, |
| 431 | // Xóa tất cả focus | 460 | // Xóa tất cả focus |
| ... | @@ -433,20 +462,28 @@ | ... | @@ -433,20 +462,28 @@ |
| 433 | // Reset active index (chỉ mất màu xanh, không xóa box) | 462 | // Reset active index (chỉ mất màu xanh, không xóa box) |
| 434 | this.activeIndex = null; | 463 | this.activeIndex = null; |
| 435 | this.selectingIndex = null; | 464 | this.selectingIndex = null; |
| 436 | - // Đảm bảo tất cả box OCR đều hiển thị (chỉ ẩn border khi cần thiết) | 465 | + |
| 437 | - this.ocrData.forEach(item => { | 466 | + // Không ẩn nút xóa các box để tránh nhầm lẫn |
| 438 | - // if (!item.isManual && item.hideBorder) { | 467 | + // Chỉ reset focus |
| 439 | - // item.hideBorder = true; | 468 | + }, |
| 440 | - // } | 469 | + |
| 441 | - if (item.isManual) { | 470 | + // Xóa box quét chọn chưa hoàn thành |
| 442 | - if (!item.showDelete) { | 471 | + removeIncompleteBoxes() { |
| 443 | - item.isDeleted = true; | 472 | + // Xóa box quét chọn chưa hoàn thành (chưa có field) |
| 444 | - } else { | 473 | + this.ocrData = this.ocrData.filter(item => { |
| 445 | - item.hideBorder = false; | 474 | + if (item.isManual && !item.field && item.showDelete) { |
| 446 | - } | 475 | + // Chỉ xóa box manual chưa có field |
| 447 | - // Hiển thị lại border cho manual box | 476 | + return false; |
| 448 | } | 477 | } |
| 478 | + return true; | ||
| 449 | }); | 479 | }); |
| 480 | + | ||
| 481 | + // Reset trạng thái quét chọn | ||
| 482 | + this.selectBox.show = false; | ||
| 483 | + this.selectBox.showDropdown = false; | ||
| 484 | + this.isMappingManually = false; | ||
| 485 | + this.manualField = ""; | ||
| 486 | + this.manualIndex = null; | ||
| 450 | }, | 487 | }, |
| 451 | // Xử lý khi click vào box | 488 | // Xử lý khi click vào box |
| 452 | onBoxClick(index) { | 489 | onBoxClick(index) { |
| ... | @@ -459,11 +496,21 @@ | ... | @@ -459,11 +496,21 @@ |
| 459 | const isDataOverridden = item.field && isFromDB && | 496 | const isDataOverridden = item.field && isFromDB && |
| 460 | this.formData[item.field] !== this.dataMapping[item.field].text; | 497 | this.formData[item.field] !== this.dataMapping[item.field].text; |
| 461 | 498 | ||
| 499 | + // Set active index và hiển thị nút xóa cho box được click | ||
| 500 | + this.activeIndex = index; | ||
| 501 | + | ||
| 502 | + // Hiển thị nút xóa cho box được click (nếu là manual box) | ||
| 503 | + if (item.isManual) { | ||
| 504 | + item.showDelete = true; | ||
| 505 | + } | ||
| 506 | + | ||
| 507 | + // Không ẩn nút xóa các box khác để tránh nhầm lẫn | ||
| 508 | + // Chỉ hiển thị nút xóa cho box được click | ||
| 509 | + | ||
| 462 | if (item.isManual) { | 510 | if (item.isManual) { |
| 463 | // Manual box | 511 | // Manual box |
| 464 | if (isFromDB && !isDataOverridden) { | 512 | if (isFromDB && !isDataOverridden) { |
| 465 | // Manual box từ DB chưa ghi đè, KHÔNG cho chọn option | 513 | // Manual box từ DB chưa ghi đè, KHÔNG cho chọn option |
| 466 | - this.activeIndex = index; | ||
| 467 | this.selectingIndex = null; | 514 | this.selectingIndex = null; |
| 468 | } else { | 515 | } else { |
| 469 | // Manual box từ DB có data ghi đè HOẶC manual box bình thường, CHO PHÉP chọn option | 516 | // Manual box từ DB có data ghi đè HOẶC manual box bình thường, CHO PHÉP chọn option |
| ... | @@ -473,7 +520,6 @@ | ... | @@ -473,7 +520,6 @@ |
| 473 | // Box OCR có field | 520 | // Box OCR có field |
| 474 | if (isFromDB && !isDataOverridden) { | 521 | if (isFromDB && !isDataOverridden) { |
| 475 | // Box có field từ DB chưa ghi đè, KHÔNG cho chọn option | 522 | // Box có field từ DB chưa ghi đè, KHÔNG cho chọn option |
| 476 | - this.activeIndex = index; | ||
| 477 | this.selectingIndex = null; | 523 | this.selectingIndex = null; |
| 478 | } else { | 524 | } else { |
| 479 | // Box có field từ DB đã ghi đè HOẶC field mới, CHO PHÉP chọn option | 525 | // Box có field từ DB đã ghi đè HOẶC field mới, CHO PHÉP chọn option |
| ... | @@ -485,7 +531,9 @@ | ... | @@ -485,7 +531,9 @@ |
| 485 | } | 531 | } |
| 486 | }, | 532 | }, |
| 487 | startSelect(e) { | 533 | startSelect(e) { |
| 488 | - if (this.isMappingManually || e.button !== 0) return; | 534 | + if (e.button !== 0) return; // Chỉ cho phép left click |
| 535 | + // Bỏ qua nếu click trên dropdown thủ công | ||
| 536 | + if (e.target.closest && e.target.closest('.manual-select')) return; | ||
| 489 | this.isSelecting = true; | 537 | this.isSelecting = true; |
| 490 | const rect = this.$refs.pdfContainer.getBoundingClientRect(); | 538 | const rect = this.$refs.pdfContainer.getBoundingClientRect(); |
| 491 | this.selectBox.startX = e.clientX - rect.left; | 539 | this.selectBox.startX = e.clientX - rect.left; |
| ... | @@ -550,7 +598,7 @@ | ... | @@ -550,7 +598,7 @@ |
| 550 | bbox: origBbox, | 598 | bbox: origBbox, |
| 551 | field: "", | 599 | field: "", |
| 552 | isManual: true, | 600 | isManual: true, |
| 553 | - showDelete: true, | 601 | + showDelete: true, // Hiển thị nút xóa ngay khi quét chọn |
| 554 | isDeleted: false, | 602 | isDeleted: false, |
| 555 | hideBorder: false | 603 | hideBorder: false |
| 556 | }); | 604 | }); |
| ... | @@ -559,8 +607,15 @@ | ... | @@ -559,8 +607,15 @@ |
| 559 | this.isMappingManually = true; | 607 | this.isMappingManually = true; |
| 560 | this.selectBox.showDropdown = true; | 608 | this.selectBox.showDropdown = true; |
| 561 | 609 | ||
| 562 | - e.stopPropagation(); | 610 | + // Set active index cho box mới tạo |
| 563 | - e.preventDefault(); | 611 | + this.activeIndex = this.ocrData.length - 1; |
| 612 | + | ||
| 613 | + // Sau khi thả chuột, bỏ qua click document kế tiếp | ||
| 614 | + this.suppressNextDocumentClick = true; | ||
| 615 | + | ||
| 616 | + // Không stopPropagation để event có thể lan truyền bình thường | ||
| 617 | + // e.stopPropagation(); | ||
| 618 | + // e.preventDefault(); | ||
| 564 | 619 | ||
| 565 | }, | 620 | }, |
| 566 | applyMapping() { | 621 | applyMapping() { |
| ... | @@ -649,6 +704,7 @@ | ... | @@ -649,6 +704,7 @@ |
| 649 | const finalText = combinedText.join(" "); | 704 | const finalText = combinedText.join(" "); |
| 650 | 705 | ||
| 651 | this.ocrData[manualIndex].field = this.manualField; | 706 | this.ocrData[manualIndex].field = this.manualField; |
| 707 | + // this.ocrData[manualIndex].showDelete = true; // Hiển thị nút xóa sau khi hoàn thành mapping | ||
| 652 | 708 | ||
| 653 | console.log('123',this.ocrData[manualIndex]) | 709 | console.log('123',this.ocrData[manualIndex]) |
| 654 | this.formData[this.manualField] = finalText.trim(); | 710 | this.formData[this.manualField] = finalText.trim(); |
| ... | @@ -670,6 +726,9 @@ | ... | @@ -670,6 +726,9 @@ |
| 670 | this.manualField = ""; | 726 | this.manualField = ""; |
| 671 | this.manualIndex = null; | 727 | this.manualIndex = null; |
| 672 | 728 | ||
| 729 | + // Giữ nguyên nút xóa cho box vừa hoàn thành | ||
| 730 | + // Không ẩn nút xóa các box khác để tránh nhầm lẫn | ||
| 731 | + | ||
| 673 | }, | 732 | }, |
| 674 | isBoxInside(inner, outer) { | 733 | isBoxInside(inner, outer) { |
| 675 | // inner: bbox của OCR item [x1, y1, x2, y2] | 734 | // inner: bbox của OCR item [x1, y1, x2, y2] | ... | ... |
-
Please register or sign in to post a comment