@chenghongyu/xpt-file-viewer 1.1.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.
Files changed (274) hide show
  1. package/.vscode/extensions.json +3 -0
  2. package/README.md +5 -0
  3. package/auto-imports.d.ts +12 -0
  4. package/components.d.ts +45 -0
  5. package/dist.zip +0 -0
  6. package/index.html +13 -0
  7. package/package.json +100 -0
  8. package/presets/eslint/.eslintrc-auto-import.json +462 -0
  9. package/presets/plugins/html.ts +14 -0
  10. package/presets/shared/env.ts +46 -0
  11. package/presets/shared/resolvers.ts +29 -0
  12. package/presets/tov.ts +132 -0
  13. package/presets/types/auto-imports.d.ts +806 -0
  14. package/presets/types/components.d.ts +164 -0
  15. package/presets/types/env.d.ts +17 -0
  16. package/public/favicon.svg +1 -0
  17. package/public/icons.svg +24 -0
  18. package/public/testfile/README.md +5 -0
  19. package/public/testfile/useFileContextMenu.js +74 -0
  20. package/public/testfile//345/217/221/347/245/250.pdf +0 -0
  21. package/public/testfile//346/216/245/346/224/266/345/207/275.docx +0 -0
  22. package/public/testfile//350/247/204/346/240/274/345/236/213/345/217/2671.txt +263 -0
  23. package/public/testfile//350/247/206/351/242/2211.mp4 +0 -0
  24. package/public/testfile//351/237/263/351/242/2211.mp3 +0 -0
  25. package/src/App.vue +8 -0
  26. package/src/api/admin/admin-2fa.js +18 -0
  27. package/src/api/admin/admin-download-log.js +37 -0
  28. package/src/api/admin/admin-login-log.js +9 -0
  29. package/src/api/admin/admin-permission.js +9 -0
  30. package/src/api/admin/admin-setting.js +121 -0
  31. package/src/api/admin/admin-share.js +20 -0
  32. package/src/api/admin/admin-short-link.js +49 -0
  33. package/src/api/admin/admin-sso.js +38 -0
  34. package/src/api/admin/admin-storage.js +189 -0
  35. package/src/api/admin/admin-user.js +61 -0
  36. package/src/api/home/common.js +9 -0
  37. package/src/api/home/file-operator.js +89 -0
  38. package/src/api/home/home.js +87 -0
  39. package/src/api/home/install.js +18 -0
  40. package/src/api/home/login.js +9 -0
  41. package/src/api/home/only-office.js +10 -0
  42. package/src/api/home/share.js +115 -0
  43. package/src/api/home/user.js +74 -0
  44. package/src/api/tools/tools-115.js +17 -0
  45. package/src/api/tools/tools-gd.js +13 -0
  46. package/src/api/tools/tools-s3.js +25 -0
  47. package/src/api/tools/tools-sharepoint.js +19 -0
  48. package/src/assets/hero.png +0 -0
  49. package/src/assets/icons/401.svg +1 -0
  50. package/src/assets/icons/403.svg +45 -0
  51. package/src/assets/icons/404.svg +1 -0
  52. package/src/assets/icons/500.svg +1 -0
  53. package/src/assets/icons/admin-login.svg +1 -0
  54. package/src/assets/icons/document.svg +1 -0
  55. package/src/assets/icons/empty.svg +145 -0
  56. package/src/assets/icons/file-type-apk.svg +1 -0
  57. package/src/assets/icons/file-type-archive.svg +1 -0
  58. package/src/assets/icons/file-type-audio.svg +1 -0
  59. package/src/assets/icons/file-type-back.svg +1 -0
  60. package/src/assets/icons/file-type-css.svg +1 -0
  61. package/src/assets/icons/file-type-deb.svg +1 -0
  62. package/src/assets/icons/file-type-dll.svg +1 -0
  63. package/src/assets/icons/file-type-doc.svg +1 -0
  64. package/src/assets/icons/file-type-document.svg +1 -0
  65. package/src/assets/icons/file-type-docx.svg +1 -0
  66. package/src/assets/icons/file-type-exe.svg +1 -0
  67. package/src/assets/icons/file-type-expression.svg +1 -0
  68. package/src/assets/icons/file-type-file.svg +1 -0
  69. package/src/assets/icons/file-type-folder.svg +1 -0
  70. package/src/assets/icons/file-type-html.svg +1 -0
  71. package/src/assets/icons/file-type-image.svg +1 -0
  72. package/src/assets/icons/file-type-java.svg +1 -0
  73. package/src/assets/icons/file-type-js.svg +1 -0
  74. package/src/assets/icons/file-type-less.svg +1 -0
  75. package/src/assets/icons/file-type-md.svg +1 -0
  76. package/src/assets/icons/file-type-office.svg +1 -0
  77. package/src/assets/icons/file-type-pdf.svg +1 -0
  78. package/src/assets/icons/file-type-php.svg +1 -0
  79. package/src/assets/icons/file-type-ppt.svg +1 -0
  80. package/src/assets/icons/file-type-pptx.svg +1 -0
  81. package/src/assets/icons/file-type-py.svg +1 -0
  82. package/src/assets/icons/file-type-rb.svg +1 -0
  83. package/src/assets/icons/file-type-root.svg +1 -0
  84. package/src/assets/icons/file-type-rpm.svg +1 -0
  85. package/src/assets/icons/file-type-rust.svg +1 -0
  86. package/src/assets/icons/file-type-script.svg +1 -0
  87. package/src/assets/icons/file-type-text.svg +1 -0
  88. package/src/assets/icons/file-type-three3d.svg +5 -0
  89. package/src/assets/icons/file-type-vbs.svg +1 -0
  90. package/src/assets/icons/file-type-video.svg +1 -0
  91. package/src/assets/icons/file-type-xls.svg +1 -0
  92. package/src/assets/icons/file-type-xlsx.svg +1 -0
  93. package/src/assets/icons/file-type-xml.svg +1 -0
  94. package/src/assets/icons/file-type-yaml.svg +1 -0
  95. package/src/assets/icons/file-upload.svg +1 -0
  96. package/src/assets/icons/github.svg +1 -0
  97. package/src/assets/icons/install-step.svg +40 -0
  98. package/src/assets/icons/reset-password.svg +1 -0
  99. package/src/assets/icons/storage-aliyun.svg +1 -0
  100. package/src/assets/icons/storage-baidu.svg +1 -0
  101. package/src/assets/icons/storage-doge-cloud.svg +207 -0
  102. package/src/assets/icons/storage-ftp.svg +13 -0
  103. package/src/assets/icons/storage-google-drive.svg +8 -0
  104. package/src/assets/icons/storage-huawei.svg +1 -0
  105. package/src/assets/icons/storage-local.svg +11 -0
  106. package/src/assets/icons/storage-minio.svg +1 -0
  107. package/src/assets/icons/storage-onedrive-china.svg +18 -0
  108. package/src/assets/icons/storage-onedrive.svg +4 -0
  109. package/src/assets/icons/storage-open115.svg +23 -0
  110. package/src/assets/icons/storage-qiniu.svg +1 -0
  111. package/src/assets/icons/storage-s3.svg +5 -0
  112. package/src/assets/icons/storage-sftp.svg +13 -0
  113. package/src/assets/icons/storage-sharepoint-china.svg +23 -0
  114. package/src/assets/icons/storage-sharepoint.svg +1 -0
  115. package/src/assets/icons/storage-tencent.svg +9 -0
  116. package/src/assets/icons/storage-ufile.svg +14 -0
  117. package/src/assets/icons/storage-upyun.svg +1 -0
  118. package/src/assets/icons/storage-webdav.svg +1 -0
  119. package/src/assets/icons/upload.svg +50 -0
  120. package/src/assets/icons/zfile-basic.svg +17 -0
  121. package/src/assets/icons/zfile-horizontal.svg +16 -0
  122. package/src/assets/icons/zfile.svg +1 -0
  123. package/src/assets/vite.svg +1 -0
  124. package/src/assets/vue.svg +1 -0
  125. package/src/components/HelloWorld.vue +319 -0
  126. package/src/components/common/QrCodePreview.vue +118 -0
  127. package/src/components/common/dialog/ZDialog.vue +171 -0
  128. package/src/components/common/dialog/types.ts +19 -0
  129. package/src/components/common/dialog/useDialog.ts +18 -0
  130. package/src/components/common/dialog/useDialogWithForm.ts +21 -0
  131. package/src/components/copy.vue +133 -0
  132. package/src/components/file/preview/AudioPlayer.vue +333 -0
  133. package/src/components/file/preview/CopyCode.vue +47 -0
  134. package/src/components/file/preview/FileGallery.vue +199 -0
  135. package/src/components/file/preview/ImageViewer.vue +432 -0
  136. package/src/components/file/preview/KkFileViewer.vue +86 -0
  137. package/src/components/file/preview/KkFileViewerDialog.vue +42 -0
  138. package/src/components/file/preview/MarkdownViewer.vue +102 -0
  139. package/src/components/file/preview/MarkdownViewerAsyncLoading.vue +17 -0
  140. package/src/components/file/preview/MarkdownViewerDialogAsyncLoading.vue +12 -0
  141. package/src/components/file/preview/OfficeViewer.vue +76 -0
  142. package/src/components/file/preview/OfficeViewerDialog.vue +55 -0
  143. package/src/components/file/preview/PdfViewer.vue +157 -0
  144. package/src/components/file/preview/PdfViewerDialog.vue +41 -0
  145. package/src/components/file/preview/TextViewer.vue +232 -0
  146. package/src/components/file/preview/TextViewerAsyncLoading.vue +22 -0
  147. package/src/components/file/preview/TextViewerDialog.vue +53 -0
  148. package/src/components/file/preview/Three3dPreview.vue +114 -0
  149. package/src/components/file/preview/Three3dPreviewDialog.vue +50 -0
  150. package/src/components/file/preview/VideoPlayer.vue +341 -0
  151. package/src/components/file/preview/VideoPlayerAsyncLoading.vue +45 -0
  152. package/src/components/file/preview/VideoPlayerDialog.vue +51 -0
  153. package/src/components/file/selectFolder/SelectFolder.vue +208 -0
  154. package/src/components/file/selectFolder/index.ts +50 -0
  155. package/src/components/file/selectFolder/types.ts +5 -0
  156. package/src/components/fileReview/AudioPlayer-copy.vue +333 -0
  157. package/src/components/fileReview/PdfViewer-copy.vue +157 -0
  158. package/src/components/fileReview/TextViewer-copy.vue +44 -0
  159. package/src/components/fileReview/VideoPlayer-copy.vue +341 -0
  160. package/src/components/messageBox/confirm/confirm.vue +137 -0
  161. package/src/components/messageBox/confirm/index.ts +27 -0
  162. package/src/components/messageBox/confirm/types.ts +15 -0
  163. package/src/components/messageBox/messageBox.ts +9 -0
  164. package/src/components/messageBox/prompt/index.ts +27 -0
  165. package/src/components/messageBox/prompt/prompt.vue +178 -0
  166. package/src/components/messageBox/prompt/types.ts +24 -0
  167. package/src/components/vue-codemirror/editor.vue +212 -0
  168. package/src/components/vue-codemirror/encodings.ts +27 -0
  169. package/src/components/vue-codemirror/index.vue +380 -0
  170. package/src/components/vue-codemirror/lang-code/cpp/index.ts +3 -0
  171. package/src/components/vue-codemirror/lang-code/css/index.ts +2 -0
  172. package/src/components/vue-codemirror/lang-code/dockerfile/index.ts +5 -0
  173. package/src/components/vue-codemirror/lang-code/erlang/index.ts +6 -0
  174. package/src/components/vue-codemirror/lang-code/go/index.ts +5 -0
  175. package/src/components/vue-codemirror/lang-code/html/index.ts +3 -0
  176. package/src/components/vue-codemirror/lang-code/java/index.ts +3 -0
  177. package/src/components/vue-codemirror/lang-code/javascript/index.ts +3 -0
  178. package/src/components/vue-codemirror/lang-code/json/index.ts +3 -0
  179. package/src/components/vue-codemirror/lang-code/jsx/index.ts +3 -0
  180. package/src/components/vue-codemirror/lang-code/lua/index.ts +6 -0
  181. package/src/components/vue-codemirror/lang-code/markdown/index.ts +2 -0
  182. package/src/components/vue-codemirror/lang-code/mysql/index.ts +3 -0
  183. package/src/components/vue-codemirror/lang-code/nginx/index.ts +6 -0
  184. package/src/components/vue-codemirror/lang-code/perl/index.ts +5 -0
  185. package/src/components/vue-codemirror/lang-code/pgsql/index.ts +2 -0
  186. package/src/components/vue-codemirror/lang-code/php/index.ts +3 -0
  187. package/src/components/vue-codemirror/lang-code/powershell/index.ts +3 -0
  188. package/src/components/vue-codemirror/lang-code/python/index.ts +2 -0
  189. package/src/components/vue-codemirror/lang-code/r/index.ts +5 -0
  190. package/src/components/vue-codemirror/lang-code/ruby/index.ts +5 -0
  191. package/src/components/vue-codemirror/lang-code/rust/index.ts +2 -0
  192. package/src/components/vue-codemirror/lang-code/shell/index.ts +5 -0
  193. package/src/components/vue-codemirror/lang-code/sql/index.ts +3 -0
  194. package/src/components/vue-codemirror/lang-code/stylus/index.ts +3 -0
  195. package/src/components/vue-codemirror/lang-code/swift/index.ts +4 -0
  196. package/src/components/vue-codemirror/lang-code/toml/index.ts +3 -0
  197. package/src/components/vue-codemirror/lang-code/tsx/index.ts +2 -0
  198. package/src/components/vue-codemirror/lang-code/typescript/index.ts +2 -0
  199. package/src/components/vue-codemirror/lang-code/vb/index.ts +3 -0
  200. package/src/components/vue-codemirror/lang-code/vbscript/index.ts +3 -0
  201. package/src/components/vue-codemirror/lang-code/xml/index.ts +2 -0
  202. package/src/components/vue-codemirror/lang-code/yaml/index.ts +3 -0
  203. package/src/components/vue-codemirror/languages.ts +8 -0
  204. package/src/components/vue-codemirror/themes.ts +5 -0
  205. package/src/components/vue-codemirror/toolbar.vue +183 -0
  206. package/src/components/vue-codemirror/types.ts +12 -0
  207. package/src/components.d.ts +49 -0
  208. package/src/composables/admin/layout/admin-layout.js +53 -0
  209. package/src/composables/admin/link/useLinkSetting.js +16 -0
  210. package/src/composables/admin/sso/baseSsoConfig.js +54 -0
  211. package/src/composables/admin/sso/useSsoConfig.js +176 -0
  212. package/src/composables/admin/storage/storage-copy.js +89 -0
  213. package/src/composables/admin/storage/storage-filter.js +64 -0
  214. package/src/composables/admin/storage/storage-list.js +202 -0
  215. package/src/composables/admin/storage/storage-password.js +101 -0
  216. package/src/composables/admin/storage/storage-readme.js +102 -0
  217. package/src/composables/admin/storage/utils/open115-util.js +61 -0
  218. package/src/composables/admin/useAdminSetting.js +60 -0
  219. package/src/composables/admin/useClientInfo.js +20 -0
  220. package/src/composables/admin/user/user-copy.js +79 -0
  221. package/src/composables/file/useBatchOperatorResult.js +19 -0
  222. package/src/composables/file/useFileContextMenu.js +74 -0
  223. package/src/composables/file/useFileData.js +243 -0
  224. package/src/composables/file/useFileLink.js +175 -0
  225. package/src/composables/file/useFileLoading.js +41 -0
  226. package/src/composables/file/useFileLongPressEvent.js +71 -0
  227. package/src/composables/file/useFileOperator.js +347 -0
  228. package/src/composables/file/useFilePreview.js +99 -0
  229. package/src/composables/file/useFilePwd.js +138 -0
  230. package/src/composables/file/useFileSelect.js +105 -0
  231. package/src/composables/file/useFileShare.js +39 -0
  232. package/src/composables/file/useFileUpload.js +1045 -0
  233. package/src/composables/file/useKkFileViewDialog.js +24 -0
  234. package/src/composables/file/useOfficeViewerDialog.js +20 -0
  235. package/src/composables/file/usePdfViewerDialog.js +22 -0
  236. package/src/composables/file/useShareActions.js +94 -0
  237. package/src/composables/file/useShareTableOperator.js +84 -0
  238. package/src/composables/file/useTableOperator.js +211 -0
  239. package/src/composables/file/useTextViewerDialog.js +22 -0
  240. package/src/composables/file/useThree3dPreviewDialog.js +23 -0
  241. package/src/composables/file/useVideoPlayerDialog.js +19 -0
  242. package/src/composables/header/useHeaderBreadcrumb.js +111 -0
  243. package/src/composables/header/useHeaderStorageList.js +150 -0
  244. package/src/composables/header/useSetting.js +58 -0
  245. package/src/composables/share/useShareData.js +178 -0
  246. package/src/composables/useDarks.ts +4 -0
  247. package/src/composables/useRouterData.js +41 -0
  248. package/src/constant/index.js +193 -0
  249. package/src/http/index.js +153 -0
  250. package/src/http/request.js +31 -0
  251. package/src/main.ts +23 -0
  252. package/src/stores/file-data.ts +108 -0
  253. package/src/stores/global-config.ts +115 -0
  254. package/src/stores/storage-config.ts +123 -0
  255. package/src/style.css +296 -0
  256. package/src/styles/admin.scss +91 -0
  257. package/src/styles/code-editor-variables.scss +52 -0
  258. package/src/styles/element-plus.scss +22 -0
  259. package/src/styles/error-page.css +26 -0
  260. package/src/styles/main.css +142 -0
  261. package/src/styles/tailwind/index.scss +33 -0
  262. package/src/utils/index.ts +7 -0
  263. package/src/utils/models/base.ts +34 -0
  264. package/src/utils/models/file.ts +95 -0
  265. package/src/utils/models/path.ts +117 -0
  266. package/src/utils/models/qrcode.ts +21 -0
  267. package/src/utils/models/time.ts +132 -0
  268. package/src/utils/models/util.ts +42 -0
  269. package/src/utils/models/window.ts +14 -0
  270. package/stats.html +4950 -0
  271. package/tsconfig.app.json +16 -0
  272. package/tsconfig.json +7 -0
  273. package/tsconfig.node.json +26 -0
  274. package/vite.config.ts +57 -0
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <div class="copy-content">
3
+ <!-- 复制按钮 -->
4
+ <div
5
+ class="copy-btn code-data-copy"
6
+ @click="copyMessage">
7
+ <i-ic-baseline-content-copy />
8
+ </div>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup>
13
+ import { toClipboard } from '@soerenmartius/vue3-clipboard'
14
+
15
+ const props = defineProps({
16
+ code: String
17
+ });
18
+
19
+ const copyMessage = () => {
20
+ toClipboard(props.code).then(() => {
21
+ ElMessage.success('复制成功');
22
+ }).catch(() => {
23
+ ElMessage.success('复制失败');
24
+ })
25
+ }
26
+ </script>
27
+
28
+ <style lang="scss" scoped>
29
+ .copy-content {
30
+ height: 0;
31
+ }
32
+ .copy-btn {
33
+ user-select: none;
34
+ opacity: 0;
35
+ position: absolute;
36
+ right: 5px;
37
+ top: 5px;
38
+ cursor: pointer;
39
+ padding: 5px;
40
+ border-radius: 3px;
41
+ transition: 0.3s;
42
+ background: rgba(255, 255, 255, 0.2);
43
+ &:active {
44
+ background: rgba(253, 253, 253, 0.575);
45
+ }
46
+ }
47
+ </style>
@@ -0,0 +1,199 @@
1
+ <template>
2
+ <!-- 画廊模式 -->
3
+ <div class="zfile-gallery-body">
4
+ <div class="zfile-img-body" v-if="transferResult.length > 0">
5
+ <div class="zfile-img-row" v-for="(rowItem, index) in transferResult" :key="index">
6
+ <div class="zfile-img-col"
7
+ @click="openGalleryImage(colItem)"
8
+ :style="{ display: globalConfigStore.zfileConfig.gallery.showInfoMode === 'hover' ? 'flex' : 'block'}"
9
+ v-for="colItem in rowItem">
10
+ <template v-if="colItem?.url">
11
+ <img class="zfile-gallery-img lazyload"
12
+ data-sizes="auto"
13
+ @load="loadImg"
14
+ :class="globalConfigStore.zfileConfig.gallery.roundedBorder ? 'zfile-gallery-img-rounded' : ''"
15
+ :data-src="colItem.url"
16
+ loading="lazy"
17
+ :alt="colItem.name"/>
18
+ <div v-if="globalConfigStore.zfileConfig.gallery.showInfo &&
19
+ globalConfigStore.zfileConfig.gallery.showInfoMode === 'hover'"
20
+ :class="globalConfigStore.zfileConfig.gallery.roundedBorder ? 'zfile-gallery-img-rounded' : ''"
21
+ v-show="loadedList.includes(colItem.name)"
22
+ class="zfile-gallery-img-hover-info">
23
+ <span class="zfile-gallery-img-text">{{colItem.name}}</span>
24
+ <span class="zfile-gallery-img-text">{{fileSizeFormat(colItem.size)}}</span>
25
+ </div>
26
+ <div
27
+ v-show="loadedList.includes(colItem.name)"
28
+ v-if="globalConfigStore.zfileConfig.gallery.showInfo &&
29
+ globalConfigStore.zfileConfig.gallery.showInfoMode === 'bottom'">
30
+ <span class="zfile-gallery-img-text"> {{ colItem.name }} </span>
31
+ </div>
32
+ </template>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ <div v-else class="h-full">
37
+ <el-empty class="h-full" description="当前文件夹无图片">
38
+ <el-button type="primary" @click="fileDataStore.imgMode = false">退出画廊模式</el-button>
39
+ </el-empty>
40
+ </div>
41
+ </div>
42
+ </template>
43
+
44
+ <script setup>
45
+ import { fileSizeFormat } from "~/utils";
46
+ import {computed, reactive, ref} from "vue";
47
+
48
+
49
+ import 'lazysizes';
50
+ import useGlobalConfigStore from "~/stores/global-config";
51
+ let globalConfigStore = useGlobalConfigStore();
52
+
53
+ import useFileDataStore from "~/stores/file-data";
54
+ let fileDataStore = useFileDataStore();
55
+
56
+ // 计算图片二维列表, 多行 * 多列
57
+ const transferResult = computed(() => {
58
+ // 获取图片列表
59
+ let imgList = fileDataStore.filterFileByType('image');
60
+
61
+ if (imgList.length === 0) {
62
+ return [];
63
+ }
64
+
65
+ // 图片二维数组, 表示每行每列的图片
66
+ let imgArray = ref([]);
67
+
68
+ // 图片列数
69
+ let galleryColumn = globalConfigStore.zfileConfig.gallery.column;
70
+
71
+ // 当前行数
72
+ let currRow = 0;
73
+
74
+ imgList.forEach((item, index) => {
75
+ if (index % galleryColumn === 0) {
76
+ if (index !== 0) {
77
+ currRow++;
78
+ }
79
+ imgArray.value[currRow] = [];
80
+ }
81
+ imgArray.value[currRow].push(item);
82
+ })
83
+
84
+ // 图片行转列函数
85
+ function transfer(oldArr) {
86
+ return oldArr[0].map((col, i) => oldArr.map(row => row[i]));
87
+ }
88
+
89
+ // 图片行转列完成后的数组
90
+ return transfer(imgArray.value);
91
+ })
92
+
93
+ // 图片行间距 px
94
+ let galleryRowSpacingPx = computed(() => {
95
+ return globalConfigStore.zfileConfig.gallery.rowSpacing + 'px';
96
+ });
97
+
98
+ // 图片列间距 px
99
+ let galleryColSpacingPx = computed(() => {
100
+ return `0 ${globalConfigStore.zfileConfig.gallery.columnSpacing / 2}px`;
101
+ });
102
+
103
+ // 图片外部容器宽度, 100 / 图片列数
104
+ let galleryRowWidth = computed(() => {
105
+ // 图片列数
106
+ let galleryColumn = globalConfigStore.zfileConfig.gallery.column;
107
+ let galleryColumnSpacing = globalConfigStore.zfileConfig.gallery.columnSpacing;
108
+ return `calc(calc(100% / ${globalConfigStore.zfileConfig.gallery.column}) - calc(${(galleryColumn - 1) * galleryColumnSpacing}px / ${galleryColumn}))`;
109
+ })
110
+
111
+
112
+ // 引入文件预览组件
113
+ import useFilePreview from '~/composables/file/useFilePreview';
114
+ const { openImage } = useFilePreview();
115
+ const openGalleryImage = (item) => {
116
+ if (!globalConfigStore.zfileConfig.imagePreview.gallery) {
117
+ return;
118
+ }
119
+ if (loadedList.includes(item.name)) {
120
+ openImage(item);
121
+ }
122
+ }
123
+
124
+ // 已加载完的图片列表, 已加载完才悬浮显示标题
125
+ let loadedList = reactive([]);
126
+ const loadImg = (e) => {
127
+ loadedList.push(e.currentTarget.alt);
128
+ }
129
+
130
+ </script>
131
+
132
+ <style lang="scss" scoped>
133
+
134
+ .zfile-gallery-body {
135
+ height: 100%;
136
+ margin-top: 10px;
137
+
138
+ .zfile-img-body {
139
+ @apply flex h-full flex-wrap;
140
+ }
141
+
142
+ .zfile-img-row {
143
+ width: v-bind('galleryRowWidth');
144
+ margin: v-bind('galleryColSpacingPx');
145
+
146
+ &:first-child {
147
+ margin-left: 0;
148
+ }
149
+
150
+ &:last-child {
151
+ margin-right: 0;
152
+ }
153
+
154
+ }
155
+
156
+ .zfile-img-col {
157
+ @apply flex overflow-hidden relative text-center;
158
+ margin-bottom: v-bind('galleryRowSpacingPx');
159
+
160
+ .zfile-gallery-img {
161
+ @apply border;
162
+ }
163
+
164
+ .zfile-gallery-img:not(.loaded) {
165
+ @apply w-full;
166
+ }
167
+
168
+ .zfile-gallery-img:not(.lazyloaded) {
169
+ @apply min-h-[150px];
170
+ }
171
+
172
+ .zfile-gallery-img-rounded {
173
+ @apply rounded-lg;
174
+ }
175
+
176
+ .zfile-gallery-img-text {
177
+ @apply overflow-ellipsis overflow-hidden whitespace-nowrap text-sm opacity-70;
178
+ }
179
+
180
+ .zfile-gallery-img-hover-info {
181
+ @apply absolute top-0 h-1/2 left-0 right-0 text-sm p-2
182
+ transition-opacity duration-300
183
+ flex justify-between
184
+ text-white space-x-10 opacity-0;
185
+ background: linear-gradient(180deg,rgba(0,0,0,.6),transparent 120px);
186
+
187
+ .zfile-gallery-img-text:last-child {
188
+ @apply text-right min-w-fit;
189
+ }
190
+ }
191
+
192
+ &:hover .zfile-gallery-img-hover-info {
193
+ @apply opacity-100;
194
+ }
195
+ }
196
+
197
+ }
198
+
199
+ </style>
@@ -0,0 +1,432 @@
1
+ <template>
2
+ <div class="image-preview-container">
3
+ <div ref="containerRef" class="preview-area">
4
+ <img :src="props.fileUrl" alt="">
5
+ </div>
6
+ </div>
7
+ </template>
8
+
9
+ <script setup>
10
+ import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
11
+ import { ElMessage } from 'element-plus'
12
+
13
+ // ==================== Props ====================
14
+ const props = defineProps({
15
+ // 单个图片 URL
16
+ fileUrl: {
17
+ type: String,
18
+ default: ''
19
+ },
20
+ // 单个图片名称
21
+ fileName: {
22
+ type: String,
23
+ default: ''
24
+ },
25
+ // 图片列表(可选)
26
+ imageList: {
27
+ type: Array,
28
+ default: () => []
29
+ },
30
+ // 默认显示索引
31
+ defaultIndex: {
32
+ type: Number,
33
+ default: 0
34
+ },
35
+ // 是否显示工具栏
36
+ showToolbar: {
37
+ type: Boolean,
38
+ default: true
39
+ },
40
+ // 是否显示缩略图
41
+ showThumbnail: {
42
+ type: Boolean,
43
+ default: true
44
+ },
45
+ // 初始缩放比例
46
+ initialZoom: {
47
+ type: Number,
48
+ default: 100
49
+ },
50
+ // 是否支持拖拽
51
+ draggable: {
52
+ type: Boolean,
53
+ default: true
54
+ },
55
+ // 是否支持鼠标滚轮缩放
56
+ wheelZoom: {
57
+ type: Boolean,
58
+ default: true
59
+ }
60
+ })
61
+
62
+ // ==================== Emits ====================
63
+ const emit = defineEmits(['load', 'error', 'switch', 'zoom', 'rotate'])
64
+
65
+ // ==================== 响应式数据 ====================
66
+ const containerRef = ref(null)
67
+ const currentImage = ref({ url: '', name: '' })
68
+ const currentIndex = ref(0)
69
+ const zoomLevel = ref(props.initialZoom)
70
+ const rotation = ref(0)
71
+ const translateX = ref(0)
72
+ const translateY = ref(0)
73
+
74
+ // 拖拽状态
75
+ const isDragging = ref(false)
76
+ const startX = ref(0)
77
+ const startY = ref(0)
78
+
79
+ // ==================== 计算属性 ====================
80
+ // 获取图片列表
81
+ const getImageList = () => {
82
+ if (props.imageList && props.imageList.length > 0) {
83
+ return props.imageList.map(item => ({
84
+ name: item.name || item.imageName || '未知图片',
85
+ url: item.url || item.imageUrl || item.path
86
+ }))
87
+ }
88
+
89
+ if (props.imageUrl) {
90
+ return [{ name: props.imageName || '图片', url: props.imageUrl }]
91
+ }
92
+
93
+ return []
94
+ }
95
+
96
+ // 图片样式
97
+ const imageStyle = computed(() => ({
98
+ transform: `
99
+ scale(${zoomLevel.value / 100})
100
+ rotate(${rotation.value}deg)
101
+ translate(${translateX.value}px, ${translateY.value}px)
102
+ `,
103
+ cursor: props.draggable ? (isDragging.value ? 'grabbing' : 'grab') : 'default'
104
+ }))
105
+
106
+ // ==================== 方法 ====================
107
+ // 格式化单个图片
108
+ const formatSingleImage = (url, name) => {
109
+ return {
110
+ name: name || '未知图片',
111
+ url: url
112
+ }
113
+ }
114
+
115
+ // 初始化图片
116
+ const initImagePreview = async () => {
117
+ const list = getImageList()
118
+
119
+ if (!list || list.length === 0) {
120
+ console.warn('图片列表为空')
121
+ return
122
+ }
123
+
124
+ // 确保索引有效
125
+ let index = props.defaultIndex
126
+ if (index < 0 || index >= list.length) {
127
+ index = 0
128
+ }
129
+
130
+ currentIndex.value = index
131
+ currentImage.value = list[index]
132
+
133
+ await nextTick()
134
+ reset()
135
+ }
136
+
137
+ // 图片加载完成
138
+ const handleImageLoad = (e) => {
139
+ emit('load', {
140
+ ...currentImage.value,
141
+ width: e.target.naturalWidth,
142
+ height: e.target.naturalHeight
143
+ })
144
+ }
145
+
146
+ // 图片加载失败
147
+ const handleImageError = (e) => {
148
+ emit('error', {
149
+ ...currentImage.value,
150
+ error: e
151
+ })
152
+ ElMessage.error('图片加载失败,请检查文件路径或格式')
153
+ }
154
+
155
+ // 放大
156
+ const zoomIn = () => {
157
+ if (zoomLevel.value < 500) {
158
+ zoomLevel.value += 25
159
+ emit('zoom', zoomLevel.value)
160
+ }
161
+ }
162
+
163
+ // 缩小
164
+ const zoomOut = () => {
165
+ if (zoomLevel.value > 25) {
166
+ zoomLevel.value -= 25
167
+ emit('zoom', zoomLevel.value)
168
+ }
169
+ }
170
+
171
+ // 向左旋转
172
+ const rotateLeft = () => {
173
+ rotation.value -= 90
174
+ emit('rotate', rotation.value)
175
+ }
176
+
177
+ // 向右旋转
178
+ const rotateRight = () => {
179
+ rotation.value += 90
180
+ emit('rotate', rotation.value)
181
+ }
182
+
183
+ // 重置
184
+ const reset = () => {
185
+ zoomLevel.value = props.initialZoom
186
+ rotation.value = 0
187
+ translateX.value = 0
188
+ translateY.value = 0
189
+ emit('zoom', zoomLevel.value)
190
+ emit('rotate', rotation.value)
191
+ }
192
+
193
+ // 上一张
194
+ const prevImage = () => {
195
+ if (currentIndex.value > 0) {
196
+ switchImage(currentIndex.value - 1)
197
+ }
198
+ }
199
+
200
+ // 下一张
201
+ const nextImage = () => {
202
+ if (currentIndex.value < getImageList().length - 1) {
203
+ switchImage(currentIndex.value + 1)
204
+ }
205
+ }
206
+
207
+ // 切换图片
208
+ const switchImage = (index) => {
209
+ const list = getImageList()
210
+ if (index >= 0 && index < list.length) {
211
+ currentIndex.value = index
212
+ currentImage.value = list[index]
213
+ reset()
214
+ emit('switch', { ...currentImage.value, index })
215
+ }
216
+ }
217
+
218
+ // ==================== 拖拽相关 ====================
219
+ const handleMouseDown = (e) => {
220
+ if (!props.draggable) return
221
+ isDragging.value = true
222
+ startX.value = e.clientX - translateX.value
223
+ startY.value = e.clientY - translateY.value
224
+ }
225
+
226
+ const handleMouseMove = (e) => {
227
+ if (!isDragging.value) return
228
+ e.preventDefault()
229
+ translateX.value = e.clientX - startX.value
230
+ translateY.value = e.clientY - startY.value
231
+ }
232
+
233
+ const handleMouseUp = () => {
234
+ isDragging.value = false
235
+ }
236
+
237
+ // ==================== 滚轮缩放 ====================
238
+ const handleWheel = (e) => {
239
+ if (!props.wheelZoom) return
240
+ e.preventDefault()
241
+
242
+ if (e.deltaY < 0) {
243
+ zoomIn()
244
+ } else {
245
+ zoomOut()
246
+ }
247
+ }
248
+
249
+ // ==================== 监听器 ====================
250
+ // 监听单个图片 URL 变化
251
+ watch(() => [props.imageUrl, props.imageName], async () => {
252
+ if (props.imageUrl) {
253
+ await initImagePreview()
254
+ }
255
+ }, { immediate: true })
256
+
257
+ // 监听图片列表变化
258
+ watch(() => props.imageList, async (newList) => {
259
+ if (newList && newList.length > 0) {
260
+ await initImagePreview()
261
+ }
262
+ }, { deep: true })
263
+
264
+ // 监听默认索引变化
265
+ watch(() => props.defaultIndex, (newIndex) => {
266
+ const list = getImageList()
267
+ if (newIndex >= 0 && newIndex < list.length) {
268
+ switchImage(newIndex)
269
+ }
270
+ })
271
+
272
+ // ==================== 生命周期 ====================
273
+ onMounted(async () => {
274
+ await nextTick()
275
+ if (props.imageUrl || (props.imageList && props.imageList.length > 0)) {
276
+ await initImagePreview()
277
+ }
278
+ })
279
+
280
+ // ==================== 暴露方法 ====================
281
+ defineExpose({
282
+ zoomIn,
283
+ zoomOut,
284
+ rotateLeft,
285
+ rotateRight,
286
+ reset,
287
+ prevImage,
288
+ nextImage,
289
+ switchImage,
290
+ getCurrentImage: () => currentImage.value,
291
+ getCurrentIndex: () => currentIndex.value
292
+ })
293
+ </script>
294
+
295
+ <style scoped lang="scss">
296
+ .image-preview-container {
297
+ width: 100%;
298
+ height: 100%;
299
+ min-height: 400px;
300
+ background-color: #f5f5f5;
301
+ border-radius: 8px;
302
+ display: flex;
303
+ flex-direction: column;
304
+ overflow: hidden;
305
+
306
+ .preview-area {
307
+ flex: 1;
308
+ display: flex;
309
+ align-items: center;
310
+ justify-content: center;
311
+ overflow: hidden;
312
+ position: relative;
313
+ background-color: #fff;
314
+
315
+ .image-wrapper {
316
+ display: flex;
317
+ align-items: center;
318
+ justify-content: center;
319
+ transition: transform 0.2s ease;
320
+ transform-origin: center center;
321
+
322
+ .preview-image {
323
+ max-width: 100%;
324
+ max-height: 100%;
325
+ object-fit: contain;
326
+ user-select: none;
327
+ -webkit-user-drag: none;
328
+ }
329
+ }
330
+
331
+ .empty-state {
332
+ color: #999;
333
+ font-size: 14px;
334
+ }
335
+ }
336
+
337
+ .toolbar {
338
+ display: flex;
339
+ align-items: center;
340
+ justify-content: center;
341
+ gap: 12px;
342
+ padding: 12px;
343
+ background-color: #fff;
344
+ border-top: 1px solid #e0e0e0;
345
+
346
+ button {
347
+ width: 36px;
348
+ height: 36px;
349
+ border: 1px solid #dcdfe6;
350
+ background-color: #fff;
351
+ border-radius: 4px;
352
+ cursor: pointer;
353
+ display: flex;
354
+ align-items: center;
355
+ justify-content: center;
356
+ transition: all 0.2s;
357
+
358
+ &:hover {
359
+ background-color: #23ade5;
360
+ border-color: #23ade5;
361
+ color: #fff;
362
+ }
363
+
364
+ &:disabled {
365
+ opacity: 0.5;
366
+ cursor: not-allowed;
367
+ }
368
+
369
+ span {
370
+ font-size: 16px;
371
+ font-weight: bold;
372
+ }
373
+ }
374
+
375
+ .zoom-level {
376
+ min-width: 50px;
377
+ text-align: center;
378
+ font-size: 14px;
379
+ color: #666;
380
+ }
381
+
382
+ .pagination {
383
+ display: flex;
384
+ align-items: center;
385
+ gap: 8px;
386
+ margin-left: 16px;
387
+
388
+ .page-info {
389
+ font-size: 14px;
390
+ color: #666;
391
+ min-width: 60px;
392
+ text-align: center;
393
+ }
394
+ }
395
+ }
396
+
397
+ .thumbnail-list {
398
+ display: flex;
399
+ gap: 8px;
400
+ padding: 12px;
401
+ background-color: #fff;
402
+ border-top: 1px solid #e0e0e0;
403
+ overflow-x: auto;
404
+
405
+ .thumbnail-item {
406
+ width: 60px;
407
+ height: 60px;
408
+ border: 2px solid transparent;
409
+ border-radius: 4px;
410
+ overflow: hidden;
411
+ cursor: pointer;
412
+ flex-shrink: 0;
413
+ transition: all 0.2s;
414
+
415
+ &:hover {
416
+ border-color: #23ade5;
417
+ }
418
+
419
+ &.active {
420
+ border-color: #23ade5;
421
+ box-shadow: 0 0 8px rgba(35, 173, 229, 0.4);
422
+ }
423
+
424
+ img {
425
+ width: 100%;
426
+ height: 100%;
427
+ object-fit: cover;
428
+ }
429
+ }
430
+ }
431
+ }
432
+ </style>