<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>
|