Showing
1 changed file
with
244 additions
and
75 deletions
| ... | @@ -25,12 +25,6 @@ | ... | @@ -25,12 +25,6 @@ |
| 25 | background-color: rgba(25, 150, 1, 0.4) !important; | 25 | background-color: rgba(25, 150, 1, 0.4) !important; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | - .bbox.focus-highlight { | ||
| 29 | - /*animation: focusPulse 2s ease-in-out;*/ | ||
| 30 | - border-color: #ff6b35 !important; | ||
| 31 | - /*background-color: rgba(255, 107, 53, 0.4) !important;*/ | ||
| 32 | - } | ||
| 33 | - | ||
| 34 | @keyframes focusPulse { | 28 | @keyframes focusPulse { |
| 35 | 0% { transform: scale(1); } | 29 | 0% { transform: scale(1); } |
| 36 | 50% { transform: scale(1.05); } | 30 | 50% { transform: scale(1.05); } |
| ... | @@ -95,7 +89,7 @@ | ... | @@ -95,7 +89,7 @@ |
| 95 | :class="{ active: index === activeIndex }" | 89 | :class="{ active: index === activeIndex }" |
| 96 | :data-field="item.field" | 90 | :data-field="item.field" |
| 97 | :style="getBoxStyle(item, index)" | 91 | :style="getBoxStyle(item, index)" |
| 98 | - @click="selectingIndex = index"> | 92 | + @click="onBoxClick(index)"> |
| 99 | 93 | ||
| 100 | <button v-if="item.isManual && item.showDelete" | 94 | <button v-if="item.isManual && item.showDelete" |
| 101 | class="delete-btn" | 95 | class="delete-btn" |
| ... | @@ -104,7 +98,7 @@ | ... | @@ -104,7 +98,7 @@ |
| 104 | 98 | ||
| 105 | 99 | ||
| 106 | <!-- Dropdown OCR --> | 100 | <!-- Dropdown OCR --> |
| 107 | - <select v-if="selectingIndex !== null && ocrData[selectingIndex].isManual" | 101 | + <select v-if="selectingIndex !== null && ocrData[selectingIndex]" |
| 108 | :style="getSelectStyle(ocrData[selectingIndex])" | 102 | :style="getSelectStyle(ocrData[selectingIndex])" |
| 109 | v-model="ocrData[selectingIndex].field" | 103 | v-model="ocrData[selectingIndex].field" |
| 110 | @change="applyMapping" | 104 | @change="applyMapping" |
| ... | @@ -133,7 +127,7 @@ | ... | @@ -133,7 +127,7 @@ |
| 133 | <input v-model="formData[field.value]" | 127 | <input v-model="formData[field.value]" |
| 134 | @focus="highlightField(field.value)" | 128 | @focus="highlightField(field.value)" |
| 135 | @click="onInputClick(field.value)" | 129 | @click="onInputClick(field.value)" |
| 136 | -{{-- @blur="removeManualBoxes"--}} | 130 | + @blur="onInputBlur(field.value)" |
| 137 | :readonly="field.value === 'customer_name' && !hasCustomerNameXY" | 131 | :readonly="field.value === 'customer_name' && !hasCustomerNameXY" |
| 138 | > | 132 | > |
| 139 | </div> | 133 | </div> |
| ... | @@ -174,6 +168,14 @@ | ... | @@ -174,6 +168,14 @@ |
| 174 | }, | 168 | }, |
| 175 | mounted() { | 169 | mounted() { |
| 176 | this.loadOCRData(); | 170 | this.loadOCRData(); |
| 171 | + | ||
| 172 | + // Thêm event listener để xóa focus khi click ra ngoài | ||
| 173 | + document.addEventListener('click', (e) => { | ||
| 174 | + // Nếu click không phải vào input hoặc box | ||
| 175 | + if (!e.target.closest('.left-panel') && !e.target.closest('.bbox')) { | ||
| 176 | + this.removeAllFocus(); | ||
| 177 | + } | ||
| 178 | + }); | ||
| 177 | }, | 179 | }, |
| 178 | computed: { | 180 | computed: { |
| 179 | fieldData() { | 181 | fieldData() { |
| ... | @@ -190,27 +192,33 @@ | ... | @@ -190,27 +192,33 @@ |
| 190 | mapFieldToBox(index, fieldName, text = null) { | 192 | mapFieldToBox(index, fieldName, text = null) { |
| 191 | if (index == null) return; | 193 | if (index == null) return; |
| 192 | 194 | ||
| 193 | - // Xóa fieldName ở box khác đảm bảo mỗi field chỉ gán cho 1 box duy nhất | 195 | + // Xóa tất cả box có fieldName trùng lặp, chỉ giữ lại box hiện tại |
| 194 | - this.ocrData.forEach((box, i) => { | 196 | + this.ocrData = this.ocrData.filter((box, i) => { |
| 195 | - if (i !== index && box.field === fieldName) { | 197 | + if (i === index) return true; // Giữ lại box hiện tại |
| 196 | - box.field = null; | 198 | + if (box.field === fieldName) { |
| 197 | - box.field_xy = null; | 199 | + // Xóa box có field trùng lặp |
| 200 | + return false; | ||
| 198 | } | 201 | } |
| 202 | + return true; // Giữ lại các box khác | ||
| 199 | }); | 203 | }); |
| 200 | 204 | ||
| 205 | + // Cập nhật lại index sau khi filter | ||
| 206 | + const newIndex = this.ocrData.findIndex(box => box.bbox === this.ocrData[index]?.bbox); | ||
| 207 | + if (newIndex === -1) return; | ||
| 208 | + | ||
| 201 | // Nếu box này từng gán field khác thì bỏ reset flag và tọa độ liên quan. | 209 | // Nếu box này từng gán field khác thì bỏ reset flag và tọa độ liên quan. |
| 202 | - const prev = this.ocrData[index].field; | 210 | + const prev = this.ocrData[newIndex].field; |
| 203 | if (prev && prev !== fieldName) { | 211 | if (prev && prev !== fieldName) { |
| 204 | if (prev === 'customer_name') { | 212 | if (prev === 'customer_name') { |
| 205 | this.hasCustomerNameXY = false; | 213 | this.hasCustomerNameXY = false; |
| 206 | this.customer_name_xy = ''; | 214 | this.customer_name_xy = ''; |
| 207 | } | 215 | } |
| 208 | - this.ocrData[index].field = null; | 216 | + this.ocrData[newIndex].field = null; |
| 209 | - this.ocrData[index].field_xy = null; | 217 | + this.ocrData[newIndex].field_xy = null; |
| 210 | } | 218 | } |
| 211 | 219 | ||
| 212 | // Gán field mới | 220 | // Gán field mới |
| 213 | - const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] | 221 | + const bbox = this.ocrData[newIndex].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] |
| 214 | 222 | ||
| 215 | const x1 = bbox[0]; | 223 | const x1 = bbox[0]; |
| 216 | const y1 = bbox[1]; | 224 | const y1 = bbox[1]; |
| ... | @@ -219,11 +227,11 @@ | ... | @@ -219,11 +227,11 @@ |
| 219 | 227 | ||
| 220 | const xyStr = `${x1},${y1},${w},${h}`; | 228 | const xyStr = `${x1},${y1},${w},${h}`; |
| 221 | 229 | ||
| 222 | - this.ocrData[index].field = fieldName; | 230 | + this.ocrData[newIndex].field = fieldName; |
| 223 | - this.ocrData[index].field_xy = xyStr; | 231 | + this.ocrData[newIndex].field_xy = xyStr; |
| 224 | 232 | ||
| 225 | // Set text | 233 | // Set text |
| 226 | - this.formData[fieldName] = (text !== null ? text : (this.ocrData[index].text || '')).trim(); | 234 | + this.formData[fieldName] = (text !== null ? text : (this.ocrData[newIndex].text || '')).trim(); |
| 227 | 235 | ||
| 228 | // KHÔNG set active index (không focus) | 236 | // KHÔNG set active index (không focus) |
| 229 | 237 | ||
| ... | @@ -238,50 +246,59 @@ | ... | @@ -238,50 +246,59 @@ |
| 238 | console.log(`Assigning field "${fieldName}" to box at index ${index} with text: "${text}"`); | 246 | console.log(`Assigning field "${fieldName}" to box at index ${index} with text: "${text}"`); |
| 239 | if (index == null) return; | 247 | if (index == null) return; |
| 240 | 248 | ||
| 241 | - // 1. Xóa box cũ của field này (nếu có) | 249 | + // 1. Xóa tất cả box có fieldName trùng lặp, chỉ giữ lại box hiện tại |
| 242 | this.ocrData = this.ocrData.filter((box, i) => { | 250 | this.ocrData = this.ocrData.filter((box, i) => { |
| 243 | - return !(i !== index && box.field === fieldName && box.isManual); | 251 | + if (i === index) return true; // Giữ lại box hiện tại |
| 252 | + if (box.field === fieldName) { | ||
| 253 | + // Xóa box có field trùng lặp | ||
| 254 | + return false; | ||
| 255 | + } | ||
| 256 | + return true; // Giữ lại các box khác | ||
| 244 | }); | 257 | }); |
| 245 | 258 | ||
| 246 | - // 2. Nếu box này từng gán field khác thì bỏ | 259 | + // 2. Cập nhật lại index sau khi filter |
| 247 | - const prev = this.ocrData[index]?.field; | 260 | + const newIndex = this.ocrData.findIndex(box => box.bbox === this.ocrData[index]?.bbox); |
| 261 | + if (newIndex === -1) return; | ||
| 262 | + | ||
| 263 | + // 3. Nếu box này từng gán field khác thì bỏ | ||
| 264 | + const prev = this.ocrData[newIndex]?.field; | ||
| 248 | if (prev && prev !== fieldName) { | 265 | if (prev && prev !== fieldName) { |
| 249 | if (prev === 'customer_name') { | 266 | if (prev === 'customer_name') { |
| 250 | this.hasCustomerNameXY = false; | 267 | this.hasCustomerNameXY = false; |
| 251 | this.customer_name_xy = ''; | 268 | this.customer_name_xy = ''; |
| 252 | } | 269 | } |
| 253 | - this.ocrData[index].field = null; | 270 | + this.ocrData[newIndex].field = null; |
| 254 | - this.ocrData[index].field_xy = null; | 271 | + this.ocrData[newIndex].field_xy = null; |
| 255 | } | 272 | } |
| 256 | 273 | ||
| 257 | - // 3. Gán field mới | 274 | + // 4. Gán field mới |
| 258 | - const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] | 275 | + const bbox = this.ocrData[newIndex].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] |
| 259 | console.log('bbox', bbox); | 276 | console.log('bbox', bbox); |
| 260 | const [x1, y1, w, h] = bbox; | 277 | const [x1, y1, w, h] = bbox; |
| 261 | const xyStr = `${x1},${y1},${w},${h}`; | 278 | const xyStr = `${x1},${y1},${w},${h}`; |
| 262 | 279 | ||
| 263 | - this.ocrData[index].field = fieldName; | 280 | + this.ocrData[newIndex].field = fieldName; |
| 264 | - this.ocrData[index].field_xy = xyStr; | 281 | + this.ocrData[newIndex].field_xy = xyStr; |
| 265 | 282 | ||
| 266 | - // 4. Gán text | 283 | + // 5. Gán text |
| 267 | - const finalText = text !== null ? text : (this.ocrData[index].text || ''); | 284 | + const finalText = text !== null ? text : (this.ocrData[newIndex].text || ''); |
| 268 | this.formData[fieldName] = finalText.trim(); | 285 | this.formData[fieldName] = finalText.trim(); |
| 269 | console.log(`formData ${fieldName}`, this.formData[fieldName]); | 286 | console.log(`formData ${fieldName}`, this.formData[fieldName]); |
| 270 | 287 | ||
| 271 | // Nếu trong manualBoxData tồn tại field này hoặc muốn tạo lại manual box từ ocrData | 288 | // Nếu trong manualBoxData tồn tại field này hoặc muốn tạo lại manual box từ ocrData |
| 272 | if (this.manualBoxData[fieldName]) { | 289 | if (this.manualBoxData[fieldName]) { |
| 273 | // Lấy tọa độ + text từ box hiện tại để tạo manual box mới | 290 | // Lấy tọa độ + text từ box hiện tại để tạo manual box mới |
| 274 | - this.createManualBoxFromDB(fieldName, this.ocrData[index].bbox, finalText); | 291 | + this.createManualBoxFromDB(fieldName, this.ocrData[newIndex].bbox, finalText); |
| 275 | // Cập nhật manualBoxData | 292 | // Cập nhật manualBoxData |
| 276 | this.manualBoxData[fieldName] = { | 293 | this.manualBoxData[fieldName] = { |
| 277 | - coords: this.ocrData[index].bbox, | 294 | + coords: this.ocrData[newIndex].bbox, |
| 278 | text: finalText | 295 | text: finalText |
| 279 | }; | 296 | }; |
| 280 | } | 297 | } |
| 281 | 298 | ||
| 282 | - // 5. Active index | 299 | + // 6. Active index |
| 283 | - this.activeIndex = index; | 300 | + this.activeIndex = newIndex; |
| 284 | - // 6. Nếu là customer_name | 301 | + // 7. Nếu là customer_name |
| 285 | if (fieldName === 'customer_name') { | 302 | if (fieldName === 'customer_name') { |
| 286 | this.hasCustomerNameXY = true; | 303 | this.hasCustomerNameXY = true; |
| 287 | this.customer_name_xy = xyStr; | 304 | this.customer_name_xy = xyStr; |
| ... | @@ -475,38 +492,49 @@ | ... | @@ -475,38 +492,49 @@ |
| 475 | top: `${top}px`, | 492 | top: `${top}px`, |
| 476 | width: `${width}px`, | 493 | width: `${width}px`, |
| 477 | height: `${height}px`, | 494 | height: `${height}px`, |
| 478 | - border: item.hideBorder ? 'none' : '2px solid ' + (index === this.activeIndex ? '#199601' : '#ff5252'), | 495 | + border: item.hideBorder ? '2px solid #ccc' : '2px solid ' + (index === this.activeIndex ? '#199601' : '#ff5252'), |
| 479 | boxSizing: 'border-box', | 496 | boxSizing: 'border-box', |
| 480 | cursor: 'pointer', | 497 | cursor: 'pointer', |
| 481 | zIndex: item.isManual ? 30 : 10 | 498 | zIndex: item.isManual ? 30 : 10 |
| 482 | }; | 499 | }; |
| 483 | }, | 500 | }, |
| 484 | highlightField(field) { | 501 | highlightField(field) { |
| 485 | - // Xóa tất cả manual box cũ trước khi tạo mới | ||
| 486 | - //this.ocrData = this.ocrData.filter(b => !b.isManual); | ||
| 487 | - | ||
| 488 | let coords, text; | 502 | let coords, text; |
| 489 | - | 503 | + let isFromDB = false; |
| 504 | + | ||
| 505 | + // Kiểm tra xem field này có phải từ DB không | ||
| 506 | + if (this.dataMapping && this.dataMapping[field]) { | ||
| 507 | + isFromDB = true; | ||
| 508 | + coords = this.dataMapping[field].coords; | ||
| 509 | + text = this.dataMapping[field].text; | ||
| 510 | + console.log(`Using dataMapping for field "${field}":`, coords, text); | ||
| 511 | + } else { | ||
| 490 | // Kiểm tra xem ocrData đã có box nào với field này chưa | 512 | // Kiểm tra xem ocrData đã có box nào với field này chưa |
| 491 | - const existingBox = this.ocrData.find(b => b.field === field && !b.isManual); | 513 | + const existingBox = this.ocrData.find(b => b.field === field && !b.isDeleted); |
| 492 | - | ||
| 493 | if (existingBox) { | 514 | if (existingBox) { |
| 494 | coords = existingBox.bbox; | 515 | coords = existingBox.bbox; |
| 495 | text = existingBox.text; | 516 | text = existingBox.text; |
| 496 | - console.log(`Using ocrData for field "${field}":`, coords, text); | 517 | + console.log(`Using existing box for field "${field}":`, coords, text); |
| 497 | } else if (this.manualBoxData[field]) { | 518 | } else if (this.manualBoxData[field]) { |
| 498 | - // Nếu không có trong ocrData, dùng manualBoxData | 519 | + // Nếu không có trong ocrData, dùng manualBoxData (tọa độ mới) |
| 499 | coords = this.manualBoxData[field].coords; | 520 | coords = this.manualBoxData[field].coords; |
| 500 | text = this.manualBoxData[field].text; | 521 | text = this.manualBoxData[field].text; |
| 501 | - console.log(`Using manualBoxData for field "${field}":`, coords, text); | 522 | + console.log(`Using manualBoxData (new coordinates) for field "${field}":`, coords, text); |
| 523 | + } | ||
| 502 | } | 524 | } |
| 503 | 525 | ||
| 504 | - // Nếu có coords thì tạo manual box | 526 | + // Nếu có coords thì tạo hoặc hiển thị lại box |
| 505 | if (coords) { | 527 | if (coords) { |
| 528 | + if (isFromDB) { | ||
| 529 | + // Tạo box manual từ DB (không có nút xóa) | ||
| 506 | this.createManualBoxFromDB(field, coords, text); | 530 | this.createManualBoxFromDB(field, coords, text); |
| 531 | + } else { | ||
| 532 | + // Hiển thị lại box manual đã quét chọn (có nút xóa) | ||
| 533 | + this.showManualBox(field, coords, text); | ||
| 534 | + } | ||
| 507 | } | 535 | } |
| 508 | 536 | ||
| 509 | - // Tìm lại index của box vừa tạo để set active | 537 | + // Tìm lại index của box để set active |
| 510 | let idx = -1; | 538 | let idx = -1; |
| 511 | for (let i = this.ocrData.length - 1; i >= 0; i--) { | 539 | for (let i = this.ocrData.length - 1; i >= 0; i--) { |
| 512 | const it = this.ocrData[i]; | 540 | const it = this.ocrData[i]; |
| ... | @@ -519,7 +547,8 @@ | ... | @@ -519,7 +547,8 @@ |
| 519 | if (idx !== -1) { | 547 | if (idx !== -1) { |
| 520 | this.activeIndex = idx; | 548 | this.activeIndex = idx; |
| 521 | this.scrollToBox(idx); | 549 | this.scrollToBox(idx); |
| 522 | - this.focusOnBox(idx); | 550 | + // Reset selectingIndex để không hiển thị dropdown khi highlight từ input |
| 551 | + this.selectingIndex = null; | ||
| 523 | } else { | 552 | } else { |
| 524 | this.activeIndex = null; | 553 | this.activeIndex = null; |
| 525 | } | 554 | } |
| ... | @@ -563,32 +592,82 @@ | ... | @@ -563,32 +592,82 @@ |
| 563 | }); | 592 | }); |
| 564 | }, | 593 | }, |
| 565 | 594 | ||
| 566 | - // Focus vào box (thêm hiệu ứng nhấp nháy) | ||
| 567 | - focusOnBox(index) { | ||
| 568 | - if (index < 0 || index >= this.ocrData.length) return; | ||
| 569 | 595 | ||
| 570 | - const item = this.ocrData[index]; | ||
| 571 | - if (!item || item.isDeleted) return; | ||
| 572 | - | ||
| 573 | - // Thêm class để tạo hiệu ứng focus | ||
| 574 | - this.$nextTick(() => { | ||
| 575 | - const boxElement = document.querySelector(`[data-field="${item.field}"]`); | ||
| 576 | - if (boxElement) { | ||
| 577 | - boxElement.classList.add('focus-highlight'); | ||
| 578 | - } | ||
| 579 | - }); | ||
| 580 | - }, | ||
| 581 | 596 | ||
| 582 | // Xử lý khi click vào input | 597 | // Xử lý khi click vào input |
| 583 | onInputClick(fieldName) { | 598 | onInputClick(fieldName) { |
| 584 | // Kiểm tra xem field này có data không | 599 | // Kiểm tra xem field này có data không |
| 585 | const fieldValue = this.formData[fieldName]; | 600 | const fieldValue = this.formData[fieldName]; |
| 586 | if (fieldValue && fieldValue.trim()) { | 601 | if (fieldValue && fieldValue.trim()) { |
| 587 | - // Nếu có data, highlight và focus vào box tương ứng | 602 | + // Nếu có data từ DB, highlight và focus vào box tương ứng |
| 588 | - // Chỉ khi click vào input mới focus và chuyển trạng thái active | ||
| 589 | this.highlightField(fieldName); | 603 | this.highlightField(fieldName); |
| 590 | } | 604 | } |
| 591 | }, | 605 | }, |
| 606 | + | ||
| 607 | + // Xử lý khi click ra ngoài input (blur) | ||
| 608 | + onInputBlur(fieldName) { | ||
| 609 | + // Khi không focus vào input nào, xóa tất cả focus | ||
| 610 | + this.removeAllFocus(); | ||
| 611 | + }, | ||
| 612 | + | ||
| 613 | + // Xóa tất cả focus | ||
| 614 | + removeAllFocus() { | ||
| 615 | + // Reset active index (chỉ mất màu xanh, không xóa box) | ||
| 616 | + this.activeIndex = null; | ||
| 617 | + | ||
| 618 | + // Ẩn hoàn toàn các box manual được tạo từ DB (không phải quét chọn thủ công) | ||
| 619 | + this.ocrData.forEach(item => { | ||
| 620 | + if (item.isManual && !item.showDelete) { | ||
| 621 | + // Box manual từ DB (không có nút xóa) - ẩn hoàn toàn | ||
| 622 | + item.isDeleted = true; | ||
| 623 | + } | ||
| 624 | + }); | ||
| 625 | + | ||
| 626 | + // Đảm bảo tất cả box OCR đều hiển thị (chỉ ẩn border khi cần thiết) | ||
| 627 | + this.ocrData.forEach(item => { | ||
| 628 | + if (!item.isManual && item.hideBorder) { | ||
| 629 | + // Chỉ ẩn border cho box OCR nằm trong vùng manual | ||
| 630 | + item.hideBorder = true; | ||
| 631 | + } | ||
| 632 | + }); | ||
| 633 | + }, | ||
| 634 | + | ||
| 635 | + // Xử lý khi click vào box | ||
| 636 | + onBoxClick(index) { | ||
| 637 | + const item = this.ocrData[index]; | ||
| 638 | + | ||
| 639 | + // Kiểm tra xem field này có phải từ DB không | ||
| 640 | + const isFromDB = this.dataMapping && this.dataMapping[item.field]; | ||
| 641 | + | ||
| 642 | + // Kiểm tra xem data có được ghi đè không (so sánh với data gốc từ DB) | ||
| 643 | + const isDataOverridden = item.field && isFromDB && | ||
| 644 | + this.formData[item.field] !== this.dataMapping[item.field].text; | ||
| 645 | + | ||
| 646 | + if (item.isManual) { | ||
| 647 | + // Manual box | ||
| 648 | + if (isFromDB && !isDataOverridden) { | ||
| 649 | + // Manual box từ DB chưa ghi đè, KHÔNG cho chọn option | ||
| 650 | + this.activeIndex = index; | ||
| 651 | + this.selectingIndex = null; | ||
| 652 | + } else { | ||
| 653 | + // Manual box từ DB có data ghi đè HOẶC manual box bình thường, CHO PHÉP chọn option | ||
| 654 | + this.selectingIndex = index; | ||
| 655 | + } | ||
| 656 | + } else if (item.field) { | ||
| 657 | + // Box OCR có field | ||
| 658 | + if (isFromDB && !isDataOverridden) { | ||
| 659 | + // Box có field từ DB chưa ghi đè, KHÔNG cho chọn option | ||
| 660 | + this.activeIndex = index; | ||
| 661 | + this.selectingIndex = null; | ||
| 662 | + } else { | ||
| 663 | + // Box có field từ DB đã ghi đè HOẶC field mới, CHO PHÉP chọn option | ||
| 664 | + this.selectingIndex = index; | ||
| 665 | + } | ||
| 666 | + } else { | ||
| 667 | + // Box OCR thông thường (chưa có field), cho phép hiển thị dropdown | ||
| 668 | + this.selectingIndex = index; | ||
| 669 | + } | ||
| 670 | + }, | ||
| 592 | startSelect(e) { | 671 | startSelect(e) { |
| 593 | if (this.isMappingManually || e.button !== 0) return; | 672 | if (this.isMappingManually || e.button !== 0) return; |
| 594 | this.isSelecting = true; | 673 | this.isSelecting = true; |
| ... | @@ -673,19 +752,41 @@ | ... | @@ -673,19 +752,41 @@ |
| 673 | , | 752 | , |
| 674 | applyMapping() { | 753 | applyMapping() { |
| 675 | const item = this.ocrData[this.selectingIndex]; | 754 | const item = this.ocrData[this.selectingIndex]; |
| 755 | + if (!item) return; | ||
| 676 | 756 | ||
| 677 | - if (item && item.isManual) { | 757 | + if (item.isManual) { |
| 758 | + // Nếu là manual box, chuyển sang chế độ manual mapping | ||
| 678 | this.manualIndex = this.selectingIndex; | 759 | this.manualIndex = this.selectingIndex; |
| 679 | this.manualField = item.field || ""; | 760 | this.manualField = item.field || ""; |
| 680 | this.applyManualMapping(); | 761 | this.applyManualMapping(); |
| 681 | return; | 762 | return; |
| 682 | } | 763 | } |
| 683 | 764 | ||
| 765 | + // Xử lý box OCR (có thể chưa có field hoặc đã có field) | ||
| 684 | if (item.field) { | 766 | if (item.field) { |
| 685 | - // this.formData[item.field] = item.text; | 767 | + // Nếu box đã có field, cập nhật lại |
| 686 | - // this.activeIndex = this.selectingIndex; | 768 | + const oldField = item.field; |
| 687 | - this.assignFieldToBox(this.selectingIndex, item.field, item.text); | 769 | + |
| 770 | + // Cập nhật formData để hiển thị trong input | ||
| 771 | + this.formData[item.field] = item.text || ''; | ||
| 772 | + | ||
| 773 | + // Cập nhật manualBoxData với tọa độ mới | ||
| 774 | + this.manualBoxData[item.field] = { | ||
| 775 | + coords: item.bbox, | ||
| 776 | + text: item.text || '' | ||
| 777 | + }; | ||
| 778 | + | ||
| 779 | + // Xóa dataMapping cũ để tránh focus về tọa độ cũ | ||
| 780 | + if (this.dataMapping && this.dataMapping[item.field]) { | ||
| 781 | + delete this.dataMapping[item.field]; | ||
| 782 | + } | ||
| 783 | + | ||
| 784 | + // Set active index | ||
| 785 | + this.activeIndex = this.selectingIndex; | ||
| 786 | + | ||
| 787 | + console.log(`Updated field "${item.field}" for box at index ${this.selectingIndex} with new coordinates`); | ||
| 688 | } | 788 | } |
| 789 | + | ||
| 689 | this.selectingIndex = null; | 790 | this.selectingIndex = null; |
| 690 | }, | 791 | }, |
| 691 | applyManualMapping() { | 792 | applyManualMapping() { |
| ... | @@ -723,17 +824,34 @@ | ... | @@ -723,17 +824,34 @@ |
| 723 | }); | 824 | }); |
| 724 | 825 | ||
| 725 | const finalText = combinedText.join(" "); | 826 | const finalText = combinedText.join(" "); |
| 726 | - // console.log('Combined text:', finalText); | 827 | + console.log('Combined text:', finalText); |
| 727 | 828 | ||
| 728 | // Gán field và text cho box manual | 829 | // Gán field và text cho box manual |
| 729 | console.log(`Assigning manual field "${this.manualField}" to box at index ${manualIndex} with text: "${finalText}"`); | 830 | console.log(`Assigning manual field "${this.manualField}" to box at index ${manualIndex} with text: "${finalText}"`); |
| 730 | - // console.log(this.ocrData[manualIndex]); | 831 | + |
| 731 | - this.assignFieldToBox(manualIndex, this.manualField, finalText); | 832 | + // Gán field trực tiếp cho box manual |
| 833 | + this.ocrData[manualIndex].field = this.manualField; | ||
| 834 | + | ||
| 835 | + // Cập nhật formData để hiển thị trong input | ||
| 836 | + this.formData[this.manualField] = finalText.trim(); | ||
| 837 | + | ||
| 838 | + // Cập nhật manualBoxData | ||
| 839 | + this.manualBoxData[this.manualField] = { | ||
| 840 | + coords: newBbox, | ||
| 841 | + text: finalText.trim() | ||
| 842 | + }; | ||
| 843 | + | ||
| 844 | + // Xóa dataMapping cũ để tránh focus về tọa độ cũ | ||
| 845 | + if (this.dataMapping && this.dataMapping[this.manualField]) { | ||
| 846 | + delete this.dataMapping[this.manualField]; | ||
| 847 | + } | ||
| 732 | 848 | ||
| 733 | // Reset trạng thái chọn | 849 | // Reset trạng thái chọn |
| 734 | this.isMappingManually = false; | 850 | this.isMappingManually = false; |
| 735 | this.selectBox.show = false; | 851 | this.selectBox.show = false; |
| 736 | this.selectBox.showDropdown = false; | 852 | this.selectBox.showDropdown = false; |
| 853 | + this.manualField = ""; | ||
| 854 | + this.manualIndex = null; | ||
| 737 | }, | 855 | }, |
| 738 | 856 | ||
| 739 | isBoxInside(inner, outer) { | 857 | isBoxInside(inner, outer) { |
| ... | @@ -820,7 +938,10 @@ | ... | @@ -820,7 +938,10 @@ |
| 820 | if (x1 >= 0 && y1 >= 0 && x2 > x1 && y2 > y1 && | 938 | if (x1 >= 0 && y1 >= 0 && x2 > x1 && y2 > y1 && |
| 821 | x2 <= this.imageWidth && y2 <= this.imageHeight) { | 939 | x2 <= this.imageWidth && y2 <= this.imageHeight) { |
| 822 | 940 | ||
| 823 | - // Tạo box manual | 941 | + // Xóa box cũ có cùng fieldName trước khi tạo mới (chỉ xóa manual box) |
| 942 | + this.ocrData = this.ocrData.filter(box => !(box.field === fieldName && box.isManual)); | ||
| 943 | + | ||
| 944 | + // Tạo box manual từ DB (không có nút xóa) | ||
| 824 | const manualBox = { | 945 | const manualBox = { |
| 825 | text: text || '', | 946 | text: text || '', |
| 826 | bbox: coords, | 947 | bbox: coords, |
| ... | @@ -839,6 +960,54 @@ | ... | @@ -839,6 +960,54 @@ |
| 839 | } else { | 960 | } else { |
| 840 | console.warn('Invalid coordinates for manual box:', coords); | 961 | console.warn('Invalid coordinates for manual box:', coords); |
| 841 | } | 962 | } |
| 963 | + }, | ||
| 964 | + | ||
| 965 | + // Hiển thị lại box manual đã quét chọn (có nút xóa) | ||
| 966 | + showManualBox(fieldName, coordinates, text) { | ||
| 967 | + if (!this.imageWidth || !this.imageHeight) { | ||
| 968 | + console.log('Cannot show manual box: Image not loaded'); | ||
| 969 | + return; | ||
| 970 | + } | ||
| 971 | + | ||
| 972 | + // Parse coordinates | ||
| 973 | + let coords; | ||
| 974 | + if (typeof coordinates === 'string') { | ||
| 975 | + coords = coordinates.split(',').map(Number); | ||
| 976 | + } else if (Array.isArray(coordinates)) { | ||
| 977 | + coords = coordinates; | ||
| 978 | + } else { | ||
| 979 | + console.error('Invalid coordinates format:', coordinates); | ||
| 980 | + return; | ||
| 981 | + } | ||
| 982 | + | ||
| 983 | + const [x1, y1, x2, y2] = coords; | ||
| 984 | + | ||
| 985 | + // Kiểm tra tọa độ có hợp lệ không | ||
| 986 | + if (x1 >= 0 && y1 >= 0 && x2 > x1 && y2 > y1 && | ||
| 987 | + x2 <= this.imageWidth && y2 <= this.imageHeight) { | ||
| 988 | + | ||
| 989 | + // Xóa box cũ có cùng fieldName trước khi hiển thị lại | ||
| 990 | + this.ocrData = this.ocrData.filter(box => !(box.field === fieldName && box.isManual)); | ||
| 991 | + | ||
| 992 | + // Hiển thị lại box manual đã quét chọn (có nút xóa) | ||
| 993 | + const manualBox = { | ||
| 994 | + text: text || '', | ||
| 995 | + bbox: coords, | ||
| 996 | + field: fieldName, | ||
| 997 | + isManual: true, | ||
| 998 | + showDelete: true, | ||
| 999 | + isDeleted: false, | ||
| 1000 | + hideBorder: false | ||
| 1001 | + }; | ||
| 1002 | + | ||
| 1003 | + this.ocrData.push(manualBox); | ||
| 1004 | + console.log('Manual box shown successfully:', manualBox); | ||
| 1005 | + | ||
| 1006 | + // Force re-render | ||
| 1007 | + this.$forceUpdate(); | ||
| 1008 | + } else { | ||
| 1009 | + console.warn('Invalid coordinates for manual box:', coords); | ||
| 1010 | + } | ||
| 842 | } | 1011 | } |
| 843 | 1012 | ||
| 844 | } | 1013 | } | ... | ... |
-
Please register or sign in to post a comment