Files
lilishop-ui/im/src/components/editor/MeEditorFileManage.vue
2022-12-28 10:08:51 +08:00

441 lines
10 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>
<el-container
class="container animated bounceInUp"
v-outside="closeBox"
v-if="show"
>
<el-header class="no-padding header" height="50px">
<p>
上传管理 <span v-show="total">({{ successNum }}/{{ total }})</span>
</p>
<i class="close-btn el-icon-close" @click="closeBox" />
</el-header>
<el-main class="no-padding mian lum-scrollbar">
<div class="empty-data" v-show="total == 0">
<SvgNotData />
<p>暂无上传文件</p>
</div>
<div
v-for="file in items"
v-show="!file.isDelete"
:key="file.hashName"
class="file-item"
>
<div class="file-header">
<div class="type-icon">{{ file.ext }}</div>
<el-tooltip :content="file.filename" placement="top-start">
<div class="filename">{{ file.filename }}</div>
</el-tooltip>
<div class="status">
<span v-if="file.status == 0">等待上传</span>
<span v-else-if="file.status == 1" style="color: #66b1ff">
正在上传...
</span>
<span v-else-if="file.status == 2" style="color: #67c23a">
已完成
</span>
<span v-else style="color: red">网络异常</span>
</div>
</div>
<div class="file-mian">
<div class="progress">
<el-progress
type="dashboard"
:percentage="file.progress"
:width="50"
:color="colors"
/>
<span class="name">上传进度</span>
</div>
<div class="detail">
<p>
文件类型<span>{{ file.filetype }}</span>
</p>
<p>
文件大小<span>{{ file.filesize }}</span>
</p>
<p>
上传时间<span>{{ file.datetime }}</span>
</p>
</div>
</div>
<div v-show="file.status == 2 || file.status == 3" class="file-means">
<div class="btns" @click="removeFile(file.hashName)">删除</div>
<div
v-show="file.status == 3"
class="btns"
@click="triggerUpload(file.hashName)"
>
继续上传
</div>
</div>
</div>
</el-main>
</el-container>
</template>
<script>
import Vue from 'vue'
import { SvgNotData } from '@/core/icons'
import { Progress } from 'element-ui'
Vue.use(Progress)
import { ServeFindFileSplitInfo, ServeFileSubareaUpload } from '@/api/upload'
import { formatSize, getFileExt, parseTime } from '@/utils/functions'
import { ServeSendTalkFile } from '@/api/chat'
export default {
name: 'MeEditorFileManage',
model: {
prop: 'show',
event: 'close',
},
props: {
show: Boolean,
},
components: {
SvgNotData,
},
data() {
return {
colors: [
{
color: '#f56c6c',
percentage: 20,
},
{
color: '#e6a23c',
percentage: 40,
},
{
color: '#5cb87a',
percentage: 60,
},
{
color: '#1989fa',
percentage: 80,
},
{
color: '#11ce65',
percentage: 100,
},
],
items: [],
}
},
computed: {
total() {
return this.items.filter(item => {
return item.isDelete === false
}).length
},
successNum() {
return this.items.filter(item => {
return item.isDelete === false && item.status == 2
}).length
},
},
methods: {
closeBox() {
this.$emit('close', false)
},
upload(file) {
ServeFindFileSplitInfo({
file_name: file.name,
file_size: file.size,
}).then(res => {
if (res.code == 200) {
const { hash_name, split_size } = res.data
this.items.unshift({
hashName: hash_name,
originalFile: file,
filename: file.name,
status: 0, // 文件上传状态 0:等待上传 1:上传中 2:上传完成 3:网络异常
progress: 0,
filesize: formatSize(file.size),
filetype: file.type || '未知',
datetime: parseTime(new Date(), '{m}-{d} {h}:{i}'),
ext: getFileExt(file.name),
forms: this.fileSlice(file, hash_name, split_size),
successNum: 0,
isDelete: false,
})
this.triggerUpload(hash_name)
}
})
},
// 处理拆分上传文件
fileSlice(file, hash, eachSize) {
const ext = getFileExt(file.name)
const splitNum = Math.ceil(file.size / eachSize) // 分片总数
const forms = []
// 处理每个分片的上传操作
for (let i = 0; i < splitNum; i++) {
let start = i * eachSize
let end = Math.min(file.size, start + eachSize)
// 构建表单
const form = new FormData()
form.append('file', file.slice(start, end))
form.append('name', file.name)
form.append('hash', hash)
form.append('ext', ext)
form.append('size', file.size)
form.append('split_index', i)
form.append('split_num', splitNum)
forms.push(form)
}
return forms
},
// 触发上传文件
triggerUpload(hashName) {
let $index = this.getFileIndex(hashName)
if ($index < 0 || this.items[$index].isDelte) {
return
}
let i = this.items[$index].successNum
let form = this.items[$index].forms[i]
let length = this.items[$index].forms.length
this.items[$index].status = 1
ServeFileSubareaUpload(form)
.then(res => {
if (res.code == 200) {
$index = this.getFileIndex(hashName)
this.items[$index].successNum++
this.items[$index].progress = Math.floor(
(this.items[$index].successNum / length) * 100
)
if (this.items[$index].successNum == length) {
this.items[$index].status = 2
if (res.data.is_file_merge) {
ServeSendTalkFile({
hash_name: res.data.hash,
receiver_id: this.$store.state.dialogue.receiver_id,
talk_type: this.$store.state.dialogue.talk_type,
})
}
} else {
this.triggerUpload(hashName)
}
} else {
this.items[$index].status = 3
}
})
.catch(() => {
$index = this.getFileIndex(hashName)
this.items[$index].status = 3
})
},
// 获取分片文件数组索引
getFileIndex(hashName) {
return this.items.findIndex(item => {
return item.hashName === hashName
})
},
removeFile(hashName) {
let index = this.getFileIndex(hashName)
this.items[index].isDelete = true
},
clear() {
this.items = []
},
},
}
</script>
<style lang="less" scoped>
.container {
position: fixed;
right: 0;
bottom: 0;
width: 400px;
height: 600px;
background-color: white;
box-shadow: 0 0 5px #eae5e5;
border: 1px solid #eae5e5;
overflow: hidden;
border-radius: 3px 3px 0 0;
.header {
height: 50px;
line-height: 50px;
position: relative;
text-indent: 20px;
border-bottom: 1px solid #f5eeee;
i {
position: absolute;
right: 20px;
top: 15px;
font-size: 20px;
cursor: pointer;
}
}
.mian {
.empty-data {
width: 100%;
height: 80px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 50%;
svg {
font-size: 70px;
}
p {
margin-top: 30px;
color: #cccccc;
font-size: 10px;
}
}
}
}
.file-item {
width: 95%;
min-height: 100px;
background-color: white;
display: flex;
flex-direction: column;
border-radius: 5px;
margin: 15px auto;
box-shadow: 0 0 5px #eae5e5;
overflow: hidden;
.file-header {
height: 45px;
display: flex;
flex-direction: row;
align-items: center;
position: relative;
border-bottom: 1px solid #f7f4f4;
.type-icon {
height: 30px;
width: 30px;
background-color: #66b1ff;
border-radius: 50%;
margin-left: 5px;
font-size: 10px;
font-weight: 200;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
color: white;
}
.filename {
margin-left: 10px;
font-size: 14px;
width: 65%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.status {
position: absolute;
right: 14px;
top: 12px;
font-size: 13px;
color: #6b6868;
font-weight: 200;
}
}
.file-mian {
padding: 8px;
display: flex;
flex-direction: row;
.progress {
width: 80px;
height: 80px;
flex-shrink: 0;
background: #f9f6f6;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
cursor: pointer;
.name {
font-size: 12px;
color: #ada8a8;
font-weight: 300;
}
}
.detail {
flex: auto;
flex-shrink: 0;
display: flex;
flex-direction: column;
padding-left: 20px;
justify-content: center;
align-items: flex-start;
font-size: 13px;
p {
margin: 3px;
color: #ada8a8;
span {
color: #595a5a;
font-weight: 500;
}
}
}
}
.file-means {
width: 96.5%;
height: 35px;
border-top: 1px dashed rgb(234, 227, 227);
margin: 3px auto;
padding-top: 5px;
display: flex;
justify-content: flex-end;
align-items: center;
.btns {
width: 80px;
height: 25px;
border: 1px solid #e6e1e1;
display: flex;
justify-content: center;
align-items: center;
margin: 3px;
border-radius: 15px;
font-size: 12px;
color: #635f5f;
cursor: pointer;
&:active {
box-shadow: 0 0 5px #eae5e5;
font-size: 13px;
}
}
}
}
</style>