Add File
This commit is contained in:
476
frontend/src/views/system/user/UserImport.vue
Normal file
476
frontend/src/views/system/user/UserImport.vue
Normal file
@@ -0,0 +1,476 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import icon_warning_filled from '@/assets/svg/icon_info_colorful.svg'
|
||||||
|
import icon_upload_outlined from '@/assets/svg/icon_upload_outlined.svg'
|
||||||
|
import icon_fileExcel_colorful from '@/assets/datasource/icon_excel.png'
|
||||||
|
import IconOpeDelete from '@/assets/svg/icon_delete.svg'
|
||||||
|
import { ref, reactive, h } from 'vue'
|
||||||
|
import {
|
||||||
|
ElMessage,
|
||||||
|
ElMessageBox,
|
||||||
|
ElLoading,
|
||||||
|
type UploadRequestOptions,
|
||||||
|
type UploadProps,
|
||||||
|
ElButton,
|
||||||
|
} from 'element-plus-secondary'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { userImportApi } from '@/api/user'
|
||||||
|
const { t } = useI18n()
|
||||||
|
const defaultTip = t('user.xls_format_files')
|
||||||
|
const loadingInstance = ref<any>(null)
|
||||||
|
const dialogShow = ref(false)
|
||||||
|
const form = ref({})
|
||||||
|
const file = ref<any>(null)
|
||||||
|
const fileName = ref('')
|
||||||
|
const errorFileKey = ref(null)
|
||||||
|
const emits = defineEmits(['refresh-grid'])
|
||||||
|
const state = reactive({
|
||||||
|
errList: [],
|
||||||
|
filesTmp: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const showLoading = () => {
|
||||||
|
loadingInstance.value = ElLoading.service({ target: '.user-import-class' })
|
||||||
|
}
|
||||||
|
const closeLoading = () => {
|
||||||
|
loadingInstance.value?.close()
|
||||||
|
}
|
||||||
|
const showDialog = () => {
|
||||||
|
file.value = null
|
||||||
|
fileName.value = ''
|
||||||
|
errorFileKey.value = null
|
||||||
|
dialogShow.value = true
|
||||||
|
}
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogShow.value = false
|
||||||
|
}
|
||||||
|
const handleExceed: UploadProps['onExceed'] = () => {
|
||||||
|
ElMessage.warning(t('userimport.exceedMsg'))
|
||||||
|
}
|
||||||
|
const handleError = () => {
|
||||||
|
ElMessage.warning(t('user.contact_the_administrator'))
|
||||||
|
}
|
||||||
|
const uploadValidate = (file: any) => {
|
||||||
|
const suffix = file.name.substring(file.name.lastIndexOf('.') + 1)
|
||||||
|
if (suffix !== 'xlsx' && suffix !== 'xls') {
|
||||||
|
ElMessage.warning(t('userimport.suffixMsg'))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.size / 1024 / 1024 > 10) {
|
||||||
|
ElMessage.warning(t('userimport.limitMsg'))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
state.errList = []
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const fileSize = ref('-')
|
||||||
|
const setFile = (options: UploadRequestOptions) => {
|
||||||
|
file.value = options.file
|
||||||
|
fileName.value = options.file.name
|
||||||
|
fileSize.value = setSize(options.file.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildFormData = (file: any, files: any, param: any) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
if (file) {
|
||||||
|
formData.append('file', file)
|
||||||
|
}
|
||||||
|
if (files) {
|
||||||
|
files.forEach((f: any) => {
|
||||||
|
formData.append('files', f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (param) {
|
||||||
|
formData.append('request', new Blob([JSON.stringify(param)], { type: 'application/json' }))
|
||||||
|
}
|
||||||
|
|
||||||
|
return formData
|
||||||
|
}
|
||||||
|
const downExcel = () => {
|
||||||
|
showLoading()
|
||||||
|
userImportApi
|
||||||
|
.downExcelTemplateApi()
|
||||||
|
.then((res) => {
|
||||||
|
const blobData = res.data
|
||||||
|
const blob = new Blob([blobData], { type: 'application/vnd.ms-excel' })
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.style.display = 'none'
|
||||||
|
link.href = URL.createObjectURL(blob)
|
||||||
|
link.download = 'user.xlsx' // 下载的文件名
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
closeLoading()
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
closeLoading()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setSize = (size: any) => {
|
||||||
|
let data = ''
|
||||||
|
const _size = Number.parseFloat(size)
|
||||||
|
if (_size < 1 * 1024) {
|
||||||
|
//如果小于0.1KB转化成B
|
||||||
|
data = _size.toFixed(2) + 'B'
|
||||||
|
} else if (_size < 1 * 1024 * 1024) {
|
||||||
|
//如果小于0.1MB转化成KB
|
||||||
|
data = (_size / 1024).toFixed(2) + 'KB'
|
||||||
|
} else if (_size < 1 * 1024 * 1024 * 1024) {
|
||||||
|
//如果小于0.1GB转化成MB
|
||||||
|
data = (_size / (1024 * 1024)).toFixed(2) + 'MB'
|
||||||
|
} else {
|
||||||
|
//其他转化成GB
|
||||||
|
data = (_size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
|
||||||
|
}
|
||||||
|
const size_str = data + ''
|
||||||
|
const len = size_str.indexOf('.')
|
||||||
|
const dec = size_str.substr(len + 1, 2)
|
||||||
|
if (dec == '00') {
|
||||||
|
//当小数点后为00时 去掉小数部分
|
||||||
|
return size_str.substring(0, len) + size_str.substr(len + 3, 2)
|
||||||
|
}
|
||||||
|
return size_str
|
||||||
|
}
|
||||||
|
|
||||||
|
const toGrid = () => {
|
||||||
|
file.value = null
|
||||||
|
fileName.value = ''
|
||||||
|
dialogShow.value = false
|
||||||
|
emits('refresh-grid')
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearFile = () => {
|
||||||
|
file.value = null
|
||||||
|
fileName.value = ''
|
||||||
|
}
|
||||||
|
const sure = () => {
|
||||||
|
const param = buildFormData(file.value, null, null)
|
||||||
|
showLoading()
|
||||||
|
userImportApi
|
||||||
|
.importUserApi(param)
|
||||||
|
.then((res) => {
|
||||||
|
closeLoading()
|
||||||
|
const data = res.data
|
||||||
|
errorFileKey.value = data.dataKey
|
||||||
|
closeDialog()
|
||||||
|
showTips(data.successCount, data.errorCount)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
closeLoading()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const downErrorExcel = () => {
|
||||||
|
if (errorFileKey.value) {
|
||||||
|
showLoading()
|
||||||
|
userImportApi
|
||||||
|
.downErrorRecordApi(errorFileKey.value)
|
||||||
|
.then((res) => {
|
||||||
|
const blobData = res.data
|
||||||
|
const blob = new Blob([blobData], { type: 'application/vnd.ms-excel' })
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.style.display = 'none'
|
||||||
|
link.href = URL.createObjectURL(blob)
|
||||||
|
link.download = 'error.xlsx' // 下载的文件名
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
closeLoading()
|
||||||
|
// closeDialog()
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
closeLoading()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const showTips = (successCount: any, errorCount: any) => {
|
||||||
|
let title = !errorCount
|
||||||
|
? t('user.data_import_successful')
|
||||||
|
: successCount
|
||||||
|
? t('user.data_import_failed')
|
||||||
|
: t('user.data_import_failed_de')
|
||||||
|
const childrenDomList = [
|
||||||
|
h('strong', null, title),
|
||||||
|
h('br', {}, {}),
|
||||||
|
h('span', null, t('user.imported_1_data', { msg: successCount })),
|
||||||
|
]
|
||||||
|
if (errorCount) {
|
||||||
|
const errorCountDom = h('span', null, t('user.import_1_data', { msg: errorCount }))
|
||||||
|
const errorDom = h('div', { class: 'error-record-tip flex-align-center' }, [
|
||||||
|
h('span', null, t('user.can')),
|
||||||
|
h(
|
||||||
|
ElButton,
|
||||||
|
{
|
||||||
|
onClick: downErrorExcel,
|
||||||
|
type: 'primary',
|
||||||
|
text: true,
|
||||||
|
class: 'down-button',
|
||||||
|
},
|
||||||
|
t('user.download_error_report')
|
||||||
|
),
|
||||||
|
h('span', null, t('user.modify_and_re_import')),
|
||||||
|
])
|
||||||
|
|
||||||
|
childrenDomList.push(errorCountDom)
|
||||||
|
childrenDomList.push(errorDom)
|
||||||
|
}
|
||||||
|
ElMessageBox.confirm('', {
|
||||||
|
confirmButtonType: 'primary',
|
||||||
|
type: !errorCount ? 'success' : successCount ? 'warning' : 'error',
|
||||||
|
autofocus: false,
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
message: h('div', { class: 'import-tip-box' }, childrenDomList),
|
||||||
|
showClose: false,
|
||||||
|
cancelButtonText: t('user.return_to_view'),
|
||||||
|
confirmButtonText: t('user.continue_importing'),
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
clearErrorRecord()
|
||||||
|
showDialog()
|
||||||
|
emits('refresh-grid')
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
clearErrorRecord()
|
||||||
|
toGrid()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const clearErrorRecord = () => {
|
||||||
|
if (errorFileKey.value) {
|
||||||
|
userImportApi.clearErrorApi(errorFileKey.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
file: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('datasource.please_enter') + t('common.empty') + t('user.file'),
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
showDialog,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogShow"
|
||||||
|
:title="t('user.batch_import')"
|
||||||
|
width="600px"
|
||||||
|
model-class="user-import-class"
|
||||||
|
@before-close="closeDialog"
|
||||||
|
>
|
||||||
|
<div class="down-template">
|
||||||
|
<span class="icon-span">
|
||||||
|
<el-icon>
|
||||||
|
<Icon name="icon_warning_filled"><icon_warning_filled class="svg-icon" /></Icon>
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
<div class="down-template-content">
|
||||||
|
<span>{{ t('user.please_first') }}</span>
|
||||||
|
<el-button type="primary" text class="down-button" @click="downExcel">{{
|
||||||
|
t('user.download_the_template')
|
||||||
|
}}</el-button>
|
||||||
|
<span>{{ t('user.required_and_upload') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-form
|
||||||
|
ref="form"
|
||||||
|
label-position="top"
|
||||||
|
:rules="rules"
|
||||||
|
class="form-content_error import-form"
|
||||||
|
:model="form"
|
||||||
|
label-width="0px"
|
||||||
|
@submit.prevent
|
||||||
|
>
|
||||||
|
<el-form-item prop="file" :label="t('user.file')" style="margin-bottom: 0px">
|
||||||
|
<div v-if="fileName" class="pdf-card">
|
||||||
|
<img :src="icon_fileExcel_colorful" width="40px" height="40px" />
|
||||||
|
<div class="file-name">
|
||||||
|
<div class="name">{{ fileName }}</div>
|
||||||
|
<div class="size">{{ fileName.split('.')[1] }} - {{ fileSize }}</div>
|
||||||
|
</div>
|
||||||
|
<el-icon class="action-btn" size="16" @click="clearFile">
|
||||||
|
<IconOpeDelete></IconOpeDelete>
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
<el-upload
|
||||||
|
v-if="fileName"
|
||||||
|
class="upload-user"
|
||||||
|
action=""
|
||||||
|
accept=".xlsx,.xls"
|
||||||
|
:on-exceed="handleExceed"
|
||||||
|
:before-upload="uploadValidate"
|
||||||
|
:on-error="handleError"
|
||||||
|
:show-file-list="false"
|
||||||
|
:file-list="state.filesTmp"
|
||||||
|
:http-request="setFile"
|
||||||
|
>
|
||||||
|
<el-button text style="line-height: 22px; height: 22px">
|
||||||
|
{{ $t('user.change_file') }}
|
||||||
|
</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<el-upload
|
||||||
|
v-else
|
||||||
|
class="upload-user"
|
||||||
|
action=""
|
||||||
|
accept=".xlsx,.xls"
|
||||||
|
:on-exceed="handleExceed"
|
||||||
|
:before-upload="uploadValidate"
|
||||||
|
:on-error="handleError"
|
||||||
|
:show-file-list="false"
|
||||||
|
:file-list="state.filesTmp"
|
||||||
|
:http-request="setFile"
|
||||||
|
>
|
||||||
|
<el-button secondary>
|
||||||
|
<el-icon size="16" style="margin-right: 4px">
|
||||||
|
<icon_upload_outlined></icon_upload_outlined>
|
||||||
|
</el-icon>
|
||||||
|
{{ t('user.upload_file') }}</el-button
|
||||||
|
>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
<el-link
|
||||||
|
v-if="!fileName"
|
||||||
|
style="width: 100%; line-height: 22px; height: 22px; justify-content: start"
|
||||||
|
class="font12"
|
||||||
|
type="info"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
{{ defaultTip }}
|
||||||
|
</el-link>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button secondary @click="closeDialog">{{ t('common.cancel') }}</el-button>
|
||||||
|
<el-button
|
||||||
|
:type="file && fileName ? 'primary' : 'info'"
|
||||||
|
:disabled="!file || !fileName"
|
||||||
|
@click="sure"
|
||||||
|
>{{ t('user.import') }}</el-button
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.upload-user {
|
||||||
|
height: 32px;
|
||||||
|
.ed-upload {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.color-danger {
|
||||||
|
:deep(.el-link--inner) {
|
||||||
|
color: var(--deDanger, #f54a45) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.font12 {
|
||||||
|
color: #8f959e !important;
|
||||||
|
font-family: var(--de-custom_font, 'PingFang');
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
.down-template {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
background: var(--ed-color-primary-80, #d2f1e9);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-left: 10px;
|
||||||
|
.icon-span {
|
||||||
|
color: var(--ed-color-primary);
|
||||||
|
font-size: 18px;
|
||||||
|
i {
|
||||||
|
top: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.down-template-content {
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-left: 10px;
|
||||||
|
.down-button {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.import-form {
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
.pdf-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 58px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 16px 0 12px;
|
||||||
|
border: 1px solid #dee0e3;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
.file-name {
|
||||||
|
margin-left: 8px;
|
||||||
|
.name {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #8f959e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ed-icon {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #646a73;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
background-color: #1f23291a;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 6px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
&::after {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.import-tip-box {
|
||||||
|
strong {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.error-record-tip {
|
||||||
|
font-size: 13px;
|
||||||
|
flex-flow: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user