adtec-core-package 3.3.1 → 3.3.2
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/package.json +1 -1
- package/src/components/autoToolTip/ElAutoToolTip.vue +1 -0
- package/src/components/upload/OfficePreview.vue +75 -13
- package/src/components/upload/OfficePreviewHeaderBar.vue +8 -1
- package/src/config/ElementPlusConfig.ts +13 -5
- package/src/config/VxeTableConfig.ts +5 -1
- package/src/utils/patchWujiePopperCoordinates.ts +91 -0
package/package.json
CHANGED
|
@@ -4,18 +4,19 @@
|
|
|
4
4
|
<office-preview-header-bar
|
|
5
5
|
v-if="showHeader"
|
|
6
6
|
:title="headerTitle"
|
|
7
|
+
:title-only="previewLoadFailed"
|
|
7
8
|
:zoom="scale"
|
|
8
9
|
:current-page="previewCurrentPage"
|
|
9
10
|
:total-pages="previewTotalPages"
|
|
10
11
|
:search-query="searchQuery"
|
|
11
12
|
:search-index="searchMatchIndex"
|
|
12
13
|
:search-total="searchMatchTotal"
|
|
13
|
-
:show-search="
|
|
14
|
-
:show-page-nav="
|
|
15
|
-
:show-zoom="
|
|
16
|
-
:show-download="
|
|
17
|
-
:show-refresh="
|
|
18
|
-
:show-fullscreen="
|
|
14
|
+
:show-search="showHeaderSearch"
|
|
15
|
+
:show-page-nav="showHeaderPageNav"
|
|
16
|
+
:show-zoom="showHeaderZoom"
|
|
17
|
+
:show-download="showHeaderDownload"
|
|
18
|
+
:show-refresh="showHeaderRefresh"
|
|
19
|
+
:show-fullscreen="showHeaderFullscreen"
|
|
19
20
|
:show-close="showClose"
|
|
20
21
|
:fullscreen="fullscreen"
|
|
21
22
|
:download-loading="downloadLoading"
|
|
@@ -79,6 +80,9 @@
|
|
|
79
80
|
<p v-if="headerTitle" class="loading-sub">{{ headerTitle }}</p>
|
|
80
81
|
</div>
|
|
81
82
|
</div>
|
|
83
|
+
<div v-else-if="previewLoadFailed" class="office-rich-lost">
|
|
84
|
+
<el-empty :description="lostDescription" />
|
|
85
|
+
</div>
|
|
82
86
|
<div v-else-if="isPreviewLoading && !fileSrc" class="office-rich-loading">
|
|
83
87
|
<div class="loading-card">
|
|
84
88
|
<span class="loading-spinner" />
|
|
@@ -137,6 +141,10 @@ const props = withDefaults(
|
|
|
137
141
|
fullscreen?: boolean
|
|
138
142
|
downloadLoading?: boolean
|
|
139
143
|
refreshLoading?: boolean
|
|
144
|
+
/** 库里有附件记录但物理文件无法加载时的提示 */
|
|
145
|
+
lostDescription?: string
|
|
146
|
+
/** 父组件已判定附件丢失(如无下载地址)时直接展示丢失态 */
|
|
147
|
+
attachmentLost?: boolean
|
|
140
148
|
}>(),
|
|
141
149
|
{
|
|
142
150
|
showHeader: true,
|
|
@@ -150,6 +158,8 @@ const props = withDefaults(
|
|
|
150
158
|
fullscreen: false,
|
|
151
159
|
downloadLoading: false,
|
|
152
160
|
refreshLoading: false,
|
|
161
|
+
lostDescription: '附件已丢失',
|
|
162
|
+
attachmentLost: false,
|
|
153
163
|
},
|
|
154
164
|
)
|
|
155
165
|
|
|
@@ -176,6 +186,7 @@ const searchQuery = ref('')
|
|
|
176
186
|
const searchMatchIndex = ref(-1)
|
|
177
187
|
const searchMatchTotal = ref(0)
|
|
178
188
|
const isPreviewLoading = ref(false)
|
|
189
|
+
const previewLoadFailed = ref(false)
|
|
179
190
|
const loadingPhase = ref<'fetch' | 'open'>('fetch')
|
|
180
191
|
|
|
181
192
|
type RichViewer = {
|
|
@@ -201,11 +212,21 @@ const isLegacyDoc = computed(() =>
|
|
|
201
212
|
|
|
202
213
|
const isRichPreview = computed(
|
|
203
214
|
() =>
|
|
215
|
+
props.attachmentLost ||
|
|
204
216
|
officeType.value === 'pdf' ||
|
|
205
217
|
officeType.value === 'excel' ||
|
|
206
218
|
officeType.value === 'docx',
|
|
207
219
|
)
|
|
208
220
|
|
|
221
|
+
const showHeaderSearch = computed(() => props.showSearch && !previewLoadFailed.value)
|
|
222
|
+
const showHeaderPageNav = computed(
|
|
223
|
+
() => props.showPageNav && !previewLoadFailed.value && officeType.value !== 'excel',
|
|
224
|
+
)
|
|
225
|
+
const showHeaderZoom = computed(() => props.showZoom && !previewLoadFailed.value)
|
|
226
|
+
const showHeaderDownload = computed(() => props.showDownload && !previewLoadFailed.value)
|
|
227
|
+
const showHeaderRefresh = computed(() => props.showRefresh && !previewLoadFailed.value)
|
|
228
|
+
const showHeaderFullscreen = computed(() => props.showFullscreen && !previewLoadFailed.value)
|
|
229
|
+
|
|
209
230
|
const isUnsupportedType = computed(
|
|
210
231
|
() => !!officeType.value && !isSupportedPreviewType(officeType.value),
|
|
211
232
|
)
|
|
@@ -235,13 +256,25 @@ const resetPreview = () => {
|
|
|
235
256
|
searchMatchIndex.value = -1
|
|
236
257
|
searchMatchTotal.value = 0
|
|
237
258
|
isPreviewLoading.value = false
|
|
259
|
+
previewLoadFailed.value = false
|
|
238
260
|
loadingPhase.value = 'fetch'
|
|
239
261
|
pdfViewerRef.value?.clearSearch()
|
|
240
262
|
excelViewerRef.value?.clearSearch()
|
|
241
263
|
docxViewerRef.value?.clearSearch()
|
|
242
264
|
}
|
|
243
265
|
|
|
266
|
+
const markPreviewLost = (err?: unknown) => {
|
|
267
|
+
isPreviewLoading.value = false
|
|
268
|
+
previewLoadFailed.value = true
|
|
269
|
+
fileSrc.value = undefined
|
|
270
|
+
emit('error', err)
|
|
271
|
+
}
|
|
272
|
+
|
|
244
273
|
const loadPreview = async () => {
|
|
274
|
+
if (props.attachmentLost) {
|
|
275
|
+
markPreviewLost()
|
|
276
|
+
return
|
|
277
|
+
}
|
|
245
278
|
if (!props.url || !officeType.value) {
|
|
246
279
|
return
|
|
247
280
|
}
|
|
@@ -262,6 +295,10 @@ const loadPreview = async () => {
|
|
|
262
295
|
}
|
|
263
296
|
loadingPhase.value = 'open'
|
|
264
297
|
const pdfBlob = await documentApi.previewWordAsPdf(props.fileId)
|
|
298
|
+
if (!pdfBlob || pdfBlob.size === 0) {
|
|
299
|
+
markPreviewLost()
|
|
300
|
+
return
|
|
301
|
+
}
|
|
265
302
|
previewAsPdf.value = true
|
|
266
303
|
fileSrc.value = pdfBlob
|
|
267
304
|
return
|
|
@@ -273,16 +310,22 @@ const loadPreview = async () => {
|
|
|
273
310
|
})
|
|
274
311
|
if (officeType.value === 'txt') {
|
|
275
312
|
txtContent.value = await res.data.text()
|
|
313
|
+
if (!txtContent.value.trim()) {
|
|
314
|
+
markPreviewLost()
|
|
315
|
+
return
|
|
316
|
+
}
|
|
276
317
|
isPreviewLoading.value = false
|
|
277
318
|
emit('loaded')
|
|
278
319
|
} else {
|
|
279
320
|
loadingPhase.value = 'open'
|
|
321
|
+
if (!res.data || res.data.size === 0) {
|
|
322
|
+
markPreviewLost()
|
|
323
|
+
return
|
|
324
|
+
}
|
|
280
325
|
fileSrc.value = res.data
|
|
281
326
|
}
|
|
282
327
|
} catch (err) {
|
|
283
|
-
|
|
284
|
-
frameworkUtils.messageError(err)
|
|
285
|
-
emit('error', err)
|
|
328
|
+
markPreviewLost(err)
|
|
286
329
|
}
|
|
287
330
|
}
|
|
288
331
|
|
|
@@ -294,8 +337,7 @@ const handleRendered = async () => {
|
|
|
294
337
|
}
|
|
295
338
|
|
|
296
339
|
const handleError = (err?: unknown) => {
|
|
297
|
-
|
|
298
|
-
emit('error', err)
|
|
340
|
+
markPreviewLost(err)
|
|
299
341
|
}
|
|
300
342
|
|
|
301
343
|
const zoomIn = () => {
|
|
@@ -357,9 +399,13 @@ const goNextSearchMatch = async () => {
|
|
|
357
399
|
}
|
|
358
400
|
|
|
359
401
|
watch(
|
|
360
|
-
() => [props.url, props.fileName, props.fileType] as const,
|
|
361
|
-
([fileUrl]) => {
|
|
402
|
+
() => [props.url, props.fileName, props.fileType, props.attachmentLost] as const,
|
|
403
|
+
([fileUrl, , , attachmentLost]) => {
|
|
362
404
|
resetPreview()
|
|
405
|
+
if (attachmentLost) {
|
|
406
|
+
markPreviewLost()
|
|
407
|
+
return
|
|
408
|
+
}
|
|
363
409
|
if (fileUrl) {
|
|
364
410
|
loadPreview()
|
|
365
411
|
}
|
|
@@ -395,6 +441,9 @@ defineExpose({
|
|
|
395
441
|
display: flex;
|
|
396
442
|
flex-direction: column;
|
|
397
443
|
overflow: hidden;
|
|
444
|
+
padding-bottom: 8px;
|
|
445
|
+
box-sizing: border-box;
|
|
446
|
+
background: #fff;
|
|
398
447
|
}
|
|
399
448
|
|
|
400
449
|
&.is-fullscreen {
|
|
@@ -402,6 +451,9 @@ defineExpose({
|
|
|
402
451
|
background: #f5f5f5 !important;
|
|
403
452
|
|
|
404
453
|
&.is-rich-preview {
|
|
454
|
+
padding-bottom: 0;
|
|
455
|
+
background: #f5f5f5 !important;
|
|
456
|
+
|
|
405
457
|
.office-rich-wrap {
|
|
406
458
|
flex: 1;
|
|
407
459
|
min-height: 0;
|
|
@@ -434,6 +486,16 @@ defineExpose({
|
|
|
434
486
|
flex-direction: column;
|
|
435
487
|
}
|
|
436
488
|
|
|
489
|
+
.office-rich-lost {
|
|
490
|
+
position: absolute;
|
|
491
|
+
inset: 0;
|
|
492
|
+
z-index: 3;
|
|
493
|
+
display: flex;
|
|
494
|
+
align-items: center;
|
|
495
|
+
justify-content: center;
|
|
496
|
+
background: #fff;
|
|
497
|
+
}
|
|
498
|
+
|
|
437
499
|
.office-rich-loading {
|
|
438
500
|
position: absolute;
|
|
439
501
|
inset: 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="office-preview-header">
|
|
2
|
+
<div class="office-preview-header" :class="{ 'is-title-only': titleOnly }">
|
|
3
3
|
<div v-if="title" class="header-title" :title="title">{{ title }}</div>
|
|
4
4
|
|
|
5
5
|
<div v-if="showSearch" class="header-search">
|
|
@@ -138,6 +138,7 @@ import { MAX_PDF_ZOOM, MIN_OFFICE_ZOOM } from '../../utils/officePreviewUtil.ts'
|
|
|
138
138
|
const props = withDefaults(
|
|
139
139
|
defineProps<{
|
|
140
140
|
title?: string
|
|
141
|
+
titleOnly?: boolean
|
|
141
142
|
zoom?: number
|
|
142
143
|
currentPage?: number
|
|
143
144
|
totalPages?: number
|
|
@@ -159,6 +160,7 @@ const props = withDefaults(
|
|
|
159
160
|
}>(),
|
|
160
161
|
{
|
|
161
162
|
title: '',
|
|
163
|
+
titleOnly: false,
|
|
162
164
|
zoom: 1,
|
|
163
165
|
currentPage: 1,
|
|
164
166
|
totalPages: 0,
|
|
@@ -234,6 +236,11 @@ const emitSearch = () => {
|
|
|
234
236
|
flex-shrink: 0;
|
|
235
237
|
}
|
|
236
238
|
|
|
239
|
+
.is-title-only .header-title {
|
|
240
|
+
max-width: none;
|
|
241
|
+
flex: 1;
|
|
242
|
+
}
|
|
243
|
+
|
|
237
244
|
.header-search {
|
|
238
245
|
display: flex;
|
|
239
246
|
align-items: center;
|
|
@@ -21,7 +21,10 @@ import elementPlus, {
|
|
|
21
21
|
ElCascader,
|
|
22
22
|
ElTable,
|
|
23
23
|
ElTooltip,
|
|
24
|
+
ElPopover,
|
|
25
|
+
ElDropdown,
|
|
24
26
|
} from 'element-plus'
|
|
27
|
+
import { patchWujiePopperCoordinates } from '../utils/patchWujiePopperCoordinates'
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* vxe 单元格编辑时,Element 下拉/日期面板默认 teleport 到 body。
|
|
@@ -85,7 +88,7 @@ ElSelect.props.fitInputWidth = {
|
|
|
85
88
|
const isWujieSubApp =
|
|
86
89
|
typeof window !== 'undefined' && !!(window as Window & { __POWERED_BY_WUJIE__?: boolean }).__POWERED_BY_WUJIE__
|
|
87
90
|
/**
|
|
88
|
-
* 无界 iframe 内 Popper
|
|
91
|
+
* 无界 iframe 内 Popper 坐标修正(与下拉框数据页同类问题)。
|
|
89
92
|
* ElSelect / ElDatePicker 等须保持 teleported 默认 true(vxe 单元格挂 body + popperClass 防 clearEdit),
|
|
90
93
|
* 仅通过 strategy/modifiers 修正偏移,避免各子应用逐页 v-bind。
|
|
91
94
|
*/
|
|
@@ -99,10 +102,15 @@ const wujiePopperOptions = {
|
|
|
99
102
|
],
|
|
100
103
|
}
|
|
101
104
|
if (isWujieSubApp) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
patchWujiePopperCoordinates()
|
|
106
|
+
const wujiePopperProps = { type: Object, default: () => wujiePopperOptions }
|
|
107
|
+
ElSelect.props.popperOptions = wujiePopperProps
|
|
108
|
+
ElDatePicker.props.popperOptions = wujiePopperProps
|
|
109
|
+
ElTreeSelect.props.popperOptions = wujiePopperProps
|
|
110
|
+
ElCascader.props.popperOptions = wujiePopperProps
|
|
111
|
+
ElTooltip.props.popperOptions = wujiePopperProps
|
|
112
|
+
ElPopover.props.popperOptions = wujiePopperProps
|
|
113
|
+
ElDropdown.props.popperOptions = wujiePopperProps
|
|
106
114
|
}
|
|
107
115
|
ElSelect.props.popperClass = {
|
|
108
116
|
type: String,
|
|
@@ -17,6 +17,7 @@ import { VxeUIPluginExportPDF } from '@vxe-ui/plugin-export-pdf'
|
|
|
17
17
|
import { jsPDF } from 'jspdf'
|
|
18
18
|
// 导入默认的语言(使用 es 构建,避免 lib CJS 在 Vite 下无 default 导出)
|
|
19
19
|
import zhCN from 'vxe-table/es/locale/lang/zh-CN'
|
|
20
|
+
import { patchWujiePopperCoordinates } from '../utils/patchWujiePopperCoordinates'
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* 导出插件(Excel/PDF)全局注册一次。
|
|
@@ -32,7 +33,9 @@ function registerVxeExportPlugins() {
|
|
|
32
33
|
VxeUI.use(VxeUIPluginExportXLSX, { ExcelJS })
|
|
33
34
|
VxeUI.use(VxeUIPluginExportPDF, { jsPDF })
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
+
import { patchWujiePopperCoordinates } from '../utils/patchWujiePopperCoordinates'
|
|
37
|
+
|
|
38
|
+
// 模块加载即注册导出插件;无界坐标补丁亦在 patch 模块内执行
|
|
36
39
|
registerVxeExportPlugins()
|
|
37
40
|
|
|
38
41
|
/**
|
|
@@ -43,6 +46,7 @@ export function initVxeTableInPage(
|
|
|
43
46
|
enablePdf: boolean = false,
|
|
44
47
|
enableElementPlus: boolean = true,
|
|
45
48
|
) {
|
|
49
|
+
patchWujiePopperCoordinates(true)
|
|
46
50
|
// 设置语言
|
|
47
51
|
VxeUI.setI18n('zh-CN', zhCN)
|
|
48
52
|
VxeUI.setLanguage('zh-CN')
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 无界 DocElementRectPlugin 将子应用 document.documentElement 的
|
|
3
|
+
* scrollTop/scrollLeft/clientWidth/clientHeight 代理到主应用窗口。
|
|
4
|
+
*
|
|
5
|
+
* vxe-table overflow tooltip 用 getAbsolutePos:top = document.scrollTop + cell.getBoundingClientRect().top
|
|
6
|
+
* 混用主应用 scroll 与 iframe 内坐标 → 浮层整体下移(常见 1~3 行高度)。
|
|
7
|
+
*
|
|
8
|
+
* Element Plus Popper → ElementPlusConfig 使用 strategy:fixed。
|
|
9
|
+
* vxe tooltip → 本补丁:document 级滚动归零 + tooltip 改为 position:fixed。
|
|
10
|
+
*/
|
|
11
|
+
const STYLE_ID = 'adtec-wujie-popper-fix-style'
|
|
12
|
+
const HTML_CLASS = 'adtec-wujie-subapp'
|
|
13
|
+
|
|
14
|
+
let patched = false
|
|
15
|
+
|
|
16
|
+
const IFRAME_DOC_PROPS = [
|
|
17
|
+
'scrollTop',
|
|
18
|
+
'scrollLeft',
|
|
19
|
+
'clientHeight',
|
|
20
|
+
'clientWidth',
|
|
21
|
+
] as const
|
|
22
|
+
|
|
23
|
+
function isWujieSubApp() {
|
|
24
|
+
return (
|
|
25
|
+
typeof window !== 'undefined' &&
|
|
26
|
+
!!(window as Window & { __POWERED_BY_WUJIE__?: boolean }).__POWERED_BY_WUJIE__
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function injectWujiePopperStyles() {
|
|
31
|
+
if (typeof document === 'undefined' || document.getElementById(STYLE_ID)) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
const style = document.createElement('style')
|
|
35
|
+
style.id = STYLE_ID
|
|
36
|
+
style.textContent = `
|
|
37
|
+
html.${HTML_CLASS} .vxe-tooltip--wrapper,
|
|
38
|
+
html.${HTML_CLASS} .vxe-table--tooltip-wrapper {
|
|
39
|
+
position: fixed !important;
|
|
40
|
+
z-index: 9999;
|
|
41
|
+
}
|
|
42
|
+
`
|
|
43
|
+
document.head.appendChild(style)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function restoreIframeDocumentMetrics(docEl: HTMLElement, body: HTMLElement) {
|
|
47
|
+
IFRAME_DOC_PROPS.forEach((key) => {
|
|
48
|
+
Object.defineProperty(docEl, key, {
|
|
49
|
+
configurable: true,
|
|
50
|
+
enumerable: true,
|
|
51
|
+
get() {
|
|
52
|
+
if (key === 'clientWidth') {
|
|
53
|
+
return window.innerWidth
|
|
54
|
+
}
|
|
55
|
+
if (key === 'clientHeight') {
|
|
56
|
+
return window.innerHeight
|
|
57
|
+
}
|
|
58
|
+
// 子应用页面滚动在表格/容器内,document 级滚动恒为 0
|
|
59
|
+
return 0
|
|
60
|
+
},
|
|
61
|
+
set(val: number) {
|
|
62
|
+
try {
|
|
63
|
+
;(body as HTMLElement & Record<string, number>)[key] = val
|
|
64
|
+
} catch {
|
|
65
|
+
// ignore
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 无界子应用内统一修正 Popper / vxe tooltip 坐标系 */
|
|
73
|
+
export function patchWujiePopperCoordinates(force = false) {
|
|
74
|
+
if (typeof window === 'undefined') {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
if (!isWujieSubApp()) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
if (patched && !force) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
document.documentElement.classList.add(HTML_CLASS)
|
|
85
|
+
injectWujiePopperStyles()
|
|
86
|
+
restoreIframeDocumentMetrics(document.documentElement, document.body)
|
|
87
|
+
patched = true
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 核心包被 import 时即尝试打补丁(覆盖未调用 initVxeTableInPage 的 vxe-grid 页面)
|
|
91
|
+
patchWujiePopperCoordinates()
|