神仙都没用 5cc3f85ed1 优化
2023-12-01 20:00:00 +08:00

446 lines
8.3 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-inner" @dragover="onDragover" @drop="onDrop">
<cl-view-group ref="ViewGroup">
<template #right>
<div class="cl-upload-space-inner__right">
<!-- 操作栏 -->
<div class="cl-upload-space-inner__header">
<el-button @click="refresh({ page: 1 })">刷新</el-button>
<div :style="{ margin: '0px 10px' }">
<cl-upload
:ref="setRefs('upload')"
menu="space"
type="file"
multiple
:show-file-list="false"
:limit="9999"
:accept="accept"
@success="onSuccess"
@upload="onUpload"
>
<el-button type="primary">点击上传</el-button>
</cl-upload>
</div>
<template v-if="!selectable">
<el-button
type="danger"
:disabled="selection.length == 0"
@click="remove()"
>删除选中文件</el-button
>
</template>
</div>
<!-- 文件区域 -->
<el-scrollbar class="cl-upload-space-inner__file" v-loading="loading">
<!-- 文件列表 -->
<template v-if="list.length > 0">
<div
class="cl-upload-space-inner__file-list"
:class="{
'is-mini': browser.isMini
}"
>
<div
class="cl-upload-space-inner__file-item"
v-for="item in list"
:key="item.preload || item.url"
@click="select(item)"
>
<upload-item :item="item" :list="list" @remove="remove" />
<!-- 已选序号 -->
<div
class="cl-upload-space-inner__file-index"
v-show="onIndex(item.id)"
>
<span>{{ onIndex(item.id) }}</span>
</div>
</div>
</div>
</template>
<!-- 空态 -->
<div v-else class="cl-upload-space-inner__file-empty">
<el-icon class="el-icon--upload">
<upload-filled />
</el-icon>
<p>将文件拖到此处或点击按钮上传</p>
</div>
</el-scrollbar>
<div class="cl-upload-space-inner__footer">
<el-pagination
:small="browser.isMini ? true : false"
:total="pagination.total"
:default-page-size="pagination.size"
v-model:current-page="pagination.page"
background
layout="prev, pager, next"
@current-change="refresh()"
/>
<span class="total" v-show="!browser.isMini"
> {{ pagination.total }} </span
>
</div>
</div>
</template>
</cl-view-group>
</div>
</template>
<script lang="ts" name="cl-upload-space-inner" setup>
import { provide, reactive, ref, watch } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { UploadFilled } from "@element-plus/icons-vue";
import { useCool } from "/@/cool";
import { useViewGroup } from "/$/base";
import UploadItem from "./upload-item/index.vue";
import { Upload } from "../types";
const props = defineProps({
limit: {
type: Number,
default: 99
},
accept: String,
selectable: Boolean
});
const emit = defineEmits(["selection-change", "confirm"]);
const { service, browser, refs, setRefs } = useCool();
const { ViewGroup } = useViewGroup({
label: "分类",
title: "文件列表",
service: service.space.type,
onEdit() {
return {
width: "400px",
props: {
labelPosition: "top"
},
dialog: {
controls: ["close"]
},
items: [
{
label: "名称",
prop: "name",
value: "",
required: true,
component: {
name: "el-input",
props: {
maxlength: 20,
clearable: true
}
}
}
]
};
},
onSelect(item) {
refresh({
classifyId: item.id,
page: 1
});
}
});
// 是否加载中
const loading = ref(false);
// 已选列表
const selection = ref<Upload.Item[]>([]);
// 文件列表
const list = ref<Upload.Item[]>([]);
// 分页信息
const pagination = reactive({
page: 1,
size: 20,
total: 0
});
// 清空选择
function clear() {
selection.value = [];
}
// 上传成功
function onSuccess<T extends { id: number }>(data: T) {
service.space.info
.add({
classifyId: ViewGroup.value?.selected?.id,
...data
})
.then((res) => {
data.id = res.id;
})
.catch((err) => {
ElMessage.error(err.message);
});
}
// 上传时
function onUpload(data: any) {
list.value.unshift(data);
}
// 请求参数
const reqParams = {
page: 1
};
// 刷新列表
async function refresh(params?: any) {
// 合并参数
Object.assign(reqParams, {
type: props.accept?.split("/")[0].replace("*", "") || undefined,
...pagination,
...params
});
// 加载中
if (reqParams.page == 1) {
loading.value = true;
}
await service.space.info.page(reqParams).then((res) => {
// 设置分页
Object.assign(pagination, res.pagination);
// 设置列表
list.value = res.list as Upload.Item[];
});
// 加载完成
loading.value = false;
}
// 选择
function select(item: Upload.Item) {
const index = selection.value.findIndex((e) => e.id === item.id);
if (index >= 0) {
selection.value.splice(index, 1);
} else {
if (props.limit == 1) {
selection.value = [item];
} else {
if (selection.value.length < props.limit) {
selection.value.push(item);
} else {
ElMessage.warning(`最多只能选择${props.limit}个文件`);
}
}
}
}
// 选择序号
function onIndex(id?: number) {
return selection.value.findIndex((e) => e.id === id) + 1;
}
// 删除选中
function remove(item?: Upload.Item) {
// 已选文件 id
const ids = item ? [item.id] : selection.value.map((e) => e.id);
ElMessageBox.confirm("此操作将删除文件, 是否继续?", "提示", {
type: "warning"
})
.then(() => {
ElMessage.success("删除成功");
// 删除文件及选择
ids.forEach((id) => {
[list.value, selection.value].forEach((list) => {
const index = list.findIndex((e) => 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, i: number) => {
setTimeout(() => {
refs.upload.upload(f);
}, i * 10);
});
}
// 监听选择
watch(
selection,
(val) => {
emit("selection-change", val);
},
{
deep: true
}
);
provide("upload-space", {
selection,
refresh,
loading,
list
});
defineExpose({
selection,
open,
close,
clear,
refresh
});
</script>
<style lang="scss">
.cl-upload-space-inner {
height: 100%;
user-select: none;
&__right {
height: 100%;
width: 100%;
}
&__header {
display: flex;
align-items: center;
height: 50px;
padding: 0 10px;
}
&__file {
height: calc(100% - 110px);
padding: 0 10px;
box-sizing: border-box;
position: relative;
&-item {
height: 0;
min-height: 140px;
min-width: 140px;
width: calc(12.5% - 10px);
padding-top: calc(12.5% - 10px);
margin: 0 10px 10px 0;
position: relative;
box-sizing: border-box;
}
&-index {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 5px;
pointer-events: none;
span {
position: absolute;
right: 5px;
top: 5px;
background-color: var(--el-color-success);
color: #fff;
display: inline-block;
height: 20px;
width: 20px;
text-align: center;
line-height: 20px;
border-radius: 20px;
font-size: 14px;
}
}
&-list {
display: flex;
flex-wrap: wrap;
&.is-mini {
.cl-upload-space-inner__file-item {
width: calc(50% - 10px);
}
}
}
&-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
height: 180px;
width: 360px;
max-width: 80%;
border-radius: 6px;
border: 1px dashed var(--el-border-color);
box-sizing: border-box;
cursor: pointer;
&:hover {
border-color: var(--color-primary);
}
i {
font-size: 67px;
color: #c0c4cc;
}
p {
font-size: 14px;
margin-top: 15px;
color: #999;
}
}
}
&__footer {
height: 60px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
.total {
font-size: 14px;
margin: 0 10px;
}
}
}
</style>