Browse Source

文件上传、文件预览

liurongli 14 giờ trước cách đây
mục cha
commit
4bf5cc47a8
100 tập tin đã thay đổi với 23292 bổ sung150 xóa
  1. 44 0
      common/common.js
  2. 79 6
      components/popupPreviewFile.vue
  3. 278 0
      components/wg-h5-preview-file/wg-h5-preview-file.vue
  4. 9 2
      manifest.json
  5. 84 26
      package.json
  6. 89 116
      pages/materialCollection/submitMaterial/submitDetail.vue
  7. 87 0
      readme.md
  8. 115 0
      static/hybrid/excel.html
  9. 3 0
      static/hybrid/excel/iconfont.css
  10. 22212 0
      static/hybrid/excel/luckyexcel.umd.js
  11. 0 0
      static/hybrid/excel/luckysheet.css
  12. 0 0
      static/hybrid/excel/luckysheet.umd.js
  13. 0 0
      static/hybrid/excel/plugin.js
  14. 3 0
      static/hybrid/excel/plugins.css
  15. 0 0
      static/hybrid/excel/pluginsCss.css
  16. 279 0
      static/hybrid/excel/viewer.properties
  17. BIN
      static/hybrid/pdf/web/cmaps/78-EUC-H.bcmap
  18. BIN
      static/hybrid/pdf/web/cmaps/78-EUC-V.bcmap
  19. BIN
      static/hybrid/pdf/web/cmaps/78-H.bcmap
  20. BIN
      static/hybrid/pdf/web/cmaps/78-RKSJ-H.bcmap
  21. BIN
      static/hybrid/pdf/web/cmaps/78-RKSJ-V.bcmap
  22. BIN
      static/hybrid/pdf/web/cmaps/78-V.bcmap
  23. BIN
      static/hybrid/pdf/web/cmaps/78ms-RKSJ-H.bcmap
  24. BIN
      static/hybrid/pdf/web/cmaps/78ms-RKSJ-V.bcmap
  25. BIN
      static/hybrid/pdf/web/cmaps/83pv-RKSJ-H.bcmap
  26. BIN
      static/hybrid/pdf/web/cmaps/90ms-RKSJ-H.bcmap
  27. BIN
      static/hybrid/pdf/web/cmaps/90ms-RKSJ-V.bcmap
  28. BIN
      static/hybrid/pdf/web/cmaps/90msp-RKSJ-H.bcmap
  29. BIN
      static/hybrid/pdf/web/cmaps/90msp-RKSJ-V.bcmap
  30. BIN
      static/hybrid/pdf/web/cmaps/90pv-RKSJ-H.bcmap
  31. BIN
      static/hybrid/pdf/web/cmaps/90pv-RKSJ-V.bcmap
  32. BIN
      static/hybrid/pdf/web/cmaps/Add-H.bcmap
  33. BIN
      static/hybrid/pdf/web/cmaps/Add-RKSJ-H.bcmap
  34. BIN
      static/hybrid/pdf/web/cmaps/Add-RKSJ-V.bcmap
  35. BIN
      static/hybrid/pdf/web/cmaps/Add-V.bcmap
  36. BIN
      static/hybrid/pdf/web/cmaps/Adobe-CNS1-0.bcmap
  37. BIN
      static/hybrid/pdf/web/cmaps/Adobe-CNS1-1.bcmap
  38. BIN
      static/hybrid/pdf/web/cmaps/Adobe-CNS1-2.bcmap
  39. BIN
      static/hybrid/pdf/web/cmaps/Adobe-CNS1-3.bcmap
  40. BIN
      static/hybrid/pdf/web/cmaps/Adobe-CNS1-4.bcmap
  41. BIN
      static/hybrid/pdf/web/cmaps/Adobe-CNS1-5.bcmap
  42. BIN
      static/hybrid/pdf/web/cmaps/Adobe-CNS1-6.bcmap
  43. BIN
      static/hybrid/pdf/web/cmaps/Adobe-CNS1-UCS2.bcmap
  44. BIN
      static/hybrid/pdf/web/cmaps/Adobe-GB1-0.bcmap
  45. BIN
      static/hybrid/pdf/web/cmaps/Adobe-GB1-1.bcmap
  46. BIN
      static/hybrid/pdf/web/cmaps/Adobe-GB1-2.bcmap
  47. BIN
      static/hybrid/pdf/web/cmaps/Adobe-GB1-3.bcmap
  48. BIN
      static/hybrid/pdf/web/cmaps/Adobe-GB1-4.bcmap
  49. BIN
      static/hybrid/pdf/web/cmaps/Adobe-GB1-5.bcmap
  50. BIN
      static/hybrid/pdf/web/cmaps/Adobe-GB1-UCS2.bcmap
  51. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Japan1-0.bcmap
  52. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Japan1-1.bcmap
  53. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Japan1-2.bcmap
  54. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Japan1-3.bcmap
  55. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Japan1-4.bcmap
  56. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Japan1-5.bcmap
  57. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Japan1-6.bcmap
  58. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Japan1-UCS2.bcmap
  59. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Korea1-0.bcmap
  60. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Korea1-1.bcmap
  61. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Korea1-2.bcmap
  62. BIN
      static/hybrid/pdf/web/cmaps/Adobe-Korea1-UCS2.bcmap
  63. BIN
      static/hybrid/pdf/web/cmaps/B5-H.bcmap
  64. BIN
      static/hybrid/pdf/web/cmaps/B5-V.bcmap
  65. BIN
      static/hybrid/pdf/web/cmaps/B5pc-H.bcmap
  66. BIN
      static/hybrid/pdf/web/cmaps/B5pc-V.bcmap
  67. BIN
      static/hybrid/pdf/web/cmaps/CNS-EUC-H.bcmap
  68. BIN
      static/hybrid/pdf/web/cmaps/CNS-EUC-V.bcmap
  69. BIN
      static/hybrid/pdf/web/cmaps/CNS1-H.bcmap
  70. BIN
      static/hybrid/pdf/web/cmaps/CNS1-V.bcmap
  71. BIN
      static/hybrid/pdf/web/cmaps/CNS2-H.bcmap
  72. 3 0
      static/hybrid/pdf/web/cmaps/CNS2-V.bcmap
  73. BIN
      static/hybrid/pdf/web/cmaps/ETHK-B5-H.bcmap
  74. BIN
      static/hybrid/pdf/web/cmaps/ETHK-B5-V.bcmap
  75. BIN
      static/hybrid/pdf/web/cmaps/ETen-B5-H.bcmap
  76. BIN
      static/hybrid/pdf/web/cmaps/ETen-B5-V.bcmap
  77. 3 0
      static/hybrid/pdf/web/cmaps/ETenms-B5-H.bcmap
  78. BIN
      static/hybrid/pdf/web/cmaps/ETenms-B5-V.bcmap
  79. BIN
      static/hybrid/pdf/web/cmaps/EUC-H.bcmap
  80. BIN
      static/hybrid/pdf/web/cmaps/EUC-V.bcmap
  81. BIN
      static/hybrid/pdf/web/cmaps/Ext-H.bcmap
  82. BIN
      static/hybrid/pdf/web/cmaps/Ext-RKSJ-H.bcmap
  83. BIN
      static/hybrid/pdf/web/cmaps/Ext-RKSJ-V.bcmap
  84. BIN
      static/hybrid/pdf/web/cmaps/Ext-V.bcmap
  85. BIN
      static/hybrid/pdf/web/cmaps/GB-EUC-H.bcmap
  86. BIN
      static/hybrid/pdf/web/cmaps/GB-EUC-V.bcmap
  87. 4 0
      static/hybrid/pdf/web/cmaps/GB-H.bcmap
  88. BIN
      static/hybrid/pdf/web/cmaps/GB-V.bcmap
  89. BIN
      static/hybrid/pdf/web/cmaps/GBK-EUC-H.bcmap
  90. BIN
      static/hybrid/pdf/web/cmaps/GBK-EUC-V.bcmap
  91. BIN
      static/hybrid/pdf/web/cmaps/GBK2K-H.bcmap
  92. BIN
      static/hybrid/pdf/web/cmaps/GBK2K-V.bcmap
  93. BIN
      static/hybrid/pdf/web/cmaps/GBKp-EUC-H.bcmap
  94. BIN
      static/hybrid/pdf/web/cmaps/GBKp-EUC-V.bcmap
  95. BIN
      static/hybrid/pdf/web/cmaps/GBT-EUC-H.bcmap
  96. BIN
      static/hybrid/pdf/web/cmaps/GBT-EUC-V.bcmap
  97. BIN
      static/hybrid/pdf/web/cmaps/GBT-H.bcmap
  98. BIN
      static/hybrid/pdf/web/cmaps/GBT-V.bcmap
  99. BIN
      static/hybrid/pdf/web/cmaps/GBTpc-EUC-H.bcmap
  100. BIN
      static/hybrid/pdf/web/cmaps/GBTpc-EUC-V.bcmap

+ 44 - 0
common/common.js

@@ -478,4 +478,48 @@ export const permTreeData = (list) => {
     })
 
     return arr
+}
+/**
+ * 根据文件后缀获取文件类型
+ * @param {string} fileType 文件后缀,如 "jpg"
+ * @returns {'image'|'video'|'audio'|'document'|''}
+ */
+export const getFileTypeBySuffix = (fileType) => {
+	// 图片后缀
+	const IMAGE_EXTS = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico', 'tiff'];
+	// 视频后缀
+	const VIDEO_EXTS = ['mp4', 'mov', 'avi', 'mkv', 'flv', 'wmv', 'rmvb', 'mpeg', 'mpg', 'webm'];
+	// 音频后缀
+	const AUDIO_EXTS = ['mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a', 'wma'];
+	// 文档后缀
+	const DOC_EXTS = ['doc', 'docx'];
+	const XLS_EXTS = ['xls', 'xlsx'];
+	const PPT_EXTS = ['ppt', 'pptx'];
+	const PDF_EXTS = ['pdf'];
+	const TXT_EXTS = ['txt'];
+	
+	if (!fileType) return '';
+	
+	// 后缀,转小写
+	const ext = fileType.toLowerCase();
+
+	if(IMAGE_EXTS.includes(ext)){
+		return 'image'
+	}else if (VIDEO_EXTS.includes(ext)){
+		return 'video'
+	}else if (AUDIO_EXTS.includes(ext)){
+		return 'audio'
+	}else if (DOC_EXTS.includes(ext)){
+		return 'word'
+	}else if (XLS_EXTS.includes(ext)){
+		return 'excel'
+	}else if (PPT_EXTS.includes(ext)){
+		return 'pptx'
+	}else if (PDF_EXTS.includes(ext)){
+		return 'pdf'
+	}else if (TXT_EXTS.includes(ext)){
+		return 'txt'
+	}else{
+		return ''
+	}
 }

+ 79 - 6
components/popupPreviewFile.vue

@@ -1,32 +1,88 @@
 <template>
 	<uni-popup ref="popupDialog" :animation="false" :is-mask-click="isMaskClick">
 		<uni-icons @click="CloseDialog" class="colse" type="closeempty" size="24"></uni-icons>
-		<image class="img_width" @click="CloseDialog" :src="filePath" mode="aspectFit"></image>
-	</uni-popup>	
+		<!-- 图片 -->
+		<image v-if="fileTypeBySuffix=='image'" class="img_width" @click="CloseDialog" :src="filePath" mode="aspectFit"></image>
+		<video v-else-if="fileTypeBySuffix=='video'" id="fileVideo" :src="filePath" @error="videoErrorCallback"></video>
+		<audio v-else-if="fileTypeBySuffix=='audio'" :src="filePath"></audio>
+		<template v-else-if="['word','excel','pptx','pdf','txt'].includes(fileTypeBySuffix)">
+			<template v-if="uniPlatform=='web'">
+				<!-- 默认指H5端 此插件只支持h5 PDF、Word、Excel、PPTX、TXT预览-->
+				<wg-h5-preview-file ref="webPreviewFile" :value="filePath" :name="fileName" :type="fileTypeBySuffix" @CloseDialog="CloseDialog" />
+			</template>
+			<template v-else-if="uniPlatform=='app'">
+				<!-- App端 -->
+			</template>
+			<template v-else-if="uniPlatform=='mp-weixin'">
+				<!-- 微信小程序端 -->
+			</template>
+			<template v-else>
+				<view class="other_file"><text>不支持预览,请点击下载: </text><text style="color: #2e64fa;font-size: 28rpx;" @click="ToDownloadFile">{{fileName}}</text></view>
+			</template>
+		</template>
+		<template v-else>
+			<view class="other_file"><text>不支持预览,请点击下载: </text><text style="color: #2e64fa;font-size: 28rpx;" @click="ToDownloadFile">{{fileName}}</text></view>
+		</template>
+	</uni-popup>
 </template>
 
 <script setup>
 	import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue';
-	import {
-		ref
-	} from 'vue';
+	import wgH5PreviewFile from '@/components/wg-h5-preview-file/wg-h5-preview-file.vue';
+	import { getFileTypeBySuffix } from '@/common/common.js';
+	import {computed, reactive,ref,nextTick} from 'vue';
 	const props = defineProps({
 		isMaskClick:{//蒙版点击是否关闭弹窗
 			type: Boolean,
 			default: true
 		},
+		fileType:{//文件后缀
+			type: String,
+			default: ''
+		},
 		filePath:{//文件路径
 			type: String,
 			default: ''
-		}
+		},
+		fileName:{//文件名称
+			type: String,
+			default: ''
+		},
 	})
+	const emit = defineEmits(['ToDownloadFile']);//下载
+	const fileTypeBySuffix = computed(()=>{//获取文件类型
+		return getFileTypeBySuffix(props.fileType)
+	});
+	const uniPlatform = uni.getSystemInfoSync().uniPlatform;// 运行平台
+	const state = reactive({});
 	const popupDialog = ref(null);
+	const webPreviewFile = ref(null);//web端文件预览
 	//打开弹窗
 	const OpenDialog = () => {
 		popupDialog.value?.open();
+		if(['word','excel','pptx','pdf','txt'].includes(fileTypeBySuffix.value)){
+			if(uniPlatform=='web'){
+				nextTick(()=>{
+					webPreviewFile.value && webPreviewFile.value.preview({
+						url: props.filePath,
+						type: fileTypeBySuffix.value
+					});
+				})
+			}
+			
+		}
 	}
+	//下载
+	const ToDownloadFile = () => {
+		emit('ToDownloadFile',{
+			fileType:props.fileType,
+			filePath:props.filePath,
+			fileName:props.fileName
+		})
+	};
 	//关闭弹框
 	const CloseDialog = () => {
+		console.log(121212)
 		popupDialog.value?.close();
 	}
 	defineExpose({
@@ -42,6 +98,23 @@
 		width: 750rpx;
 		height: 80vh;
 	}
+	.other_file{
+		margin: 0 auto;
+		background: #ffffff;
+		min-height: 160rpx;
+		height: auto;
+		display: flex;
+		width: 670rpx;
+		box-sizing: border-box;
+		padding: 20rpx;
+		border-radius: 20rpx;
+		flex-wrap: wrap;
+		align-items: center;
+		justify-content: center;
+		text-align: center;
+		font-size: 28rpx;
+		color: #333;
+	}
 	.colse{
 		position: fixed;
 		box-sizing: border-box;

+ 278 - 0
components/wg-h5-preview-file/wg-h5-preview-file.vue

@@ -0,0 +1,278 @@
+<template>
+    <view class="wrapper" v-if="webviewUrl || value">
+        <view class="close" @click="close">
+			<uni-icons v-if="!$slots.close" class="close-icon" type="close" size="40" style="color: #999999;"></uni-icons>	
+        </view>
+        <view class="wg-h5-preview-file-container" :style="containerStyle">
+            <view class="webview">
+                <!-- #ifdef H5 -->
+                <iframe
+                    class="wg-h5-preview-file-iframe"
+                    :webview-styles="webviewStyles"
+                    :src="iframeUrl" />
+                <!-- #endif -->
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+const urlMap = {
+    word: "/static/hybrid/word.html?file=",
+    pdf: "/static/hybrid/pdf/web/viewer.html?file=",
+    excel: "/static/hybrid/excel.html?file=",
+    txt: "/static/hybrid/txt.html?file=",
+    pptx: "/static/hybrid/pptx.html?file=",
+};
+export default {
+    name: "WgH5PreviewFile",
+    props: {
+        value: {
+            type: String,
+            required: true,
+        },
+        type: {
+            type: String,
+            required: true,
+            validator(v) {
+                return ["excel", "pdf", "word", "txt", "pptx"].includes(v);
+            },
+        },
+    },
+    computed: {
+        iframeUrl() {
+            return (
+                this.webviewUrl || this.buildPreviewUrl(this.type, this.value)
+            );
+        },
+        containerStyle() {
+            // 动态设置容器的实际可视高度,避免 100vh 在移动端地址栏收起/展开导致的遮挡问题
+            const style = {
+                height:
+                    this.containerHeight > 0
+                        ? `${this.containerHeight}px`
+                        : "100vh",
+            };
+            // 当不支持 CSS env/constant(safe-area-inset-bottom) 时,用内联 padding 兜底
+            if (!this.cssSafeAreaSupported && this.safeBottom > 0) {
+                style.paddingBottom = `${this.safeBottom}px`;
+            }
+            return style;
+        },
+    },
+    data() {
+        return {
+            safeBottom: 0,
+            webviewUrl: null,
+            webviewStyles: {
+                zIndex: "9999",
+            },
+            // 实际可视区域高度(px)
+            containerHeight: 0,
+            // 是否支持 CSS 安全区变量
+            cssSafeAreaSupported: false,
+        };
+    },
+    methods: {
+        preview({ url, type }) {
+            this.webviewUrl = this.buildPreviewUrl(type, url);
+        },
+        buildPreviewUrl(fileType, url = "") {
+            const relativePath = urlMap[fileType] || urlMap.word;
+            const sanitizedPath = relativePath.replace(/^\//, "");
+            const basePath = this.getPreviewBasePath();
+            return `${basePath}${sanitizedPath}${encodeURIComponent(
+                url || this.value
+            )}&safeBottom=${this.safeBottom}`;
+        },
+        getPreviewBasePath() {
+            if (typeof window !== "undefined") {
+                const configBase =
+                    (window.__uniConfig &&
+                        window.__uniConfig.router &&
+                        window.__uniConfig.router.base) ||
+                    (window.__uniConfig && window.__uniConfig.publicPath);
+                if (configBase && configBase !== "./" && configBase !== ".") {
+                    return configBase.endsWith("/")
+                        ? configBase
+                        : `${configBase}/`;
+                }
+                const pathname = window.location.pathname || "/";
+                if (pathname === "/") {
+                    return "/";
+                }
+                if (pathname.endsWith("/")) {
+                    return pathname;
+                }
+                const lastSlashIndex = pathname.lastIndexOf("/");
+                const lastSegment = pathname.slice(lastSlashIndex + 1);
+                const looksLikeFile = lastSegment.includes(".");
+                if (looksLikeFile) {
+                    return pathname.replace(/\/[^/]*$/, "/");
+                }
+                return `${pathname}/`;
+            }
+
+            return "/";
+        },
+        updateViewportHeight() {
+            if (typeof window === "undefined") {
+                this.containerHeight = 0;
+                return;
+            }
+            let height = 0;
+            if (
+                window.visualViewport &&
+                typeof window.visualViewport.height === "number"
+            ) {
+                height = Math.round(window.visualViewport.height);
+            } else {
+                height = window.innerHeight;
+            }
+            // 计算安全区底部像素,用于 iframe 参数传递与不支持 env 的兜底
+            this.safeBottom = this.getSafeAreaInsetBottomPx();
+            this.containerHeight = height;
+        },
+        addViewportListeners() {
+            if (typeof window === "undefined") return;
+            // 绑定一次,便于移除
+            this._boundUpdateViewportHeight =
+                this._boundUpdateViewportHeight ||
+                this.updateViewportHeight.bind(this);
+            window.addEventListener("resize", this._boundUpdateViewportHeight, {
+                passive: true,
+            });
+            window.addEventListener(
+                "orientationchange",
+                this._boundUpdateViewportHeight,
+                { passive: true }
+            );
+            if (window.visualViewport) {
+                window.visualViewport.addEventListener(
+                    "resize",
+                    this._boundUpdateViewportHeight,
+                    { passive: true }
+                );
+            }
+        },
+        removeViewportListeners() {
+            if (
+                typeof window === "undefined" ||
+                !this._boundUpdateViewportHeight
+            )
+                return;
+            window.removeEventListener(
+                "resize",
+                this._boundUpdateViewportHeight
+            );
+            window.removeEventListener(
+                "orientationchange",
+                this._boundUpdateViewportHeight
+            );
+            if (window.visualViewport) {
+                window.visualViewport.removeEventListener(
+                    "resize",
+                    this._boundUpdateViewportHeight
+                );
+            }
+        },
+        checkCssSafeAreaSupport() {
+            if (typeof window === "undefined" || !window.CSS || !CSS.supports)
+                return false;
+            // 支持 env 或老版本 constant
+            return (
+                CSS.supports("padding-bottom: env(safe-area-inset-bottom)") ||
+                CSS.supports("padding-bottom: constant(safe-area-inset-bottom)")
+            );
+        },
+        getSafeAreaInsetBottomPx() {
+            if (typeof window === "undefined" || !document || !document.body)
+                return 0;
+            let value = 0;
+            try {
+                const probe = document.createElement("div");
+                probe.style.position = "absolute";
+                probe.style.visibility = "hidden";
+                probe.style.pointerEvents = "none";
+                probe.style.paddingBottom = "env(safe-area-inset-bottom)";
+                document.body.appendChild(probe);
+                const v1 = parseFloat(
+                    window.getComputedStyle(probe).paddingBottom || "0"
+                );
+                document.body.removeChild(probe);
+                if (v1 > 0) return Math.round(v1);
+                // 尝试旧语法 constant()
+                const probe2 = document.createElement("div");
+                probe2.style.position = "absolute";
+                probe2.style.visibility = "hidden";
+                probe2.style.pointerEvents = "none";
+                probe2.style.paddingBottom = "constant(safe-area-inset-bottom)";
+                document.body.appendChild(probe2);
+                const v2 = parseFloat(
+                    window.getComputedStyle(probe2).paddingBottom || "0"
+                );
+                document.body.removeChild(probe2);
+                value = v2 > 0 ? Math.round(v2) : 0;
+            } catch (e) {
+                value = 0;
+            }
+            return value;
+        },
+        close() {
+            this.webviewUrl = null;
+			this.$emit('CloseDialog');
+            // uni.navigateBack();
+        },
+    },
+    mounted() {
+        this.safeBottom = 0;
+        this.cssSafeAreaSupported = this.checkCssSafeAreaSupport();
+        this.updateViewportHeight();
+        this.addViewportListeners();
+    },
+    beforeDestroy() {
+        this.removeViewportListeners();
+    },
+};
+</script>
+
+<style lang="scss">
+.wrapper {
+    position: relative;
+    .close {
+        position: fixed;
+        top: 44px;
+        right: 20px;
+        z-index: 9999;
+    }
+    .close-icon {
+        width: 46px;
+        height: 46px;
+    }
+}
+iframe {
+    z-index: 9999 !important;
+    top: 0 !important;
+}
+.wg-h5-preview-file-iframe {
+    width: 100vw;
+    height: 100% !important;
+}
+.wg-h5-preview-file-container {
+    position: fixed;
+    left: 0px;
+    bottom: 0;
+    overflow: hidden;
+    z-index: 998;
+    width: 100vw;
+    height: 100%;
+    // iOS 刘海屏底部安全区
+    padding-bottom: constant(safe-area-inset-bottom);
+    padding-bottom: env(safe-area-inset-bottom);
+    background-color: #fff;
+}
+
+.webview {
+    height: 100% !important;
+}
+</style>

+ 9 - 2
manifest.json

@@ -27,7 +27,8 @@
         ],
         /* 模块配置 */
         "modules" : {
-            "VideoPlayer" : {}
+            "VideoPlayer" : {},
+            "Camera" : {}
         },
         /* 应用发布信息 */
         "distribute" : {
@@ -55,7 +56,13 @@
             },
             /* ios打包配置 */
             "ios" : {
-                "dSYMs" : false
+                "dSYMs" : false,
+                "privacyDescription" : {
+                    "NSPhotoLibraryUsageDescription" : "需要访问相册以选择上传材料",
+                    "NSPhotoLibraryAddUsageDescription" : "需要保存图片到相册",
+                    "NSCameraUsageDescription" : "需要访问相机以拍摄上传",
+                    "NSMicrophoneUsageDescription" : "需要访问麦克风以录制音频"
+                }
             },
             /* SDK配置 */
             "sdkConfigs" : {},

+ 84 - 26
package.json

@@ -1,27 +1,85 @@
 {
-  "name": "MarkPaper",
-  "version": "1.0.0",
-  "description": "",
-  "main": "index.js",
-  "scripts": {
-    "dev:h5": "uni-app-scripts dev:h5",
-    "test": "echo \"Error: no test specified\" && exit 1"
-  },
-  "author": "",
-  "license": "ISC",
-  "dependencies": {
-    "@dcloudio/uni-template-compiler": "^2.0.2-4080720251210002",
-    "@dcloudio/vite-plugin-uni": "3.0.0-4020320240708001",
-    "@vueup/vue-quill": "^1.5.5",
-    "echarts": "^5.6.0",
-    "jsencrypt": "^3.3.2",
-    "lodash-es": "^4.17.21",
-    "pdfh5": "^3.0.0",
-    "pdfjs-dist": "^3.8.162"
-  },
-  "devDependencies": {
-    "@dcloudio/types": "^3.4.32",
-    "@dcloudio/uni-cli-i18n": "^2.0.2-4060620250520001",
-    "@dcloudio/uni-cli-shared": "^3.0.0-4020320240708001"
-  }
-}
+    "id": "wg-h5-preview-file",
+    "name": "H5端离线预览PDF、Word、Excel、PPTX、TXT 等文件,支持内网。",
+    "displayName": "wg-h5-preview-file(h5端预览文件)",
+    "version": "1.1.2",
+    "description": "支持h5端预览pdf、excel、word、txt文件",
+    "keywords": [
+        "wg-h5-preview-file"
+    ],
+    "dcloudext": {
+        "type": "component-vue",
+        "sale": {
+            "regular": {
+                "price": "0.00"
+            },
+            "sourcecode": {
+                "price": "0.00"
+            }
+        },
+        "contact": {
+            "qq": ""
+        },
+        "declaration": {
+            "ads": "无",
+            "data": "插件不采集任何数据",
+            "permissions": "无"
+        },
+        "npmurl": ""
+    },
+    "uni_modules": {
+        "dependencies": [],
+        "encrypt": [],
+        "platforms": {
+            "cloud": {
+                "tcb": "y",
+                "aliyun": "y",
+                "alipay": "y"
+            },
+            "client": {
+                "Vue": {
+                    "vue2": "y",
+                    "vue3": "n"
+                },
+                "App": {
+                    "app-vue": "y",
+                    "app-nvue": "u",
+                    "app-uvue": "u",
+                    "app-harmony": "u"
+                },
+                "H5-mobile": {
+                    "Safari": "y",
+                    "Android Browser": "y",
+                    "微信浏览器(Android)": "y",
+                    "QQ浏览器(Android)": "y"
+                },
+                "H5-pc": {
+                    "Chrome": "y",
+                    "IE": "y",
+                    "Edge": "y",
+                    "Firefox": "y",
+                    "Safari": "y"
+                },
+                "小程序": {
+                    "微信": "u",
+                    "阿里": "u",
+                    "百度": "u",
+                    "字节跳动": "u",
+                    "QQ": "u",
+                    "钉钉": "u",
+                    "快手": "u",
+                    "飞书": "u",
+                    "京东": "u"
+                },
+                "快应用": {
+                    "华为": "u",
+                    "联盟": "u"
+                }
+            }
+        }
+    },
+    "repository": "",
+    "engines": {
+        "HBuilderX": "^3.1.0"
+    }
+}

+ 89 - 116
pages/materialCollection/submitMaterial/submitDetail.vue

@@ -70,9 +70,23 @@
 				ref="fileUploader" 
 				v-model="files" 
 				mode="headless" 
+				:multiple="true"
+				:auto-upload="true"
+				:upload-url="`${apiBaseUrl}/api/v1/teach/oss/file/oss/upload_file_list`"
+				:upload-options="{
+					name: 'files',
+					header: {
+						Authorization: token
+					},
+					formData: {
+						taskId:state.taskId,
+						targetId:state.targetId
+					},
+				}"
 				:max-count="20" 
 				:maxSize="100" 
-				formats="doc,docx,xls,xlsx,ppt,pptx,png,jpg,jpeg,gif,bmp">
+				formats="doc,docx,xls,xlsx,ppt,pptx,png,jpg,jpeg,gif,bmp"
+				@uploadSuccess="onUploadSuccess">
 				</kex-file-uploader>
 				<view class="upload_box" @click="FileChooseUploader" v-if="state?.targetFileList?.targetStatus === 1 && state?.details?.taskStatus !== 2">
 					<image class="upload_icon" src="@/static/image/icon/upload_icon.png"></image>
@@ -102,7 +116,7 @@
 						<view class="upload_desc">支持Word、Excel、PPT、图片(最大100MB)</view>
 					</view>
 				</view>
-				<view class="list_item" @click="PreviewFile(item)" v-for="item in (state?.targetFileList?.taskTargetFileVos || [])" :Key="item.id">
+				<view class="list_item" @click="OpenPreviewFile(item)" v-for="item in (state?.targetFileList?.taskTargetFileVos || [])" :Key="item.id">
 					<image class="file_icon" src="@/static/image/icon/file.png"></image>
 					<view class="file_info">
 						<view class="file_name">{{item.fileName}}</view>
@@ -131,8 +145,8 @@
 		</view>
 		<!-- 删除确认框 -->
 		<popupDialog ref="popupConfirmDialogRef" :showFootBorder="true" :content="state.confirmDialog.content" @DialogConfirm="DialogConfirm" />
-		<!-- 提示框 -->
-		<popupPreviewFile ref="popupDialogRef" :filePath="state.previewPopup.filePath"></popupPreviewFile>
+		<!-- 文件预览 -->
+		<popupPreviewFile ref="popupDialogPreview" :fileType="state.previewPopup.fileType" :fileName="state.previewPopup.fileName" :filePath="state.previewPopup.filePath" @ToDownloadFile="ToDownloadFile"></popupPreviewFile>
 		<!-- 文件、图片下载 -->
 		<ss-download ref="ssdownload"></ss-download>
 	</view>
@@ -144,7 +158,7 @@
 	import popupDialog from '@/components/popupDialog.vue';
 	import popupPreviewFile from '@/components/popupPreviewFile.vue';
 	import navBar from '@/components/navBar.vue';
-	import { remainingDays,getFileSize } from '@/common/common.js';
+	import { remainingDays,getFileSize,getFileTypeBySuffix } from '@/common/common.js';
 	import {getTaskTargetFileList,DeleteFileList,DelTaskTargetFileList,updateTaskTarget,uploadTaskTargetFileList} from '@/reqApi/notification.js';
 	import {onLoad} from '@dcloudio/uni-app';
 	import config from '@/config.js'; // 引入配置文件
@@ -153,6 +167,7 @@
 		ref,
 		nextTick,
 		getCurrentInstance,
+		computed,
 		onMounted
 	} from 'vue';
 	const state = reactive({
@@ -170,11 +185,13 @@
 			targetFileIds:[]
 		},
 		previewPopup:{
-			filePath:''
+			fileType:'',
+			filePath:'',
+			fileName:''
 		}
 	});
 	const popupConfirmDialogRef = ref(null);
-	const popupDialogRef = ref(null);
+	const popupDialogPreview = ref(null);
 	const fileUploader = ref(null);
 	const ssdownload = ref(null);
 	const files = ref([]);
@@ -198,7 +215,6 @@
 		uni.removeStorageSync('isLoadSubmitMaterialTaskDetail');//返回是否重新更新任务详情数据
 		uni.removeStorageSync('isLoadSubmitMaterialIndex');//返回是否重新更新提交材料首页
 		getApiBaseUrl();//上传接口地址
-		checkTextOverflow();
 		GetTaskTargetFileList(true);
 	});
 	//上传接口地址
@@ -212,31 +228,25 @@
 	}
 	//选择文件上传
 	const FileChooseUploader = () => {
-		fileUploader.value.chooseFile({//选择上传文件
-			count: 1,
-			success: (files) => {
-				fileUploader.value.upload({//上传单个文件
-					url:`${apiBaseUrl.value}/api/v1/teach/oss/file/oss/upload_file_list`,
-					file: files[0],
-					name: 'files',
-					header: {Authorization: token.value},
-					formData: {
-						taskId:state.taskId,
-						targetId:state.targetId
-					},
-					method: 'POST',
-					withCredentials: true, // iOS 本地测远程且要带 cookie
-					success: (res) => {
-						const result = JSON.parse(res.result);
-						const fileInfoList = result.data.map(item => ({
-							...item,
-							filePath: item.fileUrl
-						}))
-						UploadTargetFileList(fileInfoList);
-					}
-				});
-			}
-		})
+		fileUploader.value && fileUploader.value.choose();
+	}
+	const onUploadSuccess = ({
+		index,
+		file,        // 当前文件
+		url,         // 优先为解析出的远程地址
+		statusCode,
+		data,        // 解析后的服务端 JSON(失败则为原始字符串)
+		res,         // 原始回调
+		fileList     // 当前全部
+	}) => {
+		console.log(res)
+		const result = JSON.parse(res.data || res.result);
+		const list = result?.data || [];
+		const fileInfoList = list.map(item => ({
+			...item,
+			filePath: item.fileUrl
+		}))
+		UploadTargetFileList(fileInfoList);
 	}
 	//具体采集任务保存上传的文件地址
 	const UploadTargetFileList = (fileInfoList) => {
@@ -255,97 +265,60 @@
 		})
 	}
 	//预览
-	const PreviewFile = (item) => {
-		console.log(item)
-		const fileType = item.fileType;
-		if(IsImageExts(fileType)){//图片预览
-			state.previewPopup.filePath = item.filePath;
-			popupDialogRef.value.OpenDialog();
-		}else{//文件预览
-			// 1. 先下载文件到本地临时目录
-			uni.downloadFile({
-				url: item.filePath,
-				success: (downloadRes) => {
-					if (downloadRes.statusCode === 200) {
-						// 2. 下载成功后,得到临时文件路径 tempFilePath
-						const localPath = downloadRes.tempFilePath;
-						// 3. 构造 file 对象(必须有 path)
-						const fileObj = {
-							name: item.fileName,
-							path: localPath,  // 关键字段
-							size: item.fileSize // 可选
-						};
-						// 4. 调用 kex-file-uploader 的 openDocument 方法预览
-						fileUploader.value?.openDocument({
-							file: fileObj,
-							success: () => console.log('预览成功'),
-							fail: (err) => console.error('预览失败', err)
-						});
-					}
-				},
-				fail: (err) => {
-					uni.showToast({ title: '文件下载失败', icon: 'none' });
-				}
-			});
-		}
-	}
-	//判断是否是图片
-	const IsImageExts = (fileType) => {
-		const type = fileType?.toLowerCase();
-		const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp','svg', 'tiff', 'tif', 'ico', 'avif', 'heic', 'heif'];
-		return imageExts.includes(type)
+	const OpenPreviewFile = (item) => {
+		state.previewPopup.fileType = item.fileType;
+		state.previewPopup.filePath = item.filePath;
+		state.previewPopup.fileName = item.fileName;
+		popupDialogPreview.value.OpenDialog();
+		// const fileType = item.fileType;
+		// if(IsImageExts(fileType)){//图片预览
+		// 	state.previewPopup.filePath = item.filePath;
+		// 	popupDialogPreview.value.OpenDialog();
+		// }else{//文件预览
+		// 	// 1. 先下载文件到本地临时目录
+		// 	uni.downloadFile({
+		// 		url: item.filePath,
+		// 		success: (downloadRes) => {
+		// 			if (downloadRes.statusCode === 200) {
+		// 				// 2. 下载成功后,得到临时文件路径 tempFilePath
+		// 				const localPath = downloadRes.tempFilePath;
+		// 				// 3. 构造 file 对象(必须有 path)
+		// 				const fileObj = {
+		// 					name: item.fileName,
+		// 					path: localPath,  // 关键字段
+		// 					size: item.fileSize // 可选
+		// 				};
+		// 				// 4. 调用 kex-file-uploader 的 openDocument 方法预览
+		// 				fileUploader.value?.openDocument({
+		// 					file: fileObj,
+		// 					success: () => console.log('预览成功'),
+		// 					fail: (err) => console.error('预览失败', err)
+		// 				});
+		// 			}
+		// 		},
+		// 		fail: (err) => {
+		// 			uni.showToast({ title: '文件下载失败', icon: 'none' });
+		// 		}
+		// 	});
+		// }
 	}
 	//下载
 	const ToDownloadFile = (item) => {
-		const fileType = IsImageExts(item.fileType)?'1':'2';//1:图片 2 文档 3 视频
+		const fileType = getFileTypeBySuffix(item.fileType);//文件类型
+		let type = '';
+		if(fileType == 'image'){
+			type = '1';//1:图片 2 文档
+		}else if(fileType == 'document'){
+			type = '2';
+		}else{
+			type = '';
+		}
 		ssdownload?.value?.toDownload({
-			type:fileType,
+			type:type,
 			src:item.filePath,
 			fileName:item.fileName
 		});
 	}
-	const checkTextOverflow = () => {
-		nextTick(() => {
-			const instance = getCurrentInstance();
-			// 安全保护
-			if (!instance || !instance.proxy) {
-				// 备用方案:全局查询(要求元素 class 唯一)
-				let taskDescAllHeight = 0,
-					taskDescHeight = 0;
-				const query = uni.createSelectorQuery();
-				query.select('.task_desc_all').boundingClientRect((rect) => {
-					taskDescAllHeight = rect?.height || 0;
-				});
-				query.select('.task_desc').boundingClientRect((rect) => {
-					taskDescHeight = rect?.height || 0;
-				});
-				query.select('.page_content_data').boundingClientRect((rect) => {
-					state.searchBoxTop = rect?.top || 0;
-				});
-				query.exec(() => {
-					// 所有回调执行完毕后触发
-					state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
-				});
-				return;
-			}
-			let taskDescAllHeight = 0,
-				taskDescHeight = 0;
-			const query = uni.createSelectorQuery().in(instance.proxy);
-			query.select('.task_desc_all').boundingClientRect((rect) => {
-				taskDescAllHeight = rect?.height || 0;
-			});
-			query.select('.task_desc').boundingClientRect((rect) => {
-				taskDescHeight = rect?.height || 0;
-			});
-			query.select('.page_content_data').boundingClientRect((rect) => {
-				state.searchBoxTop = rect?.top || 0;
-			});
-			query.exec(() => {
-				// 所有回调执行完毕后触发
-				state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
-			});
-		});
-	}
 	// 展开收起切换
 	const toggleExpand = () => {
 		state.isExpand = !state.isExpand;

+ 87 - 0
readme.md

@@ -0,0 +1,87 @@
+### wg-h5-preview-file(H5 文件预览组件)
+
+<div align="center" style="display: flex; align-items: center; gap: 20px; padding: 20px">
+  <img src="https://raw.githubusercontent.com/WGinit/Assets/refs/heads/master/project/images/word.png" alt="Word" width="88" />
+  <img src="https://raw.githubusercontent.com/WGinit/Assets/refs/heads/master/project/images/excel.png" alt="Excel" width="88" />
+  <img src="https://raw.githubusercontent.com/WGinit/Assets/refs/heads/master/project/images/pdf.png" alt="PDF" width="88" />
+  <img src="https://raw.githubusercontent.com/WGinit/Assets/refs/heads/master/project/images/ppt.png" alt="PPtx" width="88"  />
+  <img src="https://raw.githubusercontent.com/WGinit/Assets/refs/heads/master/project/images/txt.png" alt="Txt" width="68"  />
+</div>
+
+支持在 H5 端预览 PDF、Word、Excel、PPTX、TXT 常见办公文件。提供页面跳转式预览与 API 直接调用两种集成方式,开箱即用。
+
+---
+
+### 功能特性
+
+-   支持文件类型:pdf、word、excel、pptx、txt
+-   H5 端 iframe 内嵌预览页面,免安装第三方 App
+-   提供两种集成方式:页面跳转式、API 直接调用
+-   适配常见移动端与 PC 端浏览器
+
+---
+
+### 安装与引入
+
+1. 将 `uni_modules/wg-h5-preview-file` 放入项目 `uni_modules/` 目录。
+2. 在使用页面内直接使用组件标签 `<wg-h5-preview-file />` 即可。
+
+> uni-app 会自动按需注册 `uni_modules` 下的组件,一般无需手动 `components` 注册。
+
+---
+
+### 组件用法概览
+
+基础用法(页面跳转方式中使用,被动接收 URL 与类型并立即展示):
+
+```vue
+<template>
+    <wg-h5-preview-file :value="file.url" :type="file.type" />
+</template>
+```
+
+API 方式(在任意页面中通过 `ref` 主动打开预览):
+
+```vue
+<template>
+    <view>
+        <button @click="open">预览文件</button>
+        <wg-h5-preview-file ref="previewFile" />
+    </view>
+</template>
+
+<script>
+export default {
+    data() {
+        return {
+            file: { url: "/static/demo.pdf", type: "pdf" },
+        };
+    },
+    methods: {
+        open() {
+            this.$refs.previewFile.preview({
+                url: this.file.url,
+                type: this.file.type,
+            });
+        },
+    },
+};
+</script>
+```
+
+---
+
+### 属性(Props)
+
+-   `value`:String,必填。要预览的文件 URL。例如:`/static/git.pdf`。
+-   `type`:String,必填。文件类型,限定为:`pdf | word | excel | pptx | txt`。
+
+### 方法(Methods)
+
+-   `preview({ url, type })`:
+    -   说明:通过引用调用,主动打开预览。
+    -   参数:
+        -   `url`:String,文件 URL。
+        -   `type`:String,`pdf | word | excel | pptx | txt`。
+
+---

+ 115 - 0
static/hybrid/excel.html

@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html dir="ltr" mozdisallowselectionprint>
+	<head>
+		<meta charset="utf-8">
+		<title>预览excel</title>
+		<link rel="stylesheet" href="./excel/pluginsCss.css">
+		<link rel="stylesheet" href="./excel/plugins.css">
+		<link rel="stylesheet" href="./excel/luckysheet.css">
+		<link rel="stylesheet" href="./excel/iconfont.css">
+		<script src="./utils/flexible.js"></script>
+		<script src="./utils/ui.js"></script>
+		<script src="./utils/getQueryVariable.js"></script>
+		<script src="./excel/plugin.js"></script>
+		<script src="./excel/luckysheet.umd.js"></script>
+		<script src="./excel/luckyexcel.umd.js"></script>
+		<link rel="resource" type="application/l10n" href="./excel/viewer.properties">
+		<link rel="stylesheet" href="./style/ui.css" />
+	</head>
+	<style type="text/css">
+		* {
+			padding: 0;
+			margin: 0
+		}
+
+		body {
+			height: 100vh;
+			width: 100vw
+		}
+
+		#container {
+			width: 100%;
+			height: calc(100% - 10px);
+			font-size: 18px
+		}
+	</style>
+	<script>
+		(async () => {
+			var e = decodeURIComponent(getQueryVariable("file") || "");
+			const loading = createLoading();
+			loading.show();
+			console.log("excel", e);
+			LuckyExcel.transformExcelToLuckyByUrl(e, "测试.xlsl", (t, e) => {
+				if (null == t.sheets || 0 === t.sheets.length) console.warn("暂不支持该文件预览"), loading.hide();
+				else {
+					var o = getQueryVariable("safeBottom"),
+						l = (document.getElementById("container").style.height = `calc(100% - ${o}px)`, []);
+					for (let e = 0; e < t.sheets.length; e++) l[e] = {
+						name: t.sheets[e].name,
+						index: t.sheets[e].index,
+						order: t.sheets[e].order,
+						status: t.sheets[e].status,
+						config: t.sheets[e].config
+					}, l[e].config.row = 10, 1 === parseInt(t.sheets[e].status) && (l[e].celldata = t
+						.sheets[e].celldata), t.sheets[e].index, t.sheets[e].celldata;
+					window?.luckysheet.destroy(), window?.luckysheet.create({
+						lang: "zh",
+						container: "container",
+						allowUpdate: !0,
+						showtoolbar: !1,
+						showinfobar: !1,
+						showstatisticBar: !0,
+						sheetBottomConfig: !1,
+						allowEdit: !1,
+						enableAddRow: !1,
+						enableAddCol: !1,
+						sheetFormulaBar: !1,
+						data: t.sheets,
+						enableAddBackTop: !0,
+						title: t.info.name,
+						sheetRightClickConfig: {
+							delete: !1,
+							copy: !1,
+							rename: !1,
+							color: !1,
+							hide: !1,
+							move: !1
+						},
+						showsheetbarConfig: {
+							add: !1
+						},
+						devicePixelRatio: window.devicePixelRatio,
+						defaultFontSize: 20,
+						cellRightClickConfig: {
+							copy: !1,
+							copyAs: !1,
+							paste: !1,
+							insertRow: !1,
+							insertColumn: !1,
+							deleteRow: !1,
+							deleteColumn: !1,
+							deleteCell: !1,
+							hideRow: !1,
+							hideColumn: !1,
+							rowHeight: !1,
+							columnWidth: !1,
+							clear: !1,
+							matrix: !1,
+							sort: !1,
+							filter: !1,
+							chart: !1,
+							image: !1,
+							link: !1,
+							data: !1,
+							cellFormat: !1
+						}
+					});
+					loading.hide();
+				}
+			})
+		})()
+	</script>
+	<body id="body">
+		<div id="container"></div>
+	</body>
+</html>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3 - 0
static/hybrid/excel/iconfont.css


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 22212 - 0
static/hybrid/excel/luckyexcel.umd.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
static/hybrid/excel/luckysheet.css


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
static/hybrid/excel/luckysheet.umd.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
static/hybrid/excel/plugin.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3 - 0
static/hybrid/excel/plugins.css


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
static/hybrid/excel/pluginsCss.css


+ 279 - 0
static/hybrid/excel/viewer.properties

@@ -0,0 +1,279 @@
+# Copyright 2012 Mozilla Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Main toolbar buttons (tooltips and alt text for images)
+previous.title=上一页
+previous_label=上一页
+next.title=下一页
+next_label=下一页
+
+# LOCALIZATION NOTE (page.title): The tooltip for the pageNumber input.
+page.title=页面
+# LOCALIZATION NOTE (of_pages): "{{pagesCount}}" will be replaced by a number
+# representing the total number of pages in the document.
+of_pages=/ {{pagesCount}}
+# LOCALIZATION NOTE (page_of_pages): "{{pageNumber}}" and "{{pagesCount}}"
+# will be replaced by a number representing the currently visible page,
+# respectively a number representing the total number of pages in the document.
+page_of_pages=({{pageNumber}} / {{pagesCount}})
+
+zoom_out.title=缩小
+zoom_out_label=缩小
+zoom_in.title=放大
+zoom_in_label=放大
+zoom.title=缩放
+presentation_mode.title=切换到演示模式
+presentation_mode_label=演示模式
+open_file.title=打开文件
+open_file_label=打开
+print.title=打印
+print_label=打印
+download.title=下载
+download_label=下载
+bookmark.title=当前在看的内容(复制或在新窗口中打开)
+bookmark_label=当前在看
+
+save.title=保存
+save_label=保存
+bookmark1.title=当前页面(在当前页面查看 URL)
+bookmark1_label=当前页面
+
+# Secondary toolbar and context menu
+tools.title=工具
+tools_label=工具
+first_page.title=转到第一页
+first_page_label=转到第一页
+last_page.title=转到最后一页
+last_page_label=转到最后一页
+page_rotate_cw.title=顺时针旋转
+page_rotate_cw_label=顺时针旋转
+page_rotate_ccw.title=逆时针旋转
+page_rotate_ccw_label=逆时针旋转
+
+cursor_text_select_tool.title=启用文本选择工具
+cursor_text_select_tool_label=文本选择工具
+cursor_hand_tool.title=启用手形工具
+cursor_hand_tool_label=手形工具
+
+scroll_page.title=使用页面滚动
+scroll_page_label=页面滚动
+scroll_vertical.title=使用垂直滚动
+scroll_vertical_label=垂直滚动
+scroll_horizontal.title=使用水平滚动
+scroll_horizontal_label=水平滚动
+scroll_wrapped.title=使用平铺滚动
+scroll_wrapped_label=平铺滚动
+
+spread_none.title=不加入衔接页
+spread_none_label=单页视图
+spread_odd.title=加入衔接页使奇数页作为起始页
+spread_odd_label=双页视图
+spread_even.title=加入衔接页使偶数页作为起始页
+spread_even_label=书籍视图
+
+# Document properties dialog box
+document_properties.title=文档属性…
+document_properties_label=文档属性…
+document_properties_file_name=文件名:
+document_properties_file_size=文件大小:
+# LOCALIZATION NOTE (document_properties_kb): "{{size_kb}}" and "{{size_b}}"
+# will be replaced by the PDF file size in kilobytes, respectively in bytes.
+document_properties_kb={{size_kb}} KB ({{size_b}} 字节)
+# LOCALIZATION NOTE (document_properties_mb): "{{size_mb}}" and "{{size_b}}"
+# will be replaced by the PDF file size in megabytes, respectively in bytes.
+document_properties_mb={{size_mb}} MB ({{size_b}} 字节)
+document_properties_title=标题:
+document_properties_author=作者:
+document_properties_subject=主题:
+document_properties_keywords=关键词:
+document_properties_creation_date=创建日期:
+document_properties_modification_date=修改日期:
+# LOCALIZATION NOTE (document_properties_date_string): "{{date}}" and "{{time}}"
+# will be replaced by the creation/modification date, and time, of the PDF file.
+document_properties_date_string={{date}}, {{time}}
+document_properties_creator=创建者:
+document_properties_producer=PDF 生成器:
+document_properties_version=PDF 版本:
+document_properties_page_count=页数:
+document_properties_page_size=页面大小:
+document_properties_page_size_unit_inches=英寸
+document_properties_page_size_unit_millimeters=毫米
+document_properties_page_size_orientation_portrait=纵向
+document_properties_page_size_orientation_landscape=横向
+document_properties_page_size_name_a3=A3
+document_properties_page_size_name_a4=A4
+document_properties_page_size_name_letter=文本
+document_properties_page_size_name_legal=法律
+# LOCALIZATION NOTE (document_properties_page_size_dimension_string):
+# "{{width}}", "{{height}}", {{unit}}, and {{orientation}} will be replaced by
+# the size, respectively their unit of measurement and orientation, of the (current) page.
+document_properties_page_size_dimension_string={{width}} × {{height}} {{unit}}({{orientation}})
+# LOCALIZATION NOTE (document_properties_page_size_dimension_name_string):
+# "{{width}}", "{{height}}", {{unit}}, {{name}}, and {{orientation}} will be replaced by
+# the size, respectively their unit of measurement, name, and orientation, of the (current) page.
+document_properties_page_size_dimension_name_string={{width}} × {{height}} {{unit}}({{name}},{{orientation}})
+# LOCALIZATION NOTE (document_properties_linearized): The linearization status of
+# the document; usually called "Fast Web View" in English locales of Adobe software.
+document_properties_linearized=快速 Web 视图:
+document_properties_linearized_yes=是
+document_properties_linearized_no=否
+document_properties_close=关闭
+
+print_progress_message=正在准备打印文档…
+# LOCALIZATION NOTE (print_progress_percent): "{{progress}}" will be replaced by
+# a numerical per cent value.
+print_progress_percent={{progress}}%
+print_progress_close=取消
+
+# Tooltips and alt text for side panel toolbar buttons
+# (the _label strings are alt text for the buttons, the .title strings are
+# tooltips)
+toggle_sidebar.title=切换侧栏
+toggle_sidebar_notification2.title=切换侧栏(文档所含的大纲/附件/图层)
+toggle_sidebar_label=切换侧栏
+document_outline.title=显示文档大纲(双击展开/折叠所有项)
+document_outline_label=文档大纲
+attachments.title=显示附件
+attachments_label=附件
+layers.title=显示图层(双击即可将所有图层重置为默认状态)
+layers_label=图层
+thumbs.title=显示缩略图
+thumbs_label=缩略图
+current_outline_item.title=查找当前大纲项目
+current_outline_item_label=当前大纲项目
+findbar.title=在文档中查找
+findbar_label=查找
+
+additional_layers=其他图层
+# LOCALIZATION NOTE (page_landmark): "{{page}}" will be replaced by the page number.
+page_landmark=第 {{page}} 页
+# Thumbnails panel item (tooltip and alt text for images)
+# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
+# number.
+thumb_page_title=第 {{page}} 页
+# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page
+# number.
+thumb_page_canvas=页面 {{page}} 的缩略图
+
+# Find panel button title and messages
+find_input.title=查找
+find_input.placeholder=在文档中查找…
+find_previous.title=查找词语上一次出现的位置
+find_previous_label=上一页
+find_next.title=查找词语后一次出现的位置
+find_next_label=下一页
+find_highlight=全部高亮显示
+find_match_case_label=区分大小写
+find_match_diacritics_label=匹配变音符号
+find_entire_word_label=全词匹配
+find_reached_top=到达文档开头,从末尾继续
+find_reached_bottom=到达文档末尾,从开头继续
+# LOCALIZATION NOTE (find_match_count): The supported plural forms are
+# [one|two|few|many|other], with [other] as the default value.
+# "{{current}}" and "{{total}}" will be replaced by a number representing the
+# index of the currently active find result, respectively a number representing
+# the total number of matches in the document.
+find_match_count={[ plural(total) ]}
+find_match_count[one]=第 {{current}} 项,共匹配 {{total}} 项
+find_match_count[two]=第 {{current}} 项,共匹配 {{total}} 项
+find_match_count[few]=第 {{current}} 项,共匹配 {{total}} 项
+find_match_count[many]=第 {{current}} 项,共匹配 {{total}} 项
+find_match_count[other]=第 {{current}} 项,共匹配 {{total}} 项
+# LOCALIZATION NOTE (find_match_count_limit): The supported plural forms are
+# [zero|one|two|few|many|other], with [other] as the default value.
+# "{{limit}}" will be replaced by a numerical value.
+find_match_count_limit={[ plural(limit) ]}
+find_match_count_limit[zero]=超过 {{limit}} 项匹配
+find_match_count_limit[one]=超过 {{limit}} 项匹配
+find_match_count_limit[two]=超过 {{limit}} 项匹配
+find_match_count_limit[few]=超过 {{limit}} 项匹配
+find_match_count_limit[many]=超过 {{limit}} 项匹配
+find_match_count_limit[other]=超过 {{limit}} 项匹配
+find_not_found=找不到指定词语
+
+# Error panel labels
+error_more_info=更多信息
+error_less_info=更少信息
+error_close=关闭
+# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be
+# replaced by the PDF.JS version and build ID.
+error_version_info=PDF.js v{{version}} (build: {{build}})
+# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an
+# english string describing the error.
+error_message=信息:{{message}}
+# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack
+# trace.
+error_stack=堆栈:{{stack}}
+# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename
+error_file=文件:{{file}}
+# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number
+error_line=行号:{{line}}
+
+# Predefined zoom values
+page_scale_width=适合页宽
+page_scale_fit=适合页面
+page_scale_auto=自动缩放
+page_scale_actual=实际大小
+# LOCALIZATION NOTE (page_scale_percent): "{{scale}}" will be replaced by a
+# numerical scale value.
+page_scale_percent={{scale}}%
+
+# Loading indicator messages
+loading=正在加载…
+
+# Loading indicator messages
+loading_error=加载 PDF 时发生错误。
+invalid_file_error=无效或损坏的 PDF 文件。
+missing_file_error=缺少 PDF 文件。
+unexpected_response_error=意外的服务器响应。
+
+rendering_error=渲染页面时发生错误。
+
+# LOCALIZATION NOTE (annotation_date_string): "{{date}}" and "{{time}}" will be
+# replaced by the modification date, and time, of the annotation.
+annotation_date_string={{date}},{{time}}
+
+# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip.
+# "{{type}}" will be replaced with an annotation type from a list defined in
+# the PDF spec (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+text_annotation_type.alt=[{{type}} 注释]
+password_label=输入密码以打开此 PDF 文件。
+password_invalid=密码无效。请重试。
+password_ok=确定
+password_cancel=取消
+
+printing_not_supported=警告:此浏览器尚未完整支持打印功能。
+printing_not_ready=警告:此 PDF 未完成加载,无法打印。
+web_fonts_disabled=Web 字体已被禁用:无法使用嵌入的 PDF 字体。
+
+# Editor
+editor_free_text2.title=文本
+editor_free_text2_label=文本
+editor_ink2.title=绘图
+editor_ink2_label=绘图
+
+free_text2_default_content=开始输入…
+
+# Editor Parameters
+editor_free_text_color=颜色
+editor_free_text_size=字号
+editor_ink_color=颜色
+editor_ink_thickness=粗细
+editor_ink_opacity=不透明度
+
+# Editor aria
+editor_free_text2_aria_label=文本编辑器
+editor_ink2_aria_label=绘图编辑器
+editor_ink_canvas_aria_label=用户创建图像

BIN
static/hybrid/pdf/web/cmaps/78-EUC-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/78-EUC-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/78-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/78-RKSJ-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/78-RKSJ-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/78-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/78ms-RKSJ-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/78ms-RKSJ-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/83pv-RKSJ-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/90ms-RKSJ-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/90ms-RKSJ-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/90msp-RKSJ-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/90msp-RKSJ-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/90pv-RKSJ-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/90pv-RKSJ-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/Add-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/Add-RKSJ-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/Add-RKSJ-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/Add-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-CNS1-0.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-CNS1-1.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-CNS1-2.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-CNS1-3.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-CNS1-4.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-CNS1-5.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-CNS1-6.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-CNS1-UCS2.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-GB1-0.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-GB1-1.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-GB1-2.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-GB1-3.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-GB1-4.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-GB1-5.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-GB1-UCS2.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Japan1-0.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Japan1-1.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Japan1-2.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Japan1-3.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Japan1-4.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Japan1-5.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Japan1-6.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Japan1-UCS2.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Korea1-0.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Korea1-1.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Korea1-2.bcmap


BIN
static/hybrid/pdf/web/cmaps/Adobe-Korea1-UCS2.bcmap


BIN
static/hybrid/pdf/web/cmaps/B5-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/B5-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/B5pc-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/B5pc-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/CNS-EUC-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/CNS-EUC-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/CNS1-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/CNS1-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/CNS2-H.bcmap


+ 3 - 0
static/hybrid/pdf/web/cmaps/CNS2-V.bcmap

@@ -0,0 +1,3 @@
+àRCopyright 1990-2009 Adobe Systems Incorporated.
+All rights reserved.
+See ./LICENSEáCNS2-H

BIN
static/hybrid/pdf/web/cmaps/ETHK-B5-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/ETHK-B5-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/ETen-B5-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/ETen-B5-V.bcmap


+ 3 - 0
static/hybrid/pdf/web/cmaps/ETenms-B5-H.bcmap

@@ -0,0 +1,3 @@
+àRCopyright 1990-2009 Adobe Systems Incorporated.
+All rights reserved.
+See ./LICENSEá	ETen-B5-H` ^

BIN
static/hybrid/pdf/web/cmaps/ETenms-B5-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/EUC-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/EUC-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/Ext-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/Ext-RKSJ-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/Ext-RKSJ-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/Ext-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/GB-EUC-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/GB-EUC-V.bcmap


+ 4 - 0
static/hybrid/pdf/web/cmaps/GB-H.bcmap

@@ -0,0 +1,4 @@
+àRCopyright 1990-2009 Adobe Systems Incorporated.
+All rights reserved.
+See ./LICENSE!!�º]aX!!]`�21�>	�p�z�$]‚�"R‚d�-Uƒ7�*„
4„%�+ „Z „{�/…%…<�9K…b�1]†.�"‡‰`]‡,�"]ˆ
+�"]ˆh�"]‰F�"]Š$�"]‹�"]‹`�"]Œ>�"]��"]�z�"]ŽX�"]�6�"]��"]�r�"]‘P�"]’.�"]“�"]“j�"]”H�"]•&�"]–�"]–b�"]—@�"]˜�"]˜|�"]™Z�"]š8�"]›�"]›t�"]œR�"]�0�"]ž�"]žl�"]ŸJ�"] (�"]¡�"]¡d�"]¢B�"]£ �"X£~�']¤W�"]¥5�"]¦�"]¦q�"]§O�"]¨-�"]©�"]©i�"]ªG�"]«%�"]¬�"]¬a�"]­?�"]®�"]®{�"]¯Y�"]°7�"]±�"]±s�"]²Q�"]³/�"]´
�"]´k�"]µI�"]¶'�"]·�"]·c�"]¸A�"]¹�"]¹}�"]º[�"]»9

BIN
static/hybrid/pdf/web/cmaps/GB-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBK-EUC-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBK-EUC-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBK2K-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBK2K-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBKp-EUC-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBKp-EUC-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBT-EUC-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBT-EUC-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBT-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBT-V.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBTpc-EUC-H.bcmap


BIN
static/hybrid/pdf/web/cmaps/GBTpc-EUC-V.bcmap


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác