perf: 使用slot处理文件列表 (#3599)

* perf: 使用slot处理文件列表

* perf: 大小文件超出限制不上传

---------

Co-authored-by: wangruidong <940853815@qq.com>
This commit is contained in:
fit2bot 2023-12-14 10:34:06 +08:00 committed by GitHub
parent 2ac9183047
commit d51a787598
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -5,35 +5,35 @@
<div class="transition-box" style="width: calc(100% - 17px);"> <div class="transition-box" style="width: calc(100% - 17px);">
<div class="upload_input"> <div class="upload_input">
<el-button <el-button
:disabled="run_button.disabled" :disabled="runButton.disabled"
:type="run_button.el&&run_button.el.type" :type="runButton.el&&runButton.el.type"
size="mini" size="mini"
style="display: inline-block; margin: 0 2px" style="display: inline-block; margin: 0 2px"
@click="run_button.callback()" @click="runButton.callback()"
> >
<i :class="run_button.icon" style="margin-right: 4px;" />{{ run_button.name }} <i :class="runButton.icon" style="margin-right: 4px;" />{{ runButton.name }}
</el-button> </el-button>
</div> </div>
<div class="upload_input">{{ $t('users.Users') }}:</div> <div class="upload_input">{{ $t('users.Users') }}:</div>
<div class="upload_input"> <div class="upload_input">
<el-autocomplete <el-autocomplete
v-model="runas_input.value" v-model="runAsInput.value"
:fetch-suggestions="runas_input.el.query" :fetch-suggestions="runAsInput.el.query"
:placeholder="runas_input.placeholder" :placeholder="runAsInput.placeholder"
size="mini" size="mini"
style="display: inline-block; margin: 0 2px" style="display: inline-block; margin: 0 2px"
@change="runas_input.callback(runas_input.value)" @change="runAsInput.callback(runAsInput.value)"
@select="runas_input.callback(runas_input.value)" @select="runAsInput.callback(runAsInput.value)"
/> />
</div> </div>
<div class="upload_input">{{ $t('ops.UploadDir') }}:</div> <div class="upload_input">{{ $t('ops.UploadDir') }}:</div>
<div class="upload_input"> <div class="upload_input">
<el-input <el-input
v-if="dst_path_input.type==='input'" v-if="dstPathInput.type==='input'"
v-model="dst_path" v-model="dstPath"
:placeholder="dst_path_input.placeholder" :placeholder="dstPathInput.placeholder"
size="mini" size="mini"
@change="dst_path_input.callback(dst_path_input.value)" @change="dstPathInput.callback(dstPathInput.value)"
/> />
</div> </div>
<div <div
@ -45,8 +45,7 @@
ref="upload" ref="upload"
:auto-upload="false" :auto-upload="false"
:on-change="onFileChange" :on-change="onFileChange"
:on-remove="onFileChange" :value.sync="uploadFileList"
:value.sync="files"
action="" action=""
drag drag
multiple multiple
@ -59,8 +58,25 @@
<span> <span>
{{ $t('ops.uploadFileLthHelpText') }} {{ $t('ops.uploadFileLthHelpText') }}
</span> </span>
<div slot="file" slot-scope="{file}">
<li tabindex="0" class="el-upload-list__item is-ready">
<a class="el-upload-list__item-name" :style="sameFileStyle(file)">
<i class="el-icon-document" />{{ file.name }}
<i style="color: #1ab394;float: right;font-weight:normal">
{{ formatFileSize(file.size) }}
<i class="el-icon-close" @click="removeFile(file)" />
</i>
</a>
</li>
</div>
</el-upload> </el-upload>
<el-progress v-if="ShowProgress" :percentage="progressLength" /> <el-progress v-if="ShowProgress" :percentage="progressLength" />
<div
v-if="uploadFileList.length===0"
class="empty-file-tip"
>
{{ $tc('ops.NoFiles') }}
</div>
</el-card> </el-card>
</div> </div>
<b>{{ $tc('ops.output') }}:</b> <b>{{ $tc('ops.output') }}:</b>
@ -119,12 +135,11 @@ export default {
timeCost: 0, timeCost: 0,
cancel: 0 cancel: 0
}, },
xtermConfig: { xtermConfig: {},
},
DataZTree: 0, DataZTree: 0,
runas: '', runas: '',
dst_path: '', dstPath: '',
run_button: { runButton: {
type: 'button', type: 'button',
name: this.$t('ops.Transfer'), name: this.$t('ops.Transfer'),
align: 'left', align: 'left',
@ -137,7 +152,7 @@ export default {
this.execute() this.execute()
} }
}, },
runas_input: { runAsInput: {
name: this.$t('ops.runAs'), name: this.$t('ops.runAs'),
align: 'left', align: 'left',
value: '', value: '',
@ -163,7 +178,7 @@ export default {
this.runas = option this.runas = option
} }
}, },
dst_path_input: { dstPathInput: {
type: 'input', type: 'input',
name: this.$t('ops.runningPath'), name: this.$t('ops.runningPath'),
align: 'left', align: 'left',
@ -173,8 +188,6 @@ export default {
this.chdir = val this.chdir = val
} }
}, },
files: null,
src_paths: [],
treeSetting: { treeSetting: {
treeUrl: '/api/v1/perms/users/self/nodes/children-with-assets/tree/', treeUrl: '/api/v1/perms/users/self/nodes/children-with-assets/tree/',
searchUrl: '/api/v1/perms/users/self/assets/tree/', searchUrl: '/api/v1/perms/users/self/assets/tree/',
@ -192,7 +205,8 @@ export default {
iShowTree: true, iShowTree: true,
progressLength: 0, progressLength: 0,
ShowProgress: false, ShowProgress: false,
upload_interval: null upload_interval: null,
uploadFileList: []
} }
}, },
computed: { computed: {
@ -206,9 +220,9 @@ export default {
mounted() { mounted() {
this.enableWS() this.enableWS()
this.initData() this.initData()
this.handleFileList(null, [])
}, },
methods: { methods: {
formatFileSize,
async initData() { async initData() {
this.recoverStatus() this.recoverStatus()
}, },
@ -217,8 +231,8 @@ export default {
this.currentTaskId = this.$route.query.taskId this.currentTaskId = this.$route.query.taskId
getTaskDetail(this.currentTaskId).then(data => { getTaskDetail(this.currentTaskId).then(data => {
getJob(data.job_id).then(res => { getJob(data.job_id).then(res => {
this.runas_input.value = res.runas this.runAsInput.value = res.runas
this.runas_input.callback(res.runas) this.runAsInput.callback(res.runas)
this.executionInfo.status = data['status'] this.executionInfo.status = data['status']
this.executionInfo.timeCost = data['time_cost'] this.executionInfo.timeCost = data['time_cost']
this.setCostTimeInterval() this.setCostTimeInterval()
@ -283,8 +297,8 @@ export default {
}, },
setCostTimeInterval() { setCostTimeInterval() {
this.run_button.icon = 'fa fa-spinner fa-spin' this.runButton.icon = 'fa fa-spinner fa-spin'
this.run_button.disabled = true this.runButton.disabled = true
this.executionInfo.cancel = setInterval(() => { this.executionInfo.cancel = setInterval(() => {
this.executionInfo.timeCost += 0.1 this.executionInfo.timeCost += 0.1
}, 100) }, 100)
@ -315,49 +329,40 @@ export default {
handleFileList(file, fileList) { handleFileList(file, fileList) {
const filenameList = fileList.map((file) => file.name) const filenameList = fileList.map((file) => file.name)
const filenameCount = _.countBy(filenameList) const filenameCount = _.countBy(filenameList)
this.$nextTick(() => { if (filenameCount[file.name] > 1) {
const emptyFileTip = document.getElementsByClassName('empty-file-tip') this.$message.error(this.$tc('ops.DuplicateFileExists'))
if (emptyFileTip.length > 0) { file.is_same = true
emptyFileTip[0].style = 'display:none' }
} },
const fileElementList = document.getElementsByClassName('el-upload-list__item-name') sameFileStyle(file) {
if (fileElementList && fileElementList.length > 0) { if (file.is_same) {
for (const ele of fileElementList) { return { backgroundColor: 'var(--color-danger)' }
// }
if (file.name === ele.outerText) { return ''
ele.insertAdjacentHTML('beforeend', },
`<i style="color: #1ab394;float: right;font-weight:normal">${formatFileSize(file.size)}</i>`) IsFileExceedsLimit(file) {
} const isGt200M = file.size / 1024 / 1024 > 200
// if (isGt200M) {
if (file.size > 200 * 1024 * 1024) { this.$message.error(this.$tc('ops.FileSizeExceedsLimit'))
this.$message.error(this.$tc('ops.FileSizeExceedsLimit')) }
ele.style = 'background-color:var(--color-danger)' return isGt200M
}
//
if (filenameCount[ele.outerText] > 1) {
this.$message.error(this.$tc('ops.DuplicateFileExists'))
ele.style = 'background-color:var(--color-danger)'
} else {
ele.style = ''
}
}
} else {
const emptyFileElementList = document.getElementsByClassName('el-upload-list--text')[0]
const text = this.$tc('ops.NoFiles')
emptyFileElementList.insertAdjacentHTML('afterbegin',
`<div class="empty-file-tip" style="color: #c5c9cc;font-size: 18px;display: flex;justify-content: center;align-items: center;height: 100%">
${text}</div>`)
}
})
}, },
onFileChange(file, fileList) { onFileChange(file, fileList) {
file.name = this.truncateFileName(file.name) file.name = this.truncateFileName(file.name)
this.files = fileList this.uploadFileList = fileList
this.handleFileList(file, fileList) this.handleFileList(file, fileList)
}, },
removeFile(file) {
this.uploadFileList.splice(this.uploadFileList.indexOf(file), 1)
},
execute() { execute() {
const { hosts, nodes } = this.getSelectedNodesAndHosts() const { hosts, nodes } = this.getSelectedNodesAndHosts()
if (!this.files) { for (const file of this.uploadFileList) {
if (this.IsFileExceedsLimit(file)) {
return
}
}
if (!this.uploadFileList) {
this.$message.error(this.$tc('ops.RequiredUploadFile')) this.$message.error(this.$tc('ops.RequiredUploadFile'))
return return
} }
@ -373,7 +378,7 @@ export default {
assets: hosts, assets: hosts,
nodes: nodes, nodes: nodes,
module: 'shell', module: 'shell',
args: JSON.stringify({ dst_path: this.dst_path }), args: JSON.stringify({ dst_path: this.dstPath }),
type: 'upload_file', type: 'upload_file',
runas: this.runas, runas: this.runas,
runas_policy: 'skip', runas_policy: 'skip',
@ -388,7 +393,7 @@ export default {
this.progressLength = 0 this.progressLength = 0
this.ShowProgress = true this.ShowProgress = true
const form = new FormData() const form = new FormData()
for (const file of this.files) { for (const file of this.uploadFileList) {
form.append('files', file.raw) form.append('files', file.raw)
form.append('job_id', res.id) form.append('job_id', res.id)
} }
@ -415,9 +420,9 @@ export default {
this.executionInfo.timeCost = 0 this.executionInfo.timeCost = 0
this.progressLength = 0 this.progressLength = 0
this.ShowProgress = false this.ShowProgress = false
this.run_button.disabled = false this.runButton.disabled = false
clearInterval(this.upload_interval) clearInterval(this.upload_interval)
this.run_button.icon = 'fa fa-play' this.runButton.icon = 'fa fa-play'
} }
} }
} }
@ -483,7 +488,7 @@ export default {
display: flex; display: flex;
} }
>>> .el-upload { > > > .el-upload {
width: 400px; width: 400px;
flex-direction: column; flex-direction: column;
@ -493,6 +498,14 @@ export default {
} }
} }
.empty-file-tip {
position: relative;
bottom: 100px;
left: 58%;
font-size: 18px;
color: #c5c9cc;
}
> > > .el-upload-list { > > > .el-upload-list {
margin-left: 20px; margin-left: 20px;
padding: 0 10px 0 10px; padding: 0 10px 0 10px;
@ -509,7 +522,7 @@ export default {
background: white; background: white;
} }
.output >>> #terminal { .output > > > #terminal {
border: dashed 1px #d9d9d9; border: dashed 1px #d9d9d9;
margin: 0 20px 20px; margin: 0 20px 20px;
} }