Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Satini_pvduc
/
ocrpdf
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Authored by
tien_nemo
2025-08-12 22:41:54 +0700
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
Commit
63ba4a033ce95fbbb7b1ce43bb82f11c6d987ec1
63ba4a03
1 parent
da79043d
test load data
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
216 additions
and
6 deletions
app/Http/Controllers/OcrController.php
resources/views/ocr/index.blade.php
app/Http/Controllers/OcrController.php
View file @
63ba4a0
...
...
@@ -20,7 +20,7 @@ class OcrController extends Controller
$request
->
validate
([
'customer_name_text'
=>
'required|string'
,
'customer_name_xy'
=>
'required|string'
,
't
pl_name'
=>
'unique:mst_templat
e'
,
't
emplate_name'
=>
'unique:mst_template,tpl_nam
e'
,
]);
// Lưu vào bảng mst_template
...
...
@@ -50,7 +50,7 @@ class OcrController extends Controller
$imgPath
=
(
"image/data_picking_detail_1754967679.jpg"
);
$templateName
=
'nemo
_4
'
;
$templateName
=
'nemo'
;
/// Lấy từ request hoặc mặc định
...
...
resources/views/ocr/index.blade.php
View file @
63ba4a0
...
...
@@ -21,8 +21,20 @@
cursor
:
pointer
;
}
.bbox.active
{
/*border-color: #2196F3;*/
background-color
:
rgb
(
33
243
132
/
30%
);
border-color
:
#199601
!important
;
background-color
:
rgba
(
25
,
150
,
1
,
0.4
)
!important
;
}
.bbox.focus-highlight
{
animation
:
focusPulse
2s
ease-in-out
;
border-color
:
#ff6b35
!important
;
background-color
:
rgba
(
255
,
107
,
53
,
0.4
)
!important
;
}
@keyframes
focusPulse
{
0
%
{
transform
:
scale
(
1
);
}
50
%
{
transform
:
scale
(
1.05
);
}
100
%
{
transform
:
scale
(
1
);
}
}
select
{
position
:
absolute
;
...
...
@@ -120,6 +132,7 @@
<label>
@{{ field.label }}
</label>
<input
v-model=
"formData[field.value]"
@
focus=
"highlightField(field.value)"
@
click=
"onInputClick(field.value)"
:readonly=
"field.value === 'customer_name' && !hasCustomerNameXY"
>
</div>
...
...
@@ -167,6 +180,56 @@
}
},
methods
:
{
// Map field cho box (không set active, chỉ dùng để load data từ DB)
mapFieldToBox
(
index
,
fieldName
,
text
=
null
)
{
if
(
index
==
null
)
return
;
// Xóa fieldName ở box khác
this
.
ocrData
.
forEach
((
box
,
i
)
=>
{
if
(
i
!==
index
&&
box
.
field
===
fieldName
)
{
box
.
field
=
null
;
box
.
field_xy
=
null
;
}
});
// Nếu box này từng gán field khác thì bỏ
const
prev
=
this
.
ocrData
[
index
].
field
;
if
(
prev
&&
prev
!==
fieldName
)
{
if
(
prev
===
'customer_name'
)
{
this
.
hasCustomerNameXY
=
false
;
this
.
customer_name_xy
=
''
;
}
this
.
ocrData
[
index
].
field
=
null
;
this
.
ocrData
[
index
].
field_xy
=
null
;
}
// Gán field mới
const
bbox
=
this
.
ocrData
[
index
].
bbox
;
// tọa độ OCR gốc [x1, y1, x2, y2]
console
.
log
(
'1111111111111111'
,
bbox
);
const
x1
=
bbox
[
0
];
const
y1
=
bbox
[
1
];
const
w
=
bbox
[
2
];
const
h
=
bbox
[
3
];
const
xyStr
=
`
${
x1
}
,
${
y1
}
,
${
w
}
,
${
h
}
`
;
this
.
ocrData
[
index
].
field
=
fieldName
;
this
.
ocrData
[
index
].
field_xy
=
xyStr
;
// Set text
this
.
formData
[
fieldName
]
=
(
text
!==
null
?
text
:
(
this
.
ocrData
[
index
].
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
;
}
},
// Assign field và set active (dùng khi user tương tác)
assignFieldToBox
(
index
,
fieldName
,
text
=
null
)
{
if
(
index
==
null
)
return
;
...
...
@@ -206,7 +269,7 @@
// Set text
this
.
formData
[
fieldName
]
=
(
text
!==
null
?
text
:
(
this
.
ocrData
[
index
].
text
||
''
)).
trim
();
// Active index
// Active index
(focus vào box này)
this
.
activeIndex
=
index
;
// Nếu là customer_name
...
...
@@ -309,6 +372,77 @@
this
.
pdfImageUrl
=
data
.
pdfImageUrl
;
this
.
formData
=
data
.
formData
;
this
.
fieldOptions
=
data
.
fieldOptions
;
// Tự động map field cho các box OCR dựa trên formData đã load
this
.
autoMapFieldsFromFormData
();
},
// Tự động map field cho các box OCR dựa trên formData đã load từ DB
autoMapFieldsFromFormData
()
{
// Duyệt qua tất cả các field trong formData
Object
.
keys
(
this
.
formData
).
forEach
(
fieldName
=>
{
const
fieldValue
=
this
.
formData
[
fieldName
];
// Chỉ xử lý các field có giá trị (không phải template_name)
if
(
fieldValue
&&
fieldValue
.
trim
()
&&
fieldName
!==
'template_name'
)
{
// Tìm box OCR phù hợp nhất để map
const
bestMatchIndex
=
this
.
findBestMatchingBox
(
fieldName
,
fieldValue
);
if
(
bestMatchIndex
!==
-
1
)
{
// Chỉ map field, không set active (không focus)
this
.
mapFieldToBox
(
bestMatchIndex
,
fieldName
,
fieldValue
);
}
}
});
},
// Tìm box OCR phù hợp nhất để map với field
findBestMatchingBox
(
fieldName
,
fieldValue
)
{
let
bestMatchIndex
=
-
1
;
let
bestScore
=
0
;
this
.
ocrData
.
forEach
((
item
,
index
)
=>
{
if
(
item
.
isDeleted
)
return
;
// Nếu box này đã được map field khác, bỏ qua
if
(
item
.
field
&&
item
.
field
!==
fieldName
)
return
;
// Tính điểm phù hợp dựa trên text
const
text
=
item
.
text
||
''
;
const
score
=
this
.
calculateTextSimilarity
(
text
,
fieldValue
);
if
(
score
>
bestScore
)
{
bestScore
=
score
;
bestMatchIndex
=
index
;
}
});
// Chỉ map nếu điểm phù hợp đủ cao (ví dụ > 0.5)
return
bestScore
>
0.5
?
bestMatchIndex
:
-
1
;
},
// Tính điểm tương đồng giữa 2 text
calculateTextSimilarity
(
text1
,
text2
)
{
if
(
!
text1
||
!
text2
)
return
0
;
const
t1
=
text1
.
toLowerCase
().
trim
();
const
t2
=
text2
.
toLowerCase
().
trim
();
// Nếu text giống hệt nhau
if
(
t1
===
t2
)
return
1.0
;
// Nếu một text là subset của text kia
if
(
t1
.
includes
(
t2
)
||
t2
.
includes
(
t1
))
return
0.8
;
// Tính điểm dựa trên số ký tự giống nhau
let
commonChars
=
0
;
const
minLength
=
Math
.
min
(
t1
.
length
,
t2
.
length
);
for
(
let
i
=
0
;
i
<
minLength
;
i
++
)
{
if
(
t1
[
i
]
===
t2
[
i
])
commonChars
++
;
}
return
commonChars
/
Math
.
max
(
t1
.
length
,
t2
.
length
);
},
onImageLoad
()
{
const
img
=
this
.
$refs
.
pdfImage
;
...
...
@@ -348,7 +482,83 @@
break
;
}
}
this
.
activeIndex
=
idx
===
-
1
?
null
:
idx
;
if
(
idx
!==
-
1
)
{
// Set active index (chuyển trạng thái active và màu xanh)
this
.
activeIndex
=
idx
;
// Scroll đến box tương ứng
this
.
scrollToBox
(
idx
);
// Focus vào box để người dùng thấy rõ
this
.
focusOnBox
(
idx
);
}
else
{
this
.
activeIndex
=
null
;
}
},
// Scroll đến box tương ứng
scrollToBox
(
index
)
{
if
(
!
this
.
$refs
.
pdfContainer
||
index
<
0
||
index
>=
this
.
ocrData
.
length
)
return
;
const
item
=
this
.
ocrData
[
index
];
if
(
!
item
||
item
.
isDeleted
)
return
;
// Tính vị trí hiển thị của box
const
[
x1
,
y1
,
x2
,
y2
]
=
item
.
bbox
;
if
(
!
this
.
imageWidth
||
!
this
.
imageHeight
||
!
this
.
$refs
.
pdfImage
)
return
;
const
displayedWidth
=
this
.
$refs
.
pdfImage
.
clientWidth
;
const
displayedHeight
=
this
.
$refs
.
pdfImage
.
clientHeight
;
const
scaleX
=
displayedWidth
/
this
.
imageWidth
;
const
scaleY
=
displayedHeight
/
this
.
imageHeight
;
const
displayX
=
Math
.
round
(
x1
*
scaleX
);
const
displayY
=
Math
.
round
(
y1
*
scaleY
);
// Scroll đến vị trí box
const
container
=
this
.
$refs
.
pdfContainer
;
const
containerRect
=
container
.
getBoundingClientRect
();
const
scrollTop
=
container
.
scrollTop
;
const
scrollLeft
=
container
.
scrollLeft
;
// Tính vị trí scroll để box nằm ở giữa viewport
const
targetScrollTop
=
scrollTop
+
displayY
-
(
containerRect
.
height
/
2
);
const
targetScrollLeft
=
scrollLeft
+
displayX
-
(
containerRect
.
width
/
2
);
container
.
scrollTo
({
top
:
Math
.
max
(
0
,
targetScrollTop
),
left
:
Math
.
max
(
0
,
targetScrollLeft
),
behavior
:
'smooth'
});
},
// Focus vào box (thêm hiệu ứng nhấp nháy)
focusOnBox
(
index
)
{
if
(
index
<
0
||
index
>=
this
.
ocrData
.
length
)
return
;
const
item
=
this
.
ocrData
[
index
];
if
(
!
item
||
item
.
isDeleted
)
return
;
// Thêm class để tạo hiệu ứng focus
this
.
$nextTick
(()
=>
{
const
boxElement
=
document
.
querySelector
(
`[data-field="
${
item
.
field
}
"]`
);
if
(
boxElement
)
{
boxElement
.
classList
.
add
(
'focus-highlight'
);
setTimeout
(()
=>
{
boxElement
.
classList
.
remove
(
'focus-highlight'
);
},
2000
);
}
});
},
// Xử lý khi click vào input
onInputClick
(
fieldName
)
{
// Kiểm tra xem field này có data không
const
fieldValue
=
this
.
formData
[
fieldName
];
if
(
fieldValue
&&
fieldValue
.
trim
())
{
// Nếu có data, highlight và focus vào box tương ứng
// Chỉ khi click vào input mới focus và chuyển trạng thái active
this
.
highlightField
(
fieldName
);
}
},
startSelect
(
e
)
{
if
(
this
.
isMappingManually
||
e
.
button
!==
0
)
return
;
...
...
Please
register
or
sign in
to post a comment