优化upload模块

This commit is contained in:
icssoa 2022-06-30 11:29:55 +08:00
parent bbe4b1cfd8
commit 06ebfe7e60
6 changed files with 207 additions and 128 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "front-next", "name": "front-next",
"version": "5.5.0", "version": "5.5.1",
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"build": "vite build", "build": "vite build",
@ -9,7 +9,7 @@
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix" "lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix"
}, },
"dependencies": { "dependencies": {
"@cool-vue/crud": "^5.2.3", "@cool-vue/crud": "^5.2.7",
"@element-plus/icons-vue": "^1.1.3", "@element-plus/icons-vue": "^1.1.3",
"@vueuse/core": "^8.2.5", "@vueuse/core": "^8.2.5",
"axios": "^0.27.2", "axios": "^0.27.2",

View File

@ -46,15 +46,10 @@
<!-- 文件 --> <!-- 文件 -->
<template v-else> <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"> <span class="cl-upload__size">
@ -259,7 +254,7 @@ function beforeUpload(file: any, item?: Item) {
} }
const d = { const d = {
type: file.type.includes("image") ? "image" : "file", type: file.type?.includes("image") ? "image" : "file",
preload: "", preload: "",
progress: 0, progress: 0,
url: "", url: "",
@ -289,6 +284,11 @@ function remove(index: number) {
update(); update();
} }
//
function clear() {
list.value = [];
}
// //
function preview(item: Item) { function preview(item: Item) {
if (item.type == "image") { if (item.type == "image") {
@ -312,38 +312,39 @@ async function httpRequest(req: any, item?: any) {
// //
return new Promise((resolve, reject) => { 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)) { for (const i in data) {
data.append(i, params[i]); fd.append(i, data[i]);
}
} }
if (mode == "local") { //
data.append("key", fileName); if (mode == "cloud") {
} else {
fileName = [props.prefixPath, dayjs().format("YYYY-MM-DD"), fileName] fileName = [props.prefixPath, dayjs().format("YYYY-MM-DD"), fileName]
.filter(Boolean) .filter(Boolean)
.join("/"); .join("/");
data.append("key", fileName);
} }
data.append("file", req.file); //
fd.append("key", fileName);
//
fd.append("file", req.file);
// //
await service await service
.request({ .request({
url: params.host, url: host,
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "multipart/form-data" "Content-Type": "multipart/form-data"
}, },
timeout: 600000, timeout: 600000,
data, data: fd,
onUploadProgress(e: any) { onUploadProgress(e: { loaded: number; total: number }) {
item.progress = parseInt((e.loaded / e.total) * 100); item.progress = parseInt(String((e.loaded / e.total) * 100));
emit("progress", item); emit("progress", item);
}, },
proxy: mode == "local" ? true : false proxy: mode == "local" ? true : false
@ -352,7 +353,7 @@ async function httpRequest(req: any, item?: any) {
if (mode === "local") { if (mode === "local") {
item.url = res; item.url = res;
} else { } else {
item.url = `${params.preview}/${fileName}`; item.url = `${preview || host}/${fileName}`;
} }
emit("success", item); emit("success", item);
@ -375,11 +376,36 @@ async function httpRequest(req: any, item?: any) {
service.base.comm service.base.comm
.upload() .upload()
.then((res) => { .then((res) => {
next({ switch (type) {
host: res.uploadUrl, //
preview: res.host || res.publicDomain, case "cos":
...res 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); .catch(reject);
} }
@ -452,8 +478,11 @@ defineExpose({
isAdd, isAdd,
list, list,
check, check,
clear,
remove, remove,
upload(file: File) { upload(file: File) {
clear();
Upload.value.clearFiles();
Upload.value.handleStart(file); Upload.value.handleStart(file);
Upload.value.submit(); Upload.value.submit();
} }
@ -581,14 +610,11 @@ defineExpose({
} }
&__name { &__name {
height: 15px; display: inline-block;
width: 100%; width: 100%;
margin-top: 5px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px; font-size: 13px;
text-align: center; text-align: center;
word-break: break-all;
} }
&__size { &__size {

View File

@ -8,15 +8,22 @@
<cl-dialog <cl-dialog
v-model="visible" v-model="visible"
title="文件空间" title="文件空间"
height="630px" height="650px"
width="1000px" width="1080px"
keep-alive keep-alive
custom-class="cl-upload-space__dialog" custom-class="cl-upload-space__dialog"
:close-on-click-modal="false" :close-on-click-modal="false"
append-to-body append-to-body
:controls="['slot-expand', 'cl-flex1', 'fullscreen', 'close']" :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 /> <category />
@ -42,7 +49,7 @@
</div> </div>
<el-button type="success" :disabled="!isSelected" @click="confirm()" <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()" <el-button type="danger" :disabled="!isSelected" @click="remove()"
@ -55,22 +62,17 @@
class="cl-upload-space__file scroller1" class="cl-upload-space__file scroller1"
v-infinite-scroll="loadmore" v-infinite-scroll="loadmore"
v-loading="loading" v-loading="loading"
@dragover="onDragover"
@drop="onDrop"
> >
<!-- 文件列表 --> <!-- 文件列表 -->
<template v-if="list.length > 0"> <template v-if="list.length > 0">
<div class="cl-upload-space__file-list"> <div class="cl-upload-space__file-list">
<el-row :gutter="10"> <div
<el-col class="cl-upload-space__file-item"
:xs="12" v-for="item in list"
:sm="6" :key="item.preload || item.url"
v-for="item in list" >
:key="item.preload || item.url" <file-item :data="item" @select="select" @remove="remove" />
> </div>
<file-item :data="item" @select="select" @remove="remove" />
</el-col>
</el-row>
</div> </div>
</template> </template>
@ -211,7 +213,10 @@ function close() {
// //
function onSuccess(data: any) { function onSuccess(data: any) {
service.space.info service.space.info
.add(data) .add({
classifyId: category.id,
...data
})
.then((res) => { .then((res) => {
data.id = res.id; data.id = res.id;
}) })
@ -222,7 +227,6 @@ function onSuccess(data: any) {
// //
function onUpload(data: any) { function onUpload(data: any) {
data.classifyId = category.id;
list.value.unshift(data); list.value.unshift(data);
} }
@ -316,8 +320,10 @@ function onDragover(e: any) {
function onDrop(e: any) { function onDrop(e: any) {
e.preventDefault(); e.preventDefault();
e.dataTransfer.files.forEach((f: File) => { e.dataTransfer.files.forEach((f: File, i: number) => {
Upload.value.upload(f); setTimeout(() => {
Upload.value.upload(f);
}, i * 10);
}); });
} }
@ -359,7 +365,6 @@ defineExpose({
&__content { &__content {
flex: 1; flex: 1;
max-width: 100%; max-width: 100%;
padding: 0 10px;
box-sizing: border-box; box-sizing: border-box;
background-color: #fff; background-color: #fff;
border-radius: 5px; border-radius: 5px;
@ -370,16 +375,24 @@ defineExpose({
align-items: center; align-items: center;
height: 50px; height: 50px;
overflow: auto hidden; overflow: auto hidden;
padding: 0 10px;
} }
&__file { &__file {
height: calc(100% - 50px); height: calc(100% - 50px);
padding: 0 10px;
box-sizing: border-box;
position: relative; position: relative;
&-list { &-list {
.el-row { display: flex;
width: 100%; flex-wrap: wrap;
} }
&-item {
height: 150px;
width: 150px;
margin: 0 10px 10px 0;
} }
&-empty { &-empty {
@ -415,5 +428,11 @@ defineExpose({
&__footer { &__footer {
padding: 9px 0; padding: 9px 0;
} }
&.is-mini {
.cl-upload-space__file-list {
justify-content: center;
}
}
} }
</style> </style>

View File

@ -7,9 +7,8 @@
}" }"
> >
<div class="cl-upload-space-category__search"> <div class="cl-upload-space-category__search">
<el-button type="primary" @click="edit()">添加分类</el-button> <el-button type="primary" @click="edit()">添加</el-button>
<el-input v-model="keyword" placeholder="搜索" clearable />
<el-input v-model="keyword" placeholder="关键字过滤" clearable />
</div> </div>
<div class="cl-upload-space-category__list"> <div class="cl-upload-space-category__list">
@ -209,7 +208,7 @@ onMounted(() => {
border-radius: 5px; border-radius: 5px;
&.is-show { &.is-show {
width: 250px; width: 220px;
margin-right: 5px; margin-right: 5px;
} }
@ -244,18 +243,20 @@ onMounted(() => {
.item { .item {
list-style: none; list-style: none;
font-size: 14px; font-size: 13px;
height: 40px; height: 35px;
line-height: 40px; line-height: 35px;
border-bottom: 1px dashed #eee;
padding: 0 10px; padding: 0 10px;
cursor: pointer; cursor: pointer;
background-color: #f7f7f7;
margin-bottom: 10px;
border-radius: 3px;
&.is-active { &.is-active {
color: var(--color-primary); background-color: #eee;
} }
&:not(.cl-context-menu__target):hover { &:not(.is-active):hover {
background-color: #f7f7f7; background-color: #f7f7f7;
} }
} }

View File

@ -1,54 +1,53 @@
<template> <template>
<div <div class="cl-upload-space-file__wrap">
class="cl-upload-space-file" <div
:class="[`is-${info.type}`]" class="cl-upload-space-file"
@click="select" :class="[`is-${info.type}`]"
@contextmenu.stop.prevent="onContextMenu" @click="select"
> @contextmenu.stop.prevent="onContextMenu"
<!-- 错误 --> >
<template v-if="info.error"> <!-- 错误 -->
<div class="cl-upload-space-file__error">上传失败{{ info.error }}</div> <template v-if="info.error">
</template> <div class="cl-upload-space-file__error">上传失败{{ info.error }}</div>
<!-- 成功 -->
<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>
</template> </template>
<!-- 其他 --> <!-- 成功 -->
<template v-else> <template v-else>
<div class="cl-upload-space-file__icon"> <!-- 图片 -->
<el-icon :size="20"><document /></el-icon> <template v-if="info.type === 'image'">
<span>{{ extname(url) }}</span> <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> </div>
<!-- 文件名 -->
<span class="cl-upload-space-file__name">{{ fileName(url) }}</span>
<!-- 大小 -->
<span class="cl-upload-space-file__size">{{ fileSize(info.size) }}</span>
</template> </template>
<!-- 进度条 --> <!-- 遮罩层 -->
<div <div v-if="isSelected" class="cl-upload-space-file__mask">
class="cl-upload-space-file__progress" <span>{{ index + 1 }}</span>
v-if="info.progress > 0 && info.progress < 100"
>
<el-progress :percentage="info.progress" :show-text="false"></el-progress>
</div> </div>
</template>
<!-- 遮罩层 -->
<div v-if="isSelected" class="cl-upload-space-file__mask">
<span>{{ index + 1 }}</span>
</div> </div>
</div> </div>
</template> </template>
@ -98,6 +97,9 @@ function remove() {
// //
function onContextMenu(e: any) { function onContextMenu(e: any) {
ContextMenu.open(e, { ContextMenu.open(e, {
hover: {
target: "cl-upload-space-file__wrap"
},
list: [ list: [
{ {
label: "预览", label: "预览",
@ -139,7 +141,7 @@ function onContextMenu(e: any) {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 160px; height: 100%;
width: 100%; width: 100%;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
@ -149,12 +151,17 @@ function onContextMenu(e: any) {
background-color: #f7f7f7; background-color: #f7f7f7;
margin-bottom: 10px; margin-bottom: 10px;
&__wrap {
height: 100%;
width: 100%;
}
&.is-image { &.is-image {
overflow: hidden; overflow: hidden;
:deep(.el-image) { :deep(.el-image) {
height: 100%; max-height: 100%;
width: 100%; max-width: 100%;
} }
.image-error { .image-error {
@ -164,7 +171,8 @@ function onContextMenu(e: any) {
justify-content: center; justify-content: center;
font-size: 14px; font-size: 14px;
color: #f56c6c; color: #f56c6c;
height: 100%; height: 150px;
width: 150px;
background-color: #fef0f0; background-color: #fef0f0;
padding: 10px; padding: 10px;
box-sizing: border-box; box-sizing: border-box;
@ -188,14 +196,11 @@ function onContextMenu(e: any) {
} }
&__name { &__name {
height: 15px; display: inline-block;
width: 100%; width: 100%;
margin-top: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px; font-size: 13px;
text-align: center; text-align: center;
word-break: break-all;
} }
&__size { &__size {

View File

@ -984,14 +984,14 @@
"@babel/helper-validator-identifier" "^7.16.7" "@babel/helper-validator-identifier" "^7.16.7"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@cool-vue/crud@^5.2.3": "@cool-vue/crud@^5.2.7":
version "5.2.3" version "5.2.7"
resolved "https://registry.npmjs.org/@cool-vue/crud/-/crud-5.2.3.tgz#05e24d484e7748b71f4dcdf5649fd297113a6ee5" resolved "https://registry.npmjs.org/@cool-vue/crud/-/crud-5.2.7.tgz#4b3b693887dc077e839b531b7c7b8b2b94ee8c89"
integrity sha512-LWrfEnFVkIZyVUdCI/hsF3Kl4v0BrsweHMXYW7WBpfDEG/IR9n2mYTZPqFXjlqgX/vWux4YXmD0pg1cIAN2bMQ== integrity sha512-1HkYUs45vTxtZ3pYmlshnQF0YH6xYAL0txaPGN0QclIZ1DHtoyQBLfouZ+mJSOMfUnj8+RuOKlieDJRDhOK7Yg==
dependencies: dependencies:
array.prototype.flat "^1.2.4" array.prototype.flat "^1.2.4"
core-js "^3.21.1" core-js "^3.21.1"
element-plus "^2.2.5" element-plus "^2.2.6"
merge "^2.1.1" merge "^2.1.1"
mitt "^3.0.0" mitt "^3.0.0"
vue "^3.2.31" vue "^3.2.31"
@ -1038,6 +1038,13 @@
dependencies: dependencies:
"@floating-ui/core" "^0.7.3" "@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": "@hapi/hoek@^9.0.0":
version "9.2.1" version "9.2.1"
resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" 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" memoize-one "^6.0.0"
normalize-wheel-es "^1.1.2" 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: emoji-regex@^8.0.0:
version "8.0.0" version "8.0.0"
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"