@koi-br/ocr-web-sdk 1.0.0
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 +754 -0
- package/dist/index.cjs.js +538 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.esm.js +85525 -0
- package/dist/preview/PdfPreview.vue.d.ts +2 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/style.css +1 -0
- package/package.json +52 -0
- package/preview/ImagePreview.vue +235 -0
- package/preview/PdfPreview.vue +2571 -0
- package/preview/docxPreview.vue +216 -0
- package/preview/index.vue +317 -0
- package/preview/ofdPreview.vue +107 -0
- package/preview/tifPreview.vue +362 -0
- package/preview/xlsxPreview.vue +168 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="preview-container"
|
|
4
|
+
@wheel.prevent="handleWheel"
|
|
5
|
+
>
|
|
6
|
+
<!-- 工具栏 -->
|
|
7
|
+
<div class="preview-toolbar preview-toolbar-right">
|
|
8
|
+
<div class="toolbar-group">
|
|
9
|
+
<span class="scale-text">
|
|
10
|
+
{{ Math.round(scale * 100) }}%
|
|
11
|
+
</span>
|
|
12
|
+
<div class="toolbar-divider"></div>
|
|
13
|
+
<a-tooltip mini position="bottom" content="重置">
|
|
14
|
+
<a-button
|
|
15
|
+
size="small"
|
|
16
|
+
type="outline"
|
|
17
|
+
@click="reset"
|
|
18
|
+
>
|
|
19
|
+
<RefreshCcw :size="16" />
|
|
20
|
+
</a-button>
|
|
21
|
+
</a-tooltip>
|
|
22
|
+
<a-tooltip
|
|
23
|
+
v-if="originalId"
|
|
24
|
+
mini
|
|
25
|
+
position="bottom"
|
|
26
|
+
content="查看原图"
|
|
27
|
+
>
|
|
28
|
+
<a-button
|
|
29
|
+
size="small"
|
|
30
|
+
type="outline"
|
|
31
|
+
@click="original"
|
|
32
|
+
>
|
|
33
|
+
<Maximize2 :size="16" />
|
|
34
|
+
</a-button>
|
|
35
|
+
</a-tooltip>
|
|
36
|
+
<a-tooltip mini position="bottom" content="缩小">
|
|
37
|
+
<a-button
|
|
38
|
+
size="small"
|
|
39
|
+
type="outline"
|
|
40
|
+
:disabled="scale <= minScale"
|
|
41
|
+
@click="zoom(-0.1)"
|
|
42
|
+
>
|
|
43
|
+
<ZoomOut :size="16" />
|
|
44
|
+
</a-button>
|
|
45
|
+
</a-tooltip>
|
|
46
|
+
<a-tooltip mini position="bottom" content="放大">
|
|
47
|
+
<a-button
|
|
48
|
+
size="small"
|
|
49
|
+
type="outline"
|
|
50
|
+
:disabled="scale >= maxScale"
|
|
51
|
+
@click="zoom(0.1)"
|
|
52
|
+
>
|
|
53
|
+
<ZoomIn :size="16" />
|
|
54
|
+
</a-button>
|
|
55
|
+
</a-tooltip>
|
|
56
|
+
<a-tooltip
|
|
57
|
+
v-if="isDownload"
|
|
58
|
+
mini
|
|
59
|
+
position="bottom"
|
|
60
|
+
content="下载"
|
|
61
|
+
>
|
|
62
|
+
<a-button
|
|
63
|
+
size="small"
|
|
64
|
+
type="outline"
|
|
65
|
+
@click="emit('download')"
|
|
66
|
+
>
|
|
67
|
+
<Download :size="16" />
|
|
68
|
+
</a-button>
|
|
69
|
+
</a-tooltip>
|
|
70
|
+
<div class="toolbar-divider"></div>
|
|
71
|
+
<a-tooltip mini position="bottom" content="向左旋转">
|
|
72
|
+
<a-button
|
|
73
|
+
size="small"
|
|
74
|
+
type="outline"
|
|
75
|
+
@click="rotateImage('left')"
|
|
76
|
+
>
|
|
77
|
+
<RotateCw :size="16" />
|
|
78
|
+
</a-button>
|
|
79
|
+
</a-tooltip>
|
|
80
|
+
<a-tooltip mini position="bottom" content="向右旋转">
|
|
81
|
+
<a-button
|
|
82
|
+
size="small"
|
|
83
|
+
type="outline"
|
|
84
|
+
@click="rotateImage('right')"
|
|
85
|
+
>
|
|
86
|
+
<RotateCcw :size="16" />
|
|
87
|
+
</a-button>
|
|
88
|
+
</a-tooltip>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div
|
|
93
|
+
class="relative w-full h-full flex items-center justify-center overflow-hidden"
|
|
94
|
+
@mousedown="startPan"
|
|
95
|
+
@mousemove="pan"
|
|
96
|
+
@mouseup="stopPan"
|
|
97
|
+
@mouseleave="stopPan"
|
|
98
|
+
>
|
|
99
|
+
<img
|
|
100
|
+
v-if="url"
|
|
101
|
+
:src="url"
|
|
102
|
+
alt="预览图片"
|
|
103
|
+
:style="{
|
|
104
|
+
transform: `translate(${position.x}px, ${position.y}px) rotate(${rotation}deg) scale(${scale})`,
|
|
105
|
+
cursor: isPanning ? 'grabbing' : 'grab'
|
|
106
|
+
}"
|
|
107
|
+
@contextmenu.prevent
|
|
108
|
+
@mousedown.prevent
|
|
109
|
+
@load="onImageLoad"
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</template>
|
|
114
|
+
|
|
115
|
+
<script setup>
|
|
116
|
+
import { ref } from 'vue';
|
|
117
|
+
import { ZoomIn, ZoomOut, RefreshCcw, RotateCw, Download, Maximize2 } from 'lucide-vue-next';
|
|
118
|
+
|
|
119
|
+
const props = defineProps({
|
|
120
|
+
url: {
|
|
121
|
+
type: String,
|
|
122
|
+
required: true
|
|
123
|
+
},
|
|
124
|
+
minScale: {
|
|
125
|
+
type: Number,
|
|
126
|
+
default: 0.1
|
|
127
|
+
},
|
|
128
|
+
maxScale: {
|
|
129
|
+
type: Number,
|
|
130
|
+
default: 5
|
|
131
|
+
},
|
|
132
|
+
clickStep: {
|
|
133
|
+
type: Number,
|
|
134
|
+
default: 0.25
|
|
135
|
+
},
|
|
136
|
+
wheelStep: {
|
|
137
|
+
type: Number,
|
|
138
|
+
default: 0.1
|
|
139
|
+
},
|
|
140
|
+
originalId: {
|
|
141
|
+
type: String,
|
|
142
|
+
default: ''
|
|
143
|
+
},
|
|
144
|
+
isDownload: {
|
|
145
|
+
type: Boolean,
|
|
146
|
+
default: false
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const emit = defineEmits(['original', 'download']);
|
|
151
|
+
|
|
152
|
+
const rotation = ref(0);
|
|
153
|
+
const scale = ref(1);
|
|
154
|
+
const position = ref({ x: 0, y: 0 });
|
|
155
|
+
const isPanning = ref(false);
|
|
156
|
+
const lastPosition = ref({ x: 0, y: 0 });
|
|
157
|
+
|
|
158
|
+
const rotateImage = direction => {
|
|
159
|
+
if (direction === 'left') {
|
|
160
|
+
rotation.value = (rotation.value - 90) % 360;
|
|
161
|
+
} else {
|
|
162
|
+
rotation.value = (rotation.value + 90) % 360;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const zoom = (delta, isWheel = false) => {
|
|
167
|
+
const step = isWheel ? props.wheelStep : props.clickStep; // 滚轮缩放使用更小的步长
|
|
168
|
+
const newScale = scale.value + delta * step;
|
|
169
|
+
if (newScale <= props.minScale) {
|
|
170
|
+
scale.value = props.minScale;
|
|
171
|
+
} else if (newScale >= props.maxScale) {
|
|
172
|
+
scale.value = props.maxScale;
|
|
173
|
+
} else {
|
|
174
|
+
scale.value = newScale;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const handleWheel = e => {
|
|
179
|
+
e.preventDefault();
|
|
180
|
+
const delta = e.deltaY > 0 ? -1 : 1;
|
|
181
|
+
zoom(delta, true);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const startPan = e => {
|
|
185
|
+
if (e.button !== 0) return;
|
|
186
|
+
isPanning.value = true;
|
|
187
|
+
lastPosition.value = { x: e.clientX, y: e.clientY };
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const pan = e => {
|
|
191
|
+
if (!isPanning.value) return;
|
|
192
|
+
|
|
193
|
+
const deltaX = e.clientX - lastPosition.value.x;
|
|
194
|
+
const deltaY = e.clientY - lastPosition.value.y;
|
|
195
|
+
|
|
196
|
+
position.value = {
|
|
197
|
+
x: position.value.x + deltaX,
|
|
198
|
+
y: position.value.y + deltaY
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
lastPosition.value = { x: e.clientX, y: e.clientY };
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const stopPan = () => {
|
|
205
|
+
isPanning.value = false;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const reset = () => {
|
|
209
|
+
rotation.value = 0;
|
|
210
|
+
scale.value = 1;
|
|
211
|
+
position.value = { x: 0, y: 0 };
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const original = () => {
|
|
215
|
+
emit('original');
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
defineExpose({
|
|
219
|
+
reset
|
|
220
|
+
});
|
|
221
|
+
</script>
|
|
222
|
+
|
|
223
|
+
<style lang="less" scoped>
|
|
224
|
+
// 样式已统一到公共样式文件,这里只保留组件特定样式
|
|
225
|
+
.preview-container .relative {
|
|
226
|
+
flex: 1;
|
|
227
|
+
position: relative;
|
|
228
|
+
width: 100%;
|
|
229
|
+
height: 100%;
|
|
230
|
+
display: flex;
|
|
231
|
+
align-items: center;
|
|
232
|
+
justify-content: center;
|
|
233
|
+
overflow: hidden;
|
|
234
|
+
}
|
|
235
|
+
</style>
|