luo-image-annotator 0.0.4 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  一个基于 Vue 3 和 Canvas 的图片标注组件库,支持矩形、多边形、关键点、旋转矩形等多种标注类型,并提供批量标注和预览功能。
4
4
 
5
+ ## 快速开始(3分钟)
6
+
7
+ 1. 安装组件
8
+
9
+ ```bash
10
+ npm install luo-image-annotator
11
+ ```
12
+
13
+ 2. 在入口文件引入组件和样式
14
+
15
+ ```ts
16
+ import { createApp } from 'vue'
17
+ import App from './App.vue'
18
+ import { BatchAnnotator } from 'luo-image-annotator'
19
+ import 'luo-image-annotator/style.css'
20
+
21
+ const app = createApp(App)
22
+ app.component('BatchAnnotator', BatchAnnotator)
23
+ app.mount('#app')
24
+ ```
25
+
26
+ 3. 查看完整平台接入说明
27
+
28
+ - 本地:`node_modules/luo-image-annotator/组件接入与平台分层说明.md`
29
+ - 在线:`https://unpkg.com/luo-image-annotator@latest/组件接入与平台分层说明.md`
30
+
5
31
  ## 环境要求
6
32
 
7
33
  - **Node.js**: >= 18.0.0
@@ -102,6 +128,20 @@ npm publish --otp=你的恢复码字符串
102
128
  npm install luo-image-annotator
103
129
  ```
104
130
 
131
+ ### 1.1 查看完整接入文档
132
+
133
+ 安装后可直接在项目依赖目录查看完整平台接入说明:
134
+
135
+ ```text
136
+ node_modules/luo-image-annotator/组件接入与平台分层说明.md
137
+ ```
138
+
139
+ 也可以在线查看(当前发布版本):
140
+
141
+ ```text
142
+ https://unpkg.com/luo-image-annotator@latest/组件接入与平台分层说明.md
143
+ ```
144
+
105
145
  ### 2. 引入样式与组件
106
146
 
107
147
  在 `main.ts` 或 `main.js` 中引入样式和组件:
@@ -265,6 +305,29 @@ const onQaIssue = (payload) => {
265
305
  </script>
266
306
  ```
267
307
 
308
+ ### 5. BatchAnnotator 点击行为配置
309
+
310
+ 默认点击某一张图会进入编辑页。如果你希望禁用这个默认跳转并改为业务自定义点击行为,可配置 `clickToEnterEditor=false` 并监听 `imageClick`:
311
+
312
+ ```vue
313
+ <template>
314
+ <BatchAnnotator
315
+ :images="images"
316
+ :labels="labels"
317
+ :clickToEnterEditor="false"
318
+ @imageClick="handleImageClick"
319
+ @update:images="handleImagesUpdate"
320
+ @export="handleExport"
321
+ />
322
+ </template>
323
+
324
+ <script setup lang="ts">
325
+ const handleImageClick = (payload) => {
326
+ console.log(payload.index, payload.image)
327
+ }
328
+ </script>
329
+ ```
330
+
268
331
  ## 组件 API
269
332
 
270
333
  ### BatchAnnotator Props
@@ -273,6 +336,8 @@ const onQaIssue = (payload) => {
273
336
  | --- | --- | --- | --- |
274
337
  | `images` | `Array` | 是 | 图片列表,包含 `imageUrl` 和 `annotations` |
275
338
  | `labels` | `Array` | 是 | 标签定义列表,包含 `id`, `name`, `color`, `visible` |
339
+ | `actionBar` | `Object` | 否 | 底部操作栏配置;按钮未配置时默认不显示 |
340
+ | `clickToEnterEditor` | `boolean` | 否 | 是否允许点击缩略图进入编辑页,默认 `true`;设置 `false` 时不跳转 |
276
341
 
277
342
  ### BatchAnnotator Events
278
343
 
@@ -280,12 +345,13 @@ const onQaIssue = (payload) => {
280
345
  | --- | --- | --- |
281
346
  | `export` | `data: Array` | 点击“导出”按钮时触发,返回最新的图片和标注数据 |
282
347
  | `update:images` | `data: Array` | 当标注数据发生变化时触发,支持 `v-model:images` |
348
+ | `imageClick` | `payload: { index, image }` | 仅在 `clickToEnterEditor=false` 时触发,用于自定义点击图片行为 |
283
349
 
284
350
  ### ImageAnnotator Props(新增能力)
285
351
 
286
352
  | 属性名 | 类型 | 必填 | 描述 |
287
353
  | --- | --- | --- | --- |
288
- | `batchImages` | `Array` | 否 | 批量图片与标注 |
354
+ | `batchImages` | `Array` | 否 | 批量图片与标注,支持为每张图传入 `width/height` 提升缩略图标注对齐精度 |
289
355
  | `image` | `Object` | 否 | 单图模式输入,结构 `{ id, url, meta }` |
290
356
  | `labels` | `Array` | 否 | 标签定义 |
291
357
  | `annotationTypes` | `Array` | 否 | 工具类型列表 |
@@ -313,6 +379,13 @@ const onQaIssue = (payload) => {
313
379
  | `qa:issue` | 质检问题上报 |
314
380
  | `error` | 错误事件 |
315
381
 
382
+ ### 标注交互规则
383
+
384
+ - 仅当工具切换为 `select`(选择)时,才能选中已有标注。
385
+ - 当工具为 `rectangle`、`polygon`、`point`、`rotatedRect` 时,点击画布不会选中已有标注,只会进入绘制流程。
386
+ - 删除按钮仅在 `select` 模式且存在已选中标注时显示。
387
+ - 键盘 `Delete/Backspace` 仅在 `select` 模式且存在已选中标注时生效。
388
+
316
389
  ### ImageAnnotator 实例方法
317
390
 
318
391
  | 方法名 | 参数 | 描述 |
@@ -1 +1 @@
1
- .svg-icon[data-v-3928607b]{display:inline-flex;align-items:center;justify-content:center;width:1em;height:1em;fill:currentColor;font-style:normal;line-height:0;text-align:center;text-transform:none;vertical-align:-.125em;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.svg-icon[data-v-3928607b] svg{width:1em;height:1em;fill:currentColor}.size-small[data-v-3928607b]{font-size:12px}.size-large[data-v-3928607b]{font-size:20px}.annotation-container[data-v-7b013204]{display:flex;height:100%;width:100%;border:1px solid #e0e0e0;background:#f5f5f5;overflow:hidden}.left-sidebar[data-v-7b013204]{width:50px;background:#fff;border-right:1px solid #e0e0e0;display:flex;flex-direction:column;align-items:center;padding:8px 0;gap:8px;z-index:10}.tool-btn[data-v-7b013204]{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border:1px solid transparent;border-radius:4px;cursor:pointer;-webkit-user-select:none;user-select:none;font-size:18px;transition:all .2s}.tool-btn[data-v-7b013204]:hover{background:#f0f0f0}.tool-btn.active[data-v-7b013204]{background:#e3f2fd;border-color:#2196f3;color:#2196f3}.divider[data-v-7b013204]{width:80%;height:1px;background:#ddd;margin:4px 0}.center-area[data-v-7b013204]{flex:1;display:flex;flex-direction:column;position:relative;overflow:hidden}.top-bar[data-v-7b013204]{height:50px;background:#fff;border-bottom:1px solid #e0e0e0;display:flex;align-items:center;padding:0 16px}.label-selector[data-v-7b013204]{display:flex;align-items:center;gap:12px;width:100%;overflow-x:auto}.label-text[data-v-7b013204]{font-size:14px;font-weight:700;color:#555;white-space:nowrap}.tags-row[data-v-7b013204]{display:flex;gap:8px}.tag-chip[data-v-7b013204]{padding:4px 12px;border-radius:16px;font-size:12px;color:#fff;cursor:pointer;border:2px solid transparent;opacity:.7;transition:all .2s;white-space:nowrap}.tag-chip.active[data-v-7b013204]{opacity:1;transform:scale(1.05);box-shadow:0 2px 4px #0003}.canvas-wrapper[data-v-7b013204]{flex:1;background:#333;position:relative;overflow:hidden}.batch-nav[data-v-7b013204]{height:48px;background:#fff;border-top:1px solid #e0e0e0;display:flex;justify-content:center;align-items:center;gap:16px}.batch-nav button[data-v-7b013204]{padding:6px 16px;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;cursor:pointer}.batch-nav button[data-v-7b013204]:hover:not(:disabled){background:#e0e0e0}.right-sidebar[data-v-7b013204]{width:250px;background:#fff;border-left:1px solid #e0e0e0;display:flex;flex-direction:column;z-index:10}.sidebar-header[data-v-7b013204]{padding:16px;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center}.sidebar-header h3[data-v-7b013204]{margin:0;font-size:16px}.add-btn[data-v-7b013204]{padding:4px 8px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}.label-list[data-v-7b013204]{flex:1;overflow-y:auto;padding:8px}.label-item[data-v-7b013204]{margin-bottom:8px;padding:8px;background:#f9f9f9;border-radius:4px;border:1px solid #eee}.label-row[data-v-7b013204]{display:flex;align-items:center;gap:8px}.eye-icon[data-v-7b013204],.delete-icon[data-v-7b013204]{cursor:pointer;font-size:16px;-webkit-user-select:none;user-select:none;opacity:.7}.eye-icon[data-v-7b013204]:hover,.delete-icon[data-v-7b013204]:hover{opacity:1}.color-picker[data-v-7b013204]{width:24px;height:24px;padding:0;border:none;cursor:pointer;background:none}.name-input[data-v-7b013204]{flex:1;border:1px solid transparent;background:transparent;font-size:14px;padding:2px 4px}.name-input[data-v-7b013204]:focus{border-color:#2196f3;background:#fff;outline:none}.color-wrapper[data-v-7b013204]{width:16px;height:16px;border-radius:50%;cursor:pointer;border:1px solid rgba(0,0,0,.1);flex-shrink:0}.label-name[data-v-7b013204]{flex:1;font-size:14px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.action-icon[data-v-7b013204]{cursor:pointer;font-size:16px;-webkit-user-select:none;user-select:none;width:24px;text-align:center;opacity:.6}.action-icon[data-v-7b013204]:hover{opacity:1}.more-actions[data-v-7b013204]{position:relative;display:flex;justify-content:center;align-items:center}.more-actions .delete-btn[data-v-7b013204]{display:none;font-size:14px}.more-actions:hover .dots[data-v-7b013204]{display:none}.more-actions:hover .delete-btn[data-v-7b013204]{display:inline-block;color:#f44336}.modal-overlay[data-v-7b013204]{position:fixed;top:0;left:0;width:100%;height:100%;background:#00000080;display:flex;justify-content:center;align-items:center;z-index:1000}.modal-content[data-v-7b013204]{background:#fff;padding:20px;border-radius:8px;width:300px;box-shadow:0 4px 12px #00000026}.modal-content h3[data-v-7b013204]{margin:0 0 16px;font-size:18px;color:#333}.form-group[data-v-7b013204]{margin-bottom:16px}.form-group label[data-v-7b013204]{display:block;margin-bottom:8px;font-size:14px;color:#666}.modal-input[data-v-7b013204]{width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box}.color-input-wrapper[data-v-7b013204]{display:flex;align-items:center;gap:8px}.modal-color-picker[data-v-7b013204]{width:40px;height:30px;padding:0;border:none;background:none;cursor:pointer}.modal-actions[data-v-7b013204]{display:flex;justify-content:flex-end;gap:12px;margin-top:24px}.cancel-btn[data-v-7b013204]{padding:6px 16px;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;cursor:pointer;color:#666}.confirm-btn[data-v-7b013204]{padding:6px 16px;background:#2196f3;border:none;border-radius:4px;cursor:pointer;color:#fff}.cancel-btn[data-v-7b013204]:hover{background:#e0e0e0}.confirm-btn[data-v-7b013204]:hover{background:#1976d2}.thumbnail-wrapper[data-v-78bcbe0c]{position:relative;width:100%;height:100%;overflow:hidden;border-radius:4px;background:#f0f0f0}.thumbnail-image[data-v-78bcbe0c]{width:100%;height:100%;object-fit:cover;display:block}.annotation-overlay[data-v-78bcbe0c]{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.loading-placeholder[data-v-78bcbe0c]{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:#999;font-size:12px}.anno-label[data-v-78bcbe0c]{paint-order:stroke;stroke:#fff;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round}.batch-annotator[data-v-87f3e002]{width:100%;height:100vh;display:flex;flex-direction:column;background:#f5f5f5}.gallery-view[data-v-87f3e002]{flex:1;display:flex;flex-direction:column;overflow:hidden;padding:20px;position:relative}.gallery-header[data-v-87f3e002]{margin-bottom:20px;display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.gallery-header h3[data-v-87f3e002]{margin:0;font-size:20px;color:#333}.label-summary[data-v-87f3e002]{display:flex;gap:8px}.label-badge[data-v-87f3e002]{padding:4px 10px;border-radius:12px;color:#fff;font-size:12px;font-weight:700}.gallery-grid[data-v-87f3e002]{flex:1;display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:20px;overflow-y:auto;padding-bottom:80px}.gallery-item[data-v-87f3e002]{background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px #0000001a;cursor:pointer;transition:transform .2s,box-shadow .2s;display:flex;flex-direction:column;height:240px}.gallery-item[data-v-87f3e002]:hover{transform:translateY(-4px);box-shadow:0 4px 12px #00000026}.thumbnail-wrapper[data-v-87f3e002]{flex:1;overflow:hidden;position:relative}.img-meta[data-v-87f3e002]{padding:8px;font-size:12px;color:#666;display:flex;justify-content:space-between;background:#fff;border-top:1px solid #eee;height:32px;align-items:center}.bottom-bar[data-v-87f3e002]{position:absolute;bottom:0;left:0;width:100%;height:60px;background:#fff;border-top:1px solid #e0e0e0;display:flex;justify-content:space-between;align-items:center;padding:0 40px;box-shadow:0 -2px 10px #0000000d;z-index:100}.action-btn[data-v-87f3e002]{display:flex;align-items:center;gap:8px;padding:10px 24px;border:none;border-radius:4px;font-size:16px;cursor:pointer;transition:background .2s}.action-btn.primary[data-v-87f3e002]{background:#2196f3;color:#fff}.action-btn.primary[data-v-87f3e002]:hover{background:#1976d2}.action-btn.success[data-v-87f3e002]{background:#4caf50;color:#fff}.action-btn.success[data-v-87f3e002]:hover{background:#388e3c}.editor-view[data-v-87f3e002]{flex:1;display:flex;flex-direction:column;height:100%}.editor-header[data-v-87f3e002]{height:50px;background:#fff;border-bottom:1px solid #e0e0e0;display:flex;align-items:center;padding:0 16px;justify-content:space-between;flex-shrink:0}.header-left[data-v-87f3e002]{display:flex;align-items:center;gap:16px}.back-btn[data-v-87f3e002]{display:flex;align-items:center;gap:4px;background:transparent;border:1px solid #ddd;padding:6px 12px;border-radius:4px;cursor:pointer;font-size:14px;color:#666}.back-btn[data-v-87f3e002]:hover{background:#f5f5f5;color:#333}.editor-content[data-v-87f3e002]{flex:1;overflow:hidden;position:relative}
1
+ .svg-icon[data-v-3928607b]{display:inline-flex;align-items:center;justify-content:center;width:1em;height:1em;fill:currentColor;font-style:normal;line-height:0;text-align:center;text-transform:none;vertical-align:-.125em;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.svg-icon[data-v-3928607b] svg{width:1em;height:1em;fill:currentColor}.size-small[data-v-3928607b]{font-size:12px}.size-large[data-v-3928607b]{font-size:20px}.annotation-container[data-v-22c2957b]{display:flex;height:100%;width:100%;border:1px solid #e0e0e0;background:#f5f5f5;overflow:hidden}.image-list-sidebar[data-v-22c2957b]{width:180px;background:#fafbfc;border-right:1px solid #e0e0e0;display:flex;flex-direction:column}.image-list-title[data-v-22c2957b]{height:44px;display:flex;align-items:center;padding:0 12px;font-size:13px;color:#303133;border-bottom:1px solid #e8edf3;font-weight:600}.image-list-scroll[data-v-22c2957b]{flex:1;padding:8px;display:flex;flex-direction:column;gap:8px;overflow-y:auto}.image-list-item[data-v-22c2957b]{border:1px solid #e4e7ed;border-radius:6px;background:#fff;padding:6px;display:flex;flex-direction:column;gap:6px;cursor:pointer;text-align:left}.image-list-item[data-v-22c2957b]:hover{border-color:#b3d8ff}.image-list-item.active[data-v-22c2957b]{border-color:#409eff;box-shadow:0 0 0 1px #409eff inset}.image-list-stage[data-v-22c2957b]{width:100%;height:78px;position:relative;border-radius:4px;overflow:hidden}.image-list-thumb[data-v-22c2957b]{width:100%;height:100%;object-fit:cover;border-radius:4px}.image-list-text[data-v-22c2957b]{font-size:12px;color:#606266}.thumb-overlay-layer[data-v-22c2957b]{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none}.thumb-overlay-rect[data-v-22c2957b]{position:absolute;border:1px solid;box-sizing:border-box;border-radius:2px}.thumb-overlay-svg[data-v-22c2957b]{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%}.thumb-overlay-point[data-v-22c2957b]{position:absolute;width:8px;height:8px;border-radius:50%;transform:translate(-50%,-50%);box-shadow:0 0 0 1px #fff}.thumb-overlay-rotated[data-v-22c2957b]{position:absolute;border:1px solid;box-sizing:border-box;border-radius:2px;transform-origin:center center}.left-sidebar[data-v-22c2957b]{width:50px;background:#fff;border-right:1px solid #e0e0e0;display:flex;flex-direction:column;align-items:center;padding:8px 0;gap:8px;z-index:10}.tool-btn[data-v-22c2957b]{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border:1px solid transparent;border-radius:4px;cursor:pointer;-webkit-user-select:none;user-select:none;font-size:18px;transition:all .2s}.tool-btn[data-v-22c2957b]:hover{background:#f0f0f0}.tool-btn.active[data-v-22c2957b]{background:#e3f2fd;border-color:#2196f3;color:#2196f3}.divider[data-v-22c2957b]{width:80%;height:1px;background:#ddd;margin:4px 0}.center-area[data-v-22c2957b]{flex:1;display:flex;flex-direction:column;position:relative;overflow:hidden}.top-bar[data-v-22c2957b]{height:50px;background:#fff;border-bottom:1px solid #e0e0e0;display:flex;align-items:center;padding:0 16px}.label-selector[data-v-22c2957b]{display:flex;align-items:center;gap:12px;width:100%;overflow-x:auto}.label-text[data-v-22c2957b]{font-size:14px;font-weight:700;color:#555;white-space:nowrap}.tags-row[data-v-22c2957b]{display:flex;gap:8px}.tag-chip[data-v-22c2957b]{padding:4px 12px;border-radius:16px;font-size:12px;color:#fff;cursor:pointer;border:2px solid transparent;opacity:.7;transition:all .2s;white-space:nowrap}.tag-chip.active[data-v-22c2957b]{opacity:1;transform:scale(1.05);box-shadow:0 2px 4px #0003}.canvas-wrapper[data-v-22c2957b]{flex:1;background:#333;position:relative;overflow:hidden}.batch-nav[data-v-22c2957b]{height:48px;background:#fff;border-top:1px solid #e0e0e0;display:flex;justify-content:center;align-items:center;gap:16px}.batch-nav button[data-v-22c2957b]{padding:6px 16px;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;cursor:pointer}.batch-nav button[data-v-22c2957b]:hover:not(:disabled){background:#e0e0e0}.right-sidebar[data-v-22c2957b]{width:250px;background:#fff;border-left:1px solid #e0e0e0;display:flex;flex-direction:column;z-index:10}.sidebar-header[data-v-22c2957b]{padding:16px;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center}.sidebar-header h3[data-v-22c2957b]{margin:0;font-size:16px}.add-btn[data-v-22c2957b]{padding:4px 8px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}.label-list[data-v-22c2957b]{flex:1;overflow-y:auto;padding:8px}.label-item[data-v-22c2957b]{margin-bottom:8px;padding:8px;background:#f9f9f9;border-radius:4px;border:1px solid #eee}.label-row[data-v-22c2957b]{display:flex;align-items:center;gap:8px}.eye-icon[data-v-22c2957b],.delete-icon[data-v-22c2957b]{cursor:pointer;font-size:16px;-webkit-user-select:none;user-select:none;opacity:.7}.eye-icon[data-v-22c2957b]:hover,.delete-icon[data-v-22c2957b]:hover{opacity:1}.color-picker[data-v-22c2957b]{width:24px;height:24px;padding:0;border:none;cursor:pointer;background:none}.name-input[data-v-22c2957b]{flex:1;border:1px solid transparent;background:transparent;font-size:14px;padding:2px 4px}.name-input[data-v-22c2957b]:focus{border-color:#2196f3;background:#fff;outline:none}.color-wrapper[data-v-22c2957b]{width:16px;height:16px;border-radius:50%;cursor:pointer;border:1px solid rgba(0,0,0,.1);flex-shrink:0}.label-name[data-v-22c2957b]{flex:1;font-size:14px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.action-icon[data-v-22c2957b]{cursor:pointer;font-size:16px;-webkit-user-select:none;user-select:none;width:24px;text-align:center;opacity:.6}.action-icon[data-v-22c2957b]:hover{opacity:1}.more-actions[data-v-22c2957b]{position:relative;display:flex;justify-content:center;align-items:center}.more-actions .delete-btn[data-v-22c2957b]{display:none;font-size:14px}.more-actions:hover .dots[data-v-22c2957b]{display:none}.more-actions:hover .delete-btn[data-v-22c2957b]{display:inline-block;color:#f44336}.modal-overlay[data-v-22c2957b]{position:fixed;top:0;left:0;width:100%;height:100%;background:#00000080;display:flex;justify-content:center;align-items:center;z-index:1000}.modal-content[data-v-22c2957b]{background:#fff;padding:20px;border-radius:8px;width:300px;box-shadow:0 4px 12px #00000026}.modal-content h3[data-v-22c2957b]{margin:0 0 16px;font-size:18px;color:#333}.form-group[data-v-22c2957b]{margin-bottom:16px}.form-group label[data-v-22c2957b]{display:block;margin-bottom:8px;font-size:14px;color:#666}.modal-input[data-v-22c2957b]{width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box}.color-input-wrapper[data-v-22c2957b]{display:flex;align-items:center;gap:8px}.modal-color-picker[data-v-22c2957b]{width:40px;height:30px;padding:0;border:none;background:none;cursor:pointer}.modal-actions[data-v-22c2957b]{display:flex;justify-content:flex-end;gap:12px;margin-top:24px}.cancel-btn[data-v-22c2957b]{padding:6px 16px;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;cursor:pointer;color:#666}.confirm-btn[data-v-22c2957b]{padding:6px 16px;background:#2196f3;border:none;border-radius:4px;cursor:pointer;color:#fff}.cancel-btn[data-v-22c2957b]:hover{background:#e0e0e0}.confirm-btn[data-v-22c2957b]:hover{background:#1976d2}.thumbnail-wrapper[data-v-78bcbe0c]{position:relative;width:100%;height:100%;overflow:hidden;border-radius:4px;background:#f0f0f0}.thumbnail-image[data-v-78bcbe0c]{width:100%;height:100%;object-fit:cover;display:block}.annotation-overlay[data-v-78bcbe0c]{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.loading-placeholder[data-v-78bcbe0c]{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:#999;font-size:12px}.anno-label[data-v-78bcbe0c]{paint-order:stroke;stroke:#fff;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round}.batch-annotator[data-v-bbb647e1]{width:100%;height:100vh;display:flex;flex-direction:column;background:#f5f5f5}.gallery-view[data-v-bbb647e1]{flex:1;display:flex;flex-direction:column;overflow:hidden;padding:20px;position:relative}.gallery-header[data-v-bbb647e1]{margin-bottom:20px;display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.gallery-header h3[data-v-bbb647e1]{margin:0;font-size:20px;color:#333}.label-summary[data-v-bbb647e1]{display:flex;gap:8px}.label-badge[data-v-bbb647e1]{padding:4px 10px;border-radius:12px;color:#fff;font-size:12px;font-weight:700}.gallery-grid[data-v-bbb647e1]{flex:1;display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:20px;overflow-y:auto;padding-bottom:80px}.gallery-item[data-v-bbb647e1]{background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px #0000001a;cursor:pointer;transition:transform .2s,box-shadow .2s;display:flex;flex-direction:column;height:240px}.gallery-item[data-v-bbb647e1]:hover{transform:translateY(-4px);box-shadow:0 4px 12px #00000026}.thumbnail-wrapper[data-v-bbb647e1]{flex:1;overflow:hidden;position:relative}.img-meta[data-v-bbb647e1]{padding:8px;font-size:12px;color:#666;display:flex;justify-content:space-between;background:#fff;border-top:1px solid #eee;height:32px;align-items:center}.bottom-bar[data-v-bbb647e1]{position:absolute;bottom:0;left:0;width:100%;height:60px;background:#fff;border-top:1px solid #e0e0e0;display:flex;justify-content:space-between;align-items:center;padding:0 40px;box-shadow:0 -2px 10px #0000000d;z-index:100}.action-btn[data-v-bbb647e1]{display:flex;align-items:center;gap:8px;padding:10px 24px;border:none;border-radius:4px;font-size:16px;cursor:pointer;transition:background .2s}.action-btn.primary[data-v-bbb647e1]{background:#2196f3;color:#fff}.action-btn.primary[data-v-bbb647e1]:hover{background:#1976d2}.action-btn.success[data-v-bbb647e1]{background:#4caf50;color:#fff}.action-btn.success[data-v-bbb647e1]:hover{background:#388e3c}.editor-view[data-v-bbb647e1]{flex:1;display:flex;flex-direction:column;height:100%}.editor-header[data-v-bbb647e1]{height:50px;background:#fff;border-bottom:1px solid #e0e0e0;display:flex;align-items:center;padding:0 16px;justify-content:space-between;flex-shrink:0}.header-left[data-v-bbb647e1]{display:flex;align-items:center;gap:16px}.back-btn[data-v-bbb647e1]{display:flex;align-items:center;gap:4px;background:transparent;border:1px solid #ddd;padding:6px 12px;border-radius:4px;cursor:pointer;font-size:14px;color:#666}.back-btn[data-v-bbb647e1]:hover{background:#f5f5f5;color:#333}.editor-content[data-v-bbb647e1]{flex:1;overflow:hidden;position:relative}