@yxhl/specter-pui-vtk 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 +122 -0
- package/dist/specter-pui-vtk.css +1 -0
- package/dist/specter-pui.es.js +3289 -0
- package/dist/specter-pui.es.js.map +1 -0
- package/dist/specter-pui.umd.js +2 -0
- package/dist/specter-pui.umd.js.map +1 -0
- package/package.json +65 -0
- package/src/assets/css/globals.scss +250 -0
- package/src/assets/css/index.scss +271 -0
- package/src/assets/css/settings.scss +10 -0
- package/src/assets/css/variables.scss +0 -0
- package/src/assets/icon/logo.svg +9 -0
- package/src/assets/img/background.png +0 -0
- package/src/assets/img/dtx.png +0 -0
- package/src/commons/filters/dictionary.js +75 -0
- package/src/commons/filters/format.js +112 -0
- package/src/commons/filters/mask.js +25 -0
- package/src/commons/index.js +17 -0
- package/src/commons/request.js +89 -0
- package/src/commons/storage.js +41 -0
- package/src/commons/themes.js +153 -0
- package/src/commons/validation.js +72 -0
- package/src/components/README.md +35 -0
- package/src/components/assembly/VtkArea.vue +259 -0
- package/src/components/assembly/VtkCheckbox.vue +168 -0
- package/src/components/assembly/VtkCount.vue +403 -0
- package/src/components/assembly/VtkDatePicker.vue +326 -0
- package/src/components/assembly/VtkEmpty.vue +107 -0
- package/src/components/assembly/VtkFab.vue +78 -0
- package/src/components/assembly/VtkFormItem.vue +166 -0
- package/src/components/assembly/VtkImg.vue +372 -0
- package/src/components/assembly/VtkPage.vue +156 -0
- package/src/components/assembly/VtkPdf.vue +424 -0
- package/src/components/assembly/VtkProj.vue +539 -0
- package/src/components/assembly/VtkRadio.vue +82 -0
- package/src/components/assembly/VtkSearch.vue +145 -0
- package/src/components/assembly/VtkSelect.vue +104 -0
- package/src/components/assembly/VtkStepper.vue +160 -0
- package/src/components/message/alert.vue +31 -0
- package/src/components/message/confirm.vue +44 -0
- package/src/components/message/index.js +55 -0
- package/src/components/message/loading.vue +33 -0
- package/src/components/message/prompt.vue +57 -0
- package/src/components/message/toast.vue +45 -0
- package/src/components/message/vtkMessage.vue +27 -0
- package/src/composables/useMixins.js +2 -0
- package/src/composables/usePage.js +311 -0
- package/src/index.js +109 -0
- package/src/stores/message.js +79 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="m_pagination">
|
|
3
|
+
<VRow v-if="pageDatas.rowCount > 0" class="pagination box" align="center">
|
|
4
|
+
<span class="page-total d-none d-md-flex align-center">共{{ pageDatas.rowCount }}条</span>
|
|
5
|
+
<VSelect
|
|
6
|
+
v-model="pagination.limit"
|
|
7
|
+
:items="pageSizeObjs"
|
|
8
|
+
variant="solo"
|
|
9
|
+
density="compact"
|
|
10
|
+
:menu-props="{ offsetY: true }"
|
|
11
|
+
hide-details
|
|
12
|
+
class="page-select ml-2 d-none d-md-flex"
|
|
13
|
+
@update:model-value="pageSizeChange()"
|
|
14
|
+
/>
|
|
15
|
+
<span class="my-2" style="font-size: 12px">
|
|
16
|
+
<v-pagination
|
|
17
|
+
v-model="pageDatas.pageno"
|
|
18
|
+
:total-visible="6"
|
|
19
|
+
:length="pageDatas.pageCount"
|
|
20
|
+
@update:model-value="pageChange()"
|
|
21
|
+
/>
|
|
22
|
+
</span>
|
|
23
|
+
<span class="page-total ml-4 d-none d-md-flex align-center">到</span>
|
|
24
|
+
<VTextField
|
|
25
|
+
v-model="pagination.pageno"
|
|
26
|
+
variant="solo"
|
|
27
|
+
density="compact"
|
|
28
|
+
hide-details
|
|
29
|
+
class="page-go ml-2 d-none d-md-flex"
|
|
30
|
+
@keyup.enter="toPage()"
|
|
31
|
+
/>
|
|
32
|
+
<span class="page-total ml-2 d-none d-md-flex align-center">页</span>
|
|
33
|
+
</VRow>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup>
|
|
38
|
+
import { ref, reactive, computed, watch, onMounted } from 'vue';
|
|
39
|
+
|
|
40
|
+
const props = defineProps({
|
|
41
|
+
pageData: {
|
|
42
|
+
type: Object,
|
|
43
|
+
default: () => ({})
|
|
44
|
+
},
|
|
45
|
+
pageSizes: {
|
|
46
|
+
type: Array,
|
|
47
|
+
default: () => [10, 20, 30]
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const emit = defineEmits(['pageChange']);
|
|
52
|
+
|
|
53
|
+
const pagination = reactive({
|
|
54
|
+
pageno: 1,
|
|
55
|
+
limit: 10
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const pageSizeObjs = ref([]);
|
|
59
|
+
const pageDatas = ref({ ...props.pageData });
|
|
60
|
+
|
|
61
|
+
const paginationChange = computed(() => pageDatas.value.pageno);
|
|
62
|
+
|
|
63
|
+
watch(paginationChange, () => {
|
|
64
|
+
pagination.pageno = pageDatas.value.pageno;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
watch(() => props.pageData, (newVal) => {
|
|
68
|
+
pageDatas.value = { ...newVal };
|
|
69
|
+
if (newVal.rowCount < 1 && newVal.rows > 0) {
|
|
70
|
+
pageDatas.value.pageno = newVal.pageno - 1;
|
|
71
|
+
pageChange();
|
|
72
|
+
}
|
|
73
|
+
}, { deep: true });
|
|
74
|
+
|
|
75
|
+
onMounted(() => {
|
|
76
|
+
pageSizeFormat();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const toPage = () => {
|
|
80
|
+
const re = /^[0-9]+$/;
|
|
81
|
+
if (re.test(pagination.pageno) && pagination.pageno <= pageDatas.value.pageCount) {
|
|
82
|
+
pageDatas.value.pageno = parseInt(pagination.pageno);
|
|
83
|
+
pageChange();
|
|
84
|
+
} else {
|
|
85
|
+
pagination.pageno = pageDatas.value.pageno;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const pageSizeChange = () => {
|
|
90
|
+
pageDatas.value.pageCount = 1;
|
|
91
|
+
pageChange();
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const pageSizeFormat = () => {
|
|
95
|
+
const temp = props.pageSizes;
|
|
96
|
+
temp.forEach((value) => {
|
|
97
|
+
const obj = {
|
|
98
|
+
title: value + "条/页",
|
|
99
|
+
value: value
|
|
100
|
+
};
|
|
101
|
+
pageSizeObjs.value.push(obj);
|
|
102
|
+
});
|
|
103
|
+
pagination.limit = temp[0];
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const pageChange = () => {
|
|
107
|
+
const page = {
|
|
108
|
+
pageno: pageDatas.value.pageno,
|
|
109
|
+
limit: pagination.limit
|
|
110
|
+
};
|
|
111
|
+
emit('pageChange', page);
|
|
112
|
+
};
|
|
113
|
+
</script>
|
|
114
|
+
|
|
115
|
+
<style lang="scss" scoped>
|
|
116
|
+
.mt-1 {
|
|
117
|
+
margin-top: 11px !important;
|
|
118
|
+
}
|
|
119
|
+
.my-5 {
|
|
120
|
+
margin-top: 20px !important;
|
|
121
|
+
margin-bottom: 20px !important;
|
|
122
|
+
}
|
|
123
|
+
.m_pagination {
|
|
124
|
+
width: 100%;
|
|
125
|
+
min-height: 60px;
|
|
126
|
+
.box {
|
|
127
|
+
position: absolute;
|
|
128
|
+
right: 16px;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
.page-total {
|
|
132
|
+
display: flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
height: 40px; // 与 v-select 高度保持一致
|
|
135
|
+
}
|
|
136
|
+
.page-select {
|
|
137
|
+
max-width: 120px;
|
|
138
|
+
:deep(.v-input__control) {
|
|
139
|
+
min-height: 40px !important;
|
|
140
|
+
height: 40px !important;
|
|
141
|
+
}
|
|
142
|
+
:deep(.v-field) {
|
|
143
|
+
height: 40px !important;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
.page-go {
|
|
147
|
+
max-width: 80px;
|
|
148
|
+
:deep(.v-input__control) {
|
|
149
|
+
min-height: 40px !important;
|
|
150
|
+
height: 40px !important;
|
|
151
|
+
}
|
|
152
|
+
:deep(.v-field) {
|
|
153
|
+
height: 40px !important;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="vtk-pdf-container">
|
|
3
|
+
<VCard
|
|
4
|
+
:class="['vtk-pdf-card', { 'vtk-pdf-card--preview': preview }]"
|
|
5
|
+
@click="openPreview"
|
|
6
|
+
>
|
|
7
|
+
<!-- PDF封面或默认图标 -->
|
|
8
|
+
<div class="vtk-pdf-cover" :style="{ aspectRatio: aspectRatio }">
|
|
9
|
+
<VIcon size="48" color="red">mdi-file-pdf-box</VIcon>
|
|
10
|
+
<div class="vtk-pdf-label">PDF</div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<!-- PDF标题和描述 -->
|
|
14
|
+
<div class="vtk-pdf-info" v-if="showTitle || showDescription">
|
|
15
|
+
<VCardTitle v-if="showTitle" class="vtk-pdf-title text-subtitle-2 pa-2">
|
|
16
|
+
{{ title }}
|
|
17
|
+
</VCardTitle>
|
|
18
|
+
<v-card-subtitle v-if="showDescription" class="vtk-pdf-description text-caption pa-2">
|
|
19
|
+
{{ description }}
|
|
20
|
+
</v-card-subtitle>
|
|
21
|
+
</div>
|
|
22
|
+
</VCard>
|
|
23
|
+
|
|
24
|
+
<!-- 使用 v-dialog 的PDF预览 -->
|
|
25
|
+
<VDialog
|
|
26
|
+
v-model="showPreview"
|
|
27
|
+
max-width="90%"
|
|
28
|
+
fullscreen
|
|
29
|
+
@click:outside="closePreview"
|
|
30
|
+
>
|
|
31
|
+
<VCard class="pdf-viewer-card">
|
|
32
|
+
<v-toolbar dark color="red darken-1">
|
|
33
|
+
<VBtn icon dark @click="closePreview">
|
|
34
|
+
<VIcon>mdi-close</VIcon>
|
|
35
|
+
</VBtn>
|
|
36
|
+
<v-toolbar-title>{{ title }}</v-toolbar-title>
|
|
37
|
+
<VSpacer></VSpacer>
|
|
38
|
+
<v-toolbar-items>
|
|
39
|
+
<VBtn icon dark @click="downloadPdf">
|
|
40
|
+
<VIcon>mdi-download</VIcon>
|
|
41
|
+
</VBtn>
|
|
42
|
+
</v-toolbar-items>
|
|
43
|
+
</v-toolbar>
|
|
44
|
+
|
|
45
|
+
<div class="pdf-viewer-content">
|
|
46
|
+
<div class="pdf-container" ref="pdfContainer">
|
|
47
|
+
<!-- PDF内容将通过PDF.js渲染 -->
|
|
48
|
+
<div id="pdf-viewer"></div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div class="pdf-viewer-footer" v-if="pdfLoaded">
|
|
53
|
+
<VRow align="center" justify="center">
|
|
54
|
+
<VCol cols="12" sm="6" class="d-flex justify-center">
|
|
55
|
+
<VBtn icon @click="prevPage" :disabled="currentPage <= 1">
|
|
56
|
+
<VIcon>mdi-chevron-left</VIcon>
|
|
57
|
+
</VBtn>
|
|
58
|
+
<div class="mx-4">
|
|
59
|
+
<span>第 {{ currentPage }} 页 / 共 {{ pageCount }} 页</span>
|
|
60
|
+
</div>
|
|
61
|
+
<VBtn icon @click="nextPage" :disabled="currentPage >= pageCount">
|
|
62
|
+
<VIcon>mdi-chevron-right</VIcon>
|
|
63
|
+
</VBtn>
|
|
64
|
+
</VCol>
|
|
65
|
+
<VCol cols="12" sm="6" class="d-flex justify-center">
|
|
66
|
+
<VSlider
|
|
67
|
+
v-model="currentPage"
|
|
68
|
+
:min="1"
|
|
69
|
+
:max="pageCount"
|
|
70
|
+
hide-details
|
|
71
|
+
class="mx-4"
|
|
72
|
+
></VSlider>
|
|
73
|
+
</VCol>
|
|
74
|
+
</VRow>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="pdf-loading" v-if="loading">
|
|
78
|
+
<v-progress-circular indeterminate color="red"></v-progress-circular>
|
|
79
|
+
<p class="mt-2">正在加载PDF...</p>
|
|
80
|
+
</div>
|
|
81
|
+
</VCard>
|
|
82
|
+
</VDialog>
|
|
83
|
+
</div>
|
|
84
|
+
</template>
|
|
85
|
+
|
|
86
|
+
<script setup>
|
|
87
|
+
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
|
88
|
+
// import * as pdfjsLib from 'pdfjs-dist';
|
|
89
|
+
// 注意:在实际项目中,您需要安装 pdfjs-dist 库
|
|
90
|
+
// npm install pdfjs-dist
|
|
91
|
+
// import * as pdfjsLib from 'pdfjs-dist';
|
|
92
|
+
|
|
93
|
+
defineOptions({
|
|
94
|
+
name: 'VtkPdf',
|
|
95
|
+
inheritAttrs: true
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const props = defineProps({
|
|
99
|
+
src: {
|
|
100
|
+
type: String,
|
|
101
|
+
default: null,
|
|
102
|
+
},
|
|
103
|
+
preview: {
|
|
104
|
+
type: Boolean,
|
|
105
|
+
default: true
|
|
106
|
+
},
|
|
107
|
+
title: {
|
|
108
|
+
type: String,
|
|
109
|
+
default: ''
|
|
110
|
+
},
|
|
111
|
+
description: {
|
|
112
|
+
type: String,
|
|
113
|
+
default: ''
|
|
114
|
+
},
|
|
115
|
+
// 是否显示标题
|
|
116
|
+
showTitle: {
|
|
117
|
+
type: Boolean,
|
|
118
|
+
default: true
|
|
119
|
+
},
|
|
120
|
+
// 是否显示描述
|
|
121
|
+
showDescription: {
|
|
122
|
+
type: Boolean,
|
|
123
|
+
default: true
|
|
124
|
+
},
|
|
125
|
+
// 封面宽高比
|
|
126
|
+
aspectRatio: {
|
|
127
|
+
type: [String, Number],
|
|
128
|
+
default: 16/9
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// 预览状态
|
|
133
|
+
const showPreview = ref(false);
|
|
134
|
+
// PDF相关状态
|
|
135
|
+
const loading = ref(false);
|
|
136
|
+
const pdfLoaded = ref(false);
|
|
137
|
+
const currentPage = ref(1);
|
|
138
|
+
const pageCount = ref(0);
|
|
139
|
+
const pdfContainer = ref(null);
|
|
140
|
+
|
|
141
|
+
// PDF对象(实际使用时需要引入pdfjsLib)
|
|
142
|
+
let pdfDoc = null;
|
|
143
|
+
|
|
144
|
+
// 使用计算属性处理带token的URL
|
|
145
|
+
const srcWithToken = computed(() => {
|
|
146
|
+
if (!props.src) return props.src;
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// 首先尝试使用 $vtk.storage
|
|
150
|
+
if (window.$vtk && typeof window.$vtk.storage?.get === 'function') {
|
|
151
|
+
const token = window.$vtk.storage.get('token');
|
|
152
|
+
return token ? `${props.src}?stoken=${token}` : props.src;
|
|
153
|
+
}
|
|
154
|
+
// 如果 $vtk 不存在,尝试从 localStorage 获取 token
|
|
155
|
+
else {
|
|
156
|
+
const token = localStorage.getItem('token');
|
|
157
|
+
return token ? `${props.src}?stoken=${token}` : props.src;
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
// 发生错误时返回原始src
|
|
161
|
+
console.warn('VtkPdf: Failed to get token, using src without token', error);
|
|
162
|
+
return props.src;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// 打开预览
|
|
167
|
+
const openPreview = () => {
|
|
168
|
+
if (props.preview) {
|
|
169
|
+
showPreview.value = true;
|
|
170
|
+
// 延迟加载PDF以确保DOM已准备好
|
|
171
|
+
setTimeout(() => {
|
|
172
|
+
loadPdf();
|
|
173
|
+
}, 300);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// 关闭预览
|
|
178
|
+
const closePreview = () => {
|
|
179
|
+
showPreview.value = false;
|
|
180
|
+
// 清理PDF对象
|
|
181
|
+
pdfDoc = null;
|
|
182
|
+
pdfLoaded.value = false;
|
|
183
|
+
currentPage.value = 1;
|
|
184
|
+
pageCount.value = 0;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// 加载PDF(示例实现,实际使用需要PDF.js库)
|
|
188
|
+
const loadPdf = async () => {
|
|
189
|
+
if (!srcWithToken.value) return;
|
|
190
|
+
|
|
191
|
+
loading.value = true;
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
// 模拟PDF加载(实际实现需要使用PDF.js)
|
|
195
|
+
/*
|
|
196
|
+
// 实际使用时的代码示例:
|
|
197
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
|
|
198
|
+
|
|
199
|
+
const loadingTask = pdfjsLib.getDocument(srcWithToken.value);
|
|
200
|
+
pdfDoc = await loadingTask.promise;
|
|
201
|
+
pageCount.value = pdfDoc.numPages;
|
|
202
|
+
pdfLoaded.value = true;
|
|
203
|
+
|
|
204
|
+
// 渲染第一页
|
|
205
|
+
renderPage(currentPage.value);
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
// 模拟加载过程
|
|
209
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
210
|
+
pageCount.value = 10; // 模拟10页PDF
|
|
211
|
+
pdfLoaded.value = true;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error('加载PDF失败:', error);
|
|
214
|
+
} finally {
|
|
215
|
+
loading.value = false;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// 渲染PDF页面(示例实现)
|
|
220
|
+
const renderPage = async (pageNum) => {
|
|
221
|
+
if (!pdfDoc || !pdfContainer.value) return;
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
/*
|
|
225
|
+
// 实际使用时的代码示例:
|
|
226
|
+
const page = await pdfDoc.getPage(pageNum);
|
|
227
|
+
const viewport = page.getViewport({ scale: 1.5 });
|
|
228
|
+
|
|
229
|
+
// 准备canvas
|
|
230
|
+
const canvas = document.createElement('canvas');
|
|
231
|
+
const context = canvas.getContext('2d');
|
|
232
|
+
canvas.height = viewport.height;
|
|
233
|
+
canvas.width = viewport.width;
|
|
234
|
+
|
|
235
|
+
// 清空容器
|
|
236
|
+
pdfContainer.value.innerHTML = '';
|
|
237
|
+
pdfContainer.value.appendChild(canvas);
|
|
238
|
+
|
|
239
|
+
// 渲染页面
|
|
240
|
+
const renderContext = {
|
|
241
|
+
canvasContext: context,
|
|
242
|
+
viewport: viewport
|
|
243
|
+
};
|
|
244
|
+
await page.render(renderContext).promise;
|
|
245
|
+
*/
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error('渲染PDF页面失败:', error);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// 上一页
|
|
252
|
+
const prevPage = () => {
|
|
253
|
+
if (currentPage.value > 1) {
|
|
254
|
+
currentPage.value--;
|
|
255
|
+
renderPage(currentPage.value);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// 下一页
|
|
260
|
+
const nextPage = () => {
|
|
261
|
+
if (currentPage.value < pageCount.value) {
|
|
262
|
+
currentPage.value++;
|
|
263
|
+
renderPage(currentPage.value);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// 下载PDF
|
|
268
|
+
const downloadPdf = async () => {
|
|
269
|
+
if (!srcWithToken.value) return;
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
// 获取PDF文件的二进制数据
|
|
273
|
+
const response = await fetch(srcWithToken.value);
|
|
274
|
+
if (!response.ok) {
|
|
275
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const blob = await response.blob();
|
|
279
|
+
const fileName = props.title && props.title.trim() !== ''
|
|
280
|
+
? `${props.title}.pdf`
|
|
281
|
+
: 'document.pdf';
|
|
282
|
+
|
|
283
|
+
// 创建下载链接
|
|
284
|
+
const url = window.URL.createObjectURL(blob);
|
|
285
|
+
const link = document.createElement('a');
|
|
286
|
+
link.href = url;
|
|
287
|
+
link.download = fileName;
|
|
288
|
+
document.body.appendChild(link);
|
|
289
|
+
link.click();
|
|
290
|
+
|
|
291
|
+
// 清理
|
|
292
|
+
document.body.removeChild(link);
|
|
293
|
+
window.URL.revokeObjectURL(url);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.error('下载PDF失败:', error);
|
|
296
|
+
// 备用方案:在新窗口打开
|
|
297
|
+
window.open(srcWithToken.value, '_blank');
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// 监听页码变化
|
|
302
|
+
const handlePageChange = (newPage) => {
|
|
303
|
+
if (newPage >= 1 && newPage <= pageCount.value) {
|
|
304
|
+
renderPage(newPage);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// 监听 currentPage 变化
|
|
309
|
+
// 在实际实现中,您需要使用 watch 监听 currentPage 变化
|
|
310
|
+
// watch(currentPage, handlePageChange);
|
|
311
|
+
|
|
312
|
+
// 组件销毁前清理
|
|
313
|
+
onBeforeUnmount(() => {
|
|
314
|
+
pdfDoc = null;
|
|
315
|
+
});
|
|
316
|
+
</script>
|
|
317
|
+
|
|
318
|
+
<style lang="scss" scoped>
|
|
319
|
+
.vtk-pdf-container {
|
|
320
|
+
position: relative;
|
|
321
|
+
display: inline-block;
|
|
322
|
+
width: 100%;
|
|
323
|
+
height: 100%;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.vtk-pdf-card {
|
|
327
|
+
height: 100%;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.vtk-pdf-card--preview {
|
|
331
|
+
cursor: pointer;
|
|
332
|
+
transition: transform 0.2s ease-in-out;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.vtk-pdf-card--preview:hover {
|
|
336
|
+
transform: translateY(-5px);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.vtk-pdf-cover {
|
|
340
|
+
display: flex;
|
|
341
|
+
flex-direction: column;
|
|
342
|
+
align-items: center;
|
|
343
|
+
justify-content: center;
|
|
344
|
+
background-color: #f5f5f5;
|
|
345
|
+
border: 1px solid #ddd;
|
|
346
|
+
width: 100%;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.vtk-pdf-label {
|
|
350
|
+
margin-top: 8px;
|
|
351
|
+
font-weight: 500;
|
|
352
|
+
color: #666;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.vtk-pdf-info {
|
|
356
|
+
padding: 0;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.vtk-pdf-title {
|
|
360
|
+
word-break: break-word;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.vtk-pdf-description {
|
|
364
|
+
word-break: break-word;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* PDF查看器样式 */
|
|
368
|
+
.pdf-viewer-card {
|
|
369
|
+
background-color: #e0e0e0;
|
|
370
|
+
color: #333;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.pdf-viewer-content {
|
|
374
|
+
display: flex;
|
|
375
|
+
align-items: center;
|
|
376
|
+
justify-content: center;
|
|
377
|
+
height: calc(100vh - 150px);
|
|
378
|
+
position: relative;
|
|
379
|
+
background-color: #555;
|
|
380
|
+
overflow: auto;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.pdf-container {
|
|
384
|
+
display: flex;
|
|
385
|
+
align-items: center;
|
|
386
|
+
justify-content: center;
|
|
387
|
+
width: 100%;
|
|
388
|
+
height: 100%;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
#pdf-viewer {
|
|
392
|
+
max-width: 100%;
|
|
393
|
+
max-height: 100%;
|
|
394
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
|
395
|
+
background-color: white;
|
|
396
|
+
/* 实际PDF内容将在此处渲染 */
|
|
397
|
+
display: flex;
|
|
398
|
+
align-items: center;
|
|
399
|
+
justify-content: center;
|
|
400
|
+
min-height: 500px;
|
|
401
|
+
min-width: 400px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.pdf-viewer-footer {
|
|
405
|
+
padding: 16px;
|
|
406
|
+
background-color: #f5f5f5;
|
|
407
|
+
border-top: 1px solid #ddd;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.pdf-loading {
|
|
411
|
+
position: absolute;
|
|
412
|
+
top: 50%;
|
|
413
|
+
left: 50%;
|
|
414
|
+
transform: translate(-50%, -50%);
|
|
415
|
+
display: flex;
|
|
416
|
+
flex-direction: column;
|
|
417
|
+
align-items: center;
|
|
418
|
+
justify-content: center;
|
|
419
|
+
background-color: rgba(255, 255, 255, 0.9);
|
|
420
|
+
padding: 20px;
|
|
421
|
+
border-radius: 8px;
|
|
422
|
+
z-index: 100;
|
|
423
|
+
}
|
|
424
|
+
</style>
|