212 lines
6.5 KiB
Vue
212 lines
6.5 KiB
Vue
<template>
|
||
<div class="file">
|
||
<j-form layout="vertical">
|
||
<j-form-item label="下载模板">
|
||
<div class="file-download">
|
||
<j-button @click="downFile('xlsx')">模板格式.xlsx</j-button>
|
||
<j-button @click="downFile('csv')">模板格式.csv</j-button>
|
||
</div>
|
||
</j-form-item>
|
||
<j-form-item label="文件上传">
|
||
<!-- 导入系统已存在的设备数据,不会更改已存在设备的所属产品信息 -->
|
||
<a-upload-dragger
|
||
v-model:fileList="modelRef.upload"
|
||
name="file"
|
||
:action="FILE_UPLOAD"
|
||
:headers="{
|
||
'X-Access-Token': LocalStore.get(TOKEN_KEY),
|
||
}"
|
||
:maxCount="1"
|
||
:showUploadList="false"
|
||
@change="uploadChange"
|
||
:accept="
|
||
modelRef?.file?.fileType
|
||
? `.${modelRef?.file?.fileType}`
|
||
: '.xlsx'
|
||
"
|
||
:before-upload="beforeUpload"
|
||
:disabled="disabled"
|
||
@drop="handleDrop"
|
||
>
|
||
<div
|
||
style="
|
||
height: 115px;
|
||
line-height: 115px;
|
||
color: #666666;
|
||
"
|
||
>
|
||
<!-- <AIcon style="font-size: 20px;" type="PlusCircleOutlined" /> -->
|
||
点击或拖拽上传文件
|
||
</div>
|
||
</a-upload-dragger>
|
||
</j-form-item>
|
||
<div style="margin-bottom: 16px">
|
||
<j-checkbox v-model:checked="modelRef.file.autoDeploy"
|
||
>导入并启用</j-checkbox
|
||
>
|
||
</div>
|
||
</j-form>
|
||
<div v-if="importLoading" class="result">
|
||
<div v-if="flag">
|
||
<j-spin size="small" style="margin-right: 10px" />正在导入
|
||
</div>
|
||
<div v-else>
|
||
<AIcon
|
||
style="color: #08e21e; margin-right: 10px; font-size: 16px"
|
||
type="CheckCircleOutlined"
|
||
/>导入完成
|
||
</div>
|
||
<div>导入成功:{{ count }} 个</div>
|
||
<div>
|
||
导入失败:<span style="color: #ff595e">{{ errCount }}</span>
|
||
个<a v-if="errMessage && !flag && errCount > 0" style="margin-left: 20px" @click="downError">下载</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang='ts' name='DeviceImportFile'>
|
||
import { FILE_UPLOAD } from '@/api/comm';
|
||
import { TOKEN_KEY } from '@/utils/variable';
|
||
import { LocalStore, onlyMessage } from '@/utils/comm';
|
||
import { downloadFileByUrl } from '@/utils/utils';
|
||
import { deviceImport, templateDownload } from '@/api/device/instance';
|
||
import { EventSourcePolyfill } from 'event-source-polyfill';
|
||
import { message } from 'jetlinks-ui-components';
|
||
|
||
const props = defineProps({
|
||
product: {
|
||
type: String,
|
||
default: undefined,
|
||
},
|
||
});
|
||
|
||
const modelRef = reactive({
|
||
product: props.product,
|
||
upload: [],
|
||
file: {
|
||
fileType: 'xlsx',
|
||
autoDeploy: false,
|
||
},
|
||
});
|
||
|
||
const importLoading = ref<boolean>(false);
|
||
const flag = ref<boolean>(false);
|
||
const count = ref<number>(0);
|
||
const errCount = ref<number>(0);
|
||
|
||
const errMessage = ref<string>('');
|
||
const disabled = ref(false);
|
||
|
||
const downFile = async (type: string) => {
|
||
const res: any = await templateDownload(props.product!, type);
|
||
if (res) {
|
||
const blob = new Blob([res], { type: type });
|
||
const url = URL.createObjectURL(blob);
|
||
downloadFileByUrl(url, `设备导入模板`, type);
|
||
}
|
||
};
|
||
|
||
const beforeUpload = (_file: any) => {
|
||
const fileType = modelRef.file?.fileType === 'csv' ? 'csv' : 'xlsx';
|
||
const isCsv = _file.type === 'text/csv';
|
||
const isXlsx =
|
||
_file.type ===
|
||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||
if (!isCsv && fileType !== 'xlsx') {
|
||
onlyMessage('请上传.csv格式文件', 'warning');
|
||
}
|
||
if (!isXlsx && fileType !== 'csv') {
|
||
onlyMessage('请上传.xlsx格式文件', 'warning');
|
||
}
|
||
return (isCsv && fileType !== 'xlsx') || (isXlsx && fileType !== 'csv');
|
||
};
|
||
|
||
const handleDrop = () => {};
|
||
|
||
const downError = () => {
|
||
window.open(errMessage.value)
|
||
}
|
||
|
||
const submitData = async (fileUrl: string) => {
|
||
if (!!fileUrl) {
|
||
count.value = 0;
|
||
errCount.value = 0;
|
||
const autoDeploy = !!modelRef?.file?.autoDeploy || false;
|
||
importLoading.value = true;
|
||
let dt = 0;
|
||
let et = 0;
|
||
const source = new EventSourcePolyfill(
|
||
deviceImport(props.product!, fileUrl, autoDeploy),
|
||
);
|
||
source.onmessage = (e: any) => {
|
||
const res = JSON.parse(e.data);
|
||
if (res.success) {
|
||
const temp = res.result.total;
|
||
dt += temp;
|
||
count.value = dt;
|
||
} else {
|
||
if (res.detailFile) {
|
||
errMessage.value = res.detailFile
|
||
} else {
|
||
et += 1;
|
||
errCount.value = et;
|
||
}
|
||
}
|
||
disabled.value = false;
|
||
};
|
||
source.onerror = (e: { status: number }) => {
|
||
if (e.status === 403) errMessage.value = '暂无权限,请联系管理员';
|
||
flag.value = false;
|
||
disabled.value = false;
|
||
source.close();
|
||
};
|
||
source.onopen = () => {};
|
||
} else {
|
||
message.error('请先上传文件');
|
||
}
|
||
};
|
||
|
||
const uploadChange = async (info: Record<string, any>) => {
|
||
disabled.value = true;
|
||
if (info.file.status === 'done') {
|
||
const resp: any = info.file.response || { result: '' };
|
||
await submitData(resp?.result || '');
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang='less'>
|
||
.file {
|
||
.file-type-label {
|
||
display: flex;
|
||
gap: 16px;
|
||
align-items: center;
|
||
|
||
.file-type-radio {
|
||
display: flex;
|
||
flex-grow: 1;
|
||
|
||
:deep(.ant-radio-button-wrapper) {
|
||
width: 50%;
|
||
}
|
||
}
|
||
}
|
||
|
||
.file-download {
|
||
display: flex;
|
||
gap: 16px;
|
||
> button {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
}
|
||
|
||
.result {
|
||
div {
|
||
margin: 5px 0;
|
||
color: #605e5c;
|
||
}
|
||
}
|
||
}
|
||
</style> |