2021-02-28 22:24:54 +08:00

722 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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: (item, done) => {
done();
this.editCategory({ id, name });
}
},
{
label: "删除",
"suffix-icon": "el-icon-delete",
callback: (item, 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: (item, done) => {
this.selectFile(data);
done();
}
},
{
label: "删除",
"suffix-icon": "el-icon-delete",
callback: (item, 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>