tien_nemo

test load data

...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
27 27
28 .bbox.focus-highlight { 28 .bbox.focus-highlight {
29 animation: focusPulse 2s ease-in-out; 29 animation: focusPulse 2s ease-in-out;
30 - border-color: #ff6b35 !important; 30 + /*border-color: #ff6b35 !important;*/
31 - background-color: rgba(255, 107, 53, 0.4) !important; 31 + /*background-color: rgba(255, 107, 53, 0.4) !important;*/
32 } 32 }
33 33
34 @keyframes focusPulse { 34 @keyframes focusPulse {
...@@ -137,6 +137,8 @@ ...@@ -137,6 +137,8 @@
137 > 137 >
138 </div> 138 </div>
139 <button @click="saveTemplate">💾Save</button> 139 <button @click="saveTemplate">💾Save</button>
140 +{{-- <button @click="debugCoordinates" style="margin-left: 10px; background: #6c757d;">🐛Debug</button>--}}
141 +{{-- <button @click="testManualBox" style="margin-left: 10px; background: #28a745;">🧪Test Box</button>--}}
140 </div> 142 </div>
141 143
142 </div> 144 </div>
...@@ -184,7 +186,7 @@ ...@@ -184,7 +186,7 @@
184 mapFieldToBox(index, fieldName, text = null) { 186 mapFieldToBox(index, fieldName, text = null) {
185 if (index == null) return; 187 if (index == null) return;
186 188
187 - // Xóa fieldName ở box khác 189 + // Xóa fieldName ở box khác đảm bảo mỗi field chỉ gán cho 1 box duy nhất
188 this.ocrData.forEach((box, i) => { 190 this.ocrData.forEach((box, i) => {
189 if (i !== index && box.field === fieldName) { 191 if (i !== index && box.field === fieldName) {
190 box.field = null; 192 box.field = null;
...@@ -192,7 +194,7 @@ ...@@ -192,7 +194,7 @@
192 } 194 }
193 }); 195 });
194 196
195 - // Nếu box này từng gán field khác thì bỏ 197 + // Nếu box này từng gán field khác thì bỏ reset flag và tọa độ liên quan.
196 const prev = this.ocrData[index].field; 198 const prev = this.ocrData[index].field;
197 if (prev && prev !== fieldName) { 199 if (prev && prev !== fieldName) {
198 if (prev === 'customer_name') { 200 if (prev === 'customer_name') {
...@@ -206,7 +208,6 @@ ...@@ -206,7 +208,6 @@
206 // Gán field mới 208 // Gán field mới
207 const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] 209 const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2]
208 210
209 - console.log('1111111111111111',bbox);
210 const x1 = bbox[0]; 211 const x1 = bbox[0];
211 const y1 = bbox[1]; 212 const y1 = bbox[1];
212 const w = bbox[2]; 213 const w = bbox[2];
...@@ -231,6 +232,7 @@ ...@@ -231,6 +232,7 @@
231 232
232 // Assign field và set active (dùng khi user tương tác) 233 // Assign field và set active (dùng khi user tương tác)
233 assignFieldToBox(index, fieldName, text = null) { 234 assignFieldToBox(index, fieldName, text = null) {
235 + console.log(`Assigning field "${fieldName}" to box at index ${index} with text: "${text}"`);
234 if (index == null) return; 236 if (index == null) return;
235 237
236 // Xóa fieldName ở box khác 238 // Xóa fieldName ở box khác
...@@ -255,7 +257,7 @@ ...@@ -255,7 +257,7 @@
255 // Gán field mới 257 // Gán field mới
256 const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] 258 const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2]
257 259
258 - console.log('1111111111111111',bbox); 260 + console.log('2222222222222222',bbox);
259 const x1 = bbox[0]; 261 const x1 = bbox[0];
260 const y1 = bbox[1]; 262 const y1 = bbox[1];
261 const w = bbox[2]; 263 const w = bbox[2];
...@@ -307,7 +309,7 @@ ...@@ -307,7 +309,7 @@
307 customer_name_xy: this.customer_name_xy, 309 customer_name_xy: this.customer_name_xy,
308 fields: fields 310 fields: fields
309 }; 311 };
310 - console.log(fields); 312 + // console.log(fields);
311 try { 313 try {
312 const res = await fetch('/ocr/save-template', { 314 const res = await fetch('/ocr/save-template', {
313 method: 'POST', 315 method: 'POST',
...@@ -360,21 +362,44 @@ ...@@ -360,21 +362,44 @@
360 362
361 async loadOCRData() { 363 async loadOCRData() {
362 364
363 - const res = await fetch(`/ocr/data-list`); 365 + try {
364 - const data = await res.json(); 366 + const res = await fetch(`/ocr/data-list`);
367 + const data = await res.json();
365 368
366 - if (data.error) { 369 + if (data.error) {
367 - console.error(data.error); 370 + console.error('Error loading data:', data.error);
368 - return; 371 + return;
369 - } 372 + }
370 373
371 - this.ocrData = data.ocrData; 374 + this.ocrData = data.ocrData;
372 - this.pdfImageUrl = data.pdfImageUrl; 375 + this.pdfImageUrl = data.pdfImageUrl;
373 - this.formData = data.formData; 376 + this.formData = data.formData;
374 - this.fieldOptions = data.fieldOptions; 377 + this.fieldOptions = data.fieldOptions;
378 + // Đợi image load xong trước khi xử lý
379 + if (this.$refs.pdfImage && this.$refs.pdfImage.complete) {
380 + this.processLoadedData();
381 + } else {
382 + console.log('Image not loaded yet, waiting for onImageLoad');
383 + // Image sẽ được xử lý trong onImageLoad
384 + }
385 +
386 +
387 + } catch (error) {
388 + console.error('Error in loadOCRData:', error);
389 + }
390 + },
375 391
392 + // Xử lý data sau khi image đã load
393 + processLoadedData() {
376 // Tự động map field cho các box OCR dựa trên formData đã load 394 // Tự động map field cho các box OCR dựa trên formData đã load
377 this.autoMapFieldsFromFormData(); 395 this.autoMapFieldsFromFormData();
396 + // Kiểm tra và sửa lại tọa độ của các box manual
397 + // this.validateManualBoxes();
398 +
399 + // Force re-render để đảm bảo các box được vẽ
400 + this.$nextTick(() => {
401 + this.$forceUpdate();
402 + });
378 }, 403 },
379 404
380 // Tự động map field cho các box OCR dựa trên formData đã load từ DB 405 // Tự động map field cho các box OCR dựa trên formData đã load từ DB
...@@ -394,8 +419,10 @@ ...@@ -394,8 +419,10 @@
394 } 419 }
395 } 420 }
396 }); 421 });
422 +
397 }, 423 },
398 424
425 +
399 // Tìm box OCR phù hợp nhất để map với field 426 // Tìm box OCR phù hợp nhất để map với field
400 findBestMatchingBox(fieldName, fieldValue) { 427 findBestMatchingBox(fieldName, fieldValue) {
401 let bestMatchIndex = -1; 428 let bestMatchIndex = -1;
...@@ -427,7 +454,6 @@ ...@@ -427,7 +454,6 @@
427 454
428 const t1 = text1.toLowerCase().trim(); 455 const t1 = text1.toLowerCase().trim();
429 const t2 = text2.toLowerCase().trim(); 456 const t2 = text2.toLowerCase().trim();
430 -
431 // Nếu text giống hệt nhau 457 // Nếu text giống hệt nhau
432 if (t1 === t2) return 1.0; 458 if (t1 === t2) return 1.0;
433 459
...@@ -448,9 +474,18 @@ ...@@ -448,9 +474,18 @@
448 const img = this.$refs.pdfImage; 474 const img = this.$refs.pdfImage;
449 this.imageWidth = img.naturalWidth; 475 this.imageWidth = img.naturalWidth;
450 this.imageHeight = img.naturalHeight; 476 this.imageHeight = img.naturalHeight;
477 + // Nếu đã có data, xử lý ngay
478 + if (this.ocrData && this.ocrData.length > 0) {
479 + console.log('Image loaded and data exists, processing now');
480 + this.processLoadedData();
481 + } else {
482 + console.log('Image loaded but no data yet');
483 + }
451 }, 484 },
452 getBoxStyle(item, index) { 485 getBoxStyle(item, index) {
453 - if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return {}; 486 + if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) {
487 + return {};
488 + }
454 489
455 const [x1, y1, x2, y2] = item.bbox; 490 const [x1, y1, x2, y2] = item.bbox;
456 const displayedWidth = this.$refs.pdfImage.clientWidth; 491 const displayedWidth = this.$refs.pdfImage.clientWidth;
...@@ -459,14 +494,18 @@ ...@@ -459,14 +494,18 @@
459 const scaleX = displayedWidth / this.imageWidth; 494 const scaleX = displayedWidth / this.imageWidth;
460 const scaleY = displayedHeight / this.imageHeight; 495 const scaleY = displayedHeight / this.imageHeight;
461 496
497 + const left = Math.round(x1 * scaleX);
498 + const top = Math.round(y1 * scaleY);
499 + const width = Math.round((x2 - x1) * scaleX);
500 + const height = Math.round((y2 - y1) * scaleY);
501 +
462 return { 502 return {
463 position: 'absolute', 503 position: 'absolute',
464 - left: `${Math.round(x1 * scaleX)}px`, 504 + left: `${left}px`,
465 - top: `${Math.round(y1 * scaleY)}px`, 505 + top: `${top}px`,
466 - width: `${Math.round((x2 - x1) * scaleX)}px`, 506 + width: `${width}px`,
467 - height: `${Math.round((y2 - y1) * scaleY)}px`, 507 + height: `${height}px`,
468 border: item.hideBorder ? 'none' : '2px solid ' + (index === this.activeIndex ? '#199601' : '#ff5252'), 508 border: item.hideBorder ? 'none' : '2px solid ' + (index === this.activeIndex ? '#199601' : '#ff5252'),
469 - //backgroundColor: item.hideBorder ? 'transparent' : (this.activeIndex === item.field ? 'rgba(33,150,243,0.3)' : 'rgba(255,82,82,0.2)'),
470 boxSizing: 'border-box', 509 boxSizing: 'border-box',
471 cursor: 'pointer', 510 cursor: 'pointer',
472 zIndex: item.isManual ? 30 : 10 511 zIndex: item.isManual ? 30 : 10
...@@ -475,6 +514,7 @@ ...@@ -475,6 +514,7 @@
475 514
476 highlightField(field) { 515 highlightField(field) {
477 let idx = -1; 516 let idx = -1;
517 + console.log(`Highlighting field: ${field}`);
478 for (let i = this.ocrData.length - 1; i >= 0; i--) { 518 for (let i = this.ocrData.length - 1; i >= 0; i--) {
479 const it = this.ocrData[i]; 519 const it = this.ocrData[i];
480 if (!it.isDeleted && it.field === field) { 520 if (!it.isDeleted && it.field === field) {
...@@ -482,6 +522,7 @@ ...@@ -482,6 +522,7 @@
482 break; 522 break;
483 } 523 }
484 } 524 }
525 + console.log('ssss', idx, this.ocrData[idx]);
485 526
486 if (idx !== -1) { 527 if (idx !== -1) {
487 // Set active index (chuyển trạng thái active và màu xanh) 528 // Set active index (chuyển trạng thái active và màu xanh)
...@@ -500,10 +541,13 @@ ...@@ -500,10 +541,13 @@
500 if (!this.$refs.pdfContainer || index < 0 || index >= this.ocrData.length) return; 541 if (!this.$refs.pdfContainer || index < 0 || index >= this.ocrData.length) return;
501 542
502 const item = this.ocrData[index]; 543 const item = this.ocrData[index];
544 + console.log(`Scrolling to box at index ${index}:`, item);
503 if (!item || item.isDeleted) return; 545 if (!item || item.isDeleted) return;
504 546
505 // Tính vị trí hiển thị của box 547 // Tính vị trí hiển thị của box
506 - const [x1, y1, x2, y2] = item.bbox; 548 + // const [x1, y1, x2, y2] = item.bbox;
549 + const [x1, y1, x2, y2] = item.field_xy.split(',').map(Number);
550 + console.log(`Box coordinates for scrolling: [${x1}, ${y1}, ${x2}, ${y2}]`);
507 if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return; 551 if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return;
508 552
509 const displayedWidth = this.$refs.pdfImage.clientWidth; 553 const displayedWidth = this.$refs.pdfImage.clientWidth;
...@@ -601,7 +645,7 @@ ...@@ -601,7 +645,7 @@
601 const dispX2 = this.selectBox.x + this.selectBox.width; 645 const dispX2 = this.selectBox.x + this.selectBox.width;
602 const dispY2 = this.selectBox.y + this.selectBox.height; 646 const dispY2 = this.selectBox.y + this.selectBox.height;
603 647
604 - // scale: displayed -> original 648 + // scale: displayed -> original (sửa lại để chính xác hơn)
605 const displayedWidth = this.$refs.pdfImage.clientWidth; 649 const displayedWidth = this.$refs.pdfImage.clientWidth;
606 const displayedHeight = this.$refs.pdfImage.clientHeight; 650 const displayedHeight = this.$refs.pdfImage.clientHeight;
607 const scaleX = this.imageWidth / displayedWidth; 651 const scaleX = this.imageWidth / displayedWidth;
...@@ -661,39 +705,76 @@ ...@@ -661,39 +705,76 @@
661 applyManualMapping() { 705 applyManualMapping() {
662 if (!this.manualField) return; 706 if (!this.manualField) return;
663 const manualIndex = this.manualIndex; 707 const manualIndex = this.manualIndex;
708 +
664 const newBbox = this.ocrData[manualIndex].bbox; 709 const newBbox = this.ocrData[manualIndex].bbox;
710 + console.log(`33333 ${manualIndex} with field "${this.manualField}" new box ${newBbox}`);
711 + // console.log('Applying manual mapping for field:', this.manualField);
712 + // console.log('Manual bbox:', newBbox);
665 713
666 let combinedText = []; 714 let combinedText = [];
715 + let foundItems = [];
716 +
717 + // Tìm tất cả các box OCR nằm trong vùng manual
667 this.ocrData.forEach(item => { 718 this.ocrData.forEach(item => {
668 if (!item.isManual && this.isBoxInside(item.bbox, newBbox) && item.text.trim()) { 719 if (!item.isManual && this.isBoxInside(item.bbox, newBbox) && item.text.trim()) {
669 - const partial = this.getPartialText(item.text, item.bbox, newBbox); 720 + foundItems.push({
670 - if (partial) combinedText.push(partial); 721 + text: item.text,
671 - // combinedText.push(item.text.trim()); 722 + bbox: item.bbox,
723 + index: this.ocrData.indexOf(item)
724 + });
672 } 725 }
673 }); 726 });
674 727
728 + // console.log('Found OCR items in manual area:', foundItems);
729 +
730 + // Sắp xếp các item theo vị trí (từ trái sang phải, từ trên xuống dưới)
731 + foundItems.sort((a, b) => {
732 + // Ưu tiên theo Y trước (hàng), sau đó theo X (cột)
733 + if (Math.abs(a.bbox[1] - b.bbox[1]) < 20) { // Cùng hàng (tolerance 20px)
734 + return a.bbox[0] - b.bbox[0]; // Sắp xếp theo X
735 + }
736 + return a.bbox[1] - b.bbox[1]; // Sắp xếp theo Y
737 + });
738 +
739 + // Gộp text theo thứ tự đã sắp xếp
740 + foundItems.forEach(item => {
741 + combinedText.push(item.text.trim());
742 + });
743 +
675 const finalText = combinedText.join(" "); 744 const finalText = combinedText.join(" ");
676 - // this.ocrData[manualIndex].field = this.manualField; 745 + // console.log('Combined text:', finalText);
677 - // this.formData[this.manualField] = finalText;
678 - // this.activeIndex = manualIndex;
679 746
747 + // Gán field và text cho box manual
680 this.assignFieldToBox(manualIndex, this.manualField, finalText); 748 this.assignFieldToBox(manualIndex, this.manualField, finalText);
681 749
682 // Reset trạng thái chọn 750 // Reset trạng thái chọn
683 this.isMappingManually = false; 751 this.isMappingManually = false;
684 this.selectBox.show = false; 752 this.selectBox.show = false;
685 this.selectBox.showDropdown = false; 753 this.selectBox.showDropdown = false;
686 - // this.manualField = "";
687 - // this.manualIndex = null;
688 }, 754 },
689 755
690 isBoxInside(inner, outer) { 756 isBoxInside(inner, outer) {
691 - return !( 757 + // inner: bbox của OCR item [x1, y1, x2, y2]
758 + // outer: bbox của vùng manual [x1, y1, x2, y2]
759 +
760 + // Kiểm tra xem box OCR có nằm hoàn toàn trong vùng manual không
761 + const isFullyInside = (
762 + inner[0] >= outer[0] && // left edge
763 + inner[1] >= outer[1] && // top edge
764 + inner[2] <= outer[2] && // right edge
765 + inner[3] <= outer[3] // bottom edge
766 + );
767 +
768 + // Kiểm tra xem box OCR có giao nhau với vùng manual không
769 + const isOverlapping = !(
692 inner[2] < outer[0] || // box bên trái vùng chọn 770 inner[2] < outer[0] || // box bên trái vùng chọn
693 inner[0] > outer[2] || // box bên phải vùng chọn 771 inner[0] > outer[2] || // box bên phải vùng chọn
694 inner[3] < outer[1] || // box phía trên vùng chọn 772 inner[3] < outer[1] || // box phía trên vùng chọn
695 inner[1] > outer[3] // box phía dưới vùng chọn 773 inner[1] > outer[3] // box phía dưới vùng chọn
696 ); 774 );
775 +
776 + // Trả về true nếu box OCR nằm hoàn toàn trong hoặc giao nhau đáng kể
777 + return isFullyInside || isOverlapping;
697 }, 778 },
698 779
699 780
...@@ -704,6 +785,7 @@ ...@@ -704,6 +785,7 @@
704 785
705 // Chiều rộng box OCR 786 // Chiều rộng box OCR
706 const boxWidth = x2 - x1; 787 const boxWidth = x2 - x1;
788 + const boxHeight = y2 - y1;
707 789
708 // Vị trí start và end tương đối trong text 790 // Vị trí start và end tương đối trong text
709 let startRatio = Math.max(0, (sx1 - x1) / boxWidth); 791 let startRatio = Math.max(0, (sx1 - x1) / boxWidth);
...@@ -712,7 +794,18 @@ ...@@ -712,7 +794,18 @@
712 const startIndex = Math.floor(startRatio * text.length); 794 const startIndex = Math.floor(startRatio * text.length);
713 const endIndex = Math.ceil(endRatio * text.length); 795 const endIndex = Math.ceil(endRatio * text.length);
714 796
715 - return text.substring(startIndex, endIndex).trim(); 797 + const partialText = text.substring(startIndex, endIndex).trim();
798 +
799 + // console.log('Partial text calculation:', {
800 + // originalText: text,
801 + // bbox: bbox,
802 + // selectBbox: selectBbox,
803 + // startRatio, endRatio,
804 + // startIndex, endIndex,
805 + // partialText
806 + // });
807 +
808 + return partialText;
716 }, 809 },
717 getSelectStyle(item) { 810 getSelectStyle(item) {
718 if (!this.imageWidth) return { position: 'absolute' }; 811 if (!this.imageWidth) return { position: 'absolute' };
...@@ -729,6 +822,185 @@ ...@@ -729,6 +822,185 @@
729 top: `${Math.round(y2 * scaleY)}px`, 822 top: `${Math.round(y2 * scaleY)}px`,
730 zIndex: 9999 823 zIndex: 9999
731 }; 824 };
825 + },
826 +
827 + // Debug method để kiểm tra tọa độ
828 + debugCoordinates() {
829 + console.log('=== DEBUG COORDINATES ===');
830 + console.log('Image dimensions:', {
831 + natural: { width: this.imageWidth, height: this.imageHeight },
832 + displayed: {
833 + width: this.$refs.pdfImage?.clientWidth,
834 + height: this.$refs.pdfImage?.clientHeight
835 + }
836 + });
837 +
838 + console.log('Scale factors:', {
839 + scaleX: this.$refs.pdfImage ? this.$refs.pdfImage.clientWidth / this.imageWidth : 'N/A',
840 + scaleY: this.$refs.pdfImage ? this.$refs.pdfImage.clientHeight / this.imageHeight : 'N/A'
841 + });
842 +
843 + console.log('OCR Data with coordinates:');
844 + this.ocrData.forEach((item, index) => {
845 + if (!item.isDeleted) {
846 + console.log(`Item ${index}:`, {
847 + text: item.text,
848 + bbox: item.bbox,
849 + field: item.field,
850 + isManual: item.isManual
851 + });
852 + }
853 + });
854 + console.log('=== END DEBUG ===');
855 + },
856 +
857 + // Kiểm tra và sửa lại tọa độ của các box manual
858 + validateManualBoxes() {
859 + if (!this.imageWidth || !this.imageHeight) {
860 + console.log('validateManualBoxes: Image not loaded yet');
861 + return;
862 + }
863 +
864 + this.ocrData.forEach((item, index) => {
865 + if (item.isManual && !item.isDeleted) {
866 + const [x1, y1, x2, y2] = item.bbox;
867 +
868 + const isValid = (
869 + x1 >= 0 && y1 >= 0 &&
870 + x2 > x1 && y2 > y1 &&
871 + x2 <= this.imageWidth && y2 <= this.imageHeight
872 + );
873 +
874 + if (!isValid) {
875 + // Thử sửa lại tọa độ nếu có thể
876 + this.fixManualBoxCoordinates(item, index);
877 + }
878 + }
879 + });
880 +
881 + console.log('=== END VALIDATION ===');
882 +
883 + // Force re-render để đảm bảo các box được vẽ đúng
884 + this.$nextTick(() => {
885 + this.$forceUpdate();
886 + });
887 + },
888 +
889 + // Sửa lại tọa độ của box manual nếu bị lỗi
890 + fixManualBoxCoordinates(item, index) {
891 + console.log(`Attempting to fix manual box ${index}:`, item);
892 +
893 + // Nếu tọa độ âm, đặt về 0
894 + let [x1, y1, x2, y2] = item.bbox;
895 +
896 + if (x1 < 0) x1 = 0;
897 + if (y1 < 0) y1 = 0;
898 + if (x2 <= x1) x2 = x1 + 100; // Tạo width mặc định
899 + if (y2 <= y1) y2 = y1 + 50; // Tạo height mặc định
900 +
901 + // Đảm bảo không vượt quá image bounds
902 + if (x2 > this.imageWidth) x2 = this.imageWidth;
903 + if (y2 > this.imageHeight) y2 = this.imageHeight;
904 +
905 + const fixedBbox = [x1, y1, x2, y2];
906 + console.log(`Fixed bbox for manual box ${index}:`, {
907 + original: item.bbox,
908 + fixed: fixedBbox
909 + });
910 +
911 + // Cập nhật tọa độ
912 + this.$set(this.ocrData[index], 'bbox', fixedBbox);
913 + },
914 +
915 + // Test method để tạo box manual test
916 + testManualBox() {
917 + if (!this.imageWidth || !this.imageHeight) {
918 + alert('Image chưa được load. Vui lòng đợi image load xong.');
919 + return;
920 + }
921 +
922 + // Tạo một box manual test ở giữa image
923 + const centerX = Math.round(this.imageWidth / 2);
924 + const centerY = Math.round(this.imageHeight / 2);
925 + const boxSize = 100;
926 +
927 + const testBbox = [
928 + centerX - boxSize/2, // x1
929 + centerY - boxSize/2, // y1
930 + centerX + boxSize/2, // x2
931 + centerY + boxSize/2 // y2
932 + ];
933 +
934 + console.log('Creating test manual box:', {
935 + bbox: testBbox,
936 + imageDimensions: { width: this.imageWidth, height: this.imageHeight }
937 + });
938 +
939 + // Thêm box test
940 + this.ocrData.push({
941 + text: "TEST BOX",
942 + bbox: testBbox,
943 + field: "test_field",
944 + isManual: true,
945 + showDelete: true,
946 + isDeleted: false,
947 + hideBorder: false
948 + });
949 +
950 + // Force re-render
951 + this.$forceUpdate();
952 + },
953 +
954 + // Tạo box manual từ tọa độ trong DB
955 + createManualBoxFromDB(fieldName, coordinates, text) {
956 + if (!this.imageWidth || !this.imageHeight) {
957 + console.log('Cannot create manual box: Image not loaded');
958 + return;
959 + }
960 +
961 + // Parse coordinates từ string "x1,y1,x2,y2"
962 + let coords;
963 + if (typeof coordinates === 'string') {
964 + coords = coordinates.split(',').map(Number);
965 + } else if (Array.isArray(coordinates)) {
966 + coords = coordinates;
967 + } else {
968 + console.error('Invalid coordinates format:', coordinates);
969 + return;
970 + }
971 +
972 + const [x1, y1, x2, y2] = coords;
973 +
974 + console.log('Creating manual box from DB:', {
975 + fieldName,
976 + coordinates,
977 + parsed: coords,
978 + imageDimensions: { width: this.imageWidth, height: this.imageHeight }
979 + });
980 +
981 + // Kiểm tra tọa độ có hợp lệ không
982 + if (x1 >= 0 && y1 >= 0 && x2 > x1 && y2 > y1 &&
983 + x2 <= this.imageWidth && y2 <= this.imageHeight) {
984 +
985 + // Tạo box manual
986 + const manualBox = {
987 + text: text || '',
988 + bbox: coords,
989 + field: fieldName,
990 + isManual: true,
991 + showDelete: true,
992 + isDeleted: false,
993 + hideBorder: false
994 + };
995 +
996 + this.ocrData.push(manualBox);
997 + console.log('Manual box created successfully:', manualBox);
998 +
999 + // Force re-render
1000 + this.$forceUpdate();
1001 + } else {
1002 + console.warn('Invalid coordinates for manual box:', coords);
1003 + }
732 } 1004 }
733 1005
734 } 1006 }
......