tien_nemo

css drag drop

body {
font-family: sans-serif; background: #f5f5f5;
}
#app {
display: flex; gap: 20px; padding: 20px;
}
.left-panel {
width: 500px; background: #fff; padding: 15px;
border-radius: 8px; box-shadow: 0 0 5px rgba(0,0,0,0.1);
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
font-weight: bold; display: block; margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 6px;
border: 1px solid #ccc;
border-radius: 4px;
}
.right-panel {
flex: 1;
position: relative;
background: #eee;
border-radius: 8px;
overflow: hidden;
user-select: none;
}
.pdf-container {
position: relative; display: inline-block;
}
.bbox {
position: absolute;
border: 2px solid #ff5252;
/*background-color: rgba(255, 82, 82, 0.2);*/
cursor: pointer;
}
.bbox.active {
border-color: #199601 !important;
background-color: rgba(25, 150, 1, 0.4) !important;
}
@keyframes focusPulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
select {
position: absolute;
z-index: 10;
background: #fff;
border: 1px solid #ccc;
}
.select-box {
position: absolute;
/*border: 2px dashed #2196F3;*/
background-color: rgba(33, 150, 243, 0.2);
pointer-events: none;
z-index: 5;
}
.delete-btn {
position: absolute;
top: 50%;
right: -40px;
transform: translateY(-50%);
color: #ff4d4d;
border: none;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
padding: 3px 6px;
z-index: 20;
background: white;
}
.edge {
position: absolute;
z-index: 25;
}
.edge.top, .edge.bottom {
height: 8px;
cursor: ns-resize;;
}
.edge.left, .edge.right {
width: 8px;
cursor: ew-resize;
}
.edge.top {
top: -4px;
left: 0;
right: 0;
}
.edge.bottom {
bottom: -4px;
left: 0;
right: 0;
}
.edge.left {
top: 0;
bottom: 0;
left: -4px;
}
.edge.right {
top: 0;
bottom: 0;
right: -4px;
}
.corner {
position: absolute;
width: 14px;
height: 14px;
background: transparent;
border: 2px solid transparent; /* mặc định trong suốt, chỉ tô 2 cạnh */
z-index: 30;
opacity: .95;
transition: border-width .08s ease, transform .08s ease, opacity .08s ease;
pointer-events: auto; /* bắt sự kiện kéo resize */
}
/* Mỗi góc hiện 2 cạnh + bo tròn đúng góc */
.corner.nw {
top: -8px; left: -8px;
/*border-left-color: var(--corner-color);*/
/*border-top-color: var(--corner-color);*/
border-top-left-radius: 6px;
cursor: nwse-resize;
}
.corner.ne {
top: -8px; right: -8px;
/*border-right-color: var(--corner-color);*/
/*border-top-color: var(--corner-color);*/
border-top-right-radius: 6px;
cursor: nesw-resize;
}
.corner.sw {
bottom: -8px; left: -8px;
/*border-left-color: var(--corner-color);*/
/*border-bottom-color: var(--corner-color);*/
border-bottom-left-radius: 6px;
cursor: nesw-resize;
}
.corner.se {
bottom: -8px; right: -8px;
/*border-right-color: var(--corner-color);*/
/*border-bottom-color: var(--corner-color);*/
border-bottom-right-radius: 6px;
cursor: nwse-resize;
}
/* Hiệu ứng khi hover – dày hơn, rõ hơn */
.corner:hover {
border-width: 3px;
opacity: 1;
transform: scale(1.02);
}
<html lang="en"><head>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OCR Mapping with Manual Select Tool</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<style>
body { font-family: sans-serif; background: #f5f5f5; }
#app { display: flex; gap: 20px; padding: 20px; }
.left-panel {
width: 500px; background: #fff; padding: 15px;
border-radius: 8px; box-shadow: 0 0 5px rgba(0,0,0,0.1);
}
.form-group { margin-bottom: 15px; }
.form-group label { font-weight: bold; display: block; margin-bottom: 5px; }
.form-group input { width: 100%; padding: 6px; border: 1px solid #ccc; border-radius: 4px; }
.right-panel { flex: 1; position: relative; background: #eee; border-radius: 8px; overflow: hidden; user-select: none; }
.pdf-container { position: relative; display: inline-block; }
.bbox {
position: absolute;
border: 2px solid #ff5252;
/*background-color: rgba(255, 82, 82, 0.2);*/
cursor: pointer;
}
.bbox.active {
border-color: #199601 !important;
background-color: rgba(25, 150, 1, 0.4) !important;
}
@keyframes focusPulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
select {
position: absolute;
z-index: 10;
background: #fff;
border: 1px solid #ccc;
}
.select-box {
position: absolute;
/*border: 2px dashed #2196F3;*/
background-color: rgba(33, 150, 243, 0.2);
pointer-events: none;
z-index: 5;
}
.delete-btn {
position: absolute;
bottom: -10px;
right: -10px;
background: #ff4d4d;
color: #fff;
border: none;
border-radius: 50%;
cursor: pointer;
font-size: 14px;
padding: 3px 6px;
z-index: 20;
}
</style>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ asset('css/ocr.css') }}">
</head>
<body>
<meta name="csrf-token" content="{{ csrf_token() }}">
......@@ -90,9 +37,21 @@
:style="getBoxStyle(item, index)"
@click="onBoxClick(index)">
<div class="edge top" data-handle="top"></div>
<div class="edge right" data-handle="right"></div>
<div class="edge bottom" data-handle="bottom"></div>
<div class="edge left" data-handle="left"></div>
<!-- 4 góc -->
<div class="corner nw" data-handle="nw"></div>
<div class="corner ne" data-handle="ne"></div>
<div class="corner sw" data-handle="sw"></div>
<div class="corner se" data-handle="se"></div>
<button v-if="item.isManual && item.showDelete"
class="delete-btn"
@click.stop="deleteBox(index)">🗑</button>
@click.stop="deleteBox(index)">
<i class="fa-solid fa-delete-left"></i>
</button>
</div>
......