mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2024-11-01 14:10:27 +08:00
更新 upload 插件
This commit is contained in:
parent
52e345712e
commit
d9b2405184
@ -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) {
|
||||
|
@ -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 = () => {
|
||||
|
@ -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;
|
||||
// 上传请求
|
||||
toUpload(req.file, {
|
||||
prefixPath: props.prefixPath,
|
||||
onProgress(progress) {
|
||||
item.progress = progress;
|
||||
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;
|
||||
|
||||
Object.assign(item, res);
|
||||
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);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
ElMessage.error("上传配置错误");
|
||||
}
|
||||
}
|
||||
|
||||
// 检测是否还有未上传的文件
|
||||
|
@ -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
src/plugins/upload/hooks/index.ts
Normal file
180
src/plugins/upload/hooks/index.ts
Normal file
@ -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
src/plugins/upload/types/index.d.ts
vendored
6
src/plugins/upload/types/index.d.ts
vendored
@ -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,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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user