Explorar el Código

更新 upload 插件

神仙都没用 hace 1 año
padre
commit
d9b2405184

+ 1 - 11
src/plugins/upload/components/upload-item/index.vue

@@ -111,7 +111,7 @@ import { ZoomIn, Delete, VideoPause, VideoPlay } from "@element-plus/icons-vue";
 import { ContextMenu } from "@cool-vue/crud";
 import { useCool } from "/@/cool";
 import { extname } from "/@/cool/utils";
-import { download, fileName, getRule } from "../../utils";
+import { fileName, getRule } from "../../utils";
 import { ElMessage } from "element-plus";
 import { useClipboard } from "@vueuse/core";
 import Viewer from "./viewer.vue";
@@ -172,16 +172,6 @@ function onContextMenu(e: any) {
 					done();
 				}
 			},
-			{
-				label: "下载",
-				callback(done) {
-					if (props.item.url) {
-						download(props.item.url);
-					}
-
-					done();
-				}
-			},
 			{
 				label: "复制链接",
 				callback(done) {

+ 5 - 23
src/plugins/upload/components/upload-item/viewer.vue

@@ -11,34 +11,18 @@
 	</div>
 
 	<!-- 文档 -->
-	<cl-dialog
-		v-model="doc.visible"
-		title="文档预览"
-		height="70vh"
-		width="80%"
-		:scrollbar="false"
-		:controls="['slot-download', 'fullscreen', 'close']"
-	>
-		<template #slot-download>
-			<button type="button" class="cl-dialog__controls-icon" @click="download(doc.url)">
-				<el-icon>
-					<icon-download />
-				</el-icon>
-			</button>
-		</template>
-
+	<cl-dialog v-model="doc.visible" title="文档预览" height="70vh" width="80%" :scrollbar="false">
 		<div class="viewer-doc" v-loading="doc.loading">
-			<iframe :src="doc.previewUrl" :ref="setRefs('docIframe')" />
+			<iframe :src="doc.url" :ref="setRefs('docIframe')" />
 		</div>
 	</cl-dialog>
 </template>
 
 <script lang="ts" setup name="file-viewer">
 import { reactive, nextTick } from "vue";
-import { getType, download } from "../../utils";
+import { getType } from "../../utils";
 import type { Upload } from "../../types";
 import { useCool } from "/@/cool";
-import { Download as IconDownload } from "@element-plus/icons-vue";
 
 const { refs, setRefs } = useCool();
 
@@ -52,8 +36,7 @@ const img = reactive({
 const doc = reactive({
 	visible: false,
 	loading: false,
-	url: "",
-	previewUrl: ""
+	url: ""
 });
 
 // 打开
@@ -77,8 +60,7 @@ function open(item: Upload.Item) {
 		if (["word", "excel", "ppt", "pdf"].includes(type)) {
 			doc.visible = true;
 			doc.loading = true;
-			doc.previewUrl = `https://view.officeapps.live.com/op/view.aspx?src=${decodeURIComponent(url)}`;
-			doc.url = url;
+			doc.url = `https://view.officeapps.live.com/op/view.aspx?src=${decodeURIComponent(url)}`;
 
 			nextTick(() => {
 				refs.docIframe.onload = () => {

+ 26 - 167
src/plugins/upload/components/upload.vue

@@ -22,7 +22,7 @@
 						:http-request="httpRequest"
 						:headers="headers"
 						:multiple="multiple"
-						:disabled="uploadDisabled"
+						:disabled="disabled"
 					>
 						<slot>
 							<el-button type="success">{{ text }}</el-button>
@@ -61,7 +61,7 @@
 							:http-request="httpRequest"
 							:headers="headers"
 							:multiple="multiple"
-							:disabled="uploadDisabled"
+							:disabled="disabled"
 						>
 							<slot>
 								<div class="cl-upload__demo is-dragger" v-if="drag">
@@ -103,7 +103,7 @@
 							}
 						"
 						:headers="headers"
-						:disabled="uploadDisabled"
+						:disabled="disabled"
 						v-if="showFileList"
 					>
 						<slot name="item" :item="item" :index="index">
@@ -127,15 +127,15 @@
 <script lang="ts" setup name="cl-upload">
 import { computed, ref, watch, type PropType, nextTick } from "vue";
 import { isArray, isNumber } from "lodash-es";
-import { type AxiosProgressEvent } from "axios";
 import Draggable from "vuedraggable";
 import { ElMessage } from "element-plus";
 import { PictureFilled, UploadFilled } from "@element-plus/icons-vue";
 import { useForm } from "@cool-vue/crud";
-import { useCool, module } from "/@/cool";
+import { useCool } from "/@/cool";
 import { useBase } from "/$/base";
 import { uuid, isPromise } from "/@/cool/utils";
-import { getUrls, getType, pathJoin } from "../utils";
+import { getUrls, getType } from "../utils";
+import { useUpload } from "../hooks";
 import UploadItem from "./upload-item/index.vue";
 import type { Upload } from "../types";
 
@@ -187,15 +187,7 @@ const props = defineProps({
 	// 上传前钩子
 	beforeUpload: Function,
 	// 云端上传路径前缀
-	prefixPath: {
-		type: String,
-		default: "app"
-	},
-	// 云端上传路径
-	menu: {
-		type: String,
-		default: "base"
-	},
+	prefixPath: String,
 
 	// CRUD穿透值
 	isEdit: Boolean,
@@ -206,12 +198,10 @@ const props = defineProps({
 
 const emit = defineEmits(["update:modelValue", "upload", "success", "error", "progress"]);
 
-const { service, refs, setRefs } = useCool();
+const { refs, setRefs } = useCool();
 const { user } = useBase();
 const Form = useForm();
-
-// 模块配置
-const { options } = module.get("upload");
+const { options, toUpload } = useUpload();
 
 // 元素尺寸
 const size = computed(() => {
@@ -224,11 +214,6 @@ const disabled = computed(() => {
 	return props.isDisabled || props.disabled;
 });
 
-// 上传禁用
-const uploadDisabled = computed(() => {
-	return disabled.value;
-});
-
 // 最大上传数量
 const limit = props.limit || options.limit.upload;
 
@@ -357,7 +342,7 @@ function clear() {
 }
 
 // 文件上传请求
-async function httpRequest(req: any, item?: any) {
+async function httpRequest(req: any, item?: Upload.Item) {
 	if (!item) {
 		item = list.value.find((e) => e.uid == req.file.uid);
 	}
@@ -366,149 +351,23 @@ async function httpRequest(req: any, item?: any) {
 		return false;
 	}
 
-	// 文件id
-	const fileId = uuid("");
-
-	try {
-		// 上传模式、类型
-		const { mode, type } = await service.base.comm.uploadMode();
-
-		// 本地上传
-		const isLocal = mode == "local";
-
-		// 文件名
-		const fileName = fileId + "_" + req.file.name;
-
-		// Key
-		const key = isLocal ? fileName : pathJoin(props.prefixPath, props.menu, fileName);
-
-		// 多种上传请求
-		return new Promise((resolve, reject) => {
-			// 上传到云端
-			async function next({
-				host,
-				preview,
-				data
-			}: {
-				host: string;
-				preview?: string;
-				data?: any;
-			}) {
-				const fd = new FormData();
-
-				// key
-				fd.append("key", key);
-
-				// 签名数据
-				for (const i in data) {
-					if (!fd.has(i)) {
-						fd.append(i, data[i]);
-					}
-				}
-
-				// 文件
-				fd.append("file", req.file);
-
-				// 上传
-				await service
-					.request({
-						url: host,
-						method: "POST",
-						headers: {
-							"Content-Type": "multipart/form-data",
-							Authorization: isLocal ? user.token : null
-						},
-						timeout: 600000,
-						data: fd,
-						onUploadProgress(e: AxiosProgressEvent) {
-							item.progress = e.total ? Math.floor((e.loaded / e.total) * 100) : 0;
-							emit("progress", item);
-						},
-						proxy: isLocal,
-						NProgress: false
-					})
-					.then((res) => {
-						item.key = encodeURIComponent(key);
-
-						if (isLocal) {
-							item.url = res;
-						} else {
-							item.url = pathJoin(preview || host, item.key);
-						}
-
-						item.fileId = fileId;
-
-						emit("success", item);
-						resolve(item.url);
-						update();
-					})
-					.catch((err) => {
-						ElMessage.error(err.message);
-						item.error = err.message;
-						emit("error", item);
-						reject(err);
-					});
-			}
-
-			if (isLocal) {
-				next({
-					host: "/admin/base/comm/upload"
-				});
-			} else {
-				service.base.comm
-					.upload(
-						type == "aws"
-							? {
-									key
-								}
-							: {}
-					)
-					.then((res) => {
-						switch (type) {
-							// 腾讯
-							case "cos":
-								next({
-									host: res.url,
-									data: res.credentials
-								});
-								break;
-							// 阿里
-							case "oss":
-								next({
-									host: res.host,
-									preview: res.publicDomain,
-									data: {
-										OSSAccessKeyId: res.OSSAccessKeyId,
-										policy: res.policy,
-										signature: res.signature
-									}
-								});
-								break;
-							// 七牛
-							case "qiniu":
-								next({
-									host: res.uploadUrl,
-									preview: res.publicDomain,
-									data: {
-										token: res.token
-									}
-								});
-								break;
-							// aws
-							case "aws":
-								next({
-									host: res.url,
-									data: res.fields
-								});
-								break;
-						}
-					})
-					.catch(reject);
-			}
+	// 上传请求
+	toUpload(req.file, {
+		prefixPath: props.prefixPath,
+		onProgress(progress) {
+			item.progress = progress;
+			emit("progress", item);
+		}
+	})
+		.then((res) => {
+			Object.assign(item, res);
+			emit("success", item);
+			update();
+		})
+		.catch((err) => {
+			item.error = err.message;
+			emit("error", item);
 		});
-	} catch (err) {
-		ElMessage.error("上传配置错误");
-	}
 }
 
 // 检测是否还有未上传的文件

+ 7 - 2
src/plugins/upload/config.ts

@@ -3,8 +3,8 @@ export default () => {
 		label: "文件上传",
 		description: "基于 el-upload 封装的文件上传组件",
 		author: "COOL",
-		version: "1.0.0",
-		updateTime: "2024-02-01",
+		version: "1.1.0",
+		updateTime: "2024-02-05",
 		demo: [
 			{
 				name: "基础用法",
@@ -47,6 +47,11 @@ export default () => {
 				// 上传大小限制
 				size: 100
 			},
+			// 云端上传路径前缀
+			prefixPath: {
+				type: String,
+				default: "app/base"
+			},
 			// 规则
 			rules: [
 				{

+ 180 - 0
src/plugins/upload/hooks/index.ts

@@ -0,0 +1,180 @@
+import { ElMessage } from "element-plus";
+import { module, service } from "/@/cool";
+import { uuid } from "/@/cool/utils";
+import { pathJoin } from "../utils";
+import { useBase } from "/$/base";
+import { type AxiosProgressEvent } from "axios";
+import type { Upload } from "../types";
+import { merge } from "lodash-es";
+
+export function useUpload() {
+	const { options } = module.get("upload");
+	const { user } = useBase();
+
+	// 上传
+	async function toUpload(
+		file: File,
+		opts: Upload.Options = {}
+	): Promise<{
+		key: string;
+		url: string;
+		fileId: string;
+	}> {
+		return new Promise(async (resolve, reject) => {
+			// 合并配置
+			const { prefixPath, onProgress } = merge(opts, options);
+
+			// 文件id
+			const fileId = uuid("");
+
+			try {
+				// 上传模式、类型
+				const { mode, type } = await service.base.comm.uploadMode();
+
+				// 本地上传
+				const isLocal = mode == "local";
+
+				// 文件名
+				const fileName = fileId + "_" + file.name;
+
+				// Key
+				let key = isLocal ? fileName : pathJoin(prefixPath!, fileName);
+
+				// 多种上传请求
+				// 上传到云端
+				async function next({
+					host,
+					preview,
+					data
+				}: {
+					host: string;
+					preview?: string;
+					data?: any;
+				}) {
+					const fd = new FormData();
+
+					// key
+					fd.append("key", key);
+
+					// 签名数据
+					for (const i in data) {
+						if (!fd.has(i)) {
+							fd.append(i, data[i]);
+						}
+					}
+
+					// 文件
+					fd.append("file", file);
+
+					// 上传
+					await service
+						.request({
+							url: host,
+							method: "POST",
+							headers: {
+								"Content-Type": "multipart/form-data",
+								Authorization: isLocal ? user.token : null
+							},
+							timeout: 600000,
+							data: fd,
+							onUploadProgress(e: AxiosProgressEvent) {
+								const progress = e.total
+									? Math.floor((e.loaded / e.total) * 100)
+									: 0;
+
+								onProgress?.(progress);
+							},
+							proxy: isLocal,
+							NProgress: false
+						})
+						.then((res) => {
+							key = encodeURIComponent(key);
+
+							let url = "";
+
+							if (isLocal) {
+								url = res;
+							} else {
+								url = pathJoin(preview || host, key);
+							}
+
+							resolve({
+								key,
+								url,
+								fileId
+							});
+						})
+						.catch((err) => {
+							ElMessage.error(err.message);
+							reject(err);
+						});
+				}
+
+				if (isLocal) {
+					next({
+						host: "/admin/base/comm/upload"
+					});
+				} else {
+					service.base.comm
+						.upload(
+							type == "aws"
+								? {
+										key
+									}
+								: {}
+						)
+						.then((res) => {
+							switch (type) {
+								// 腾讯
+								case "cos":
+									next({
+										host: res.url,
+										data: res.credentials
+									});
+									break;
+								// 阿里
+								case "oss":
+									next({
+										host: res.host,
+										preview: res.publicDomain,
+										data: {
+											OSSAccessKeyId: res.OSSAccessKeyId,
+											policy: res.policy,
+											signature: res.signature
+										}
+									});
+									break;
+								// 七牛
+								case "qiniu":
+									next({
+										host: res.uploadUrl,
+										preview: res.publicDomain,
+										data: {
+											token: res.token
+										}
+									});
+									break;
+								// aws
+								case "aws":
+									next({
+										host: res.url,
+										data: res.fields
+									});
+									break;
+							}
+						})
+						.catch(reject);
+				}
+			} catch (err) {
+				ElMessage.error("文件上传失败");
+				console.error("[upload]", err);
+				reject(err);
+			}
+		});
+	}
+
+	return {
+		options,
+		toUpload
+	};
+}

+ 6 - 0
src/plugins/upload/types/index.d.ts

@@ -15,4 +15,10 @@ export declare namespace Upload {
 		isPlay?: boolean;
 		[key: string]: any;
 	}
+
+	interface Options {
+		prefixPath?: string;
+		onProgress?(progress: number): void;
+		[key: string]: any;
+	}
 }

+ 1 - 12
src/plugins/upload/utils/index.ts

@@ -1,7 +1,7 @@
 import { last } from "lodash-es";
 import { filename, extname } from "/@/cool/utils";
 import { module } from "/@/cool";
-import type { Upload } from "../types";
+import { Upload } from "../types";
 
 // 模块参数
 const { options } = module.get("upload");
@@ -74,14 +74,3 @@ export function pathJoin(...parts: string[]): string {
 		return normalizedParts.join("/");
 	}
 }
-
-// 下载
-export function download(url: string) {
-	const a = document.createElement("a");
-	a.href = url;
-	a.download = url;
-	a.target = "_blank";
-	a.style.display = "none";
-	document.body.appendChild(a);
-	a.click();
-}