update:文件选择优化
This commit is contained in:
139
src/components/common/table/choseFile.vue
Normal file
139
src/components/common/table/choseFile.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<div class="comp-chose-file">
|
||||
<el-upload
|
||||
class="upload-btn"
|
||||
v-model:file-list="fileList"
|
||||
:multiple="multiple"
|
||||
:auto-upload="false"
|
||||
@change="changeFun"
|
||||
>
|
||||
<el-button type="primary" :disabled="disabled">选择文件</el-button>
|
||||
<template #file="{ file, index }">
|
||||
<div class="file-item">
|
||||
<div class="file-icon">
|
||||
<el-icon :size="16">
|
||||
<Document />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="file-name">
|
||||
{{ file.name }}
|
||||
</div>
|
||||
<div class="file-options">
|
||||
<el-icon v-if="file.fileId" class="btn preview" :size="16" @click="downloadFun(file)">
|
||||
<Download />
|
||||
</el-icon>
|
||||
<el-icon v-if="file.fileId" class="btn preview" :size="16" @click="previewFun(file)">
|
||||
<View />
|
||||
</el-icon>
|
||||
<el-icon v-if="!disabled" class="btn del" :size="16" @click="removeFun(index)">
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<FilePreview v-model="previewVisible" :fileId="fileId" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { Document, View, Delete, Download } from '@element-plus/icons-vue';
|
||||
import FilePreview from '@/components/common/filePreview/index.vue';
|
||||
import { downloadFileById } from '@/utils/file';
|
||||
import { isArray } from 'lodash-es';
|
||||
|
||||
interface Props {
|
||||
modelValue: any;
|
||||
disabled?: boolean;
|
||||
multiple?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: [],
|
||||
disabled: false,
|
||||
multiple: true,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'change', 'remove']);
|
||||
const fileList = ref<any>([]);
|
||||
const fileId = ref<any>(0);
|
||||
const previewVisible = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: any) => {
|
||||
fileList.value = isArray(val) ? val : [];
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => fileList.value,
|
||||
(val: any) => {
|
||||
emit('update:modelValue', val);
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
const previewFun = (data: any) => {
|
||||
fileId.value = data.fileId;
|
||||
previewVisible.value = true;
|
||||
};
|
||||
|
||||
const downloadFun = (data: any) => {
|
||||
downloadFileById(data.fileId);
|
||||
};
|
||||
|
||||
const changeFun = (data: any) => {
|
||||
emit('change', data);
|
||||
};
|
||||
|
||||
const removeFun = (index: number) => {
|
||||
emit('remove', fileList.value[index]);
|
||||
fileList.value.splice(index, 1);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.comp-chose-file {
|
||||
width: 100%;
|
||||
.upload-btn {
|
||||
width: 100%;
|
||||
}
|
||||
.file-item {
|
||||
display: flex;
|
||||
line-height: 18px;
|
||||
padding: 0 4px;
|
||||
.file-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
.file-name {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-secondary);
|
||||
padding-right: 5px;
|
||||
word-break: break-word;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.file-options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.btn {
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
&.preview {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
&.del {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
v-if="item.inputMode === 'input'"
|
||||
v-bind="attrs"
|
||||
v-model="formData[item.key]"
|
||||
:placeholder="(item.disabled && showDisabled) ? '' : '请输入'"
|
||||
:placeholder="item.disabled && showDisabled ? '' : '请输入'"
|
||||
clearable
|
||||
:disabled="item.disabled && showDisabled"
|
||||
@input="(val: any) => changeFun(item.key, val)"
|
||||
@@ -15,7 +15,7 @@
|
||||
v-model="formData[item.key]"
|
||||
type="textarea"
|
||||
autosize
|
||||
:placeholder="(item.disabled && showDisabled) ? '' : '请输入'"
|
||||
:placeholder="item.disabled && showDisabled ? '' : '请输入'"
|
||||
:disabled="item.disabled && showDisabled"
|
||||
@input="(val: any) => changeFun(item.key, val)"
|
||||
/>
|
||||
@@ -23,7 +23,7 @@
|
||||
v-if="item.inputMode === 'inputNumber'"
|
||||
v-bind="attrs"
|
||||
v-model="formData[item.key]"
|
||||
:placeholder="(item.disabled && showDisabled) ? '' : '请输入'"
|
||||
:placeholder="item.disabled && showDisabled ? '' : '请输入'"
|
||||
:min="0"
|
||||
:step="item.step"
|
||||
step-strictly
|
||||
@@ -34,7 +34,7 @@
|
||||
v-if="item.inputMode === 'select'"
|
||||
v-bind="attrs"
|
||||
v-model="formData[item.key]"
|
||||
:placeholder="(item.disabled && showDisabled) ? '' : '请选择'"
|
||||
:placeholder="item.disabled && showDisabled ? '' : '请选择'"
|
||||
clearable
|
||||
:options="item.options"
|
||||
:disabled="item.disabled && showDisabled"
|
||||
@@ -47,7 +47,12 @@
|
||||
:disabled="item.disabled && showDisabled"
|
||||
@change="(val: any) => changeFun(item.key, val)"
|
||||
>
|
||||
<el-radio v-for="(val) in item.options" :key="val.value" :label="val.label" :value="val.value" />
|
||||
<el-radio
|
||||
v-for="val in item.options"
|
||||
:key="val.value"
|
||||
:label="val.label"
|
||||
:value="val.value"
|
||||
/>
|
||||
</el-radio-group>
|
||||
<el-checkbox-group
|
||||
v-if="item.inputMode === 'checkbox'"
|
||||
@@ -56,7 +61,12 @@
|
||||
:disabled="item.disabled && showDisabled"
|
||||
@change="(val: any) => changeFun(item.key, val)"
|
||||
>
|
||||
<el-checkbox v-for="(val) in item.options" :key="val.value" :label="val.label" :value="val.value" />
|
||||
<el-checkbox
|
||||
v-for="val in item.options"
|
||||
:key="val.value"
|
||||
:label="val.label"
|
||||
:value="val.value"
|
||||
/>
|
||||
</el-checkbox-group>
|
||||
<MultipleSelect
|
||||
v-if="item.inputMode === 'multipleSelect'"
|
||||
@@ -72,7 +82,7 @@
|
||||
type="datetime"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
:placeholder="(item.disabled && showDisabled) ? '' : '请选择'"
|
||||
:placeholder="item.disabled && showDisabled ? '' : '请选择'"
|
||||
clearable
|
||||
:disabled="item.disabled && showDisabled"
|
||||
@change="(val: any) => changeFun(item.key, val)"
|
||||
@@ -90,20 +100,14 @@
|
||||
:disabled="item.disabled && showDisabled"
|
||||
@change="(val: any) => changeFun(item.key, val)"
|
||||
/>
|
||||
<el-upload
|
||||
<ChoseFile
|
||||
v-if="item.inputMode === 'choseFile'"
|
||||
v-bind="attrs"
|
||||
v-model:file-list="formData[item.key]"
|
||||
class="upload-btn"
|
||||
:limit="item.limit"
|
||||
:multiple="item.limit ? (item.limit > 1) : false"
|
||||
:auto-upload="false"
|
||||
v-model="formData[item.key]"
|
||||
:disabled="item.disabled && showDisabled"
|
||||
@change="(val: any) => changeFun(item.key, val)"
|
||||
@remove="(val: any) => removeFun(item.key, val)"
|
||||
>
|
||||
<template #trigger><el-button type="primary" :disabled="item.disabled && showDisabled">选择文件</el-button></template>
|
||||
</el-upload>
|
||||
/>
|
||||
<UploadImg
|
||||
v-if="item.inputMode === 'uploadImg'"
|
||||
v-model="formData[item.key]"
|
||||
@@ -150,6 +154,7 @@
|
||||
import { ref, watch } from 'vue';
|
||||
import MultipleSelect from './multipleSelect.vue';
|
||||
import UploadImg from './uploadImg.vue';
|
||||
import ChoseFile from './choseFile.vue';
|
||||
import UserSelect from '@/components/common/userSelect/index.vue';
|
||||
import ProjectSelect from '@/components/common/projectSelect/index.vue';
|
||||
import ApproveList from '@/components/common/approveList/index.vue';
|
||||
@@ -168,9 +173,12 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
attrs: {},
|
||||
});
|
||||
|
||||
watch(() => props.form, (val: any) => {
|
||||
formData.value = val;
|
||||
});
|
||||
watch(
|
||||
() => props.form,
|
||||
(val: any) => {
|
||||
formData.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(['change', 'remove']);
|
||||
|
||||
@@ -192,9 +200,3 @@ const removeFun = (key: string, val: any) => {
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.upload-btn {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
<template>
|
||||
<div class="comp-upload-list">
|
||||
<div ref="dragRef" class="btn" @click="openFun" @mousedown="startDragFun"><el-icon :size="22"><Upload /></el-icon></div>
|
||||
<el-drawer
|
||||
title="上传列表"
|
||||
v-model="listVisible"
|
||||
size="400"
|
||||
:modal="false"
|
||||
modal-penetrable
|
||||
>
|
||||
<div ref="dragRef" class="btn" @click="openFun" @mousedown="startDragFun">
|
||||
<el-icon :size="22"><Upload /></el-icon>
|
||||
</div>
|
||||
<el-drawer title="上传列表" v-model="listVisible" size="400" :modal="false" modal-penetrable>
|
||||
<div class="content">
|
||||
<div v-if="listData.length > 0" class="list">
|
||||
<div v-for="(item, index) in listData" :key="item.file.name" class="item">
|
||||
@@ -23,7 +19,7 @@
|
||||
<div class="progress">
|
||||
<el-progress
|
||||
:show-text="false"
|
||||
:percentage="Number(item.data.current / item.data.total * 100) || 0"
|
||||
:percentage="Number((item.data.current / item.data.total) * 100) || 0"
|
||||
:status="item.data.status === '-1' ? 'exception' : ''"
|
||||
:stroke-width="5"
|
||||
/>
|
||||
@@ -31,11 +27,18 @@
|
||||
<div class="info">
|
||||
<div class="speed">{{ item.data.speed || '--' }}/s</div>
|
||||
<div class="size">
|
||||
{{ formatFileSize((item.data.current / item.data.total * item.file.size) || 0) || '--' }}/{{ formatFileSize(item.file.size) }}
|
||||
{{
|
||||
formatFileSize((item.data.current / item.data.total) * item.file.size || 0) ||
|
||||
'--'
|
||||
}}/{{ formatFileSize(item.file.size) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="options"><el-icon v-if="item.data.status !== '1'" :size="16" @click="removeFun(index)"><Delete /></el-icon></div>
|
||||
<div class="options">
|
||||
<el-icon v-if="item.data.status !== '1'" :size="16" @click="removeFun(index)"
|
||||
><Delete
|
||||
/></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-task">暂无上传任务</div>
|
||||
@@ -49,14 +52,15 @@ import { ref } from 'vue';
|
||||
import { Upload, Delete } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { formatFileSize } from '@/utils/file';
|
||||
import { chunkUploadToMinioApi, callBackknowledgeFileApi, chunkUploadCallbackApi } from '@/api/data/data';
|
||||
import { chunkUploadToMinioApi, chunkUploadCallbackApi } from '@/api/data/data';
|
||||
import emitter from '@/utils/eventBus';
|
||||
|
||||
const taskStatusObj: any = {}; // 更具uploadTaskId和businessId记录所以任务文件的上传状态
|
||||
const listVisible = ref(false);
|
||||
const listData = ref<any>([]);
|
||||
const chunkSize = 1024 * 1024 * 10; // 每片10MB
|
||||
const UPLOAD_FILE_STATUS: any = { // TODO
|
||||
const UPLOAD_FILE_STATUS: any = {
|
||||
// TODO
|
||||
'-1': '上传失败',
|
||||
'0': '待上传',
|
||||
'1': '上传中',
|
||||
@@ -90,7 +94,7 @@ const initFun = (data: any) => {
|
||||
};
|
||||
|
||||
// 分片并上传
|
||||
const sliceFileFun = async(fileIndex: number) => {
|
||||
const sliceFileFun = async (fileIndex: number) => {
|
||||
const fileObj = listData.value[fileIndex];
|
||||
const file = fileObj.file;
|
||||
const fileData = fileObj.data;
|
||||
@@ -119,7 +123,7 @@ const sliceFileFun = async(fileIndex: number) => {
|
||||
fileObj.data.total = totalChunks;
|
||||
fileObj.data.current = chunkIndex + 1;
|
||||
fileObj.data.speed = res.speed;
|
||||
fileObj.data.status = (chunkIndex + 1 === totalChunks) ? '2' : '1';
|
||||
fileObj.data.status = chunkIndex + 1 === totalChunks ? '2' : '1';
|
||||
} else {
|
||||
fileObj.data.status = '-1';
|
||||
}
|
||||
@@ -142,9 +146,15 @@ const sliceFileFun = async(fileIndex: number) => {
|
||||
const callBackFun = (data: any) => {
|
||||
const { uploadTaskId, isApprove, taskType } = data;
|
||||
const totalTaskNum = Object.keys(taskStatusObj[uploadTaskId]).length;
|
||||
const finishTaskNum = Object.values(taskStatusObj[uploadTaskId]).filter(val => val === '2' || val === '-1').length;
|
||||
const successData = Object.entries(taskStatusObj[uploadTaskId]).filter(([, value]) => value === '2').map(([key]) => key);
|
||||
const failData = Object.entries(taskStatusObj[uploadTaskId]).filter(([, value]) => value === '-1').map(([key]) => key);
|
||||
const finishTaskNum = Object.values(taskStatusObj[uploadTaskId]).filter(
|
||||
(val) => val === '2' || val === '-1'
|
||||
).length;
|
||||
const successData = Object.entries(taskStatusObj[uploadTaskId])
|
||||
.filter(([, value]) => value === '2')
|
||||
.map(([key]) => key);
|
||||
const failData = Object.entries(taskStatusObj[uploadTaskId])
|
||||
.filter(([, value]) => value === '-1')
|
||||
.map(([key]) => key);
|
||||
if (finishTaskNum === totalTaskNum) {
|
||||
const params = {
|
||||
uploadTaskId: uploadTaskId,
|
||||
@@ -153,14 +163,14 @@ const callBackFun = (data: any) => {
|
||||
isApprove,
|
||||
};
|
||||
const apiObj: any = {
|
||||
2: callBackknowledgeFileApi, // 知识库后处理
|
||||
4: chunkUploadCallbackApi,
|
||||
2: chunkUploadCallbackApi, // 知识库后处理
|
||||
4: chunkUploadCallbackApi, // 交付物后处理
|
||||
};
|
||||
apiObj[taskType](params);
|
||||
}
|
||||
};
|
||||
|
||||
const uploadFun = async(params: any) => {
|
||||
const uploadFun = async (params: any) => {
|
||||
const starTime = new Date().getTime();
|
||||
const res: any = await chunkUploadToMinioApi(params);
|
||||
const data = res.data || {};
|
||||
|
||||
Reference in New Issue
Block a user