Showing
2 changed files
with
145 additions
and
231 deletions
| ... | @@ -17,13 +17,11 @@ class OcrController extends Controller | ... | @@ -17,13 +17,11 @@ class OcrController extends Controller |
| 17 | public function store(Request $request) | 17 | public function store(Request $request) |
| 18 | { | 18 | { |
| 19 | 19 | ||
| 20 | +// dd($request->all()); | ||
| 20 | $request->validate([ | 21 | $request->validate([ |
| 21 | 'customer_name_text' => 'required|string', | 22 | 'customer_name_text' => 'required|string', |
| 22 | 'customer_name_xy' => 'required|string', | 23 | 'customer_name_xy' => 'required|string', |
| 23 | 'template_name' => 'required|string|unique:mst_template,tpl_name', | 24 | 'template_name' => 'required|string|unique:mst_template,tpl_name', |
| 24 | - 'fields' => 'required|array', | ||
| 25 | - 'fields.*.name' => 'required|string', | ||
| 26 | - 'fields.*.xy' => 'required|string', | ||
| 27 | ]); | 25 | ]); |
| 28 | 26 | ||
| 29 | try { | 27 | try { |
| ... | @@ -35,11 +33,11 @@ class OcrController extends Controller | ... | @@ -35,11 +33,11 @@ class OcrController extends Controller |
| 35 | ]); | 33 | ]); |
| 36 | 34 | ||
| 37 | // Lưu các field khác vào dt_template | 35 | // Lưu các field khác vào dt_template |
| 38 | - foreach ($request->fields as $field) { | 36 | + foreach ($request->fields as $field => $value) { |
| 39 | DtTemplate::create([ | 37 | DtTemplate::create([ |
| 40 | 'tpl_id' => $mst->id, | 38 | 'tpl_id' => $mst->id, |
| 41 | - 'field_name' => $field['name'], | 39 | + 'field_name' => $field, |
| 42 | - 'field_xy' => $field['xy'], | 40 | + 'field_xy' => is_array($value['coords']) ? implode(',', $value['coords']) : $value['coords'], |
| 43 | ]); | 41 | ]); |
| 44 | } | 42 | } |
| 45 | 43 | ||
| ... | @@ -61,7 +59,7 @@ class OcrController extends Controller | ... | @@ -61,7 +59,7 @@ class OcrController extends Controller |
| 61 | { | 59 | { |
| 62 | try { | 60 | try { |
| 63 | // Lấy template name từ request hoặc mặc định | 61 | // Lấy template name từ request hoặc mặc định |
| 64 | - $templateName = $request->get('template_name', 'nemo'); | 62 | + $templateName = $request->get('template_name', 'A'); |
| 65 | 63 | ||
| 66 | // Giả sử file OCR JSON & ảnh nằm trong storage/app/public/image/ | 64 | // Giả sử file OCR JSON & ảnh nằm trong storage/app/public/image/ |
| 67 | $jsonPath = public_path("image/data_picking_detail_1754967679.json"); | 65 | $jsonPath = public_path("image/data_picking_detail_1754967679.json"); |
| ... | @@ -77,6 +75,7 @@ class OcrController extends Controller | ... | @@ -77,6 +75,7 @@ class OcrController extends Controller |
| 77 | } | 75 | } |
| 78 | 76 | ||
| 79 | $formData = []; | 77 | $formData = []; |
| 78 | + $dataMapping = []; | ||
| 80 | 79 | ||
| 81 | if ($templateName) { | 80 | if ($templateName) { |
| 82 | $mst = MstTemplate::where('tpl_name', $templateName)->first(); | 81 | $mst = MstTemplate::where('tpl_name', $templateName)->first(); |
| ... | @@ -94,6 +93,11 @@ class OcrController extends Controller | ... | @@ -94,6 +93,11 @@ class OcrController extends Controller |
| 94 | 93 | ||
| 95 | // field_name => text | 94 | // field_name => text |
| 96 | $formData[$detail->field_name] = $text; | 95 | $formData[$detail->field_name] = $text; |
| 96 | + | ||
| 97 | + $dataMapping[$detail->field_name] = [ | ||
| 98 | + 'text' => $text, | ||
| 99 | + 'coords' => $coords | ||
| 100 | + ]; | ||
| 97 | } | 101 | } |
| 98 | } else { | 102 | } else { |
| 99 | $formData = [ | 103 | $formData = [ |
| ... | @@ -112,6 +116,7 @@ class OcrController extends Controller | ... | @@ -112,6 +116,7 @@ class OcrController extends Controller |
| 112 | 'ocrData' => $ocrData, | 116 | 'ocrData' => $ocrData, |
| 113 | 'pdfImageUrl' => $imgPath, | 117 | 'pdfImageUrl' => $imgPath, |
| 114 | 'formData' => $formData, | 118 | 'formData' => $formData, |
| 119 | + 'dataMapping' => $dataMapping, | ||
| 115 | 'fieldOptions' => [ | 120 | 'fieldOptions' => [ |
| 116 | [ 'value' => 'template_name', 'label' => 'Tên Mẫu PDF' ], | 121 | [ 'value' => 'template_name', 'label' => 'Tên Mẫu PDF' ], |
| 117 | [ 'value' => 'customer_name', 'label' => 'Tên khách hàng' ], | 122 | [ 'value' => 'customer_name', 'label' => 'Tên khách hàng' ], | ... | ... |
| ... | @@ -26,8 +26,8 @@ | ... | @@ -26,8 +26,8 @@ |
| 26 | } | 26 | } |
| 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 | ||
| ... | @@ -133,12 +133,11 @@ | ... | @@ -133,12 +133,11 @@ |
| 133 | <input v-model="formData[field.value]" | 133 | <input v-model="formData[field.value]" |
| 134 | @focus="highlightField(field.value)" | 134 | @focus="highlightField(field.value)" |
| 135 | @click="onInputClick(field.value)" | 135 | @click="onInputClick(field.value)" |
| 136 | +{{-- @blur="removeManualBoxes"--}} | ||
| 136 | :readonly="field.value === 'customer_name' && !hasCustomerNameXY" | 137 | :readonly="field.value === 'customer_name' && !hasCustomerNameXY" |
| 137 | > | 138 | > |
| 138 | </div> | 139 | </div> |
| 139 | <button @click="saveTemplate">💾Save</button> | 140 | <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>--}} | ||
| 142 | </div> | 141 | </div> |
| 143 | 142 | ||
| 144 | </div> | 143 | </div> |
| ... | @@ -156,6 +155,7 @@ | ... | @@ -156,6 +155,7 @@ |
| 156 | activeIndex: null, | 155 | activeIndex: null, |
| 157 | manualField: "", | 156 | manualField: "", |
| 158 | formData: {}, | 157 | formData: {}, |
| 158 | + manualBoxData: {}, | ||
| 159 | fieldOptions: [], | 159 | fieldOptions: [], |
| 160 | customer_name_xy: '', | 160 | customer_name_xy: '', |
| 161 | hasCustomerNameXY: false, | 161 | hasCustomerNameXY: false, |
| ... | @@ -182,6 +182,10 @@ | ... | @@ -182,6 +182,10 @@ |
| 182 | } | 182 | } |
| 183 | }, | 183 | }, |
| 184 | methods: { | 184 | methods: { |
| 185 | + removeManualBoxes() { | ||
| 186 | + this.ocrData = this.ocrData.filter(b => !b.isManual); | ||
| 187 | + this.activeIndex = null; | ||
| 188 | + }, | ||
| 185 | // Map field cho box (không set active, chỉ dùng để load data từ DB) | 189 | // Map field cho box (không set active, chỉ dùng để load data từ DB) |
| 186 | mapFieldToBox(index, fieldName, text = null) { | 190 | mapFieldToBox(index, fieldName, text = null) { |
| 187 | if (index == null) return; | 191 | if (index == null) return; |
| ... | @@ -230,21 +234,17 @@ | ... | @@ -230,21 +234,17 @@ |
| 230 | } | 234 | } |
| 231 | }, | 235 | }, |
| 232 | 236 | ||
| 233 | - // Assign field và set active (dùng khi user tương tác) | ||
| 234 | assignFieldToBox(index, fieldName, text = null) { | 237 | assignFieldToBox(index, fieldName, text = null) { |
| 235 | console.log(`Assigning field "${fieldName}" to box at index ${index} with text: "${text}"`); | 238 | console.log(`Assigning field "${fieldName}" to box at index ${index} with text: "${text}"`); |
| 236 | if (index == null) return; | 239 | if (index == null) return; |
| 237 | 240 | ||
| 238 | - // Xóa fieldName ở box khác | 241 | + // 1. Xóa box cũ của field này (nếu có) |
| 239 | - this.ocrData.forEach((box, i) => { | 242 | + this.ocrData = this.ocrData.filter((box, i) => { |
| 240 | - if (i !== index && box.field === fieldName) { | 243 | + return !(i !== index && box.field === fieldName && box.isManual); |
| 241 | - box.field = null; | ||
| 242 | - box.field_xy = null; | ||
| 243 | - } | ||
| 244 | }); | 244 | }); |
| 245 | 245 | ||
| 246 | - // Nếu box này từng gán field khác thì bỏ | 246 | + // 2. Nếu box này từng gán field khác thì bỏ |
| 247 | - const prev = this.ocrData[index].field; | 247 | + const prev = this.ocrData[index]?.field; |
| 248 | if (prev && prev !== fieldName) { | 248 | if (prev && prev !== fieldName) { |
| 249 | if (prev === 'customer_name') { | 249 | if (prev === 'customer_name') { |
| 250 | this.hasCustomerNameXY = false; | 250 | this.hasCustomerNameXY = false; |
| ... | @@ -254,27 +254,34 @@ | ... | @@ -254,27 +254,34 @@ |
| 254 | this.ocrData[index].field_xy = null; | 254 | this.ocrData[index].field_xy = null; |
| 255 | } | 255 | } |
| 256 | 256 | ||
| 257 | - // Gán field mới | 257 | + // 3. Gán field mới |
| 258 | 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] |
| 259 | - | 259 | + console.log('bbox', bbox); |
| 260 | - console.log('2222222222222222',bbox); | 260 | + const [x1, y1, w, h] = bbox; |
| 261 | - const x1 = bbox[0]; | ||
| 262 | - const y1 = bbox[1]; | ||
| 263 | - const w = bbox[2]; | ||
| 264 | - const h = bbox[3]; | ||
| 265 | - | ||
| 266 | const xyStr = `${x1},${y1},${w},${h}`; | 261 | const xyStr = `${x1},${y1},${w},${h}`; |
| 267 | 262 | ||
| 268 | this.ocrData[index].field = fieldName; | 263 | this.ocrData[index].field = fieldName; |
| 269 | this.ocrData[index].field_xy = xyStr; | 264 | this.ocrData[index].field_xy = xyStr; |
| 270 | 265 | ||
| 271 | - // Set text | 266 | + // 4. Gán text |
| 272 | - this.formData[fieldName] = (text !== null ? text : (this.ocrData[index].text || '')).trim(); | 267 | + const finalText = text !== null ? text : (this.ocrData[index].text || ''); |
| 268 | + this.formData[fieldName] = finalText.trim(); | ||
| 269 | + console.log(`formData ${fieldName}`, this.formData[fieldName]); | ||
| 270 | + | ||
| 271 | + // 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]) { | ||
| 273 | + // 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); | ||
| 275 | + // Cập nhật manualBoxData | ||
| 276 | + this.manualBoxData[fieldName] = { | ||
| 277 | + coords: this.ocrData[index].bbox, | ||
| 278 | + text: finalText | ||
| 279 | + }; | ||
| 280 | + } | ||
| 273 | 281 | ||
| 274 | - // Active index (focus vào box này) | 282 | + // 5. Active index |
| 275 | this.activeIndex = index; | 283 | this.activeIndex = index; |
| 276 | - | 284 | + // 6. Nếu là customer_name |
| 277 | - // Nếu là customer_name | ||
| 278 | if (fieldName === 'customer_name') { | 285 | if (fieldName === 'customer_name') { |
| 279 | this.hasCustomerNameXY = true; | 286 | this.hasCustomerNameXY = true; |
| 280 | this.customer_name_xy = xyStr; | 287 | this.customer_name_xy = xyStr; |
| ... | @@ -284,29 +291,47 @@ | ... | @@ -284,29 +291,47 @@ |
| 284 | 291 | ||
| 285 | async saveTemplate() { | 292 | async saveTemplate() { |
| 286 | 293 | ||
| 287 | - if (!this.hasCustomerNameXY) { | 294 | + let customer_name = null; |
| 295 | + let customer_coords = null; | ||
| 296 | + let fields = []; | ||
| 297 | + if (this.manualBoxData.customer_name) { | ||
| 298 | + // Lấy từ manualBoxData nếu có | ||
| 299 | + customer_name = this.manualBoxData.customer_name.text; | ||
| 300 | + customer_coords = this.manualBoxData.customer_name.coords.join(','); | ||
| 301 | + fields = this.manualBoxData; | ||
| 302 | + } else { | ||
| 303 | + // Không có → tìm trong ocrData | ||
| 304 | + const found = this.ocrData.find(item => item.field === 'customer_name'); | ||
| 305 | + if (found) { | ||
| 306 | + customer_name = found.text; | ||
| 307 | + customer_coords = found.field_xy; | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + const fieldsByName = {}; | ||
| 311 | + this.ocrData.forEach(box => { | ||
| 312 | + if (box.field && !box.isDeleted) { | ||
| 313 | + // chỉ giữ 1 bản ghi cuối cùng cho mỗi field (box gần nhất) | ||
| 314 | + fieldsByName[box.field] = { | ||
| 315 | + text: box.field, | ||
| 316 | + coords: box.field_xy || '' | ||
| 317 | + }; | ||
| 318 | + } | ||
| 319 | + }); | ||
| 320 | + // // convert to array | ||
| 321 | + fields = (fieldsByName); | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + console.log('fields:', fields); | ||
| 325 | + | ||
| 326 | + if (!customer_coords) { | ||
| 288 | alert("Bạn phải map customer_name (quét/select) trước khi lưu."); | 327 | alert("Bạn phải map customer_name (quét/select) trước khi lưu."); |
| 289 | return; | 328 | return; |
| 290 | } | 329 | } |
| 291 | 330 | ||
| 292 | - // build fields array: lấy những box có field gán, map unique by field name -> sử dụng field_xy | ||
| 293 | - const fieldsByName = {}; | ||
| 294 | - this.ocrData.forEach(box => { | ||
| 295 | - if (box.field && !box.isDeleted) { | ||
| 296 | - // chỉ giữ 1 bản ghi cuối cùng cho mỗi field (box gần nhất) | ||
| 297 | - fieldsByName[box.field] = { | ||
| 298 | - name: box.field, | ||
| 299 | - xy: box.field_xy || '' | ||
| 300 | - }; | ||
| 301 | - } | ||
| 302 | - }); | ||
| 303 | - // convert to array | ||
| 304 | - const fields = Object.values(fieldsByName); | ||
| 305 | - | ||
| 306 | const payload = { | 331 | const payload = { |
| 307 | - customer_name_text: this.formData.customer_name || '', | 332 | + customer_name_text: customer_name || '', |
| 308 | template_name: this.formData.template_name || this.formData.customer_name, | 333 | template_name: this.formData.template_name || this.formData.customer_name, |
| 309 | - customer_name_xy: this.customer_name_xy, | 334 | + customer_name_xy: customer_coords || [], |
| 310 | fields: fields | 335 | fields: fields |
| 311 | }; | 336 | }; |
| 312 | // console.log(fields); | 337 | // console.log(fields); |
| ... | @@ -375,6 +400,7 @@ | ... | @@ -375,6 +400,7 @@ |
| 375 | this.pdfImageUrl = data.pdfImageUrl; | 400 | this.pdfImageUrl = data.pdfImageUrl; |
| 376 | this.formData = data.formData; | 401 | this.formData = data.formData; |
| 377 | this.fieldOptions = data.fieldOptions; | 402 | this.fieldOptions = data.fieldOptions; |
| 403 | + this.dataMapping = data.dataMapping; | ||
| 378 | // Đợi image load xong trước khi xử lý | 404 | // Đợi image load xong trước khi xử lý |
| 379 | if (this.$refs.pdfImage && this.$refs.pdfImage.complete) { | 405 | if (this.$refs.pdfImage && this.$refs.pdfImage.complete) { |
| 380 | this.processLoadedData(); | 406 | this.processLoadedData(); |
| ... | @@ -394,7 +420,6 @@ | ... | @@ -394,7 +420,6 @@ |
| 394 | // Tự động map field cho các box OCR dựa trên formData đã load | 420 | // Tự động map field cho các box OCR dựa trên formData đã load |
| 395 | this.autoMapFieldsFromFormData(); | 421 | this.autoMapFieldsFromFormData(); |
| 396 | // Kiểm tra và sửa lại tọa độ của các box manual | 422 | // Kiểm tra và sửa lại tọa độ của các box manual |
| 397 | - // this.validateManualBoxes(); | ||
| 398 | 423 | ||
| 399 | // Force re-render để đảm bảo các box được vẽ | 424 | // Force re-render để đảm bảo các box được vẽ |
| 400 | this.$nextTick(() => { | 425 | this.$nextTick(() => { |
| ... | @@ -404,20 +429,13 @@ | ... | @@ -404,20 +429,13 @@ |
| 404 | 429 | ||
| 405 | // Tự động map field cho các box OCR dựa trên formData đã load từ DB | 430 | // Tự động map field cho các box OCR dựa trên formData đã load từ DB |
| 406 | autoMapFieldsFromFormData() { | 431 | autoMapFieldsFromFormData() { |
| 407 | - // Duyệt qua tất cả các field trong formData | 432 | + this.manualBoxData = {}; // reset |
| 408 | - Object.keys(this.formData).forEach(fieldName => { | 433 | + Object.keys(this.dataMapping).forEach(fieldName => { |
| 409 | - const fieldValue = this.formData[fieldName]; | 434 | + const { text, coords } = this.dataMapping[fieldName]; |
| 410 | - | 435 | + // Ví dụ: tạo box từ dữ liệu |
| 411 | - // Chỉ xử lý các field có giá trị (không phải template_name) | 436 | + //this.createManualBoxFromDB(fieldName, coords, text); |
| 412 | - if (fieldValue && fieldValue.trim() && fieldName !== 'template_name') { | 437 | + |
| 413 | - // Tìm box OCR phù hợp nhất để map | 438 | + this.manualBoxData[fieldName] = { text, coords }; |
| 414 | - const bestMatchIndex = this.findBestMatchingBox(fieldName, fieldValue); | ||
| 415 | - | ||
| 416 | - if (bestMatchIndex !== -1) { | ||
| 417 | - // Chỉ map field, không set active (không focus) | ||
| 418 | - this.mapFieldToBox(bestMatchIndex, fieldName, fieldValue); | ||
| 419 | - } | ||
| 420 | - } | ||
| 421 | }); | 439 | }); |
| 422 | 440 | ||
| 423 | }, | 441 | }, |
| ... | @@ -512,9 +530,55 @@ | ... | @@ -512,9 +530,55 @@ |
| 512 | }; | 530 | }; |
| 513 | }, | 531 | }, |
| 514 | 532 | ||
| 533 | + // highlightField(field) { | ||
| 534 | + // let idx = -1; | ||
| 535 | + // for (let i = this.ocrData.length - 1; i >= 0; i--) { | ||
| 536 | + // const it = this.ocrData[i]; | ||
| 537 | + // if (!it.isDeleted && it.field === field) { | ||
| 538 | + // idx = i; | ||
| 539 | + // break; | ||
| 540 | + // } | ||
| 541 | + // } | ||
| 542 | + // | ||
| 543 | + // if (idx !== -1) { | ||
| 544 | + // // Set active index (chuyển trạng thái active và màu xanh) | ||
| 545 | + // this.activeIndex = idx; | ||
| 546 | + // // Scroll đến box tương ứng | ||
| 547 | + // this.scrollToBox(idx); | ||
| 548 | + // // Focus vào box để người dùng thấy rõ | ||
| 549 | + // this.focusOnBox(idx); | ||
| 550 | + // } else { | ||
| 551 | + // this.activeIndex = null; | ||
| 552 | + // } | ||
| 553 | + // }, | ||
| 554 | + | ||
| 515 | highlightField(field) { | 555 | highlightField(field) { |
| 556 | + // Xóa tất cả manual box cũ trước khi tạo mới | ||
| 557 | + // this.ocrData = this.ocrData.filter(b => !b.isManual); | ||
| 558 | + | ||
| 559 | + let coords, text; | ||
| 560 | + | ||
| 561 | + // Kiểm tra xem ocrData đã có box nào với field này chưa | ||
| 562 | + const existingBox = this.ocrData.find(b => b.field === field && !b.isManual); | ||
| 563 | + | ||
| 564 | + if (existingBox) { | ||
| 565 | + coords = existingBox.bbox; | ||
| 566 | + text = existingBox.text; | ||
| 567 | + console.log(`Using ocrData for field "${field}":`, coords, text); | ||
| 568 | + } else if (this.manualBoxData[field]) { | ||
| 569 | + // Nếu không có trong ocrData, dùng manualBoxData | ||
| 570 | + coords = this.manualBoxData[field].coords; | ||
| 571 | + text = this.manualBoxData[field].text; | ||
| 572 | + console.log(`Using manualBoxData for field "${field}":`, coords, text); | ||
| 573 | + } | ||
| 574 | + | ||
| 575 | + // Nếu có coords thì tạo manual box | ||
| 576 | + if (coords) { | ||
| 577 | + this.createManualBoxFromDB(field, coords, text); | ||
| 578 | + } | ||
| 579 | + | ||
| 580 | + // Tìm lại index của box vừa tạo để set active | ||
| 516 | let idx = -1; | 581 | let idx = -1; |
| 517 | - console.log(`Highlighting field: ${field}`); | ||
| 518 | for (let i = this.ocrData.length - 1; i >= 0; i--) { | 582 | for (let i = this.ocrData.length - 1; i >= 0; i--) { |
| 519 | const it = this.ocrData[i]; | 583 | const it = this.ocrData[i]; |
| 520 | if (!it.isDeleted && it.field === field) { | 584 | if (!it.isDeleted && it.field === field) { |
| ... | @@ -522,32 +586,27 @@ | ... | @@ -522,32 +586,27 @@ |
| 522 | break; | 586 | break; |
| 523 | } | 587 | } |
| 524 | } | 588 | } |
| 525 | - console.log('ssss', idx, this.ocrData[idx]); | ||
| 526 | 589 | ||
| 527 | if (idx !== -1) { | 590 | if (idx !== -1) { |
| 528 | - // Set active index (chuyển trạng thái active và màu xanh) | ||
| 529 | this.activeIndex = idx; | 591 | this.activeIndex = idx; |
| 530 | - // Scroll đến box tương ứng | ||
| 531 | this.scrollToBox(idx); | 592 | this.scrollToBox(idx); |
| 532 | - // Focus vào box để người dùng thấy rõ | ||
| 533 | this.focusOnBox(idx); | 593 | this.focusOnBox(idx); |
| 534 | } else { | 594 | } else { |
| 535 | this.activeIndex = null; | 595 | this.activeIndex = null; |
| 536 | } | 596 | } |
| 537 | }, | 597 | }, |
| 538 | 598 | ||
| 599 | + | ||
| 539 | // Scroll đến box tương ứng | 600 | // Scroll đến box tương ứng |
| 540 | scrollToBox(index) { | 601 | scrollToBox(index) { |
| 541 | if (!this.$refs.pdfContainer || index < 0 || index >= this.ocrData.length) return; | 602 | if (!this.$refs.pdfContainer || index < 0 || index >= this.ocrData.length) return; |
| 542 | 603 | ||
| 543 | const item = this.ocrData[index]; | 604 | const item = this.ocrData[index]; |
| 544 | - console.log(`Scrolling to box at index ${index}:`, item); | ||
| 545 | if (!item || item.isDeleted) return; | 605 | if (!item || item.isDeleted) return; |
| 546 | 606 | ||
| 547 | // Tính vị trí hiển thị của box | 607 | // Tính vị trí hiển thị của box |
| 548 | - // const [x1, y1, x2, y2] = item.bbox; | 608 | + const [x1, y1, x2, y2] = item.bbox; |
| 549 | - const [x1, y1, x2, y2] = item.field_xy.split(',').map(Number); | 609 | + //const [x1, y1, x2, y2] = item.field_xy.split(',').map(Number); |
| 550 | - console.log(`Box coordinates for scrolling: [${x1}, ${y1}, ${x2}, ${y2}]`); | ||
| 551 | if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return; | 610 | if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return; |
| 552 | 611 | ||
| 553 | const displayedWidth = this.$refs.pdfImage.clientWidth; | 612 | const displayedWidth = this.$refs.pdfImage.clientWidth; |
| ... | @@ -587,9 +646,6 @@ | ... | @@ -587,9 +646,6 @@ |
| 587 | const boxElement = document.querySelector(`[data-field="${item.field}"]`); | 646 | const boxElement = document.querySelector(`[data-field="${item.field}"]`); |
| 588 | if (boxElement) { | 647 | if (boxElement) { |
| 589 | boxElement.classList.add('focus-highlight'); | 648 | boxElement.classList.add('focus-highlight'); |
| 590 | - setTimeout(() => { | ||
| 591 | - boxElement.classList.remove('focus-highlight'); | ||
| 592 | - }, 2000); | ||
| 593 | } | 649 | } |
| 594 | }); | 650 | }); |
| 595 | }, | 651 | }, |
| ... | @@ -683,6 +739,7 @@ | ... | @@ -683,6 +739,7 @@ |
| 683 | 739 | ||
| 684 | e.stopPropagation(); | 740 | e.stopPropagation(); |
| 685 | e.preventDefault(); | 741 | e.preventDefault(); |
| 742 | + | ||
| 686 | } | 743 | } |
| 687 | , | 744 | , |
| 688 | applyMapping() { | 745 | applyMapping() { |
| ... | @@ -707,9 +764,6 @@ | ... | @@ -707,9 +764,6 @@ |
| 707 | const manualIndex = this.manualIndex; | 764 | const manualIndex = this.manualIndex; |
| 708 | 765 | ||
| 709 | const newBbox = this.ocrData[manualIndex].bbox; | 766 | 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); | ||
| 713 | 767 | ||
| 714 | let combinedText = []; | 768 | let combinedText = []; |
| 715 | let foundItems = []; | 769 | let foundItems = []; |
| ... | @@ -725,8 +779,6 @@ | ... | @@ -725,8 +779,6 @@ |
| 725 | } | 779 | } |
| 726 | }); | 780 | }); |
| 727 | 781 | ||
| 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) | 782 | // 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) => { | 783 | foundItems.sort((a, b) => { |
| 732 | // Ưu tiên theo Y trước (hàng), sau đó theo X (cột) | 784 | // Ưu tiên theo Y trước (hàng), sau đó theo X (cột) |
| ... | @@ -745,6 +797,8 @@ | ... | @@ -745,6 +797,8 @@ |
| 745 | // console.log('Combined text:', finalText); | 797 | // console.log('Combined text:', finalText); |
| 746 | 798 | ||
| 747 | // Gán field và text cho box manual | 799 | // Gán field và text cho box manual |
| 800 | + console.log(`Assigning manual field "${this.manualField}" to box at index ${manualIndex} with text: "${finalText}"`); | ||
| 801 | + // console.log(this.ocrData[manualIndex]); | ||
| 748 | this.assignFieldToBox(manualIndex, this.manualField, finalText); | 802 | this.assignFieldToBox(manualIndex, this.manualField, finalText); |
| 749 | 803 | ||
| 750 | // Reset trạng thái chọn | 804 | // Reset trạng thái chọn |
| ... | @@ -794,18 +848,7 @@ | ... | @@ -794,18 +848,7 @@ |
| 794 | const startIndex = Math.floor(startRatio * text.length); | 848 | const startIndex = Math.floor(startRatio * text.length); |
| 795 | const endIndex = Math.ceil(endRatio * text.length); | 849 | const endIndex = Math.ceil(endRatio * text.length); |
| 796 | 850 | ||
| 797 | - const partialText = text.substring(startIndex, endIndex).trim(); | 851 | + return 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; | ||
| 809 | }, | 852 | }, |
| 810 | getSelectStyle(item) { | 853 | getSelectStyle(item) { |
| 811 | if (!this.imageWidth) return { position: 'absolute' }; | 854 | if (!this.imageWidth) return { position: 'absolute' }; |
| ... | @@ -824,133 +867,6 @@ | ... | @@ -824,133 +867,6 @@ |
| 824 | }; | 867 | }; |
| 825 | }, | 868 | }, |
| 826 | 869 | ||
| 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 | 870 | // Tạo box manual từ tọa độ trong DB |
| 955 | createManualBoxFromDB(fieldName, coordinates, text) { | 871 | createManualBoxFromDB(fieldName, coordinates, text) { |
| 956 | if (!this.imageWidth || !this.imageHeight) { | 872 | if (!this.imageWidth || !this.imageHeight) { |
| ... | @@ -971,13 +887,6 @@ | ... | @@ -971,13 +887,6 @@ |
| 971 | 887 | ||
| 972 | const [x1, y1, x2, y2] = coords; | 888 | const [x1, y1, x2, y2] = coords; |
| 973 | 889 | ||
| 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 | 890 | // Kiểm tra tọa độ có hợp lệ không |
| 982 | if (x1 >= 0 && y1 >= 0 && x2 > x1 && y2 > y1 && | 891 | if (x1 >= 0 && y1 >= 0 && x2 > x1 && y2 > y1 && |
| 983 | x2 <= this.imageWidth && y2 <= this.imageHeight) { | 892 | x2 <= this.imageWidth && y2 <= this.imageHeight) { |
| ... | @@ -988,13 +897,13 @@ | ... | @@ -988,13 +897,13 @@ |
| 988 | bbox: coords, | 897 | bbox: coords, |
| 989 | field: fieldName, | 898 | field: fieldName, |
| 990 | isManual: true, | 899 | isManual: true, |
| 991 | - showDelete: true, | 900 | + showDelete: false, |
| 992 | isDeleted: false, | 901 | isDeleted: false, |
| 993 | - hideBorder: false | 902 | + hideBorder: true |
| 994 | }; | 903 | }; |
| 995 | 904 | ||
| 996 | this.ocrData.push(manualBox); | 905 | this.ocrData.push(manualBox); |
| 997 | - console.log('Manual box created successfully:', manualBox); | 906 | + // console.log('Manual box created successfully:', manualBox); |
| 998 | 907 | ||
| 999 | // Force re-render | 908 | // Force re-render |
| 1000 | this.$forceUpdate(); | 909 | this.$forceUpdate(); | ... | ... |
-
Please register or sign in to post a comment