tien_nemo

format code

......@@ -62,7 +62,6 @@
<body>
<meta name="csrf-token" content="{{ csrf_token() }}">
<div id="app">
<!-- Right: PDF viewer + select tool -->
<div class="right-panel" >
<div class="pdf-container" ref="pdfContainer"
@mousedown="startSelect"
......@@ -120,17 +119,15 @@
</div>
</div>
<!-- Left: Form inputs -->
<div class="left-panel">
<div v-for="field in fieldOptions" :key="field.value" class="form-group">
<label>@{{ field.label }}</label>
<input v-model="formData[field.value]"
@focus="highlightField(field.value)"
@click="onInputClick(field.value)"
@blur="onInputBlur(field.value)"
@blur="removeAllFocus()"
>
{{-- :readonly="field.value === 'customer_name' && !hasCustomerNameXY"--}}
</div>
<button @click="saveTemplate">💾Save</button>
</div>
......@@ -160,7 +157,6 @@
}
},
created() {
// Chỉ tạo formData cho các field cần mapping
this.fieldOptions
.forEach(f => {
this.$set(this.formData, f.value, "");
......@@ -171,7 +167,6 @@
// Thêm event listener để xóa focus khi click ra ngoài
document.addEventListener('click', (e) => {
// Nếu click không phải vào input hoặc box
if (!e.target.closest('.left-panel') && !e.target.closest('.bbox')) {
this.removeAllFocus();
}
......@@ -191,12 +186,11 @@
// Xóa tất cả box có fieldName trùng lặp, chỉ giữ lại box hiện tại
this.ocrData = this.ocrData.filter((box, i) => {
if (i === index) return true; // Giữ lại box hiện tại
if (i === index) return true;
if (box.field === fieldName) {
// Xóa box có field trùng lặp
return false;
}
return true; // Giữ lại các box khác
return true;
});
// Cập nhật lại index sau khi filter
......@@ -214,8 +208,7 @@
this.ocrData[newIndex].field_xy = null;
}
// Gán field mới
const bbox = this.ocrData[newIndex].bbox; // tọa độ OCR gốc [x1, y1, x2, y2]
const bbox = this.ocrData[newIndex].bbox;
const x1 = bbox[0];
const y1 = bbox[1];
......@@ -223,86 +216,17 @@
const h = bbox[3];
const xyStr = `${x1},${y1},${w},${h}`;
this.ocrData[newIndex].field = fieldName;
this.ocrData[newIndex].field_xy = xyStr;
// Set text
this.formData[fieldName] = (text !== null ? text : (this.ocrData[newIndex].text || '')).trim();
// KHÔNG set active index (không focus)
// Nếu là customer_name
if (fieldName === 'customer_name') {
this.hasCustomerNameXY = true;
this.customer_name_xy = xyStr;
}
},
assignFieldToBox(index, fieldName, text = null) {
console.log(`Assigning field "${fieldName}" to box at index ${index} with text: "${text}"`);
if (index == null) return;
// 1. Xóa tất cả box có fieldName trùng lặp, chỉ giữ lại box hiện tại
this.ocrData = this.ocrData.filter((box, i) => {
if (i === index) return true; // Giữ lại box hiện tại
if (box.field === fieldName) {
// Xóa box có field trùng lặp
return false;
}
return true; // Giữ lại các box khác
});
// 2. Cập nhật lại index sau khi filter
const newIndex = this.ocrData.findIndex(box => box.bbox === this.ocrData[index]?.bbox);
if (newIndex === -1) return;
// 3. Nếu box này từng gán field khác thì bỏ
const prev = this.ocrData[newIndex]?.field;
if (prev && prev !== fieldName) {
if (prev === 'customer_name') {
this.hasCustomerNameXY = false;
this.customer_name_xy = '';
}
this.ocrData[newIndex].field = null;
this.ocrData[newIndex].field_xy = null;
}
// 4. Gán field mới
const bbox = this.ocrData[newIndex].bbox; // tọa độ OCR gốc [x1, y1, x2, y2]
console.log('bbox', bbox);
const [x1, y1, w, h] = bbox;
const xyStr = `${x1},${y1},${w},${h}`;
this.ocrData[newIndex].field = fieldName;
this.ocrData[newIndex].field_xy = xyStr;
// 5. Gán text
const finalText = text !== null ? text : (this.ocrData[newIndex].text || '');
this.formData[fieldName] = finalText.trim();
console.log(`formData ${fieldName}`, this.formData[fieldName]);
// Nếu trong manualBoxData tồn tại field này hoặc muốn tạo lại manual box từ ocrData
if (this.manualBoxData[fieldName]) {
// Lấy tọa độ + text từ box hiện tại để tạo manual box mới
this.createManualBoxFromDB(fieldName, this.ocrData[newIndex].bbox, finalText);
// Cập nhật manualBoxData
this.manualBoxData[fieldName] = {
coords: this.ocrData[newIndex].bbox,
text: finalText
};
}
// 6. Active index
this.activeIndex = newIndex;
// 7. Nếu là customer_name
if (fieldName === 'customer_name') {
this.hasCustomerNameXY = true;
this.customer_name_xy = xyStr;
}
},
async saveTemplate() {
let customer_name = null;
......@@ -315,7 +239,6 @@
fields = this.manualBoxData;
console.log('Using manualBoxData for customer_name:', customer_name, customer_coords);
} else {
// Không có → tìm trong ocrData
const found = this.ocrData.find(item => item.field === 'customer_name');
if (found) {
customer_name = found.text;
......@@ -325,20 +248,17 @@
const fieldsByName = {};
this.ocrData.forEach(box => {
if (box.field && !box.isDeleted) {
// chỉ giữ 1 bản ghi cuối cùng cho mỗi field (box gần nhất)
fieldsByName[box.field] = {
text: box.field,
coords: box.field_xy || ''
};
}
});
// // convert to array
fields = (fieldsByName);
console.log('Using ocrData for customer_name:', customer_name, customer_coords);
}
// console.log('fields:', fields);
if (!customer_coords || !customer_name) {
alert("Bạn phải map customer_name (quét/select) trước khi lưu.");
return;
......@@ -350,7 +270,7 @@
customer_name_xy: customer_coords || [],
fields: fields
};
// console.log(fields);
try {
const res = await fetch('/ocr/save-template', {
method: 'POST',
......@@ -377,7 +297,6 @@
const item = this.ocrData[index];
if (item.isManual) {
const manualBbox = item.bbox;
// Hiện lại border các box OCR gốc nằm trong vùng thủ công
this.ocrData.forEach(o => {
if (!o.isManual && this.isBoxInside(o.bbox, manualBbox)) {
......@@ -415,17 +334,8 @@
this.ocrData = data.ocrData;
this.pdfImageUrl = data.pdfImageUrl;
this.fieldOptions = data.fieldOptions;
// this.formData = data.formData;
this.dataMapping = data.dataMapping;
this.is_template = data.is_template;
// Đợi image load xong trước khi xử lý
// if (this.$refs.pdfImage && this.$refs.pdfImage.complete) {
// // this.processLoadedData();
// } else {
// console.log('Image not loaded yet, waiting for onImageLoad');
// // Image sẽ được xử lý trong onImageLoad
// }
} catch (error) {
console.error('Error in loadOCRData:', error);
......@@ -434,16 +344,13 @@
// Xử lý data sau khi image đã load
processLoadedData() {
// Tự động map field cho các box OCR dựa trên formData đã load
this.autoMapFieldsFromFormData();
// Force re-render để đảm bảo các box được vẽ
this.$nextTick(() => {
this.$forceUpdate();
});
},
// Tự động map field cho các box OCR dựa trên formData đã load từ DB
autoMapFieldsFromFormData() {
this.manualBoxData = {}; // reset
......@@ -452,7 +359,6 @@
}
const tolerance = 20; // ngưỡng chenh lech toa do y cho cùng 1 hàng
Object.keys(this.dataMapping).forEach(fieldName => {
let { coords, text } = this.dataMapping[fieldName];
let foundItems = this.ocrData
......@@ -470,13 +376,10 @@
const combinedText = foundItems.map(f => f.text.trim());
const finalText = combinedText.join(" ");
// Lưu vào dataMapping
this.dataMapping[fieldName] = {
text: finalText,
coords: coords
};
// Nếu là template thì gán vào formData
if (this.is_template) {
this.formData[fieldName] = finalText && finalText.trim() !== "" ? finalText : text;
}
......@@ -493,7 +396,6 @@
const img = this.$refs.pdfImage;
this.imageWidth = img.naturalWidth;
this.imageHeight = img.naturalHeight;
// Nếu đã có data, xử lý ngay
if (this.ocrData && this.ocrData.length > 0) {
console.log('Image loaded and data exists, processing now');
this.processLoadedData();
......@@ -555,7 +457,6 @@
}
}
// Nếu có coords thì tạo hoặc hiển thị lại box
if (coords) {
if (isFromDB) {
// Tạo box manual từ DB (không có nút xóa)
......@@ -586,8 +487,6 @@
}
},
// Scroll đến box tương ứng
scrollToBox(index) {
if (!this.$refs.pdfContainer || index < 0 || index >= this.ocrData.length) return;
......@@ -623,8 +522,6 @@
});
},
// Xử lý khi click vào input
onInputClick(fieldName) {
// Kiểm tra xem field này có data không
......@@ -635,12 +532,6 @@
}
},
// Xử lý khi click ra ngoài input (blur)
onInputBlur(fieldName) {
// Khi không focus vào input nào, xóa tất cả focus
this.removeAllFocus();
},
// Xóa tất cả focus
removeAllFocus() {
// Reset active index (chỉ mất màu xanh, không xóa box)
......@@ -779,8 +670,7 @@
e.stopPropagation();
e.preventDefault();
}
,
},
applyMapping() {
const item = this.ocrData[this.selectingIndex];
if (!item) return;
......@@ -790,8 +680,6 @@
box.field = '';
}
});
console.log(`mapping box ${this.selectingIndex} with field "${item.field}" item.isManual: ${item.isManual}`);
if (item.isManual) {
// Nếu là manual box, chuyển sang chế độ manual mapping
this.manualIndex = this.selectingIndex;
......@@ -802,12 +690,9 @@
// Xử lý box OCR (có thể chưa có field hoặc đã có field)
if (item.field) {
// Nếu box đã có field, cập nhật lại
const oldField = item.field;
console.log(`Updating box OCR at index ${this.selectingIndex} from field "${oldField}" to "${item.field}" "${item.bbox}"`);
// Cập nhật formData để hiển thị trong input
this.formData[item.field] = item.text || '';
// Cập nhật manualBoxData với tọa độ mới (box OCR không có nút xóa)
this.manualBoxData[item.field] = {
coords: item.bbox,
......@@ -819,8 +704,6 @@
if (this.dataMapping && this.dataMapping[item.field]) {
delete this.dataMapping[item.field];
}
// Set active index
this.activeIndex = this.selectingIndex;
}
......@@ -871,13 +754,8 @@
const finalText = combinedText.join(" ");
// Gán field trực tiếp cho box manual
this.ocrData[manualIndex].field = this.manualField;
// Cập nhật formData để hiển thị trong input
this.formData[this.manualField] = finalText.trim();
// Cập nhật manualBoxData (box quét chọn có nút xóa)
this.manualBoxData[this.manualField] = {
coords: newBbox,
text: finalText.trim(),
......@@ -926,11 +804,7 @@
getPartialText(text, bbox, selectBbox) {
const [x1, y1, x2, y2] = bbox;
const [sx1, sy1, sx2, sy2] = selectBbox;
// Chiều rộng box OCR
const boxWidth = x2 - x1;
const boxHeight = y2 - y1;
// Vị trí start và end tương đối trong text
let startRatio = Math.max(0, (sx1 - x1) / boxWidth);
let endRatio = Math.min(1, (sx2 - x1) / boxWidth);
......@@ -982,8 +856,6 @@
// Xóa box cũ có cùng fieldName trước khi tạo mới (chỉ xóa manual box)
this.ocrData = this.ocrData.filter(box => !(box.field === fieldName && box.isManual));
// Tạo box manual từ DB (không có nút xóa)
const manualBox = {
text: text || '',
bbox: coords,
......@@ -1045,8 +917,6 @@
};
this.ocrData.push(manualBox);
console.log(`Manual box shown successfully: ${isFromOCR ? 'from OCR (no delete btn)' : 'from manual selection (with delete btn)'}`);
// Force re-render
this.$forceUpdate();
} else {
......