adtec-core-package 3.1.6 → 3.1.8

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.
Files changed (31) hide show
  1. package/adtec-core-package/adtec-core-package.css +1 -0
  2. package/adtec-core-package/adtec-core-package.js +41216 -0
  3. package/adtec-core-package/adtec-core-package.umd.cjs +87 -0
  4. package/adtec-core-package/favicon.ico +0 -0
  5. package/adtec-core-package-3.1.7.tgz +0 -0
  6. package/package/.editorconfig +6 -0
  7. package/package/adtec-core-package/adtec-core-package.css +1 -0
  8. package/package/adtec-core-package/adtec-core-package.js +41216 -0
  9. package/package/adtec-core-package/adtec-core-package.umd.cjs +87 -0
  10. package/package/adtec-core-package/favicon.ico +0 -0
  11. package/package/index.html +13 -0
  12. package/package/prebuilt/umo-editor/favicon.ico +0 -0
  13. package/package/prebuilt/umo-editor/umo-editor.css +1 -0
  14. package/package/public/favicon.ico +0 -0
  15. package/package/src/assets/base.css +86 -0
  16. package/package/src/assets/main.css +35 -0
  17. package/package/src/components/editor-main/src/extensions/bookmark.js +110 -0
  18. package/package.json +4 -2
  19. package/src/api/DocumentApi.ts +11 -1
  20. package/src/components/upload/DocxJsViewer.vue +286 -0
  21. package/src/components/upload/ExcelJsViewer.vue +444 -0
  22. package/src/components/upload/FileView.vue +102 -159
  23. package/src/components/upload/OfficePreview.vue +481 -0
  24. package/src/components/upload/OfficePreviewHeaderBar.vue +315 -0
  25. package/src/components/upload/OfficePreviewToolbar.vue +203 -0
  26. package/src/components/upload/PdfJsViewer.vue +920 -0
  27. package/src/utils/docxPreviewUtil.ts +108 -0
  28. package/src/utils/excelPreviewUtil.ts +307 -0
  29. package/src/utils/officePreviewUtil.ts +122 -0
  30. package/src/utils/pdfSearchUtil.ts +127 -0
  31. package/src/utils/toSameOriginFileUrl.ts +1 -1
@@ -0,0 +1,444 @@
1
+ <template>
2
+ <div class="excel-js-viewer">
3
+ <div ref="scrollRef" class="excel-grid-scroll">
4
+ <div v-if="loading" class="excel-loading">正在解析表格…</div>
5
+ <div v-else-if="sheets.length === 0" class="excel-empty">表格为空或无法解析</div>
6
+ <div v-else class="excel-scale-host" :style="scaleHostStyle">
7
+ <table class="excel-table" :style="tableStyle">
8
+ <thead>
9
+ <tr>
10
+ <th class="excel-corner" />
11
+ <th
12
+ v-for="col in columnHeaders"
13
+ :key="col"
14
+ class="excel-col-header"
15
+ :style="{ minWidth: `${getColWidth(col)}px`, width: `${getColWidth(col)}px` }"
16
+ >
17
+ {{ toExcelColumnLabel(col) }}
18
+ </th>
19
+ </tr>
20
+ </thead>
21
+ <tbody>
22
+ <tr v-for="(row, rowIndex) in activeSheet?.rows || []" :key="rowIndex">
23
+ <th class="excel-row-header">{{ (row[0]?.row ?? rowIndex) + 1 }}</th>
24
+ <td
25
+ v-for="cell in row"
26
+ :key="`${cell.row}-${cell.col}`"
27
+ :rowspan="cell.rowSpan"
28
+ :colspan="cell.colSpan"
29
+ :data-row="cell.row"
30
+ :data-col="cell.col"
31
+ :class="cellClass(cell)"
32
+ :style="cellInlineStyle(cell)"
33
+ @click="selectCell(cell)"
34
+ >
35
+ {{ cell.value }}
36
+ </td>
37
+ </tr>
38
+ </tbody>
39
+ </table>
40
+ </div>
41
+ </div>
42
+
43
+ <div v-if="sheets.length > 1" class="excel-sheet-tabs">
44
+ <button
45
+ v-for="(sheet, index) in sheets"
46
+ :key="sheet.name + index"
47
+ type="button"
48
+ class="excel-sheet-tab"
49
+ :class="{ 'is-active': currentSheet === index + 1 }"
50
+ @click="switchSheet(index + 1)"
51
+ >
52
+ {{ sheet.name }}
53
+ </button>
54
+ </div>
55
+
56
+ <div v-if="sheets.length > 0 && !loading" class="excel-zoom-float">
57
+ <button type="button" class="excel-zoom-btn" title="放大" @click="emit('zoom-in')">+</button>
58
+ <button type="button" class="excel-zoom-btn" title="缩小" @click="emit('zoom-out')">−</button>
59
+ </div>
60
+ </div>
61
+ </template>
62
+
63
+ <script setup lang="ts">
64
+ import { computed, nextTick, ref, shallowRef, watch } from 'vue'
65
+ import {
66
+ type ExcelPreviewCell,
67
+ type ExcelPreviewSheet,
68
+ type ExcelSearchMatch,
69
+ cellStyleToCss,
70
+ parseExcelWorkbook,
71
+ searchExcelSheets,
72
+ toExcelColumnLabel,
73
+ } from '../../utils/excelPreviewUtil.ts'
74
+
75
+ const props = withDefaults(
76
+ defineProps<{
77
+ src?: Blob | ArrayBuffer
78
+ scale?: number
79
+ }>(),
80
+ { scale: 1 },
81
+ )
82
+
83
+ const emit = defineEmits<{
84
+ loaded: []
85
+ error: [err?: unknown]
86
+ 'page-change': [page: number]
87
+ 'zoom-in': []
88
+ 'zoom-out': []
89
+ }>()
90
+
91
+ const scrollRef = ref<HTMLElement | null>(null)
92
+ const sheets = shallowRef<ExcelPreviewSheet[]>([])
93
+ const loading = ref(false)
94
+ const currentSheet = ref(1)
95
+ const searchMatches = ref<ExcelSearchMatch[]>([])
96
+ const activeMatchIndex = ref(-1)
97
+ const selectedCell = ref<{ row: number; col: number } | null>(null)
98
+
99
+ const totalPages = ref(0)
100
+ const activeSheet = computed(() => sheets.value[currentSheet.value - 1])
101
+
102
+ const columnHeaders = computed(() => {
103
+ const sheet = activeSheet.value
104
+ if (!sheet) return []
105
+ const cols: number[] = []
106
+ for (let c = sheet.startCol; c <= sheet.endCol; c++) cols.push(c)
107
+ return cols
108
+ })
109
+
110
+ const userScale = computed(() => props.scale ?? 1)
111
+ const tableStyle = computed(() => ({
112
+ transform: `scale(${userScale.value})`,
113
+ transformOrigin: 'top left',
114
+ }))
115
+ const scaleHostStyle = computed(() => {
116
+ const sheet = activeSheet.value
117
+ if (!sheet) return {}
118
+ const rowCount = sheet.rows.length || 1
119
+ const colCount = columnHeaders.value.length || 1
120
+ const baseWidth = columnHeaders.value.reduce((sum, col) => sum + getColWidth(col), 42)
121
+ const baseHeight = rowCount * 24 + 28
122
+ return {
123
+ width: `${Math.max(baseWidth, colCount * 72 + 42) * userScale.value}px`,
124
+ height: `${baseHeight * userScale.value}px`,
125
+ }
126
+ })
127
+
128
+ const activeMatch = computed(() =>
129
+ activeMatchIndex.value >= 0 ? searchMatches.value[activeMatchIndex.value] : null,
130
+ )
131
+
132
+ const getColWidth = (col: number) => activeSheet.value?.colWidths[col] ?? 72
133
+
134
+ const cellClass = (cell: ExcelPreviewCell) => {
135
+ const classes: string[] = []
136
+ const hasMatch = searchMatches.value.some(
137
+ (m) => m.sheetIndex === currentSheet.value - 1 && m.row === cell.row && m.col === cell.col,
138
+ )
139
+ if (hasMatch) classes.push('excel-cell--match')
140
+ if (
141
+ activeMatch.value &&
142
+ activeMatch.value.sheetIndex === currentSheet.value - 1 &&
143
+ activeMatch.value.row === cell.row &&
144
+ activeMatch.value.col === cell.col
145
+ ) {
146
+ classes.push('excel-cell--active')
147
+ }
148
+ if (
149
+ selectedCell.value &&
150
+ selectedCell.value.row === cell.row &&
151
+ selectedCell.value.col === cell.col
152
+ ) {
153
+ classes.push('excel-cell--selected')
154
+ }
155
+ return classes.length > 0 ? classes : undefined
156
+ }
157
+
158
+ const cellInlineStyle = (cell: ExcelPreviewCell) => {
159
+ const css = cellStyleToCss(cell.style)
160
+ if (cell.width) {
161
+ css.minWidth = `${cell.width}px`
162
+ css.width = `${cell.width}px`
163
+ css.maxWidth = `${Math.max(cell.width, 280)}px`
164
+ }
165
+ return css
166
+ }
167
+
168
+ const selectCell = (cell: ExcelPreviewCell) => {
169
+ selectedCell.value = { row: cell.row, col: cell.col }
170
+ }
171
+
172
+ const switchSheet = async (sheetIndex: number) => {
173
+ await scrollToPage(sheetIndex)
174
+ }
175
+
176
+ const resetViewer = () => {
177
+ sheets.value = []
178
+ totalPages.value = 0
179
+ currentSheet.value = 1
180
+ searchMatches.value = []
181
+ activeMatchIndex.value = -1
182
+ selectedCell.value = null
183
+ loading.value = false
184
+ }
185
+
186
+ const loadWorkbook = async (src: Blob | ArrayBuffer) => {
187
+ loading.value = true
188
+ try {
189
+ sheets.value = await parseExcelWorkbook(src)
190
+ totalPages.value = sheets.value.length
191
+ currentSheet.value = 1
192
+ selectedCell.value = null
193
+ emit('page-change', 1)
194
+ emit('loaded')
195
+ } catch (err) {
196
+ emit('error', err)
197
+ } finally {
198
+ loading.value = false
199
+ }
200
+ }
201
+
202
+ const scrollToCell = async (match: ExcelSearchMatch) => {
203
+ const targetSheet = match.sheetIndex + 1
204
+ if (currentSheet.value !== targetSheet) {
205
+ currentSheet.value = targetSheet
206
+ emit('page-change', targetSheet)
207
+ }
208
+ await nextTick()
209
+ const container = scrollRef.value
210
+ const cellEl = container?.querySelector(
211
+ `td[data-row="${match.row}"][data-col="${match.col}"]`,
212
+ ) as HTMLElement | null
213
+ if (!container || !cellEl) return
214
+ const top = cellEl.offsetTop - container.clientHeight * 0.3
215
+ const left = cellEl.offsetLeft - container.clientWidth * 0.2
216
+ container.scrollTo({ top: Math.max(0, top), left: Math.max(0, left), behavior: 'smooth' })
217
+ }
218
+
219
+ const scrollToPage = async (pageNum: number) => {
220
+ const target = Math.min(Math.max(1, pageNum), totalPages.value || 1)
221
+ currentSheet.value = target
222
+ selectedCell.value = null
223
+ emit('page-change', target)
224
+ await nextTick()
225
+ scrollRef.value?.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
226
+ }
227
+
228
+ const runSearch = async (query: string) => {
229
+ const matches = searchExcelSheets(sheets.value, query)
230
+ searchMatches.value = matches
231
+ if (matches.length === 0) {
232
+ activeMatchIndex.value = -1
233
+ return { total: 0, index: -1 }
234
+ }
235
+ activeMatchIndex.value = 0
236
+ await scrollToCell(matches[0])
237
+ return { total: matches.length, index: 0 }
238
+ }
239
+
240
+ const clearSearch = () => {
241
+ searchMatches.value = []
242
+ activeMatchIndex.value = -1
243
+ }
244
+
245
+ const gotoSearchMatch = async (direction: 1 | -1) => {
246
+ if (searchMatches.value.length === 0) return { total: 0, index: -1 }
247
+ const total = searchMatches.value.length
248
+ let next = activeMatchIndex.value + direction
249
+ if (next < 0) next = total - 1
250
+ if (next >= total) next = 0
251
+ activeMatchIndex.value = next
252
+ await scrollToCell(searchMatches.value[next])
253
+ return { total, index: next }
254
+ }
255
+
256
+ const refitLayout = () => {
257
+ scrollRef.value?.scrollTo({ top: 0, left: 0 })
258
+ }
259
+
260
+ watch(
261
+ () => props.src,
262
+ (src) => {
263
+ resetViewer()
264
+ if (src) void loadWorkbook(src)
265
+ },
266
+ { immediate: true },
267
+ )
268
+
269
+ defineExpose({
270
+ totalPages,
271
+ scrollToPage,
272
+ runSearch,
273
+ clearSearch,
274
+ gotoSearchMatch,
275
+ refitLayout,
276
+ })
277
+ </script>
278
+
279
+ <style scoped lang="scss">
280
+ .excel-js-viewer {
281
+ position: relative;
282
+ display: flex;
283
+ flex: 1;
284
+ flex-direction: column;
285
+ width: 100%;
286
+ min-height: 0;
287
+ background: #e6e6e6;
288
+ }
289
+
290
+ .excel-grid-scroll {
291
+ flex: 1;
292
+ min-height: 0;
293
+ overflow: auto;
294
+ background: #e6e6e6;
295
+ }
296
+
297
+ .excel-scale-host {
298
+ display: inline-block;
299
+ min-width: 100%;
300
+ padding: 0;
301
+ }
302
+
303
+ .excel-table {
304
+ border-collapse: separate;
305
+ border-spacing: 0;
306
+ background: #fff;
307
+ font-size: 12px;
308
+ line-height: 1.35;
309
+ }
310
+
311
+ .excel-corner,
312
+ .excel-col-header,
313
+ .excel-row-header {
314
+ position: sticky;
315
+ z-index: 2;
316
+ background: #f0f0f0;
317
+ color: #444;
318
+ font-weight: 400;
319
+ text-align: center;
320
+ user-select: none;
321
+ }
322
+
323
+ .excel-corner {
324
+ top: 0;
325
+ left: 0;
326
+ z-index: 4;
327
+ width: 42px;
328
+ min-width: 42px;
329
+ border-right: 1px solid #c7c7c7;
330
+ border-bottom: 1px solid #c7c7c7;
331
+ }
332
+
333
+ .excel-col-header {
334
+ top: 0;
335
+ z-index: 3;
336
+ min-width: 72px;
337
+ height: 22px;
338
+ padding: 2px 6px;
339
+ border-right: 1px solid #d4d4d4;
340
+ border-bottom: 1px solid #c7c7c7;
341
+ }
342
+
343
+ .excel-row-header {
344
+ left: 0;
345
+ z-index: 2;
346
+ width: 42px;
347
+ min-width: 42px;
348
+ padding: 2px 4px;
349
+ border-right: 1px solid #c7c7c7;
350
+ border-bottom: 1px solid #d4d4d4;
351
+ }
352
+
353
+ .excel-table td {
354
+ min-width: 72px;
355
+ max-width: 320px;
356
+ padding: 2px 6px;
357
+ border-right: 1px solid #d4d4d4;
358
+ border-bottom: 1px solid #d4d4d4;
359
+ color: #111827;
360
+ vertical-align: middle;
361
+ word-break: break-word;
362
+ white-space: pre-wrap;
363
+ background: #fff;
364
+ cursor: cell;
365
+ }
366
+
367
+ .excel-cell--selected {
368
+ box-shadow: inset 0 0 0 2px #217346 !important;
369
+ }
370
+
371
+ .excel-cell--match {
372
+ background: rgba(255, 214, 0, 0.35) !important;
373
+ }
374
+
375
+ .excel-cell--active {
376
+ background: rgba(255, 87, 34, 0.45) !important;
377
+ box-shadow: inset 0 0 0 2px rgba(255, 87, 34, 0.75);
378
+ }
379
+
380
+ .excel-sheet-tabs {
381
+ display: flex;
382
+ gap: 2px;
383
+ align-items: stretch;
384
+ min-height: 30px;
385
+ padding: 0 6px;
386
+ overflow-x: auto;
387
+ background: #f0f0f0;
388
+ border-top: 1px solid #c7c7c7;
389
+ }
390
+
391
+ .excel-sheet-tab {
392
+ flex-shrink: 0;
393
+ padding: 4px 14px;
394
+ border: none;
395
+ border-top: 2px solid transparent;
396
+ background: #e2e2e2;
397
+ color: #333;
398
+ font-size: 12px;
399
+ cursor: pointer;
400
+
401
+ &.is-active {
402
+ background: #fff;
403
+ border-top-color: #217346;
404
+ font-weight: 600;
405
+ }
406
+ }
407
+
408
+ .excel-zoom-float {
409
+ position: absolute;
410
+ right: 16px;
411
+ bottom: 42px;
412
+ z-index: 5;
413
+ display: flex;
414
+ flex-direction: column;
415
+ gap: 8px;
416
+ }
417
+
418
+ .excel-zoom-btn {
419
+ width: 34px;
420
+ height: 34px;
421
+ border: none;
422
+ border-radius: 50%;
423
+ background: rgba(255, 255, 255, 0.92);
424
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
425
+ color: #333;
426
+ font-size: 20px;
427
+ line-height: 1;
428
+ cursor: pointer;
429
+
430
+ &:hover {
431
+ background: #fff;
432
+ }
433
+ }
434
+
435
+ .excel-loading,
436
+ .excel-empty {
437
+ display: flex;
438
+ align-items: center;
439
+ justify-content: center;
440
+ min-height: 240px;
441
+ color: #6b7280;
442
+ font-size: 14px;
443
+ }
444
+ </style>