mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2025-12-17 08:25:52 +08:00
441 lines
10 KiB
Vue
441 lines
10 KiB
Vue
<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>
|