feat: 导入通道校验
* feat(s7): 数据采集增加S7协议 * fix: s7点位死区校验 * fix: s7点位配置 * fix: 修复s7点位bug * fix: s7点位bug修复 * feat: 新增点位导入功能 * feat: 新增导入功能 * feat: 导入通道校验
This commit is contained in:
parent
d304baf94f
commit
9517cbb954
|
@ -67,3 +67,5 @@ export const getStates = () => server.get('/dictionary/running-state/items')
|
|||
export const getSnapTypes = () => server.get('/s7/client/s7codecs/list')
|
||||
|
||||
export const getArea = () => server.get('/s7/client/s7area/list')
|
||||
|
||||
export const exportTemplate = (provider: string, format: string) =>server.get(`/data-collect/point/${provider}/template.${format}`, {}, {responseType: 'blob'})
|
|
@ -0,0 +1,212 @@
|
|||
<template>
|
||||
<j-modal visible title="导入" @cancel="emit('closeImport')" @ok="emit('closeImport')" :width="800" maskClosable="false">
|
||||
<div class="import-content">
|
||||
<div class="column">
|
||||
<p>上传文件</p>
|
||||
<div class="import">
|
||||
<a-upload-dragger v-model:fileList="fileList" name="file" :action="`${FILE_UPLOAD}?options=tempFile`"
|
||||
:headers="{
|
||||
'X-Access-Token': LocalStore.get(TOKEN_KEY),
|
||||
}" :limit="1" :showUploadList="false" @change="uploadChange" :accept="['xlsx', 'xls', 'csv']"
|
||||
:before-upload="beforeUpload">
|
||||
<div class="dragger-box">
|
||||
<AIcon class="icon" type="PlusCircleFilled" />
|
||||
<span style="margin: 16px 0 8px 0">点击或拖拽上传文件</span>
|
||||
<span>格式:.xlsx, .csv</span>
|
||||
</div>
|
||||
</a-upload-dragger>
|
||||
</div>
|
||||
</div>
|
||||
<div class="importing-status" v-if="importStatus == 'importing'">
|
||||
<loading-outlined />
|
||||
正在导入
|
||||
</div>
|
||||
<div class="column" v-if="importStatus != 'wait'">
|
||||
<p>
|
||||
<check-outlined style="color: #00a4ff" />导入成功 总数量
|
||||
{{ successNumber }}
|
||||
</p>
|
||||
<span v-if="failNumber">
|
||||
<close-outlined style="color: #e50012" />导入失败 总数量
|
||||
{{ failNumber }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<p>Excel导入模板</p>
|
||||
<div class="file-download">
|
||||
<j-button @click="downTemplate('xlsx')" class="btn">
|
||||
下载模板
|
||||
</j-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ArrowLeftOutlined,
|
||||
CheckOutlined,
|
||||
CloseOutlined,
|
||||
LoadingOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { FILE_UPLOAD } from '@/api/comm';
|
||||
import { TOKEN_KEY, BASE_API_PATH } from '@/utils/variable';
|
||||
import { LocalStore, onlyMessage } from '@/utils/comm';
|
||||
import {
|
||||
exportTemplate
|
||||
} from '@/api/data-collect/collector';
|
||||
import { downloadFileByUrl } from '@/utils/utils';
|
||||
import { getToken } from '@/utils/comm';
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['closeImport'])
|
||||
const fileList = ref()
|
||||
type ImportStatus = 'wait' | 'importing' | 'done';
|
||||
const importStatus = ref<ImportStatus>('wait'); //导入进度
|
||||
const successNumber = ref(0); //导入成功数量
|
||||
const failNumber = ref(0); //导入失败数量
|
||||
const errorMessage = ref();
|
||||
const detailFile = ref('')
|
||||
const uploadChange = async (info: Record<string, any>) => {
|
||||
if (info.file.status === 'done') {
|
||||
const resp: any = info.file.response || { result: '' };
|
||||
handleImport(resp)
|
||||
}
|
||||
};
|
||||
|
||||
const beforeUpload = (_file: any) => {
|
||||
const isCsv = _file.type === 'text/csv';
|
||||
const isXlsx =
|
||||
_file.type ===
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
if (!isCsv && !isXlsx) {
|
||||
onlyMessage('请上传.csv或.xlsx格式文件', 'warning');
|
||||
}
|
||||
return isCsv || isXlsx;
|
||||
};
|
||||
const downTemplate = async (type: string) => {
|
||||
const res: any = await exportTemplate(<string>props.data?.provider, type);
|
||||
if (res) {
|
||||
const blob = new Blob([res], { type: type });
|
||||
const url = URL.createObjectURL(blob);
|
||||
downloadFileByUrl(url, `${props.data?.provider}导入模版`, type);
|
||||
}
|
||||
};
|
||||
const handleImport = async (file: any) => {
|
||||
let message: any = []
|
||||
importStatus.value = 'importing';
|
||||
let event: EventSource
|
||||
event = new EventSource(
|
||||
`${BASE_API_PATH}/data-collect/point/${props.data?.collectorId
|
||||
}/${props.data?.provider}/import?:X_Access_Token=${getToken()
|
||||
}&fileUrl=${file.result}`,
|
||||
{ withCredentials: true },
|
||||
);
|
||||
event.onopen = (e) => {
|
||||
// pushMessage.value = []
|
||||
console.log('open');
|
||||
};
|
||||
event.onmessage = (e) => {
|
||||
console.log(e, '123')
|
||||
const result = JSON.parse(e.data);
|
||||
if (result.success) {
|
||||
successNumber.value++;
|
||||
} else {
|
||||
if (result.rowNumber !== -1) {
|
||||
failNumber.value++;
|
||||
message.push({
|
||||
rowNumber: `第${result.rowNumber}行`,
|
||||
message: result.message,
|
||||
name: result.name
|
||||
})
|
||||
errorMessage.value = JSON.stringify(message)
|
||||
} else {
|
||||
detailFile.value = result.detailFile;
|
||||
}
|
||||
}
|
||||
};
|
||||
event.onerror = (err) => {
|
||||
importStatus.value = 'done';
|
||||
event.close();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.import-content {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding: 20px;
|
||||
|
||||
.column {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.import {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.importing-status {
|
||||
align-items: center;
|
||||
}
|
||||
&.last {
|
||||
margin-top: 135px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-upload-select) {
|
||||
width: 100% !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.ant-row {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-btn) {
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
|
||||
&.ant-btn-link {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dragger-box {
|
||||
margin: 46px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #666666;
|
||||
|
||||
.icon {
|
||||
font-size: 30px;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.file-download {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
.btn {
|
||||
border: none;
|
||||
background-color: #ECECF0;
|
||||
width: 152px;
|
||||
color: #666666;
|
||||
}
|
||||
}</style>
|
|
@ -30,7 +30,17 @@
|
|||
/></template>
|
||||
新增点位
|
||||
</PermissionButton>
|
||||
|
||||
<PermissionButton
|
||||
v-if="['MODBUS_TCP', 'COLLECTOR_GATEWAY','snap7'].includes(data?.provider)"
|
||||
type="primary"
|
||||
@click="handleImport"
|
||||
hasPermission="DataCollect/Collector:add"
|
||||
>
|
||||
<!-- <template #icon
|
||||
><AIcon type="PlusOutlined"
|
||||
/></template> -->
|
||||
批量导入
|
||||
</PermissionButton>
|
||||
<PermissionButton
|
||||
v-if="data?.provider === 'OPC_UA'"
|
||||
type="primary"
|
||||
|
@ -315,6 +325,7 @@
|
|||
/>
|
||||
<SaveS7 v-if="visible.saveS7" :data="current" @change="saveChange"/>
|
||||
<Scan v-if="visible.scan" :data="current" @change="saveChange" />
|
||||
<Import v-if="visible.import" :data="current" @close-import="closeImport"/>
|
||||
</j-spin>
|
||||
</template>
|
||||
<script lang="ts" setup name="PointPage">
|
||||
|
@ -341,7 +352,7 @@ import { map } from 'rxjs/operators';
|
|||
import dayjs from 'dayjs';
|
||||
import { responsiveArray } from 'ant-design-vue/lib/_util/responsiveObserve';
|
||||
import SaveS7 from './Save/SaveS7.vue';
|
||||
|
||||
import Import from './components/Import/index.vue'
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
|
@ -368,7 +379,8 @@ const visible = reactive({
|
|||
writePoint: false,
|
||||
batchUpdate: false,
|
||||
scan: false,
|
||||
saveS7:false
|
||||
saveS7:false,
|
||||
import:false
|
||||
});
|
||||
const current: any = ref({});
|
||||
const accessModesOption = ref();
|
||||
|
@ -541,6 +553,10 @@ const handlScan = () => {
|
|||
visible.scan = true;
|
||||
current.value = cloneDeep(props.data);
|
||||
};
|
||||
const handleImport = () =>{
|
||||
visible.import = true
|
||||
current.value = cloneDeep(props.data)
|
||||
}
|
||||
const clickEdit = async (data: object) => {
|
||||
visible.writePoint = true;
|
||||
current.value = cloneDeep(data);
|
||||
|
@ -676,6 +692,11 @@ const onCheckAllChange = (e: any) => {
|
|||
}
|
||||
};
|
||||
|
||||
const closeImport = () =>{
|
||||
visible.import = false
|
||||
tableRef.value.reload()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => tableRef?.value?._dataSource,
|
||||
(value) => {
|
||||
|
|
Loading…
Reference in New Issue