feat: 导入通道校验

* feat(s7): 数据采集增加S7协议

* fix: s7点位死区校验

* fix: s7点位配置

* fix: 修复s7点位bug

* fix: s7点位bug修复

* feat: 新增点位导入功能

* feat: 新增导入功能

* feat: 导入通道校验
This commit is contained in:
qiaochuLei 2023-11-17 13:48:46 +08:00 committed by GitHub
parent d304baf94f
commit 9517cbb954
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 239 additions and 4 deletions

View File

@ -66,4 +66,6 @@ export const getStates = () => server.get('/dictionary/running-state/items')
export const getSnapTypes = () => server.get('/s7/client/s7codecs/list') export const getSnapTypes = () => server.get('/s7/client/s7codecs/list')
export const getArea = () => server.get('/s7/client/s7area/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'})

View File

@ -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>

View File

@ -30,7 +30,17 @@
/></template> /></template>
新增点位 新增点位
</PermissionButton> </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 <PermissionButton
v-if="data?.provider === 'OPC_UA'" v-if="data?.provider === 'OPC_UA'"
type="primary" type="primary"
@ -315,6 +325,7 @@
/> />
<SaveS7 v-if="visible.saveS7" :data="current" @change="saveChange"/> <SaveS7 v-if="visible.saveS7" :data="current" @change="saveChange"/>
<Scan v-if="visible.scan" :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> </j-spin>
</template> </template>
<script lang="ts" setup name="PointPage"> <script lang="ts" setup name="PointPage">
@ -341,7 +352,7 @@ import { map } from 'rxjs/operators';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { responsiveArray } from 'ant-design-vue/lib/_util/responsiveObserve'; import { responsiveArray } from 'ant-design-vue/lib/_util/responsiveObserve';
import SaveS7 from './Save/SaveS7.vue'; import SaveS7 from './Save/SaveS7.vue';
import Import from './components/Import/index.vue'
const props = defineProps({ const props = defineProps({
data: { data: {
type: Object, type: Object,
@ -368,7 +379,8 @@ const visible = reactive({
writePoint: false, writePoint: false,
batchUpdate: false, batchUpdate: false,
scan: false, scan: false,
saveS7:false saveS7:false,
import:false
}); });
const current: any = ref({}); const current: any = ref({});
const accessModesOption = ref(); const accessModesOption = ref();
@ -541,6 +553,10 @@ const handlScan = () => {
visible.scan = true; visible.scan = true;
current.value = cloneDeep(props.data); current.value = cloneDeep(props.data);
}; };
const handleImport = () =>{
visible.import = true
current.value = cloneDeep(props.data)
}
const clickEdit = async (data: object) => { const clickEdit = async (data: object) => {
visible.writePoint = true; visible.writePoint = true;
current.value = cloneDeep(data); current.value = cloneDeep(data);
@ -676,6 +692,11 @@ const onCheckAllChange = (e: any) => {
} }
}; };
const closeImport = () =>{
visible.import = false
tableRef.value.reload()
}
watch( watch(
() => tableRef?.value?._dataSource, () => tableRef?.value?._dataSource,
(value) => { (value) => {