优化 upload 模块,添加响应式处理

This commit is contained in:
icssoa 2021-03-28 03:09:02 +08:00
parent a1790e65f5
commit 1c9f7caeaf
9 changed files with 916 additions and 743 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "cool-admin-vue", "name": "cool-admin-vue",
"version": "3.1.7", "version": "3.2.0",
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",

View File

@ -9,7 +9,7 @@ export default {
list: [fMenu] list: [fMenu]
}, },
getters: { getters: {
// 窗口列表 // 页面进程列表
processList: state => state.list processList: state => state.list
}, },
mutations: { mutations: {

View File

@ -22,7 +22,7 @@
</div> </div>
<div class="container"> <div class="container">
<cl-crud ref="crud" @load="onLoad" :on-refresh="onRefresh"> <cl-crud ref="crud" :on-refresh="onRefresh" @load="onLoad">
<el-row type="flex"> <el-row type="flex">
<cl-refresh-btn></cl-refresh-btn> <cl-refresh-btn></cl-refresh-btn>
<cl-add-btn></cl-add-btn> <cl-add-btn></cl-add-btn>
@ -123,36 +123,30 @@ export default {
columns: [ columns: [
{ {
type: "selection", type: "selection",
align: "center", width: 60
width: "60"
}, },
{ {
prop: "headImg", prop: "headImg",
label: "头像", label: "头像"
align: "center"
}, },
{ {
prop: "name", prop: "name",
label: "姓名", label: "姓名",
align: "center",
"min-width": 150 "min-width": 150
}, },
{ {
prop: "username", prop: "username",
label: "用户名", label: "用户名",
align: "center",
"min-width": 150 "min-width": 150
}, },
{ {
prop: "nickName", prop: "nickName",
label: "昵称", label: "昵称",
align: "center",
"min-width": 150 "min-width": 150
}, },
{ {
prop: "departmentName", prop: "departmentName",
label: "部门名称", label: "部门名称",
align: "center",
"min-width": 150 "min-width": 150
}, },
{ {
@ -164,19 +158,16 @@ export default {
{ {
prop: "phone", prop: "phone",
label: "手机号码", label: "手机号码",
align: "center",
"min-width": 150 "min-width": 150
}, },
{ {
prop: "remark", prop: "remark",
label: "备注", label: "备注",
align: "center",
"min-width": 150 "min-width": 150
}, },
{ {
prop: "status", prop: "status",
label: "状态", label: "状态",
align: "center",
"min-width": 120, "min-width": 120,
dict: [ dict: [
{ {
@ -194,15 +185,13 @@ export default {
{ {
prop: "createTime", prop: "createTime",
label: "创建时间", label: "创建时间",
align: "center",
sortable: "custom", sortable: "custom",
"min-width": 150 "min-width": 150
}, },
{ {
align: "center",
type: "op", type: "op",
buttons: ["slot-move-btn", "edit", "delete"], buttons: ["slot-move-btn", "edit", "delete"],
width: "160px" width: 160
} }
] ]
}, },
@ -271,7 +260,7 @@ export default {
prop: "password", prop: "password",
label: "密码", label: "密码",
span: 12, span: 12,
hidden: ":isEdit", hidden: ":isAdd",
component: { component: {
name: "el-input", name: "el-input",
attrs: { attrs: {
@ -362,7 +351,7 @@ export default {
}, },
{ {
prop: "tips", prop: "tips",
hidden: ":isAdd", hidden: ":isEdit",
component: ( component: (
<div> <div>
<i class="el-icon-warning"></i> <i class="el-icon-warning"></i>

View File

@ -12,7 +12,7 @@
'append-to-body': true, 'append-to-body': true,
'close-on-click-modal': false 'close-on-click-modal': false
}" }"
:controls="['slot-session', 'cl-flex1', 'fullscreen', 'close']" :controls="['slot-expand', 'cl-flex1', 'fullscreen', 'close']"
> >
<div class="cl-chat"> <div class="cl-chat">
<!-- 会话列表 --> <!-- 会话列表 -->
@ -29,7 +29,8 @@
</div> </div>
</div> </div>
<template #slot-session> <!-- 展开按钮 -->
<template #slot-expand>
<button v-if="session"> <button v-if="session">
<i <i
class="el-icon-notebook-2" class="el-icon-notebook-2"

View File

@ -1,5 +1,5 @@
import Upload from "./index.vue"; import Upload from "./index.vue";
import UploadSpace from "./space.vue"; import UploadSpace from "./space/index.vue";
export default { export default {
Upload, Upload,

View File

@ -1,721 +0,0 @@
<template>
<div class="cl-upload-space__wrap">
<slot>
<el-button v-if="showButton" size="mini" @click="open">点击上传</el-button>
</slot>
<!-- 弹框 -->
<cl-dialog :visible.sync="visible" v-bind="props" :op-list="['close']">
<div class="cl-upload-space">
<!-- 类目 -->
<div class="cl-upload-space__category">
<div class="cl-upload-space__category-search">
<el-button type="primary" size="mini" @click="editCategory()"
>添加分类</el-button
>
<el-input
v-model="category.keyword"
placeholder="输入关键字过滤"
size="mini"
></el-input>
</div>
<div class="cl-upload-space__category-list">
<ul>
<li
v-for="(item, index) in categoryList"
:key="index"
:class="{
'is-active': item.id == category.current.id
}"
@click="selectCategory(item)"
@contextmenu.stop.prevent="openCategoryContextMenu($event, item)"
>
{{ item.name }}
</li>
</ul>
</div>
</div>
<!-- 内容 -->
<div class="cl-upload-space__content">
<!-- 操作栏 -->
<div class="cl-upload-space__opbar">
<el-button
type="success"
size="mini"
:disabled="selection.length === 0"
@click="confirmFile()"
>使用选中文件</el-button
>
<el-button
type="danger"
size="mini"
:disabled="selection.length === 0"
@click="deleteFile()"
>删除选中文件</el-button
>
<cl-upload
style="margin-left: 10px"
list-type="slot"
:action="action"
:accept="accept"
:limit-size="limitSize"
:show-file-list="false"
:headers="headers"
:data="data"
:disabled="disabled"
:rename="rename"
:on-success="onSuccess"
:on-progress="onProgress"
:before-upload="beforeUpload"
>
<el-button size="mini" type="primary">点击上传</el-button>
</cl-upload>
</div>
<!-- 文件区域 -->
<div
class="cl-upload-space__file"
v-loading="file.loading"
element-loading-text="拼命加载中"
>
<!-- 文件列表 -->
<el-row v-if="file.list.length > 0">
<el-col :span="6" v-for="item in file.list" :key="item.id">
<file-item
:value="item"
:element-loading-text="item.progress"
v-loading="item.loading"
></file-item>
</el-col>
</el-row>
<!-- 空态 -->
<div class="cl-upload-space__file-empty" v-else>
<cl-upload
drag
:action="action"
:accept="accept"
:limit-size="limitSize"
:headers="headers"
:data="data"
:disabled="disabled"
:rename="rename"
:on-success="onSuccess"
:on-progress="onProgress"
:before-upload="beforeUpload"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处<em>点击上传</em>
</div>
</cl-upload>
</div>
</div>
<!-- 分页 -->
<el-pagination
background
:page-size="file.pagination.size"
:current-page="file.pagination.page"
:total="file.pagination.total"
@current-change="onCurrentChange"
></el-pagination>
</div>
</div>
</cl-dialog>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import { last, isEmpty } from "cl-admin/utils";
export default {
name: "cl-upload-space",
componentName: "UploadSpace",
props: {
//
action: String,
//
limit: {
type: Number,
default: 8
},
// (MB)
limitSize: {
type: Number,
default: 10
},
//
disabled: Boolean,
// uuid
rename: Boolean,
//
headers: Object,
//
data: Object,
//
accept: String,
//
detailData: Boolean,
//
showButton: {
type: Boolean,
default: true
}
},
components: {
fileItem: {
props: {
value: Object
},
computed: {
parent() {
let parent = this;
while (parent.$options.componentName != "UploadSpace") {
parent = parent.$parent;
}
return parent;
}
},
methods: {
onSelect() {
this.parent.selectFile(this.value);
},
onContextMenu(e) {
this.parent.openFileContextMenu(e, this.value);
e.stopPropagation();
e.preventDefault();
}
},
render() {
if (!this.value) {
return null;
}
let itemEl = null;
const { url, type, selected, id } = this.value;
const fileType = (type || "").split("/")[0];
switch (fileType) {
case "image":
itemEl = <el-image fit="cover" src={url} lazy></el-image>;
break;
case "video":
itemEl = (
<video
controls
src={url}
style={{
"max-height": "100%",
"max-width": "100%"
}}></video>
);
break;
default:
itemEl = <span>{url}</span>;
break;
}
return (
<div
class={["cl-upload-space__file-item", `is-${fileType}`]}
on-click={this.onSelect}
on-contextmenu={this.onContextMenu}>
{itemEl}
<div class="cl-upload-space__file-size"></div>
{selected && (
<div class="cl-upload-space__file-mask">
<i class="el-icon-success"></i>
</div>
)}
</div>
);
}
}
},
data() {
return {
visible: false,
props: {
title: "文件空间",
props: {
"close-on-click-modal": false,
"append-to-body": true,
width: "1000px"
}
},
category: {
list: [],
current: {},
keyword: ""
},
file: {
list: [],
pagination: {
page: 1,
size: 12,
total: 0
},
loading: false
}
};
},
computed: {
...mapGetters(["token"]),
categoryList() {
return this.category.list.filter(e => e.name.includes(this.category.keyword));
},
selection() {
return this.file.list.filter(e => e.selected);
}
},
filters: {
file_name(url) {
return last(url.split("."));
}
},
created() {
this.refreshCategory().then(() => {
this.category.current = this.category.list[0];
this.refreshFile();
});
},
methods: {
open(key) {
this.visible = true;
},
close() {
this.visible = false;
this.$nextTick(() => {
this.file.list.map(e => {
this.$set(e, "selected", false);
});
});
},
//
onSuccess(res, file) {
let item = this.file.list.find(e => file.uid == e.uid);
if (item) {
item.url = res.data;
this.$service.space.info
.add({
url: res.data,
type: item.type,
classifyId: item.classifyId
})
.then(res => {
item.loading = false;
item.id = res.id;
})
.catch(err => {
this.$message.error(err);
});
}
},
//
beforeUpload({ tempFilePath, type, uid }) {
this.file.list.unshift({
url: tempFilePath,
type,
uid,
classifyId: this.category.current.id,
loading: true,
progress: "0%"
});
},
//
onProgress({ percent }, file) {
let item = this.file.list.find(({ uid }) => uid == file.uid);
if (item) {
item.progress = percent + "%";
}
},
//
refreshFile(params) {
this.file.loading = true;
this.$service.space.info
.page({
...this.file.pagination,
...params,
classifyId: this.category.current.id,
type: this.accept
})
.then(res => {
this.file.pagination = res.pagination;
this.file.list = res.list;
})
.done(() => {
this.file.loading = false;
});
},
//
refreshCategory() {
return this.$service.space.type.list().then(res => {
res.unshift({
name: "全部文件",
id: null
});
this.category.list = res;
});
},
//
editCategory(item = {}) {
this.$crud.openForm({
title: "添加分类",
width: "400px",
items: [
{
label: "分类名称",
prop: "name",
value: item.name,
component: {
name: "el-input",
attrs: {
placeholder: "请填写分类名称"
}
},
rules: {
required: true,
message: "分类名称不能为空"
}
}
],
on: {
submit: (data, { done, close }) => {
let next = null;
if (!item.id) {
next = this.$service.space.type.add(data);
} else {
next = this.$service.space.type.update({
...data,
id: item.id
});
}
next.then(() => {
this.refreshCategory();
close();
}).catch(err => {
this.$message.error(err);
done();
});
}
}
});
},
//
selectCategory(item) {
this.category.current = item;
this.file.pagination = {
page: 1,
size: 12,
total: 0
};
this.refreshFile({
classifyId: item.id
});
},
//
openCategoryContextMenu(e, { id, name }) {
if (!id) {
return false;
}
this.$crud.openContextMenu(e, {
list: [
{
label: "编辑",
"suffix-icon": "el-icon-edit",
callback: (_, done) => {
done();
this.editCategory({ id, name });
}
},
{
label: "删除",
"suffix-icon": "el-icon-delete",
callback: (_, done) => {
done();
this.$confirm(`此操作将删除【${name}】下的文件, 是否继续?`, "提示", {
type: "warning"
})
.then(() => {
this.$service.space.type
.delete({
ids: id
})
.then(() => {
this.$message.success("删除成功");
this.refreshCategory();
//
if (id == this.category.current.id) {
this.category.current = this.category.list[0];
this.refreshFile();
}
})
.catch(err => {
console.error(err);
this.$message.error(err);
});
})
.catch(() => {});
}
}
]
});
},
//
openFileContextMenu(e, data) {
this.$crud.openContextMenu(e, {
list: [
{
label: data.selected ? "取消选中" : "选中",
"suffix-icon": data.selected ? "el-icon-close" : "el-icon-check",
callback: (_, done) => {
this.selectFile(data);
done();
}
},
{
label: "删除",
"suffix-icon": "el-icon-delete",
callback: (_, done) => {
this.deleteFile(data);
done();
}
}
]
});
},
//
confirmFile() {
const selection = this.selection.filter((e, i) => i < this.limit);
const urls = selection.map(e => e.url).join(",");
this.$emit("input", urls);
this.$emit("confirm", this.detailData ? selection : urls);
this.close();
},
//
selectFile(item) {
this.$set(item, "selected", !item.selected);
},
//
deleteFile(...selection) {
if (isEmpty(selection)) {
selection = this.selection;
}
this.$confirm("此操作将删除文件, 是否继续?", "提示", {
type: "warning"
})
.then(() => {
this.$message.success("删除成功");
this.file.list = this.file.list.filter(
e => !selection.map(e => e.id).includes(e.id)
);
this.$service.space.info
.delete({
ids: selection.map(e => e.id).join(",")
})
.catch(err => {
this.$message.error(err);
});
})
.catch(() => {});
},
//
onCurrentChange(i) {
this.refreshFile({
page: i
});
}
}
};
</script>
<style lang="scss" scoped>
.cl-upload-space {
display: flex;
min-height: 520px;
&__category {
width: 250px;
margin-right: 20px;
&-search {
display: flex;
align-items: center;
margin-bottom: 5px;
.el-button {
margin-right: 10px;
}
}
&-list {
overflow: hidden auto;
ul {
li {
list-style: none;
font-size: 14px;
height: 40px;
line-height: 40px;
border-bottom: 1px dashed #eee;
padding: 0 5px;
cursor: pointer;
&.is-active {
color: #409eff;
}
}
}
}
}
&__content {
flex: 1;
}
&__opbar {
display: flex;
align-items: center;
margin-bottom: 10px;
}
&__file {
height: calc(100% - 80px);
overflow: hidden auto;
margin-bottom: 10px;
/deep/.cl-upload-space__file-item {
display: flex;
align-items: center;
justify-content: center;
height: 160px;
width: 160px;
cursor: pointer;
position: relative;
border-radius: 3px;
box-sizing: border-box;
border: 1px solid #eee;
margin: 5px 0;
&.is-image {
overflow: hidden;
img {
height: 100%;
width: 100%;
}
}
&.is-video {
video {
max-height: 100%;
width: 100%;
}
}
.cl-upload-space__file-size {
position: absolute;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.cl-upload-space__file-mask {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
i {
font-size: 30px;
color: #67c23a;
}
}
}
&-empty {
display: flex;
align-items: center;
justify-content: center;
margin-top: 100px;
& > div {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 6px;
cursor: pointer;
height: 180px;
width: 360px;
i {
font-size: 67px;
color: #c0c4cc;
}
}
}
}
}
</style>

View File

@ -0,0 +1,275 @@
<template>
<div
class="cl-upload-space-category"
:class="{
'is-position': browser.isMini,
'is-show': space.category.visible
}"
>
<div class="cl-upload-space-category__search">
<el-button type="primary" size="mini" @click="edit()">添加分类</el-button>
<el-input v-model="keyword" placeholder="输入关键字过滤" size="mini"></el-input>
</div>
<div class="cl-upload-space-category__list">
<ul class="scroller1">
<li
v-for="(item, index) in flist"
:key="index"
:class="{
'is-active': item.id == current
}"
@click="select(item.id)"
@contextmenu.stop.prevent="openContextMenu($event, item)"
>
{{ item.name }}
</li>
</ul>
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import { isEmpty } from "cl-admin/utils";
export default {
name: "cl-upload-space-category",
props: {
value: [Number]
},
inject: ["space"],
data() {
return {
list: [],
current: undefined,
keyword: ""
};
},
computed: {
...mapGetters(["browser"]),
flist() {
return this.list.filter(e => e.name.includes(this.keyword));
}
},
watch: {
current: {
handler(id) {
this.$emit("input", id);
this.$emit("change", id);
}
}
},
created() {
this.refresh();
},
methods: {
//
refresh() {
return this.$service.space.type.list().then(res => {
res.unshift({
name: "全部文件",
id: null
});
this.list = res;
if (!isEmpty(res)) {
if (!this.current) {
this.current = res[0].id;
}
}
});
},
//
edit(item = {}) {
this.$crud.openForm({
title: "添加分类",
width: "400px",
items: [
{
label: "分类名称",
prop: "name",
value: item.name,
component: {
name: "el-input",
attrs: {
placeholder: "请填写分类名称"
}
},
rules: {
required: true,
message: "分类名称不能为空"
}
}
],
on: {
submit: (data, { done, close }) => {
let next = null;
if (!item.id) {
next = this.$service.space.type.add(data);
} else {
next = this.$service.space.type.update({
...data,
id: item.id
});
}
next.then(() => {
this.refresh();
close();
}).catch(err => {
this.$message.error(err);
done();
});
}
}
});
},
//
select(id) {
this.current = id;
//
if (this.browser.isMini) {
this.space.category.visible = false;
}
},
//
openContextMenu(e, { id, name }) {
if (!id) {
return false;
}
this.$crud.openContextMenu(e, {
list: [
{
label: "刷新",
"suffix-icon": "el-icon-edit",
callback: (_, done) => {
this.refresh();
done();
}
},
{
label: "编辑",
"suffix-icon": "el-icon-edit",
callback: (_, done) => {
this.edit({ id, name });
done();
}
},
{
label: "删除",
"suffix-icon": "el-icon-delete",
callback: (_, done) => {
this.$confirm(`此操作将删除【${name}】下的文件, 是否继续?`, "提示", {
type: "warning"
})
.then(() => {
this.$service.space.type
.delete({
ids: [id]
})
.then(() => {
this.$message.success("删除成功");
if (id == this.current) {
this.current = null;
}
this.refresh();
})
.catch(err => {
this.$message.error(err);
});
})
.catch(() => null);
done();
}
}
]
});
}
}
};
</script>
<style lang="scss" scoped>
.cl-upload-space-category {
height: 100%;
width: 0;
background-color: #fff;
overflow: hidden;
transition: width 0.2s ease-in-out;
border-radius: 5px;
&.is-show {
width: 250px;
margin-right: 5px;
}
&.is-position {
position: absolute;
left: 5px;
top: 51px;
height: calc(100% - 56px);
z-index: 3000;
&.is-show {
width: calc(100% - 10px);
}
}
&__search {
display: flex;
align-items: center;
padding: 10px;
.el-button {
margin-right: 10px;
}
}
&__list {
height: calc(100% - 48px);
padding: 0 10px;
ul {
height: 100%;
li {
list-style: none;
font-size: 14px;
height: 40px;
line-height: 40px;
border-bottom: 1px dashed #eee;
padding: 0 10px;
cursor: pointer;
&.is-active {
color: $color-primary;
}
&:hover {
background-color: #f7f7f7;
}
}
}
}
}
</style>

View File

@ -0,0 +1,167 @@
<template>
<div
class="cl-upload-space-item"
:class="[`is-${type}`]"
@click.stop.prevent="select"
@contextmenu.stop.prevent="openContextMenu"
>
<!-- 错误 -->
<template v-if="value.error">
<div class="cl-upload-space-item__error">上传失败{{ value.error }}</div>
</template>
<!-- 成功 -->
<template v-else>
<!-- 图片 -->
<template v-if="type === 'image'">
<el-image fit="cover" :src="value.url" lazy></el-image>
</template>
<!-- 视频 -->
<template v-else-if="type === 'video'">
<video
controls
:src="value.url"
:style="{
'max-height': '100%',
'max-width': '100%'
}"
></video>
</template>
<!-- 其他 -->
<template v-else>
<span>{{ value.url }}</span>
</template>
</template>
<!-- 大小 -->
<div class="cl-upload-space-item__size"></div>
<!-- 遮罩层 -->
<div class="cl-upload-space-item__mask" v-if="isSelected">
<span>{{ index + 1 }}</span>
</div>
</div>
</template>
<script>
export default {
name: "cl-upload-space-item",
props: {
value: Object
},
inject: ["space"],
computed: {
index() {
return this.space.selection.findIndex(e => e.id === this.value.id);
},
isSelected() {
return this.index >= 0;
},
type() {
return (this.value.type || "").split("/")[0];
}
},
methods: {
select() {
this.$emit("select", this.value);
},
remove() {
this.$emit("remove", this.value);
},
openContextMenu(e) {
this.$crud.openContextMenu(e, {
list: [
{
label: this.isSelected ? "取消选中" : "选中",
"suffix-icon": this.isSelected ? "el-icon-close" : "el-icon-check",
callback: (_, done) => {
this.select();
done();
}
},
{
label: "删除",
"suffix-icon": "el-icon-delete",
callback: (_, done) => {
this.remove();
done();
}
}
]
});
}
}
};
</script>
<style lang="scss" scoped>
.cl-upload-space-item {
display: flex;
align-items: center;
justify-content: center;
height: 160px;
width: 160px;
cursor: pointer;
position: relative;
border-radius: 3px;
box-sizing: border-box;
border: 1px solid #eee;
margin: 5px 10px 5px 0;
&.is-image {
overflow: hidden;
}
&.is-video {
video {
max-height: 100%;
width: 100%;
}
}
&__size {
position: absolute;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.3);
}
&__error {
padding: 10px;
color: red;
}
&__mask {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.3);
span {
position: absolute;
right: 10px;
top: 10px;
background-color: #67c23a;
color: #fff;
display: inline-block;
height: 20px;
width: 20px;
text-align: center;
line-height: 20px;
border-radius: 20px;
}
}
}
</style>

View File

@ -0,0 +1,462 @@
<template>
<div class="cl-upload-space__wrap">
<slot>
<el-button v-if="showButton" size="mini" @click="open">点击上传</el-button>
</slot>
<!-- 弹框 -->
<cl-dialog
title="文件空间"
height="630px"
width="1000px"
:visible.sync="visible"
:props="{
'close-on-click-modal': false,
'append-to-body': true,
customClass: 'dialog-upload-space'
}"
:controls="['slot-expand', 'cl-flex1', 'fullscreen', 'close']"
>
<div class="cl-upload-space">
<!-- 类目 -->
<category v-model="category.id" @change="refresh()" />
<!-- 内容 -->
<div class="cl-upload-space__content">
<!-- 操作栏 -->
<div class="cl-upload-space__header scroller1">
<el-button size="mini" @click="refresh()">刷新</el-button>
<cl-upload
style="margin: 0 10px"
list-type="slot"
:action="action"
:accept="accept"
:limit-size="limitSize"
:show-file-list="false"
:headers="headers"
:data="data"
:disabled="disabled"
:rename="rename"
:on-success="onSuccess"
:on-error="onError"
:on-progress="onProgress"
:before-upload="beforeUpload"
>
<el-button size="mini" type="primary">点击上传</el-button>
</cl-upload>
<el-button
type="success"
size="mini"
:disabled="!isSelected"
@click="confirm()"
>使用选中文件 {{ this.limitTip }}</el-button
>
<el-button
type="danger"
size="mini"
:disabled="!isSelected"
@click="remove()"
>删除选中文件</el-button
>
</div>
<!-- 文件区域 -->
<div
class="cl-upload-space__file scroller1"
v-loading="loading"
element-loading-text="拼命加载中"
>
<!-- 文件列表 -->
<template v-if="list.length > 0">
<div class="cl-upload-space__file-list">
<file-item
v-for="item in list"
:key="item.id"
:value="item"
:element-loading-text="item.progress"
v-loading="item.loading"
@select="select"
@remove="remove"
></file-item>
</div>
</template>
<!-- 空态 -->
<div class="cl-upload-space__file-empty" v-else>
<cl-upload
drag
:action="action"
:accept="accept"
:limit-size="limitSize"
:headers="headers"
:data="data"
:disabled="disabled"
:rename="rename"
:on-success="onSuccess"
:on-error="onError"
:on-progress="onProgress"
:before-upload="beforeUpload"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处<em>点击上传</em>
</div>
</cl-upload>
</div>
</div>
<!-- 分页 -->
<div class="cl-upload-space__footer">
<el-pagination
background
:page-size="pagination.size"
:current-page="pagination.page"
:total="pagination.total"
@current-change="
page => {
refresh({ page });
}
"
></el-pagination>
</div>
</div>
</div>
<!-- 展开按钮 -->
<template #slot-expand>
<button>
<i
class="el-icon-notebook-2"
v-if="category.visible"
@click="category.visible = false"
></i>
<i class="el-icon-arrow-left" v-else @click="category.visible = true"></i>
</button>
</template>
</cl-dialog>
</div>
</template>
<script>
import { isEmpty } from "cl-admin/utils";
import Category from "./category";
import FileItem from "./file-item";
import { mapGetters } from "vuex";
export default {
name: "cl-upload-space",
props: {
//
action: String,
//
limit: {
type: Number,
default: 9
},
// (MB)
limitSize: {
type: Number,
default: 10
},
//
disabled: Boolean,
// uuid
rename: Boolean,
//
headers: Object,
//
data: Object,
//
accept: String,
//
detailData: Boolean,
//
showButton: {
type: Boolean,
default: true
}
},
components: {
Category,
FileItem
},
provide() {
return {
space: this
};
},
data() {
return {
visible: true,
loading: false,
category: {
id: null,
visible: true
},
selection: [],
list: [],
pagination: {
page: 1,
size: 12,
total: 0
}
};
},
computed: {
...mapGetters(["browser"]),
limitTip() {
return this.selection.length + "/" + this.limit;
},
isSelected() {
return !isEmpty(this.selection);
}
},
watch: {
"browser.isMini": {
immediate: true,
handler(val) {
this.category.visible = val ? false : true;
}
}
},
methods: {
open() {
this.visible = true;
},
close() {
this.visible = false;
this.clear();
},
clear() {
this.selection = [];
},
//
onSuccess(res, file) {
const item = this.list.find(e => file.uid == e.uid);
if (item) {
item.url = res.data;
this.$service.space.info
.add({
url: res.data,
type: item.type,
classifyId: item.classifyId
})
.then(res => {
item.loading = false;
item.id = res.id;
})
.catch(err => {
this.$message.error(err);
});
}
},
//
onError(err, file) {
const item = this.list.find(e => file.uid == e.uid);
if (item) {
item.loading = false;
this.$set(item, "error", err);
}
},
//
beforeUpload({ tempFilePath, type, uid }) {
this.list.unshift({
url: tempFilePath,
type,
uid,
classifyId: this.category.id,
loading: true,
progress: "0%"
});
},
//
onProgress({ percent }, file) {
const item = this.list.find(({ uid }) => uid == file.uid);
if (item) {
item.progress = percent + "%";
}
},
//
refresh(params) {
//
this.clear();
this.loading = true;
this.$service.space.info
.page({
...this.pagination,
...params,
classifyId: this.category.id,
type: this.accept
})
.then(res => {
this.pagination = res.pagination;
this.list = res.list;
})
.done(() => {
this.loading = false;
});
},
//
confirm() {
const urls = this.selection.map(e => e.url).join(",");
this.$emit("input", urls);
this.$emit("confirm", this.detailData ? this.selection : urls);
this.close();
},
//
select(item) {
const index = this.selection.findIndex(e => e.id === item.id);
if (index >= 0) {
this.selection.splice(index, 1);
} else {
if (this.selection.length < this.limit) {
this.selection.push(item);
}
}
},
//
remove(...selection) {
if (isEmpty(selection)) {
selection = this.selection;
}
// id
const ids = selection.map(e => e.id);
this.$confirm("此操作将删除文件, 是否继续?", "提示", {
type: "warning"
})
.then(() => {
this.$message.success("删除成功");
//
ids.forEach(id => {
[this.list, this.selection].forEach(list => {
const index = list.findIndex(e => e.id === id);
list.splice(index, 1);
});
});
//
this.$service.space.info
.delete({
ids
})
.catch(err => {
this.$message.error(err);
});
})
.catch(() => null);
}
}
};
</script>
<style lang="scss">
.dialog-upload-space {
.el-dialog {
&__body {
padding: 0;
}
}
}
</style>
<style lang="scss" scoped>
.cl-upload-space {
display: flex;
height: 100%;
box-sizing: border-box;
background-color: #f7f7f7;
padding: 5px;
&__content {
flex: 1;
max-width: 100%;
padding: 0 10px;
box-sizing: border-box;
background-color: #fff;
border-radius: 5px;
}
&__header {
display: flex;
align-items: center;
height: 50px;
overflow: auto hidden;
}
&__file {
height: calc(100% - 100px);
position: relative;
&-list {
display: flex;
flex-wrap: wrap;
}
&-empty {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: calc(50% - 90px);
left: calc(50% - 160px);
/deep/.cl-upload {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 6px;
cursor: pointer;
.el-upload-dragger {
height: 180px;
width: 320px;
}
i {
font-size: 67px;
color: #c0c4cc;
}
}
}
}
&__footer {
padding: 9px 0;
}
}
</style>