mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2025-12-16 08:38:48 +00:00
420 lines
7.7 KiB
Vue
420 lines
7.7 KiB
Vue
<template>
|
||
<div class="cl-upload-space__wrap">
|
||
<slot>
|
||
<el-button @click="open" v-if="showBtn">点击上传</el-button>
|
||
</slot>
|
||
|
||
<!-- 弹框 -->
|
||
<cl-dialog
|
||
v-model="visible"
|
||
title="文件空间"
|
||
height="630px"
|
||
width="1000px"
|
||
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">
|
||
<!-- 类目 -->
|
||
<category />
|
||
|
||
<!-- 内容 -->
|
||
<div class="cl-upload-space__content">
|
||
<!-- 操作栏 -->
|
||
<div class="cl-upload-space__header scroller1">
|
||
<el-button @click="refresh({ page: 1 })">刷新</el-button>
|
||
|
||
<div :style="{ marginLeft: '10px' }">
|
||
<cl-upload
|
||
ref="Upload"
|
||
type="file"
|
||
:show-file-list="false"
|
||
:disabled="disabled"
|
||
:limit="9999"
|
||
multiple
|
||
@success="onSuccess"
|
||
@upload="onUpload"
|
||
>
|
||
<el-button type="primary">点击上传</el-button>
|
||
</cl-upload>
|
||
</div>
|
||
|
||
<el-button type="success" :disabled="!isSelected" @click="confirm()"
|
||
>使用选中文件 {{ selection.length }} / {{ limit }}</el-button
|
||
>
|
||
|
||
<el-button type="danger" :disabled="!isSelected" @click="remove()"
|
||
>删除选中文件</el-button
|
||
>
|
||
</div>
|
||
|
||
<!-- 文件区域 -->
|
||
<div
|
||
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>
|
||
</template>
|
||
|
||
<!-- 空态 -->
|
||
<div v-else class="cl-upload-space__file-empty">
|
||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||
<p>将文件拖到此处,或点击上传</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 展开按钮 -->
|
||
<template #slot-expand>
|
||
<button class="cl-dialog__controls-icon">
|
||
<el-icon @click="category.visible = false" v-if="category.visible">
|
||
<notebook />
|
||
</el-icon>
|
||
<el-icon @click="category.visible = true" v-else>
|
||
<arrow-left />
|
||
</el-icon>
|
||
</button>
|
||
</template>
|
||
</cl-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts">
|
||
export default {
|
||
name: "cl-upload-space"
|
||
};
|
||
</script>
|
||
|
||
<script lang="ts" setup>
|
||
import { computed, onMounted, provide, reactive, ref, watch } from "vue";
|
||
import { ElMessage, ElMessageBox } from "element-plus";
|
||
import { module } from "/@/cool/utils";
|
||
import { isEmpty } from "lodash";
|
||
import Category from "./space/category.vue";
|
||
import FileItem from "./space/file-item.vue";
|
||
import { useCool } from "/@/cool";
|
||
import { useBase } from "/$/base";
|
||
import { Notebook, ArrowLeft, UploadFilled } from "@element-plus/icons-vue";
|
||
|
||
const props = defineProps({
|
||
// 绑定值
|
||
modelValue: String,
|
||
// 选择图片的数量
|
||
limit: Number,
|
||
// 是否禁用
|
||
disabled: Boolean,
|
||
// 显示按钮
|
||
showBtn: {
|
||
type: Boolean,
|
||
default: true
|
||
}
|
||
});
|
||
|
||
const emit = defineEmits(["update:modelValue", "confirm"]);
|
||
|
||
const { service } = useCool();
|
||
|
||
// 缓存
|
||
const { app } = useBase();
|
||
|
||
// 模块配置
|
||
const { options } = module.get("upload");
|
||
|
||
// cl-upload
|
||
const Upload = ref<any>();
|
||
|
||
// 选择图片的数量
|
||
const limit = props.limit || options.limit.select;
|
||
|
||
// 是否可见
|
||
const visible = ref<boolean>(false);
|
||
|
||
// 是否加载中
|
||
const loading = ref<boolean>(false);
|
||
|
||
// 已选列表
|
||
const selection = ref<any[]>([]);
|
||
|
||
// 文件列表
|
||
const list = ref<any[]>([]);
|
||
|
||
// 类目数据
|
||
const category = reactive<any>({
|
||
id: null,
|
||
visible: true
|
||
});
|
||
|
||
// 分页信息
|
||
const pagination = reactive<any>({
|
||
page: 1,
|
||
size: 20,
|
||
total: 0
|
||
});
|
||
|
||
// 监听屏幕大小变化
|
||
watch(
|
||
() => app.browser.isMini,
|
||
(val) => {
|
||
category.visible = val ? false : true;
|
||
},
|
||
{
|
||
immediate: true
|
||
}
|
||
);
|
||
|
||
// 是否选中
|
||
const isSelected = computed(() => !isEmpty(selection.value));
|
||
|
||
// 共享
|
||
provide("space", {
|
||
category,
|
||
selection,
|
||
refresh,
|
||
loading
|
||
});
|
||
|
||
// 打开
|
||
function open() {
|
||
visible.value = true;
|
||
}
|
||
|
||
// 清空选择
|
||
function clear() {
|
||
selection.value = [];
|
||
}
|
||
|
||
// 关闭
|
||
function close() {
|
||
visible.value = false;
|
||
clear();
|
||
}
|
||
|
||
// 上传成功
|
||
function onSuccess(data: any) {
|
||
service.space.info
|
||
.add(data)
|
||
.then((res) => {
|
||
data.id = res.id;
|
||
})
|
||
.catch((err) => {
|
||
ElMessage.error(err.message);
|
||
});
|
||
}
|
||
|
||
// 上传时
|
||
function onUpload(data: any) {
|
||
data.classifyId = category.id;
|
||
list.value.unshift(data);
|
||
}
|
||
|
||
// 刷新资源文件
|
||
async function refresh(params: any = {}) {
|
||
// 清空选择
|
||
clear();
|
||
|
||
const d = {
|
||
...pagination,
|
||
...params,
|
||
classifyId: category.id
|
||
};
|
||
|
||
// 加载中
|
||
if (d.page == 1) {
|
||
loading.value = true;
|
||
}
|
||
|
||
await service.space.info.page(d).then((res) => {
|
||
// 合并
|
||
Object.assign(pagination, res.pagination);
|
||
|
||
if (d.page == 1) {
|
||
list.value = [];
|
||
}
|
||
|
||
list.value.push(...res.list);
|
||
});
|
||
|
||
// 加载完成
|
||
loading.value = false;
|
||
}
|
||
|
||
// 确认选中
|
||
function confirm() {
|
||
emit("update:modelValue", selection.value.map((e: any) => e.url).join(","));
|
||
emit("confirm", selection.value);
|
||
|
||
close();
|
||
}
|
||
|
||
// 选择
|
||
function select(item: any) {
|
||
const index = selection.value.findIndex((e: any) => e.id === item.id);
|
||
|
||
if (index >= 0) {
|
||
selection.value.splice(index, 1);
|
||
} else {
|
||
if (selection.value.length < limit) {
|
||
selection.value.push(item);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 删除选中
|
||
function remove(item?: any) {
|
||
// 已选文件 id
|
||
const ids: number[] = item ? [item.id] : selection.value.map((e: any) => e.id);
|
||
|
||
ElMessageBox.confirm("此操作将删除文件, 是否继续?", "提示", {
|
||
type: "warning"
|
||
})
|
||
.then(() => {
|
||
ElMessage.success("删除成功");
|
||
|
||
// 删除文件及选择
|
||
ids.forEach((id) => {
|
||
[list.value, selection.value].forEach((list) => {
|
||
const index = list.findIndex((e: any) => e.id === id);
|
||
list.splice(index, 1);
|
||
});
|
||
});
|
||
|
||
// 删除请求
|
||
service.space.info
|
||
.delete({
|
||
ids
|
||
})
|
||
.catch((err) => {
|
||
ElMessage.error(err.message);
|
||
});
|
||
})
|
||
.catch(() => null);
|
||
}
|
||
|
||
function onDragover(e: any) {
|
||
e.preventDefault();
|
||
}
|
||
|
||
function onDrop(e: any) {
|
||
e.preventDefault();
|
||
|
||
e.dataTransfer.files.forEach((f: File) => {
|
||
Upload.value.upload(f);
|
||
});
|
||
}
|
||
|
||
// 加载更多
|
||
function loadmore() {
|
||
if (list.value.length && list.value.length < pagination.total) {
|
||
refresh({
|
||
page: pagination.page + 1
|
||
});
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
refresh();
|
||
});
|
||
|
||
defineExpose({
|
||
open,
|
||
close,
|
||
clear,
|
||
refresh
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.cl-upload-space {
|
||
display: flex;
|
||
height: 100%;
|
||
box-sizing: border-box;
|
||
background-color: #f7f7f7;
|
||
padding: 5px;
|
||
|
||
&__dialog {
|
||
.el-dialog__body {
|
||
padding: 0;
|
||
}
|
||
}
|
||
|
||
&__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% - 50px);
|
||
position: relative;
|
||
|
||
&-list {
|
||
.el-row {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
&-empty {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: absolute;
|
||
top: calc(50% - 90px);
|
||
left: calc(50% - 180px);
|
||
height: 180px;
|
||
width: 360px;
|
||
border-radius: 5px;
|
||
border: 2px dashed #eee;
|
||
cursor: pointer;
|
||
|
||
&:hover {
|
||
border-color: var(--color-primary);
|
||
}
|
||
|
||
i {
|
||
font-size: 67px;
|
||
color: #c0c4cc;
|
||
}
|
||
|
||
p {
|
||
font-size: 14px;
|
||
margin-top: 15px;
|
||
}
|
||
}
|
||
}
|
||
|
||
&__footer {
|
||
padding: 9px 0;
|
||
}
|
||
}
|
||
</style>
|