mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2024-11-01 06:02:38 +08:00
优化upload模块
This commit is contained in:
parent
bbe4b1cfd8
commit
06ebfe7e60
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "front-next",
|
||||
"version": "5.5.0",
|
||||
"version": "5.5.1",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "vite build",
|
||||
@ -9,7 +9,7 @@
|
||||
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cool-vue/crud": "^5.2.3",
|
||||
"@cool-vue/crud": "^5.2.7",
|
||||
"@element-plus/icons-vue": "^1.1.3",
|
||||
"@vueuse/core": "^8.2.5",
|
||||
"axios": "^0.27.2",
|
||||
|
@ -46,15 +46,10 @@
|
||||
|
||||
<!-- 文件 -->
|
||||
<template v-else>
|
||||
<div class="cl-upload__icon">
|
||||
<!-- 图标 -->
|
||||
<el-icon :size="20"><document /></el-icon>
|
||||
<!-- 扩展名 -->
|
||||
<span>{{ extname(item.preload) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 文件名 -->
|
||||
<span class="cl-upload__name">{{ fileName(item.preload) }}</span>
|
||||
<span class="cl-upload__name"
|
||||
>{{ fileName(item.preload) }}.{{ extname(item.preload) }}</span
|
||||
>
|
||||
|
||||
<!-- 大小 -->
|
||||
<span class="cl-upload__size">
|
||||
@ -259,7 +254,7 @@ function beforeUpload(file: any, item?: Item) {
|
||||
}
|
||||
|
||||
const d = {
|
||||
type: file.type.includes("image") ? "image" : "file",
|
||||
type: file.type?.includes("image") ? "image" : "file",
|
||||
preload: "",
|
||||
progress: 0,
|
||||
url: "",
|
||||
@ -289,6 +284,11 @@ function remove(index: number) {
|
||||
update();
|
||||
}
|
||||
|
||||
// 清空
|
||||
function clear() {
|
||||
list.value = [];
|
||||
}
|
||||
|
||||
// 预览
|
||||
function preview(item: Item) {
|
||||
if (item.type == "image") {
|
||||
@ -312,38 +312,39 @@ async function httpRequest(req: any, item?: any) {
|
||||
|
||||
// 多种上传请求
|
||||
return new Promise((resolve, reject) => {
|
||||
async function next(params: any) {
|
||||
const data = new FormData();
|
||||
// 上传到云端
|
||||
async function next({ host, preview, data }: any) {
|
||||
const fd = new FormData();
|
||||
|
||||
for (const i in params) {
|
||||
if (!["host", "publicDomain", "fileKey", "uploadUrl", "preview"].includes(i)) {
|
||||
data.append(i, params[i]);
|
||||
}
|
||||
// 签名数据
|
||||
for (const i in data) {
|
||||
fd.append(i, data[i]);
|
||||
}
|
||||
|
||||
if (mode == "local") {
|
||||
data.append("key", fileName);
|
||||
} else {
|
||||
// 云端拼接路径
|
||||
if (mode == "cloud") {
|
||||
fileName = [props.prefixPath, dayjs().format("YYYY-MM-DD"), fileName]
|
||||
.filter(Boolean)
|
||||
.join("/");
|
||||
data.append("key", fileName);
|
||||
}
|
||||
|
||||
data.append("file", req.file);
|
||||
// 文件名
|
||||
fd.append("key", fileName);
|
||||
// 文件
|
||||
fd.append("file", req.file);
|
||||
|
||||
// 上传
|
||||
await service
|
||||
.request({
|
||||
url: params.host,
|
||||
url: host,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
},
|
||||
timeout: 600000,
|
||||
data,
|
||||
onUploadProgress(e: any) {
|
||||
item.progress = parseInt((e.loaded / e.total) * 100);
|
||||
data: fd,
|
||||
onUploadProgress(e: { loaded: number; total: number }) {
|
||||
item.progress = parseInt(String((e.loaded / e.total) * 100));
|
||||
emit("progress", item);
|
||||
},
|
||||
proxy: mode == "local" ? true : false
|
||||
@ -352,7 +353,7 @@ async function httpRequest(req: any, item?: any) {
|
||||
if (mode === "local") {
|
||||
item.url = res;
|
||||
} else {
|
||||
item.url = `${params.preview}/${fileName}`;
|
||||
item.url = `${preview || host}/${fileName}`;
|
||||
}
|
||||
|
||||
emit("success", item);
|
||||
@ -375,11 +376,36 @@ async function httpRequest(req: any, item?: any) {
|
||||
service.base.comm
|
||||
.upload()
|
||||
.then((res) => {
|
||||
next({
|
||||
host: res.uploadUrl,
|
||||
preview: res.host || res.publicDomain,
|
||||
...res
|
||||
});
|
||||
switch (type) {
|
||||
// 腾讯
|
||||
case "cos":
|
||||
next({
|
||||
host: res.url,
|
||||
data: res.credentials
|
||||
});
|
||||
break;
|
||||
// 阿里
|
||||
case "oss":
|
||||
next({
|
||||
host: res.host,
|
||||
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;
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
}
|
||||
@ -452,8 +478,11 @@ defineExpose({
|
||||
isAdd,
|
||||
list,
|
||||
check,
|
||||
clear,
|
||||
remove,
|
||||
upload(file: File) {
|
||||
clear();
|
||||
Upload.value.clearFiles();
|
||||
Upload.value.handleStart(file);
|
||||
Upload.value.submit();
|
||||
}
|
||||
@ -581,14 +610,11 @@ defineExpose({
|
||||
}
|
||||
|
||||
&__name {
|
||||
height: 15px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
&__size {
|
||||
|
@ -8,15 +8,22 @@
|
||||
<cl-dialog
|
||||
v-model="visible"
|
||||
title="文件空间"
|
||||
height="630px"
|
||||
width="1000px"
|
||||
height="650px"
|
||||
width="1080px"
|
||||
keep-alive
|
||||
custom-class="cl-upload-space__dialog"
|
||||
:close-on-click-modal="false"
|
||||
append-to-body
|
||||
:controls="['slot-expand', 'cl-flex1', 'fullscreen', 'close']"
|
||||
>
|
||||
<div class="cl-upload-space">
|
||||
<div
|
||||
class="cl-upload-space"
|
||||
:class="{
|
||||
'is-mini': app.browser.isMini
|
||||
}"
|
||||
@dragover="onDragover"
|
||||
@drop="onDrop"
|
||||
>
|
||||
<!-- 类目 -->
|
||||
<category />
|
||||
|
||||
@ -42,7 +49,7 @@
|
||||
</div>
|
||||
|
||||
<el-button type="success" :disabled="!isSelected" @click="confirm()"
|
||||
>使用选中文件 {{ selection.length }} / {{ limit }}</el-button
|
||||
>使用选中文件 {{ selection.length }}/{{ limit }}</el-button
|
||||
>
|
||||
|
||||
<el-button type="danger" :disabled="!isSelected" @click="remove()"
|
||||
@ -55,22 +62,17 @@
|
||||
class="cl-upload-space__file scroller1"
|
||||
v-infinite-scroll="loadmore"
|
||||
v-loading="loading"
|
||||
@dragover="onDragover"
|
||||
@drop="onDrop"
|
||||
>
|
||||
<!-- 文件列表 -->
|
||||
<template v-if="list.length > 0">
|
||||
<div class="cl-upload-space__file-list">
|
||||
<el-row :gutter="10">
|
||||
<el-col
|
||||
:xs="12"
|
||||
:sm="6"
|
||||
v-for="item in list"
|
||||
:key="item.preload || item.url"
|
||||
>
|
||||
<file-item :data="item" @select="select" @remove="remove" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div
|
||||
class="cl-upload-space__file-item"
|
||||
v-for="item in list"
|
||||
:key="item.preload || item.url"
|
||||
>
|
||||
<file-item :data="item" @select="select" @remove="remove" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -211,7 +213,10 @@ function close() {
|
||||
// 上传成功
|
||||
function onSuccess(data: any) {
|
||||
service.space.info
|
||||
.add(data)
|
||||
.add({
|
||||
classifyId: category.id,
|
||||
...data
|
||||
})
|
||||
.then((res) => {
|
||||
data.id = res.id;
|
||||
})
|
||||
@ -222,7 +227,6 @@ function onSuccess(data: any) {
|
||||
|
||||
// 上传时
|
||||
function onUpload(data: any) {
|
||||
data.classifyId = category.id;
|
||||
list.value.unshift(data);
|
||||
}
|
||||
|
||||
@ -316,8 +320,10 @@ function onDragover(e: any) {
|
||||
function onDrop(e: any) {
|
||||
e.preventDefault();
|
||||
|
||||
e.dataTransfer.files.forEach((f: File) => {
|
||||
Upload.value.upload(f);
|
||||
e.dataTransfer.files.forEach((f: File, i: number) => {
|
||||
setTimeout(() => {
|
||||
Upload.value.upload(f);
|
||||
}, i * 10);
|
||||
});
|
||||
}
|
||||
|
||||
@ -359,7 +365,6 @@ defineExpose({
|
||||
&__content {
|
||||
flex: 1;
|
||||
max-width: 100%;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
@ -370,16 +375,24 @@ defineExpose({
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
overflow: auto hidden;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
&__file {
|
||||
height: calc(100% - 50px);
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
&-list {
|
||||
.el-row {
|
||||
width: 100%;
|
||||
}
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&-item {
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
&-empty {
|
||||
@ -415,5 +428,11 @@ defineExpose({
|
||||
&__footer {
|
||||
padding: 9px 0;
|
||||
}
|
||||
|
||||
&.is-mini {
|
||||
.cl-upload-space__file-list {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -7,9 +7,8 @@
|
||||
}"
|
||||
>
|
||||
<div class="cl-upload-space-category__search">
|
||||
<el-button type="primary" @click="edit()">添加分类</el-button>
|
||||
|
||||
<el-input v-model="keyword" placeholder="关键字过滤" clearable />
|
||||
<el-button type="primary" @click="edit()">添加</el-button>
|
||||
<el-input v-model="keyword" placeholder="搜索" clearable />
|
||||
</div>
|
||||
|
||||
<div class="cl-upload-space-category__list">
|
||||
@ -209,7 +208,7 @@ onMounted(() => {
|
||||
border-radius: 5px;
|
||||
|
||||
&.is-show {
|
||||
width: 250px;
|
||||
width: 220px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@ -244,18 +243,20 @@ onMounted(() => {
|
||||
|
||||
.item {
|
||||
list-style: none;
|
||||
font-size: 14px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
border-bottom: 1px dashed #eee;
|
||||
font-size: 13px;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
background-color: #f7f7f7;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 3px;
|
||||
|
||||
&.is-active {
|
||||
color: var(--color-primary);
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
&:not(.cl-context-menu__target):hover {
|
||||
&:not(.is-active):hover {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,53 @@
|
||||
<template>
|
||||
<div
|
||||
class="cl-upload-space-file"
|
||||
:class="[`is-${info.type}`]"
|
||||
@click="select"
|
||||
@contextmenu.stop.prevent="onContextMenu"
|
||||
>
|
||||
<!-- 错误 -->
|
||||
<template v-if="info.error">
|
||||
<div class="cl-upload-space-file__error">上传失败:{{ info.error }}</div>
|
||||
</template>
|
||||
|
||||
<!-- 成功 -->
|
||||
<template v-else>
|
||||
<!-- 图片 -->
|
||||
<template v-if="info.type === 'image'">
|
||||
<el-image fit="contain" :src="url" lazy>
|
||||
<template #error>
|
||||
<div class="image-error">
|
||||
<span>{{ url }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div class="cl-upload-space-file__wrap">
|
||||
<div
|
||||
class="cl-upload-space-file"
|
||||
:class="[`is-${info.type}`]"
|
||||
@click="select"
|
||||
@contextmenu.stop.prevent="onContextMenu"
|
||||
>
|
||||
<!-- 错误 -->
|
||||
<template v-if="info.error">
|
||||
<div class="cl-upload-space-file__error">上传失败:{{ info.error }}</div>
|
||||
</template>
|
||||
|
||||
<!-- 其他 -->
|
||||
<!-- 成功 -->
|
||||
<template v-else>
|
||||
<div class="cl-upload-space-file__icon">
|
||||
<el-icon :size="20"><document /></el-icon>
|
||||
<span>{{ extname(url) }}</span>
|
||||
<!-- 图片 -->
|
||||
<template v-if="info.type === 'image'">
|
||||
<el-image fit="contain" :src="url" lazy>
|
||||
<template #error>
|
||||
<div class="image-error">
|
||||
<span>{{ url }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</template>
|
||||
|
||||
<!-- 其他 -->
|
||||
<template v-else>
|
||||
<!-- 文件名 -->
|
||||
<span class="cl-upload-space-file__name"
|
||||
>{{ fileName(url) }}.{{ extname(url) }}</span
|
||||
>
|
||||
|
||||
<!-- 大小 -->
|
||||
<span class="cl-upload-space-file__size">{{ fileSize(info.size) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<div
|
||||
class="cl-upload-space-file__progress"
|
||||
v-if="info.progress > 0 && info.progress < 100"
|
||||
>
|
||||
<el-progress :percentage="info.progress" :show-text="false"></el-progress>
|
||||
</div>
|
||||
|
||||
<!-- 文件名 -->
|
||||
<span class="cl-upload-space-file__name">{{ fileName(url) }}</span>
|
||||
|
||||
<!-- 大小 -->
|
||||
<span class="cl-upload-space-file__size">{{ fileSize(info.size) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<div
|
||||
class="cl-upload-space-file__progress"
|
||||
v-if="info.progress > 0 && info.progress < 100"
|
||||
>
|
||||
<el-progress :percentage="info.progress" :show-text="false"></el-progress>
|
||||
<!-- 遮罩层 -->
|
||||
<div v-if="isSelected" class="cl-upload-space-file__mask">
|
||||
<span>{{ index + 1 }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 遮罩层 -->
|
||||
<div v-if="isSelected" class="cl-upload-space-file__mask">
|
||||
<span>{{ index + 1 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -98,6 +97,9 @@ function remove() {
|
||||
// 右键菜单
|
||||
function onContextMenu(e: any) {
|
||||
ContextMenu.open(e, {
|
||||
hover: {
|
||||
target: "cl-upload-space-file__wrap"
|
||||
},
|
||||
list: [
|
||||
{
|
||||
label: "预览",
|
||||
@ -139,7 +141,7 @@ function onContextMenu(e: any) {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 160px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@ -149,12 +151,17 @@ function onContextMenu(e: any) {
|
||||
background-color: #f7f7f7;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__wrap {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.is-image {
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.el-image) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.image-error {
|
||||
@ -164,7 +171,8 @@ function onContextMenu(e: any) {
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
color: #f56c6c;
|
||||
height: 100%;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
background-color: #fef0f0;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
@ -188,14 +196,11 @@ function onContextMenu(e: any) {
|
||||
}
|
||||
|
||||
&__name {
|
||||
height: 15px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
&__size {
|
||||
|
38
yarn.lock
38
yarn.lock
@ -984,14 +984,14 @@
|
||||
"@babel/helper-validator-identifier" "^7.16.7"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@cool-vue/crud@^5.2.3":
|
||||
version "5.2.3"
|
||||
resolved "https://registry.npmjs.org/@cool-vue/crud/-/crud-5.2.3.tgz#05e24d484e7748b71f4dcdf5649fd297113a6ee5"
|
||||
integrity sha512-LWrfEnFVkIZyVUdCI/hsF3Kl4v0BrsweHMXYW7WBpfDEG/IR9n2mYTZPqFXjlqgX/vWux4YXmD0pg1cIAN2bMQ==
|
||||
"@cool-vue/crud@^5.2.7":
|
||||
version "5.2.7"
|
||||
resolved "https://registry.npmjs.org/@cool-vue/crud/-/crud-5.2.7.tgz#4b3b693887dc077e839b531b7c7b8b2b94ee8c89"
|
||||
integrity sha512-1HkYUs45vTxtZ3pYmlshnQF0YH6xYAL0txaPGN0QclIZ1DHtoyQBLfouZ+mJSOMfUnj8+RuOKlieDJRDhOK7Yg==
|
||||
dependencies:
|
||||
array.prototype.flat "^1.2.4"
|
||||
core-js "^3.21.1"
|
||||
element-plus "^2.2.5"
|
||||
element-plus "^2.2.6"
|
||||
merge "^2.1.1"
|
||||
mitt "^3.0.0"
|
||||
vue "^3.2.31"
|
||||
@ -1038,6 +1038,13 @@
|
||||
dependencies:
|
||||
"@floating-ui/core" "^0.7.3"
|
||||
|
||||
"@floating-ui/dom@^0.5.3":
|
||||
version "0.5.4"
|
||||
resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz#4eae73f78bcd4bd553ae2ade30e6f1f9c73fe3f1"
|
||||
integrity sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==
|
||||
dependencies:
|
||||
"@floating-ui/core" "^0.7.3"
|
||||
|
||||
"@hapi/hoek@^9.0.0":
|
||||
version "9.2.1"
|
||||
resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17"
|
||||
@ -2994,6 +3001,27 @@ element-plus@^2.2.5:
|
||||
memoize-one "^6.0.0"
|
||||
normalize-wheel-es "^1.1.2"
|
||||
|
||||
element-plus@^2.2.6:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.npmjs.org/element-plus/-/element-plus-2.2.6.tgz#60b9e91a2159526123d1b950263de37947153433"
|
||||
integrity sha512-N9G4yWSxDt1YtreCJgt7UaSsXKuR4Fzb3ThzlBjbGDYDhcHijsrLL3qkdLZgeoSB13LRyr9pgP1ljNXdaYGa+g==
|
||||
dependencies:
|
||||
"@ctrl/tinycolor" "^3.4.1"
|
||||
"@element-plus/icons-vue" "^2.0.5"
|
||||
"@floating-ui/dom" "^0.5.3"
|
||||
"@popperjs/core" "npm:@sxzz/popperjs-es@^2.11.7"
|
||||
"@types/lodash" "^4.14.182"
|
||||
"@types/lodash-es" "^4.17.6"
|
||||
"@vueuse/core" "^8.6.0"
|
||||
async-validator "^4.1.1"
|
||||
dayjs "^1.11.3"
|
||||
escape-html "^1.0.3"
|
||||
lodash "^4.17.21"
|
||||
lodash-es "^4.17.21"
|
||||
lodash-unified "^1.0.2"
|
||||
memoize-one "^6.0.0"
|
||||
normalize-wheel-es "^1.1.2"
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
|
Loading…
Reference in New Issue
Block a user