forked from TALL/tall3-pc-keti
69 changed files with 14689 additions and 8593 deletions
@ -0,0 +1 @@ |
|||
regisrty https://registry.npm.taobao.org |
File diff suppressed because it is too large
@ -1,85 +0,0 @@ |
|||
<template> |
|||
<el-form ref="searchDeviceForm" :inline="true" :model="searchDevice"> |
|||
<el-form-item label="选择站点"> |
|||
<el-select v-model="searchDevice.deviceId" placeholder="请选择站点" @change="change"> |
|||
<el-option label="全部" value></el-option> |
|||
<el-option |
|||
v-for="item in devices" |
|||
:key="item.deviceId" |
|||
:label="`${item.address}-${item.deviceId}`" |
|||
:value="item.deviceId" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
|
|||
<!-- <el-form-item label="类型">--> |
|||
<!-- <el-select v-model="searchDevice.type" placeholder="选择查询类型">--> |
|||
<!-- <el-option label="全部" value=""></el-option>--> |
|||
<!-- <el-option label="事件上报" value="EVENT"></el-option>--> |
|||
<!-- <el-option label="业务上报" value="DATA"></el-option>--> |
|||
<!-- </el-select>--> |
|||
<!-- </el-form-item>--> |
|||
|
|||
<!-- <el-form-item label="状态">--> |
|||
<!-- <el-select v-model="searchDevice.status" placeholder="选择查询类型">--> |
|||
<!-- <el-option label="全部" value=""></el-option>--> |
|||
<!-- <el-option label="PENDING" value="PENDING"></el-option>--> |
|||
<!-- <el-option label="SUCCESS" value="SUCCESS"></el-option>--> |
|||
<!-- </el-select>--> |
|||
<!-- </el-form-item>--> |
|||
|
|||
<el-form-item> |
|||
<el-button type="primary" @click="onSubmit"> |
|||
<i class="el-icon-search mr-2"></i> |
|||
查询 |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, defineEmits, reactive, ref, watch } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
|
|||
const emit = defineEmits(['search']); |
|||
const searchDevice = reactive({ |
|||
deviceId: '', |
|||
paging: true, |
|||
page: 1, |
|||
size: 50, |
|||
sort: [ |
|||
{ |
|||
col: 'createdAt', |
|||
order: 'DESC', |
|||
}, |
|||
], |
|||
type: '', |
|||
status: '', |
|||
}); |
|||
const searchDeviceForm = ref(null); // form |
|||
const store = useStore(); |
|||
const devices = computed(() => store.state.device.devices); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
|
|||
// 监听currentDeviceId |
|||
watch( |
|||
() => currentDeviceId.value, |
|||
newValue => { |
|||
if (newValue) { |
|||
searchDevice.deviceId !== newValue && (searchDevice.deviceId = newValue); |
|||
} |
|||
}, |
|||
{ immediate: true }, |
|||
); |
|||
|
|||
const change = e => { |
|||
store.commit('device/setCurrentDeviceId', e); |
|||
}; |
|||
|
|||
// 提交 |
|||
const onSubmit = () => { |
|||
searchDeviceForm.value.validate(() => { |
|||
emit('search', { ...searchDevice }); |
|||
}); |
|||
}; |
|||
</script> |
@ -1,92 +0,0 @@ |
|||
<template> |
|||
<template v-if="data"> |
|||
<el-table :data="data" :max-height="contentHeight" border stripe style="width: 100%"> |
|||
<el-table-column type="expand"> |
|||
<template #default="props"> |
|||
<div class="mx-4"> |
|||
<FunctionConfigApplied v-if="props.row" :data="props.row" /> |
|||
</div> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="设备ID" min-min-width="100" prop="deviceId" /> |
|||
<el-table-column align="center" label="上次配置时间" min-width="200"> |
|||
<template #default="scope"> |
|||
{{ dayjs(new Date(+scope.row.settingTime)).format('YYYY-MM-DD HH:mm:ss') }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="状态" min-width="100"> |
|||
<template #default="scope"> |
|||
<StatusTagPending :type="scope.row.settingStatus" /> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<!-- <el-pagination--> |
|||
<!-- :current-page="devicesAll.page.page"--> |
|||
<!-- :default-page-size="50"--> |
|||
<!-- :page-count="devicesAll.page.count"--> |
|||
<!-- :page-size="devicesAll.page.size"--> |
|||
<!-- :page-sizes="[1, 10, 20, 50, 100]"--> |
|||
<!-- :total="devicesAll.page.total"--> |
|||
<!-- background--> |
|||
<!-- class="my-3 float-right"--> |
|||
<!-- layout="total, sizes, prev, pager, next, jumper"--> |
|||
<!-- @size-change="onSizeChange"--> |
|||
<!-- @current-change="onCurrentPageChange"--> |
|||
<!-- ></el-pagination>--> |
|||
</template> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, onMounted, ref, watchEffect } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { getConfigFunction } from 'apis'; |
|||
import dayjs from 'dayjs'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import StatusTagPending from 'components/config/status-tag-pending.vue'; |
|||
import FunctionConfigApplied from 'components/config/function-config-applied.vue'; |
|||
|
|||
const store = useStore(); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); |
|||
const contentHeight = ref(600); |
|||
const data = ref(null); |
|||
|
|||
// 设置表格高度 |
|||
onMounted(() => { |
|||
const winHeight = document.documentElement.clientHeight; |
|||
contentHeight.value = winHeight - 250; |
|||
}); |
|||
|
|||
watchEffect(() => { |
|||
const deviceId = currentDeviceId.value; |
|||
onSearch(deviceId); |
|||
}); |
|||
|
|||
/** |
|||
* 删除 |
|||
* @param {number} page 页码 |
|||
* @param {number} size 每页条数 |
|||
*/ |
|||
async function onSearch(deviceId = currentDeviceId.value) { |
|||
const params = { deviceId }; |
|||
try { |
|||
const resData = await getConfigFunction(params); |
|||
data.value = resData; |
|||
} catch (error) { |
|||
ElMessage.error(error.message); |
|||
console.error(error); |
|||
} |
|||
} |
|||
|
|||
currentDeviceId.value && onSearch(currentDeviceId.value); |
|||
|
|||
// 当前页码变化 |
|||
// const onCurrentPageChange = page => { |
|||
// onSearch(page, devicesAll.value.page.size || 50); |
|||
// }; |
|||
|
|||
// 每页条数变化 |
|||
// const onSizeChange = size => { |
|||
// onSearch(1, size); |
|||
// }; |
|||
</script> |
@ -1,93 +0,0 @@ |
|||
<template> |
|||
<template v-if="data"> |
|||
<el-table :data="data" :max-height="contentHeight" border stripe style="width: 100%"> |
|||
<el-table-column type="expand"> |
|||
<template #default="props"> |
|||
<div class="mx-4"> |
|||
<NetworkConfigApplied v-if="props.row" :data="props.row" /> |
|||
</div> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="设备ID" min-min-width="100" prop="deviceId" /> |
|||
|
|||
<el-table-column align="center" label="上次配置时间" min-width="200"> |
|||
<template #default="scope"> |
|||
{{ dayjs(new Date(+scope.row.settingTime)).format('YYYY-MM-DD HH:mm:ss') }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="状态" min-width="100"> |
|||
<template #default="scope"> |
|||
<StatusTagPending :type="scope.row.settingStatus" /> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<!-- <el-pagination--> |
|||
<!-- :current-page="devicesAll.page.page"--> |
|||
<!-- :default-page-size="50"--> |
|||
<!-- :page-count="devicesAll.page.count"--> |
|||
<!-- :page-size="devicesAll.page.size"--> |
|||
<!-- :page-sizes="[1, 10, 20, 50, 100]"--> |
|||
<!-- :total="devicesAll.page.total"--> |
|||
<!-- background--> |
|||
<!-- class="my-3 float-right"--> |
|||
<!-- layout="total, sizes, prev, pager, next, jumper"--> |
|||
<!-- @size-change="onSizeChange"--> |
|||
<!-- @current-change="onCurrentPageChange"--> |
|||
<!-- ></el-pagination>--> |
|||
</template> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, onMounted, ref, watchEffect } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import dayjs from 'dayjs'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import StatusTagPending from 'components/config/status-tag-pending.vue'; |
|||
import NetworkConfigApplied from 'components/config/network-config-applied.vue'; |
|||
import { getConfigNetwork } from 'apis'; |
|||
|
|||
const store = useStore(); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); |
|||
const contentHeight = ref(600); |
|||
const data = ref(null); |
|||
|
|||
// 设置表格高度 |
|||
onMounted(() => { |
|||
const winHeight = document.documentElement.clientHeight; |
|||
contentHeight.value = winHeight - 250; |
|||
}); |
|||
|
|||
watchEffect(() => { |
|||
const deviceId = currentDeviceId.value; |
|||
onSearch(deviceId); |
|||
}); |
|||
|
|||
/** |
|||
* 删除 |
|||
* @param {number} page 页码 |
|||
* @param {number} size 每页条数 |
|||
*/ |
|||
async function onSearch(deviceId = currentDeviceId.value) { |
|||
const params = { deviceId }; |
|||
try { |
|||
const resData = await getConfigNetwork(params); |
|||
data.value = resData; |
|||
} catch (error) { |
|||
ElMessage.error(error.message); |
|||
console.error(error); |
|||
} |
|||
} |
|||
|
|||
currentDeviceId.value && onSearch(currentDeviceId.value); |
|||
|
|||
// 当前页码变化 |
|||
// const onCurrentPageChange = page => { |
|||
// onSearch(page, devicesAll.value.page.size || 50); |
|||
// }; |
|||
|
|||
// 每页条数变化 |
|||
// const onSizeChange = size => { |
|||
// onSearch(1, size); |
|||
// }; |
|||
</script> |
@ -1,42 +0,0 @@ |
|||
<script setup> |
|||
import { computed, ref, watch } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
|
|||
const store = useStore(); |
|||
const devices = computed(() => store.state.device.devices); // 设备/站点列表 |
|||
const deviceId = ref(''); // 选中的设备id |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
|
|||
// 监听currentDeviceId |
|||
watch( |
|||
() => currentDeviceId.value, |
|||
newValue => { |
|||
if (newValue && deviceId.value !== newValue) { |
|||
deviceId.value = newValue; |
|||
} |
|||
}, |
|||
{ immediate: true }, |
|||
); |
|||
|
|||
/** |
|||
* 设备id修改 |
|||
* 更新store里的deviceId |
|||
*/ |
|||
function onChange(event) { |
|||
store.commit('device/setCurrentDeviceId', event); |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="mb-3"> |
|||
<span class="text-sm text-gray-500">选择站点:</span> |
|||
<el-select v-model="deviceId" placeholder="选择站点" @change="onChange"> |
|||
<el-option |
|||
v-for="item in devices" |
|||
:key="item.deviceId" |
|||
:label="`${item.address}-${item.deviceId}`" |
|||
:value="item.deviceId" |
|||
></el-option> |
|||
</el-select> |
|||
</div> |
|||
</template> |
@ -1,77 +0,0 @@ |
|||
<template> |
|||
<el-row :gutter="10"> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>金属腐蚀采样频率(分钟):</span> |
|||
<span>{{ data.frequency.metal }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>温湿度采样频率(分钟):</span> |
|||
<span>{{ data.frequency.th }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>SO2采样频率(分钟):</span> |
|||
<span>{{ data.frequency.so2 }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>盐分采样频率(分钟):</span> |
|||
<span>{{ data.frequency.salt }}</span> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>电池电压低阈值(V):</span> |
|||
<span>{{ data.batteryLow }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>电池电压高阈值(V):</span> |
|||
<span>{{ data.batteryHigh }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>太阳能板电压高阈值(V):</span> |
|||
<span>{{ data.sunHigh }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>湿度高阈值(RH%):</span> |
|||
<span>{{ data.humidityHigh }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>温度低阈值(℃):</span> |
|||
<span>{{ data.temperatureLow }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>温度高阈值(℃):</span> |
|||
<span>{{ data.temperatureHigh }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>金属腐蚀采集个数:</span> |
|||
<span>{{ data.count }}</span> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>安全模式:</span> |
|||
<span>{{ SER[data.securityMode] }}</span> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>上报类型:</span> |
|||
<span class="mr-8">{{ REPORT_TYPE[data.report.type] }}</span> |
|||
|
|||
<span v-if="data.report.type === 'POINT'"> |
|||
<span v-for="item in data.report.timePoints" :key="item" class="mr-3">{{ item }}</span> |
|||
</span> |
|||
<span v-else>{{ data.report.cycle }}分钟</span> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<!-- 刷新下发按钮 --> |
|||
<!-- <Refresh @refresh="onSearch(currentDeviceI, 1)" /> --> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { defineProps } from 'vue'; |
|||
import { SER } from '@/config/log'; |
|||
import { REPORT_TYPE } from '@/config/config'; |
|||
|
|||
defineProps({ data: Object }); |
|||
</script> |
@ -1,287 +0,0 @@ |
|||
<template> |
|||
<el-form ref="functionForm" :model="data" label-position="top"> |
|||
<StatusAndLastTime v-if="data.settingStatus" :setting-status="data.settingStatus" :setting-time="+data.settingTime" /> |
|||
|
|||
<el-row :gutter="20"> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="金属腐蚀采样频率(分钟)" prop="frequency.metal"> |
|||
<el-input-number v-model="data.frequency.metal" :min="0" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="温湿度采样频率(分钟)" prop="frequency.th"> |
|||
<el-input-number v-model="data.frequency.th" :min="0" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="SO2采样频率(分钟)" prop="frequency.so2"> |
|||
<el-input-number v-model="data.frequency.so2" :min="0" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="盐分采样频率(分钟)" prop="frequency.salt"> |
|||
<el-input-number v-model="data.frequency.salt" :min="0" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="电池电压低阈值(V)" prop="batteryLow"> |
|||
<el-input-number v-model="data.batteryLow" :min="0" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="电池电压高阈值(V)" prop="batteryHigh"> |
|||
<el-input-number v-model="data.batteryHigh" :min="0" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="太阳能板电压高阈值(V)" prop="sunHigh"> |
|||
<el-input-number v-model="data.sunHigh" :min="0" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="湿度高阈值(RH%)" prop="humidityHigh"> |
|||
<el-input-number v-model="data.humidityHigh" :min="0" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="温度低阈值(℃)" prop="temperatureLow"> |
|||
<el-input-number v-model="data.temperatureLow" :min="-30" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="温度高阈值(℃)" prop="temperatureHigh"> |
|||
<el-input-number v-model="data.temperatureHigh" :min="0" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="金属腐蚀采集个数" prop="count"> |
|||
<el-input v-model="data.count" :min="0" disabled style="width: 180px" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="安全模式" prop="securityMode"> |
|||
<!-- <el-input v-model="data.securityMode"></el-input> --> |
|||
<el-radio v-model="data.securityMode" label="OPEN">不加密</el-radio> |
|||
<el-radio v-model="data.securityMode" label="ENCRYPTION">加密</el-radio> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row :gutter="20"> |
|||
<el-col :span="12"> |
|||
<el-form-item label="上报时间" prop="report.type" style="margin-bottom: 0 !important"> |
|||
<el-radio-group v-model="data.report.type"> |
|||
<el-radio label="POINT">定时上报</el-radio> |
|||
<el-radio label="CYCLE">周期上报(分钟)</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<!-- 定时上报输入--> |
|||
<el-row v-if="data.report.type === 'POINT'" :gutter="20"> |
|||
<el-col v-for="(item, index) in data.report.timePoints" :key="index" :lg="6" :md="12" :sm="24" :xl="4"> |
|||
<el-form-item prop="report.timePoints" style="margin-bottom: 10px !important"> |
|||
<el-time-picker v-model="data.report.timePoints[index]" :placeholder="`上报时间点${index + 1}`" format="HH:mm"></el-time-picker> |
|||
<el-button circle type="text" @click="removeTimePoint(index)"> |
|||
<i class="el-icon-circle-close text-lg text-red-600"></i> |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col v-if="!data.report.timePoints || data.report.timePoints.length < 6" :lg="6" :md="8" :sm="12" :xl="4"> |
|||
<el-button plain @click="addTimePoints"> |
|||
<i class="el-icon-plus px-6"></i> |
|||
</el-button> |
|||
</el-col> |
|||
</el-row> |
|||
<!--周期上报输入--> |
|||
<el-row v-else :gutter="20"> |
|||
<el-col :span="12"> |
|||
<el-form-item prop="report.cycle"> |
|||
<el-input-number v-model="data.report.cycle" :max="1440" :min="10" step="10" /> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row :gutter="20" class="mt-5 pl-2"> |
|||
<el-form-item> |
|||
<el-button type="primary" @click="onSubmit">提交</el-button> |
|||
<el-button @click="onReset">重置</el-button> |
|||
</el-form-item> |
|||
</el-row> |
|||
</el-form> |
|||
|
|||
<!-- 刷新下发按钮 --> |
|||
<Refresh @refresh="onSearch(currentDeviceId, 1)" /> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, defineEmits, defineProps, ref, watch } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import cloneDeep from 'lodash/cloneDeep'; |
|||
import isDate from 'lodash/isDate'; |
|||
import { createConfigFunction, getConfigAppliedFunction } from 'apis'; |
|||
import Refresh from 'components/config/refresh.vue'; |
|||
import StatusAndLastTime from 'components/config/status-and-last-time.vue'; |
|||
import { functionConfig } from '@/config/config'; |
|||
|
|||
const data = ref(functionConfig); |
|||
|
|||
const functionForm = ref(null); // form |
|||
const store = useStore(); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); |
|||
const emit = defineEmits(['status']); |
|||
const props = defineProps({ activeName: String }); |
|||
|
|||
/** |
|||
* 格式化时间 |
|||
*/ |
|||
const formatTime = date => { |
|||
if (!isDate(date)) return ''; |
|||
const hour = date.getHours(); |
|||
const minute = date.getMinutes(); |
|||
return `${hour}:${minute}`; |
|||
}; |
|||
|
|||
/** |
|||
* 查询网络参数配置 |
|||
* @param {string} deviceId 设备id |
|||
* @param {number} type 0数据库查询 1刷新查询 |
|||
*/ |
|||
async function onSearch(deviceId, type = 0) { |
|||
const params = { |
|||
deviceId, |
|||
type, |
|||
}; |
|||
const resData = await getConfigAppliedFunction(params); |
|||
if (resData && resData.time) { |
|||
// 格式化ms为日期格式 |
|||
resData.time = new Date(+resData.time); |
|||
} |
|||
if (resData && resData.status) { |
|||
// 设置 设置状态 |
|||
emit('status', resData.status); |
|||
} else { |
|||
emit('status', ''); |
|||
} |
|||
|
|||
if (resData && resData.report.type === 'POINT') { |
|||
const { timePoints } = resData.report; |
|||
// 如果是按照时间点上传的 |
|||
if (timePoints && timePoints.length) { |
|||
for (let i = 0; i < timePoints.length; i++) { |
|||
const date = new Date().toLocaleDateString(); |
|||
const item = resData.report.timePoints[i]; |
|||
const reg = /^\d{1,2}:\d{1,2}$/; |
|||
if (item && reg.test(item)) { |
|||
resData.report.timePoints[i] = new Date(`${date} ${item}`); |
|||
} else { |
|||
resData.report.timePoints[i] = ''; |
|||
} |
|||
} |
|||
} else { |
|||
resData.report = ['']; |
|||
} |
|||
} |
|||
data.value = resData || functionConfig; |
|||
} |
|||
|
|||
watch( |
|||
currentDeviceId, |
|||
newDeviceId => { |
|||
newDeviceId && props.activeName === 'function' && onSearch(newDeviceId); |
|||
}, |
|||
{ immediate: true }, |
|||
); |
|||
|
|||
// 提交表单 |
|||
const onSubmit = () => { |
|||
functionForm.value.validate(async () => { |
|||
try { |
|||
if (!currentDeviceId.value) { |
|||
return ElMessage.error('请选择站点'); |
|||
} |
|||
const param = cloneDeep({ |
|||
...data.value, |
|||
deviceId: currentDeviceId.value, |
|||
}); |
|||
param.time = new Date(param.time).getTime(); |
|||
if (param.report.type === 'POINT') { |
|||
// 按时间点上报 格式化时间格式 |
|||
const points = [...param.report.timePoints]; |
|||
|
|||
for (let i = 0; i < points.length; i++) { |
|||
if (points[i]) { |
|||
points[i] = formatTime(points[i]); |
|||
} |
|||
} |
|||
|
|||
const validated = validateTimePoints(points); |
|||
if (!validated) { |
|||
ElMessage.error('上报时间点不能重复'); |
|||
return; |
|||
} |
|||
|
|||
param.report.timePoints = points; |
|||
} |
|||
await createConfigFunction(param); |
|||
ElMessage.success('提交成功'); |
|||
} catch (error) { |
|||
throw new Error(error); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// 重置表单 |
|||
const onReset = () => { |
|||
functionForm.value.resetFields(); |
|||
}; |
|||
|
|||
// currentDeviceId && onSearch(currentDeviceId.value); |
|||
// 添加时间上报时间点输入框 |
|||
function addTimePoints() { |
|||
const { timePoints } = data.value.report; |
|||
console.log(data.value.report); |
|||
if (!timePoints || !timePoints.length) { |
|||
data.value.report.timePoints = ['']; |
|||
} else { |
|||
if (timePoints.length >= 6) return; |
|||
data.value.report.timePoints.push(''); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 验证上报时间点是否重复 |
|||
* @param {array|null} points ["时:分"] |
|||
* @returns {boolean} |
|||
*/ |
|||
function validateTimePoints(points) { |
|||
if (!points || !points.length) return true; |
|||
const obj = {}; |
|||
points.forEach(item => { |
|||
if (obj[item]) { |
|||
obj[item] += 1; |
|||
} else { |
|||
obj[item] = 1; |
|||
} |
|||
}); |
|||
// eslint-disable-next-line no-restricted-syntax |
|||
for (const key in obj) { |
|||
if (obj[key] > 1) return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 移除上报时间点 |
|||
* @param {number} index 索引 |
|||
*/ |
|||
function removeTimePoint(index) { |
|||
data.value.report.timePoints.splice(index, 1); |
|||
} |
|||
</script> |
@ -1,65 +0,0 @@ |
|||
<template> |
|||
<el-row :gutter="10"> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>上报IP地址1:</span> |
|||
<span>{{ data.ip1 }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>上报端口号1:</span> |
|||
<span>{{ data.port1 }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>上报IP地址2:</span> |
|||
<span>{{ data.ip2 }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>上报端口号2:</span> |
|||
<span>{{ data.port2 }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>上报IP地址3:</span> |
|||
<span>{{ data.ip3 }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>上报端口号3:</span> |
|||
<span>{{ data.port3 }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>上报IP地址4:</span> |
|||
<span>{{ data.ip4 }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>上报端口号4:</span> |
|||
<span>{{ data.port4 }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>备用ip地址:</span> |
|||
<span>{{ data.ipBackup }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>备用端口号:</span> |
|||
<span>{{ data.portBackup }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>用户名:</span> |
|||
<span>{{ data.account }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>密码:</span> |
|||
<span>{{ data.password }}</span> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :xl="6" :xs="24" class="text-sm text-gray-500 py-3"> |
|||
<span>APN:</span> |
|||
<span>{{ data.apn }}</span> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<!-- 刷新下发按钮 --> |
|||
<!-- <Refresh @refresh="onSearch(currentDeviceI, 1)" /> --> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { defineProps } from 'vue'; |
|||
|
|||
defineProps({ data: Object }); |
|||
</script> |
@ -1,150 +0,0 @@ |
|||
<template> |
|||
<el-form ref="networkForm" :model="data" label-position="top"> |
|||
<StatusAndLastTime v-if="data.settingStatus" :setting-status="data.settingStatus" :setting-time="+data.settingTime" /> |
|||
|
|||
<el-row :gutter="20"> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="上报IP1地址" prop="ip1"> |
|||
<el-input v-model="data.ip1"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="上报端口号1" prop="port1"> |
|||
<el-input v-model="data.port1"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="上报IP2地址" prop="ip2"> |
|||
<el-input v-model="data.ip2"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="上报端口号2" prop="port2"> |
|||
<el-input v-model="data.port2"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="上报IP3地址" prop="ip3"> |
|||
<el-input v-model="data.ip3"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="上报端口号3" prop="port3"> |
|||
<el-input v-model="data.port3"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="备用ip地址" prop="ipBackup"> |
|||
<el-input v-model="data.ipBackup"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="备用端口号" prop="portBackup"> |
|||
<el-input v-model="data.portBackup"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="用户名" prop="account"> |
|||
<el-input v-model="data.account"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="密码" prop="password"> |
|||
<el-input v-model="data.password"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="APN" prop="apn"> |
|||
<el-input v-model="data.apn"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row :gutter="20" class="mt-5 pl-2"> |
|||
<el-form-item> |
|||
<el-button type="primary" @click="onSubmit">提交</el-button> |
|||
<el-button @click="onReset">重置</el-button> |
|||
</el-form-item> |
|||
</el-row> |
|||
</el-form> |
|||
|
|||
<!-- 刷新下发按钮 --> |
|||
<Refresh @refresh="onSearch(currentDeviceId, 1)" /> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, defineEmits, defineProps, ref, watch } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import { createConfigNetwork, getConfigAppliedNetwork } from 'apis'; |
|||
import Refresh from 'components/config/refresh.vue'; |
|||
import StatusAndLastTime from 'components/config/status-and-last-time.vue'; |
|||
import { networkConfig } from '@/config/config'; |
|||
|
|||
const data = ref(networkConfig); |
|||
const networkForm = ref(null); // form |
|||
const store = useStore(); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); |
|||
const emit = defineEmits(['status']); |
|||
const props = defineProps({ activeName: String }); |
|||
|
|||
/** |
|||
* 查询网络参数配置 |
|||
* @param {string} deviceId 设备id |
|||
* @param {number} type 0数据库查询 1刷新查询 |
|||
*/ |
|||
async function onSearch(deviceId, type = 0) { |
|||
try { |
|||
const params = { |
|||
deviceId, |
|||
type, |
|||
}; |
|||
const resData = await getConfigAppliedNetwork(params); |
|||
data.value = resData || networkConfig; |
|||
if (resData && resData.status) { |
|||
emit('status', resData.status); |
|||
} else { |
|||
emit('status', ''); |
|||
} |
|||
} catch (error) { |
|||
throw new Error(error); |
|||
} |
|||
} |
|||
|
|||
watch( |
|||
currentDeviceId, |
|||
newDeviceId => { |
|||
newDeviceId && props.activeName === 'network' && onSearch(newDeviceId); |
|||
}, |
|||
{ immediate: true }, |
|||
); |
|||
|
|||
// 提交表单 |
|||
const onSubmit = () => { |
|||
networkForm.value.validate(async () => { |
|||
const param = { |
|||
...data.value, |
|||
deviceId: currentDeviceId.value, |
|||
}; |
|||
try { |
|||
await createConfigNetwork(param); |
|||
ElMessage.success('提交成功'); |
|||
} catch (error) { |
|||
console.error('error: ', error); |
|||
ElMessage.error('提交失败, 请稍后重试'); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// 重置表单 |
|||
const onReset = () => { |
|||
networkForm.value.resetFields(); |
|||
}; |
|||
|
|||
// currentDeviceId && onSearch(currentDeviceId.value); |
|||
</script> |
@ -1,19 +0,0 @@ |
|||
<script setup> |
|||
import { defineEmits } from 'vue'; |
|||
|
|||
defineEmits(['refresh']); |
|||
</script> |
|||
|
|||
<template> |
|||
<el-tooltip class="refresh-btn shadow-md" effect="dark" content="刷新" placement="top-start"> |
|||
<el-button type="primary" icon="el-icon-refresh" circle @click="$emit('refresh')"></el-button> |
|||
</el-tooltip> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
.refresh-btn { |
|||
position: fixed; |
|||
right: 20px; |
|||
bottom: 20px; |
|||
} |
|||
</style> |
@ -1,19 +0,0 @@ |
|||
<template> |
|||
<el-row class="text-gray-500 text-sm my-2 flex items-center"> |
|||
<span class="mr-10"> |
|||
配置状态:<el-tag :type="PEND_TYPE[settingStatus].type">{{ PEND_TYPE[settingStatus].text }}</el-tag> |
|||
</span> |
|||
<span>最后配置时间:{{ dayjs(settingTime).format('YYYY-MM-DD HH:mm:ss') }}</span> |
|||
</el-row> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import dayjs from 'dayjs'; |
|||
import { defineProps } from 'vue'; |
|||
import { PEND_TYPE } from '@/config/config'; |
|||
|
|||
defineProps({ |
|||
settingStatus: String, |
|||
settingTime: Number, |
|||
}); |
|||
</script> |
@ -1,12 +0,0 @@ |
|||
<template> |
|||
<el-tag :type="PEND_TYPE[type].type"> |
|||
{{ PEND_TYPE[type].text }} |
|||
</el-tag> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { defineProps } from 'vue'; |
|||
import { PEND_TYPE } from '@/config/config'; |
|||
|
|||
defineProps({ type: String }); |
|||
</script> |
@ -1,193 +0,0 @@ |
|||
<template> |
|||
<el-form ref="deviceCreate" :model="data" :rules="deviceRules" label-position="top"> |
|||
<el-row :gutter="20"> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="设备ID(6位)" prop="deviceId"> |
|||
<el-input v-model="data.deviceId"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="站点名称" prop="address"> |
|||
<el-input v-model="data.address"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="产品类型" prop="type"> |
|||
<!-- <el-input v-model="data.securityMode"></el-input> --> |
|||
<el-radio v-model="data.type" label="IACD">大气腐蚀</el-radio> |
|||
<el-radio v-model="data.type" label="OTHER">其他</el-radio> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="设备完整ID" prop="deviceFullId"> |
|||
<el-input v-model="data.deviceFullId"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="地区" prop="area"> |
|||
<el-input v-model="data.area"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="联系人" prop="contact"> |
|||
<el-input v-model="data.contact"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="电话" prop="phone"> |
|||
<el-input v-model="data.phone"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="经度" prop="lon"> |
|||
<el-input v-model="data.lon"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="纬度" prop="lat"> |
|||
<el-input v-model="data.lat"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="负责人" prop="head"> |
|||
<el-input v-model="data.head"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="安装位置" prop="installLocation"> |
|||
<el-input v-model="data.installLocation"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="安装时间" prop="installTime"> |
|||
<el-date-picker v-model="data.installTime" placeholder="安装时间" style="width: 100%" type="datetime"></el-date-picker> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="正式运行时间" prop="runTime"> |
|||
<el-date-picker v-model="data.runTime" placeholder="正式运行时间" style="width: 100%" type="datetime"></el-date-picker> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="链路地址" prop="linkAddress"> |
|||
<el-input v-model="data.linkAddress"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="探头编号" prop="probNo"> |
|||
<el-input v-model="data.probNo"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="试样" prop="simple"> |
|||
<el-input v-model="data.simple"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="sim卡1" prop="sim1"> |
|||
<el-input v-model="data.sim1"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="设备朝向" prop="deviceDirection"> |
|||
<el-input v-model="data.deviceDirection"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="协议版本" prop="protocolVersion"> |
|||
<el-input v-model="data.protocolVersion"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="与主站后台联调情况" prop="joint"> |
|||
<el-input v-model="data.joint" type="textarea"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="备注" prop="remark"> |
|||
<el-input v-model="data.remark" type="textarea"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row> |
|||
<el-col :span="24"> |
|||
<el-form-item label="维修记录" prop="operationRecord"> |
|||
<el-input v-model="data.operationRecord" type="textarea"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row :gutter="20" class="mt-5 pl-2"> |
|||
<el-form-item> |
|||
<el-button type="primary" @click="onSubmit">提交</el-button> |
|||
<el-button @click="onReset">重置</el-button> |
|||
</el-form-item> |
|||
</el-row> |
|||
</el-form> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { defineEmits, reactive, ref } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { ElMessage, ElMessageBox } from 'element-plus'; |
|||
import { createDevice } from 'apis'; |
|||
import { deviceData, deviceRules } from '@/config/config'; |
|||
|
|||
const data = reactive(deviceData); |
|||
const deviceCreate = ref(null); // form |
|||
const store = useStore(); |
|||
const emit = defineEmits(['hide']); |
|||
|
|||
// 重置表单 |
|||
const onReset = () => { |
|||
deviceCreate.value.resetFields(); |
|||
}; |
|||
|
|||
// 提交表单 |
|||
const onSubmit = () => { |
|||
deviceCreate.value.validate(async valid => { |
|||
if (!valid) return; |
|||
if (data.installTime) { |
|||
data.installTime = new Date(data.installTime).getTime(); |
|||
} |
|||
if (data.runTime) { |
|||
data.runTime = new Date(data.runTime).getTime(); |
|||
} |
|||
try { |
|||
await createDevice({ ...data }); |
|||
store.commit('device/unshiftDevice', { ...data }); |
|||
ElMessageBox.confirm('添加成功,是否继续添加', '提示', { |
|||
confirmButtonText: '继续添加', |
|||
cancelButtonText: '关闭', |
|||
type: 'success', |
|||
}) |
|||
.then(() => { |
|||
// 确认继续添加 |
|||
onReset(); |
|||
}) |
|||
.catch(() => { |
|||
onReset(); |
|||
emit('on-hide'); |
|||
}); |
|||
await store.dispatch('device/getDevices'); // 更新站点列表 |
|||
} catch (error) { |
|||
ElMessage.error(error); |
|||
throw new Error(error); |
|||
} |
|||
}); |
|||
}; |
|||
</script> |
@ -1,13 +0,0 @@ |
|||
<template> |
|||
<el-dialog v-model="show" title="编辑设备信息" fullscreen="true" @close="$emit('toggle-mdoal')"> |
|||
<DeviceEdit v-if="show" :edit="true" @cancel="$emit('toggle-mdoal')" /> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { defineProps, defineEmits } from 'vue'; |
|||
import DeviceEdit from 'views/device-edit.vue'; |
|||
|
|||
defineProps({ show: Boolean }); |
|||
defineEmits(['toggle-mdoal']); |
|||
</script> |
@ -1,233 +0,0 @@ |
|||
<template> |
|||
<SearchBar :loading-search="loadingSearch" :show-command="true" :show-export="true" :show-pass-setting="true" @search="onSearch" /> |
|||
|
|||
<template v-if="data"> |
|||
<el-table :data="data" :max-height="contentHeight" border stripe style="width: 100%"> |
|||
<el-table-column align="center" fixed label="设备ID" min-width="80" prop="deviceId" /> |
|||
<el-table-column align="center" label="采集时间" min-width="170"> |
|||
<template #default="scope"> |
|||
{{ formatTime(+scope.row.time) }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="后台接收时间" min-width="170"> |
|||
<template #default="scope"> |
|||
{{ formatTime(+scope.row.createdAt) }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="锌腐蚀电流(nA)" min-width="130" prop="corrosion1" /> |
|||
<el-table-column align="center" label="铜腐蚀电流(nA)" min-width="130" prop="corrosion2" /> |
|||
<el-table-column align="center" label="铝腐蚀电流(nA)" min-width="130" prop="corrosion3" /> |
|||
<el-table-column align="center" label="钢腐蚀电流(nA)" min-width="130" prop="corrosion4" /> |
|||
<el-table-column align="center" label="环境温度(℃)" min-width="110" prop="environmentTemperature" /> |
|||
<el-table-column align="center" label="环境湿度(RH%)" min-width="130" prop="environmentHumidity" /> |
|||
<el-table-column align="center" label="SO2(ppb)" min-width="90" prop="so2" /> |
|||
<el-table-column align="center" label="盐分温度(℃)" min-width="110" prop="saltT" /> |
|||
<el-table-column align="center" label="盐分阻抗(Ω)" min-width="110" prop="saltR" /> |
|||
<el-table-column align="center" label="机箱温度(℃)" min-width="110" prop="deviceTemperature" /> |
|||
<el-table-column align="center" label="机箱湿度(RH%)" min-width="130" prop="deviceHumidity" /> |
|||
<el-table-column align="center" label="太阳能板电压(V)" min-width="140" prop="solarVoltage" /> |
|||
<el-table-column align="center" label="蓄电池电压(V)" min-width="120" prop="batteryVoltage" /> |
|||
<el-table-column align="center" label="电压百分比" min-width="94" prop="batteryVoltagePercentage" /> |
|||
<el-table-column align="center" label="剩余电量(mAH)" min-width="140" prop="batteryVoltageRemain" /> |
|||
<el-table-column align="center" label="消耗电量(mAH)" min-width="140" prop="batteryLoss" /> |
|||
<el-table-column align="center" label="ICCID" min-width="190" prop="iccid" /> |
|||
<el-table-column align="center" label="IMEI" min-width="150" prop="imei" /> |
|||
<el-table-column align="center" label="信号强度" min-width="80" prop="signal" /> |
|||
<el-table-column align="center" label="基站编号" min-width="130" prop="stationNo" /> |
|||
<el-table-column align="center" label="硬件版本" min-width="80" prop="hardwareVersion" /> |
|||
<el-table-column align="center" label="软件版本" min-width="80" prop="softwareVersion" /> |
|||
</el-table> |
|||
|
|||
<el-pagination |
|||
:current-page="page.page" |
|||
:default-page-size="50" |
|||
:page-count="page.count" |
|||
:page-size="page.size" |
|||
:page-sizes="[1, 10, 20, 50, 100]" |
|||
:total="page.total" |
|||
background |
|||
class="my-3 float-right" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
@current-change="onCurrentPageChange" |
|||
@size-change="onSizeChange" |
|||
@prev-click="onPrev" |
|||
@next-click="onNext" |
|||
></el-pagination> |
|||
</template> |
|||
|
|||
<el-badge v-if="missingData && missingData.length" :max="99" :value="missingData.length" class="pass-button animate-bounce"> |
|||
<div circle class="shadow-xl btn" type="primary" @click="showMissing = true"> |
|||
<i class="el-icon-upload"></i> |
|||
</div> |
|||
</el-badge> |
|||
|
|||
<el-dialog v-model="showMissing" custom-class="device-dialog" title="待补传列表" top="30px" width="80%"> |
|||
<MissingData :data="missingData" @on-success="onPassSuccess" /> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, onMounted, ref } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { getDatas } from 'apis'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import dayjs from 'dayjs'; |
|||
import SearchBar from 'components/history/search-bar-data.vue'; |
|||
import MissingData from 'components/history/missing-data.vue'; |
|||
|
|||
const search = ref({}); |
|||
const page = ref({ |
|||
page: 1, |
|||
size: 50, |
|||
}); |
|||
let timer = null; |
|||
const store = useStore(); |
|||
const token = computed(() => store.getters['user/token']); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
const contentHeight = ref(600); |
|||
const loadingSearch = ref(false); |
|||
const data = ref(null); |
|||
const showMissing = ref(false); |
|||
const missingData = ref([]); |
|||
|
|||
// 获取设备完整信息列表 |
|||
const getData = async () => { |
|||
try { |
|||
if (token && token.value) { |
|||
if (!currentDeviceId.value) { |
|||
return ElMessage.error('请选择站点'); |
|||
} |
|||
|
|||
const { date, missingIntervalInMs } = search.value; |
|||
if (!date || date.length !== 2) { |
|||
return ElMessage.error('请选择时间 '); |
|||
} |
|||
const params = { |
|||
deviceId: currentDeviceId.value, |
|||
gatheredDateRange: date, |
|||
paging: true, |
|||
missingIntervalInMs, |
|||
page: page.value.page, |
|||
size: page.value.size, |
|||
sort: [ |
|||
{ |
|||
col: 'time', |
|||
order: 'DESC', |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
loadingSearch.value = true; |
|||
const resData = await getDatas(params); |
|||
loadingSearch.value = false; |
|||
|
|||
data.value = resData.data; |
|||
page.value = resData.page; |
|||
timer && clearTimeout(timer); |
|||
timer = null; |
|||
|
|||
if (resData.missingData) { |
|||
missingData.value = [...resData.missingData]; |
|||
missingData.value.forEach(item => { |
|||
item.uuid = `${item.deviceId}-${item.startTime}-${item.endTime}`; |
|||
}); |
|||
} |
|||
} else { |
|||
timer = setTimeout(() => { |
|||
getData(); |
|||
}, 20); |
|||
} |
|||
} catch (error) { |
|||
ElMessage.error(error.message || '获取数据失败'); |
|||
console.log('error: ', error); |
|||
} |
|||
}; |
|||
|
|||
// getData(); |
|||
|
|||
// 设置表格高度 |
|||
onMounted(() => { |
|||
const winHeight = document.documentElement.clientHeight; |
|||
contentHeight.value = winHeight - 260; |
|||
}); |
|||
|
|||
/** |
|||
* 监听sear h信息 |
|||
* @param {object} payload search组件emi 的数据 |
|||
*/ |
|||
const onSearch = payload => { |
|||
search.value = { ...payload }; |
|||
getData(); |
|||
}; |
|||
/** |
|||
* 当前 码变化 |
|||
* 更新page 重新 取数据 |
|||
* @param {number} e 的页码 |
|||
*/ |
|||
const onCurrentPageChange = e => { |
|||
page.value.page = e; |
|||
getData(); |
|||
}; |
|||
|
|||
const onSizeChange = e => { |
|||
page.value.size = e; |
|||
getData(); |
|||
}; |
|||
|
|||
// 下一页 |
|||
const onNext = e => { |
|||
page.value.page = e + 1; |
|||
getData(); |
|||
}; |
|||
|
|||
// 上一页 |
|||
const onPrev = e => { |
|||
page.value.page = e - 1; |
|||
getData(); |
|||
}; |
|||
|
|||
/** |
|||
* 格式化时间 |
|||
* @param {number} time 时间戳 |
|||
* @returns {string} |
|||
*/ |
|||
function formatTime(time) { |
|||
return dayjs(new Date(time)).format('YYYY-MM-DD HH:mm:ss'); |
|||
} |
|||
|
|||
// 补传成功 标记当前记录 |
|||
function onPassSuccess(uuid) { |
|||
// eslint-disable-next-line no-restricted-syntax |
|||
for (const item of missingData.value) { |
|||
if (item.uuid === uuid) { |
|||
item.disabled = true; |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.device-dialog :deep(.el-dialog__body) { |
|||
padding-top: 0 !important; |
|||
} |
|||
|
|||
.pass-button { |
|||
z-index: 999; |
|||
position: fixed; |
|||
right: 30px; |
|||
bottom: 40px; |
|||
} |
|||
|
|||
.pass-button .btn { |
|||
width: 56px; |
|||
height: 56px; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background-color: #409eff; |
|||
border-radius: 50%; |
|||
color: #fff; |
|||
font-size: 26px; |
|||
cursor: pointer; |
|||
} |
|||
</style> |
@ -1,42 +0,0 @@ |
|||
<template> |
|||
<template v-if="data"> |
|||
<el-table :data="data" :max-height="contentHeight" border class="mt-6" stripe style="width: 100%"> |
|||
<el-table-column align="center" label="设备ID" min-min-width="100" prop="deviceId" /> |
|||
<el-table-column align="center" label="开始时间" min-width="200"> |
|||
<template #default="scope"> |
|||
{{ dayjs(new Date(+scope.row.startTime)).format('YYYY-MM-DD HH:mm:ss') }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="截止时间" min-width="200"> |
|||
<template #default="scope"> |
|||
{{ dayjs(new Date(+scope.row.endTime)).format('YYYY-MM-DD HH:mm:ss') }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" header-align="center" label="下发状态" min-width="150"> |
|||
<template #default="scope"> |
|||
<StatusTagPending :type="scope.row.status" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" header-align="center" label="类型" min-width="150"> |
|||
<template #default="scope"> |
|||
<span v-if="scope.row && scope.row.type && PENDING_TYPE[scope.row.type]">{{ PENDING_TYPE[scope.row.type].text }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</template> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { defineProps, onMounted, ref } from 'vue'; |
|||
import dayjs from 'dayjs'; |
|||
import { PENDING_TYPE } from '@/config/config'; |
|||
|
|||
defineProps({ data: Object }); |
|||
|
|||
const contentHeight = ref(600); |
|||
|
|||
onMounted(() => { |
|||
const winHeight = document.documentElement.clientHeight; |
|||
contentHeight.value = winHeight - 150; |
|||
}); |
|||
</script> |
@ -1,35 +0,0 @@ |
|||
<template> |
|||
<SearchHistoryLog @search="getData" /> |
|||
|
|||
<TableHistoryLog :data="data" /> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, ref } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { getSendHistory } from 'apis'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import SearchHistoryLog from 'components/history/search-history-log.vue'; |
|||
import TableHistoryLog from 'components/history/history-log-table.vue'; |
|||
|
|||
const store = useStore(); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
const data = ref([]); |
|||
|
|||
async function getData( |
|||
params = { |
|||
deviceId: currentDeviceId.value, |
|||
status: '', |
|||
type: '', |
|||
}, |
|||
) { |
|||
try { |
|||
const resData = await getSendHistory(params); |
|||
data.value = resData ? [...resData] : []; |
|||
} catch (error) { |
|||
ElMessage.error(error.message || '查询失败'); |
|||
} |
|||
} |
|||
|
|||
getData(); |
|||
</script> |
@ -1,162 +0,0 @@ |
|||
<template> |
|||
<SearchBar :show-export="true" :show-type-select="true" @search="onSearch" /> |
|||
|
|||
<template v-if="data"> |
|||
<el-table :data="data" :max-height="contentHeight" border stripe style="width: 100%"> |
|||
<el-table-column align="center" fixed label="设备ID" min-width="80" prop="deviceId" /> |
|||
<el-table-column align="center" label="采集时间" min-width="170"> |
|||
<template #default="scope"> |
|||
{{ formatTime(+scope.row.time) }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="后台接收时间" min-width="170"> |
|||
<template #default="scope"> |
|||
{{ formatTime(+scope.row.createdAt) }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="锌腐蚀电流(nA)" min-width="130" prop="corrosion1" /> |
|||
<el-table-column align="center" label="铜腐蚀电流(nA)" min-width="130" prop="corrosion2" /> |
|||
<el-table-column align="center" label="铝腐蚀电流(nA)" min-width="130" prop="corrosion3" /> |
|||
<el-table-column align="center" label="钢腐蚀电流(nA)" min-width="130" prop="corrosion4" /> |
|||
<el-table-column align="center" label="环境温度(℃)" min-width="110" prop="environmentTemperature" /> |
|||
<el-table-column align="center" label="环境湿度(RH%)" min-width="130" prop="environmentHumidity" /> |
|||
<el-table-column align="center" label="SO2(ppb)" min-width="90" prop="so2" /> |
|||
<el-table-column align="center" label="盐分温度(℃)" min-width="110" prop="saltT" /> |
|||
<el-table-column align="center" label="盐分阻抗(Ω)" min-width="110" prop="saltR" /> |
|||
<el-table-column align="center" label="太阳能板电压(V)" min-width="140" prop="solarVoltage" /> |
|||
<el-table-column align="center" label="蓄电池电压(V)" min-width="120" prop="batteryVoltage" /> |
|||
<el-table-column align="center" label="机箱温度(℃)" min-width="110" prop="deviceTemperature" /> |
|||
<el-table-column align="center" label="机箱湿度(RH%)" min-width="130" prop="deviceHumidity" /> |
|||
</el-table> |
|||
|
|||
<el-pagination |
|||
:current-page="page.page" |
|||
:default-page-size="50" |
|||
:page-count="page.count" |
|||
:page-size="page.size" |
|||
:page-sizes="[1, 10, 20, 50, 100]" |
|||
:total="page.total" |
|||
background |
|||
class="my-3 float-right" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
@current-change="onCurrentPageChange" |
|||
@size-change="onSizeChange" |
|||
@prev-click="onPrev" |
|||
@next-click="onNext" |
|||
></el-pagination> |
|||
</template> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { getDatas } from 'apis'; |
|||
import SearchBar from 'components/history/search-bar-data.vue'; |
|||
import dayjs from 'dayjs'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import { computed, onMounted, ref } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
|
|||
const search = ref({}); |
|||
const page = ref({ |
|||
page: 1, |
|||
size: 50, |
|||
}); |
|||
let timer = null; |
|||
const store = useStore(); |
|||
const token = computed(() => store.getters['user/token']); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
const contentHeight = ref(600); |
|||
const data = ref(null); |
|||
|
|||
// 获取设备完整信息列表 |
|||
const getData = async () => { |
|||
try { |
|||
if (token && token.value) { |
|||
if (!currentDeviceId.value) { |
|||
return ElMessage.error('请选择站点'); |
|||
} |
|||
|
|||
const options = { ...search.value }; |
|||
const date = options && options.date ? options.date : []; |
|||
console.log(date); |
|||
if (!date || date.length !== 2) { |
|||
return ElMessage.error('请选择时间 '); |
|||
} |
|||
const params = { |
|||
deviceId: currentDeviceId.value, |
|||
gatheredDateRange: date, |
|||
paging: true, |
|||
page: page.value.page, |
|||
size: page.value.size, |
|||
sort: [ |
|||
{ |
|||
col: 'time', |
|||
order: 'DESC', |
|||
}, |
|||
], |
|||
}; |
|||
const resData = await getDatas(params); |
|||
data.value = resData.data; |
|||
page.value = resData.page; |
|||
timer && clearTimeout(timer); |
|||
timer = null; |
|||
} else { |
|||
timer = setTimeout(() => { |
|||
getData(); |
|||
}, 20); |
|||
} |
|||
} catch (error) { |
|||
ElMessage.error(error.message || '获取数据失败'); |
|||
console.log('error: ', error); |
|||
} |
|||
}; |
|||
|
|||
// 设置表格高度 |
|||
onMounted(() => { |
|||
const winHeight = document.documentElement.clientHeight; |
|||
contentHeight.value = winHeight - 250; |
|||
}); |
|||
|
|||
/** |
|||
* 监听sear h信息 |
|||
* @param {object} payload search组件emi 的数据 |
|||
*/ |
|||
const onSearch = payload => { |
|||
search.value = { ...payload }; |
|||
getData(); |
|||
}; |
|||
/** |
|||
* 当前 码变化 |
|||
* 更新page 重新 取数据 |
|||
* @param {number} e 的页码 |
|||
*/ |
|||
const onCurrentPageChange = e => { |
|||
page.value.page = e; |
|||
getData(); |
|||
}; |
|||
|
|||
const onSizeChange = e => { |
|||
page.value.size = e; |
|||
getData(); |
|||
}; |
|||
|
|||
// 下一页 |
|||
const onNext = e => { |
|||
page.value.page = e + 1; |
|||
getData(); |
|||
}; |
|||
|
|||
// 上一页 |
|||
const onPrev = e => { |
|||
page.value.page = e - 1; |
|||
getData(); |
|||
}; |
|||
|
|||
/** |
|||
* 格式化时间 |
|||
* @param {number} time 时间戳 |
|||
* @returns {string} |
|||
*/ |
|||
function formatTime(time) { |
|||
return dayjs(new Date(time)).format('YYYY-MM-DD HH:mm:ss'); |
|||
} |
|||
</script> |
@ -1,48 +0,0 @@ |
|||
<template> |
|||
<el-table :data="data" :max-height="contentHeight" border class="mt-6" stripe style="width: 100%; margin-top: 0"> |
|||
<el-table-column align="center" label="设备ID" min-width="100" prop="deviceId" /> |
|||
<el-table-column align="center" label="起始时间" min-width="200"> |
|||
<template #default="scope"> |
|||
{{ dayjs(new Date(+scope.row.startTime)).format('YYYY-MM-DD HH:mm:ss') }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="截止时间" min-width="200"> |
|||
<template #default="scope"> |
|||
{{ dayjs(new Date(+scope.row.endTime)).format('YYYY-MM-DD HH:mm:ss') }} |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column align="center" header-align="center" label="操作" min-width="100"> |
|||
<template #default="scope"> |
|||
<el-button :disabled="scope.row.disabled" plain size="small" type="success" @click="handlePass(scope.row)">补传 </el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { defineProps, defineEmits, ref } from 'vue'; |
|||
import dayjs from 'dayjs'; |
|||
import { sendCommand } from 'apis'; |
|||
import { ElMessage } from 'element-plus'; |
|||
|
|||
defineProps({ data: Object }); |
|||
const emit = defineEmits(['on-success']); |
|||
|
|||
const contentHeight = ref(600); |
|||
|
|||
/** |
|||
* 发送补传指令 |
|||
* @param { Object } item |
|||
* @returns {Promise<void>} |
|||
*/ |
|||
async function handlePass(item) { |
|||
try { |
|||
await sendCommand({ ...item }); |
|||
ElMessage.success('发送成功'); |
|||
emit('on-success', item.uuid); |
|||
} catch (error) { |
|||
ElMessage.error(error.message || '发送补传指令失败'); |
|||
} |
|||
} |
|||
</script> |
@ -1,148 +0,0 @@ |
|||
<template> |
|||
<el-form ref="searchDeviceForm" :inline="true" :model="searchDevice"> |
|||
<el-form-item label="选择站点:"> |
|||
<el-select v-model="searchDevice.deviceId" placeholder="请选择站点" @change="change"> |
|||
<!-- <el-option label="全部" value></el-option> --> |
|||
<el-option |
|||
v-for="item in devices" |
|||
:key="item.deviceId" |
|||
:label="`${item.address}-${item.deviceId}`" |
|||
:value="item.deviceId" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="起始时间:"> |
|||
<el-date-picker |
|||
v-model="searchDevice.date[0]" |
|||
format="YYYY-MM-DD HH:mm" |
|||
placeholder="起始时间" |
|||
style="width: 180px" |
|||
type="datetime" |
|||
></el-date-picker> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="截止时间:"> |
|||
<el-date-picker |
|||
v-model="searchDevice.date[1]" |
|||
format="YYYY-MM-DD HH:mm" |
|||
placeholder="截止时间" |
|||
style="width: 180px" |
|||
type="datetime" |
|||
></el-date-picker> |
|||
</el-form-item> |
|||
|
|||
<el-form-item v-if="showPassSetting" label="补传间隔(h):"> |
|||
<el-input-number v-model="searchDevice.missingIntervalInMs" :min="0" controls-position="right" style="width: 100px" /> |
|||
</el-form-item> |
|||
|
|||
<el-form-item> |
|||
<template v-if="showCommand"> |
|||
<el-button type="primary" @click="onSubmit">查询历史数据</el-button> |
|||
<!-- <el-button type="warning" @click="onSend('EVENT')">查询历史事件</el-button>--> |
|||
</template> |
|||
<el-button v-else :loading="loadingSearch" type="primary" @click="onSubmit"> |
|||
<i class="el-icon-search mr-2"></i> |
|||
查询 |
|||
</el-button> |
|||
</el-form-item> |
|||
|
|||
<el-form-item v-if="showExport"> |
|||
<el-button :loading="loadingExport" type="success" @click="onExport"> |
|||
<i class="el-icon-download mr-2"></i> |
|||
导出 |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { exportHistory } from 'apis'; |
|||
import dayjs from 'dayjs'; |
|||
import { computed, defineEmits, defineProps, reactive, ref, watch } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
|
|||
const emit = defineEmits(['search']); |
|||
const searchDevice = reactive({ |
|||
deviceId: '', |
|||
date: [dayjs().subtract(7, 'day').startOf('day'), new Date()], |
|||
missingIntervalInMs: 1, |
|||
}); |
|||
const searchDeviceForm = ref(null); // form |
|||
const store = useStore(); |
|||
const devices = computed(() => store.state.device.devices); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
const loadingExport = ref(false); |
|||
|
|||
defineProps({ |
|||
showExport: Boolean, |
|||
loadingSearch: Boolean, |
|||
showCommand: Boolean, |
|||
showPassSetting: Boolean, |
|||
}); |
|||
|
|||
// 监听currentDeviceId |
|||
watch( |
|||
[searchDeviceForm, currentDeviceId], |
|||
([newForm, newDeviceId]) => { |
|||
if (newDeviceId && newForm) { |
|||
searchDevice.deviceId !== newDeviceId && (searchDevice.deviceId = newDeviceId); |
|||
onSubmit(); |
|||
} |
|||
}, |
|||
{ immediate: true }, |
|||
); |
|||
|
|||
const change = e => { |
|||
store.commit('device/setCurrentDeviceId', e); |
|||
}; |
|||
|
|||
// 生成查询参数 |
|||
function generateParams() { |
|||
const { deviceId, date, missingIntervalInMs } = searchDevice; |
|||
const params = { |
|||
deviceId, |
|||
missingIntervalInMs: missingIntervalInMs * 60 * 60 * 1000, |
|||
}; |
|||
|
|||
if (date) { |
|||
const start = +dayjs(date[0]).format('x'); |
|||
const end = +dayjs(date[1]).format('x'); |
|||
params.date = [start, end]; |
|||
} |
|||
|
|||
return params; |
|||
} |
|||
|
|||
// 提交 |
|||
function onSubmit() { |
|||
searchDeviceForm.value.validate(valid => { |
|||
if (valid) { |
|||
const params = generateParams(); |
|||
emit('search', params); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// 导出 |
|||
async function onExport() { |
|||
try { |
|||
loadingExport.value = true; |
|||
const params = generateParams(); |
|||
params.sort = [ |
|||
{ |
|||
col: 'time', |
|||
order: 'DESC', |
|||
}, |
|||
]; |
|||
params.paging = false; |
|||
params.gatheredDateRange = params.date; |
|||
const resData = await exportHistory(params); |
|||
loadingExport.value = false; |
|||
resData && (window.location.href = resData); |
|||
} catch (error) { |
|||
loadingExport.value = false; |
|||
throw new Error(error); |
|||
} |
|||
} |
|||
</script> |
@ -1,77 +0,0 @@ |
|||
<template> |
|||
<el-form ref="searchHistoryLogForm" :inline="true" :model="searchDevice"> |
|||
<el-form-item label="选择站点"> |
|||
<el-select v-model="searchDevice.deviceId" placeholder="请选择站点" @change="change"> |
|||
<el-option label="全部" value></el-option> |
|||
<el-option |
|||
v-for="item in devices" |
|||
:key="item.deviceId" |
|||
:label="`${item.address}-${item.deviceId}`" |
|||
:value="item.deviceId" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="类型"> |
|||
<el-select v-model="searchDevice.type" placeholder="选择查询类型"> |
|||
<el-option label="全部" value=""></el-option> |
|||
<el-option label="事件上报" value="EVENT"></el-option> |
|||
<el-option label="业务上报" value="DATA"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="状态"> |
|||
<el-select v-model="searchDevice.status" placeholder="选择查询类型"> |
|||
<el-option label="全部" value=""></el-option> |
|||
<el-option label="待下发" value="PENDING"></el-option> |
|||
<el-option label="下发成功" value="SUCCESS"></el-option> |
|||
<el-option label="下发失败" value="FAIL"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
|
|||
<el-form-item> |
|||
<el-button type="primary" @click="onSubmit"> |
|||
<i class="el-icon-search mr-2"></i> |
|||
查询 |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, defineEmits, reactive, ref, watch } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
|
|||
const emit = defineEmits(['search']); |
|||
const searchDevice = reactive({ |
|||
deviceId: '', |
|||
type: '', |
|||
status: '', |
|||
}); |
|||
const searchHistoryLogForm = ref(null); // form |
|||
const store = useStore(); |
|||
const devices = computed(() => store.state.device.devices); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
|
|||
// 监听currentDeviceId |
|||
watch( |
|||
() => currentDeviceId.value, |
|||
newValue => { |
|||
if (newValue) { |
|||
searchDevice.deviceId !== newValue && (searchDevice.deviceId = newValue); |
|||
} |
|||
}, |
|||
{ immediate: true }, |
|||
); |
|||
|
|||
const change = e => { |
|||
store.commit('device/setCurrentDeviceId', e); |
|||
}; |
|||
|
|||
// 提交 |
|||
const onSubmit = () => { |
|||
searchHistoryLogForm.value.validate(() => { |
|||
emit('search', { ...searchDevice }); |
|||
}); |
|||
}; |
|||
</script> |
@ -1,23 +0,0 @@ |
|||
<script setup> |
|||
import { computed } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
|
|||
const store = useStore(); |
|||
const toggleCollapse = () => { |
|||
console.log('Toggle Collapse'); |
|||
store.commit('toggleCollapse'); |
|||
}; |
|||
const menu = computed(() => store.state.menu); |
|||
</script> |
|||
|
|||
<template> |
|||
<h1 class="text-lg font-medium py-3 px-6 shadow"> |
|||
<i |
|||
class="el-icon-guide mr-2" |
|||
:class="{ 'text-gray-800': !menu.collapse, 'text-gray-400': menu.collapse }" |
|||
@click="toggleCollapse" |
|||
v-if="menu.show" |
|||
></i> |
|||
{{ $route.meta.title || '智能大气腐蚀监测平台' }} |
|||
</h1> |
|||
</template> |
@ -1,49 +0,0 @@ |
|||
<template> |
|||
<div id="device-overview-container" style="height: 354px"></div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, onMounted, ref, watchEffect } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import isEmpty from 'lodash/isEmpty'; |
|||
import { generateChartOption } from 'utils/overview'; |
|||
import * as echarts from 'echarts/core'; |
|||
import { TitleComponent, TooltipComponent } from 'echarts/components'; |
|||
import { PieChart } from 'echarts/charts'; |
|||
import { LabelLayout } from 'echarts/features'; |
|||
import { CanvasRenderer } from 'echarts/renderers'; |
|||
|
|||
echarts.use([TitleComponent, TooltipComponent, PieChart, CanvasRenderer, LabelLayout]); |
|||
|
|||
const store = useStore(); |
|||
const count = computed(() => store.state.device.count); |
|||
const isMounted = ref(false); |
|||
let myChart = null; |
|||
|
|||
watchEffect(() => { |
|||
if (isEmpty(count.value) || !isMounted.value) return; |
|||
if (!myChart) { |
|||
initChart(); |
|||
render(count.value); |
|||
} else { |
|||
render(count.value); |
|||
} |
|||
}); |
|||
|
|||
// 初始化chart |
|||
function initChart() { |
|||
const chartDom = document.getElementById('device-overview-container'); |
|||
myChart = echarts.init(chartDom); |
|||
} |
|||
|
|||
// 渲染chart |
|||
function render(rawCount) { |
|||
const option = generateChartOption(rawCount); |
|||
option && myChart.setOption(option); |
|||
} |
|||
|
|||
onMounted(() => { |
|||
initChart(); |
|||
isMounted.value = true; |
|||
}); |
|||
</script> |
@ -1,93 +0,0 @@ |
|||
<template> |
|||
<el-card class="pb-4" shadow="hover"> |
|||
<el-row :gutter="20"> |
|||
<el-col :span="12"> |
|||
<div class="text-sm text-gray-400">设备总数</div> |
|||
<div class="text-gray-500 text-4xl mt-1 mb-3">{{ count.total }}</div> |
|||
<div class="line-wrap bg-blue-50"> |
|||
<div :style="{ width: `${countPercent.total}` }" class="line bg-blue-300"></div> |
|||
</div> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<div class="text-sm text-gray-400">正常数量</div> |
|||
<div class="text-gray-500 text-4xl mt-1 mb-3">{{ count.normal }}</div> |
|||
<div class="line-wrap bg-lime-50"> |
|||
<div :style="{ width: `${countPercent.normal}` }" class="line bg-lime-300"></div> |
|||
</div> |
|||
</el-col> |
|||
<el-col :span="12" class="mt-10"> |
|||
<div class="text-sm text-gray-400">离线数量</div> |
|||
<div class="text-gray-500 text-4xl mt-1 mb-3">{{ count.offline }}</div> |
|||
<div class="line-wrap bg-gray-50"> |
|||
<div :style="{ width: `${countPercent.offline}` }" class="line bg-gray-300"></div> |
|||
</div> |
|||
</el-col> |
|||
<el-col :span="12" class="mt-10"> |
|||
<div class="text-sm text-gray-400">报警数量</div> |
|||
<div class="text-gray-500 text-4xl mt-1 mb-3">{{ count.warning }}</div> |
|||
<div class="line-wrap bg-yellow-50"> |
|||
<div :style="{ width: `${countPercent.warning}` }" class="line bg-yellow-300"></div> |
|||
</div> |
|||
</el-col> |
|||
<el-col :span="12" class="mt-10"> |
|||
<div class="text-sm text-gray-400">在线数量</div> |
|||
<div class="text-gray-500 text-4xl mt-1 mb-3">{{ count.online }}</div> |
|||
<div class="line-wrap bg-green-50"> |
|||
<div :style="{ width: `${countPercent.online}` }" class="line bg-green-300"></div> |
|||
</div> |
|||
</el-col> |
|||
<el-col :span="12" class="mt-10"> |
|||
<div class="text-sm text-gray-400">故障数量</div> |
|||
<div class="text-gray-500 text-4xl mt-1 mb-3">{{ count.fault }}</div> |
|||
<div class="line-wrap bg-red-50"> |
|||
<div :style="{ width: `${countPercent.fault}` }" class="line bg-red-300"></div> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</el-card> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
|
|||
const store = useStore(); |
|||
const count = computed(() => store.state.device.count); |
|||
const countPercent = computed(() => { |
|||
return { |
|||
online: computeWidth(count.value.online), |
|||
offline: computeWidth(count.value.offline), |
|||
warning: computeWidth(count.value.warning), |
|||
fault: computeWidth(count.value.fault), |
|||
total: computeWidth(count.value.total), |
|||
normal: computeWidth(count.value.normal), |
|||
}; |
|||
}); |
|||
|
|||
/** |
|||
* 计算宽度百分比 |
|||
* @param {number} itemCount |
|||
* @returns {string} |
|||
*/ |
|||
function computeWidth(itemCount) { |
|||
const percent = (itemCount * 100) / count.value.total; |
|||
return `${percent}%`; |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.line-wrap { |
|||
position: relative; |
|||
width: 60%; |
|||
height: 10px; |
|||
border-radius: 5px; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.line { |
|||
position: absolute; |
|||
left: 0; |
|||
top: 0; |
|||
height: 100%; |
|||
} |
|||
</style> |
@ -1,112 +0,0 @@ |
|||
<template> |
|||
<template v-if="devicesAll && devicesAll.data"> |
|||
<el-table :data="devicesAll.data" :max-height="contentHeight" border class="mt-6" stripe style="width: 100%"> |
|||
<el-table-column align="center" label="站点名称" min-width="150" prop="address" /> |
|||
<el-table-column align="center" label="设备ID" min-width="100" prop="deviceId" /> |
|||
<el-table-column align="center" label="注册时间" min-width="200"> |
|||
<template #default="scope"> |
|||
{{ dayjs(new Date(+scope.row.signupTime)).format('YYYY-MM-DD HH:mm:ss') }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="最近上传时间" min-width="200"> |
|||
<template #default="scope"> |
|||
{{ dayjs(new Date(+scope.row.lastUploadTime)).format('YYYY-MM-DD HH:mm:ss') }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" header-align="center" label="设备状态" min-width="150"> |
|||
<template #default="scope"> |
|||
<el-tag v-if="scope.row.status" :color="STATUS_COLOR[scope.row.status].color" style="color: #fff"> |
|||
{{ STATUS_COLOR[scope.row.status].text }} |
|||
</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" header-align="center" label="产品类型" min-width="150"> |
|||
<template #default="scope"> |
|||
<span v-if="scope.row.type && ASDU_TYPE[scope.row.type]">{{ ASDU_TYPE[scope.row.type].text }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" header-align="center" label="协议版本" min-width="150" prop="protocolVersion" /> |
|||
</el-table> |
|||
|
|||
<el-pagination |
|||
:current-page="devicesAll.page.page" |
|||
:default-page-size="50" |
|||
:page-count="devicesAll.page.count" |
|||
:page-size="devicesAll.page.size" |
|||
:page-sizes="[1, 10, 20, 50, 100]" |
|||
:total="devicesAll.page.total" |
|||
background |
|||
class="my-3 float-right" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
@size-change="onSizeChange" |
|||
@current-change="onCurrentPageChange" |
|||
></el-pagination> |
|||
</template> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, onMounted, ref } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import dayjs from 'dayjs'; |
|||
import { STATUS_COLOR } from '@/config/config'; |
|||
import { ASDU_TYPE } from '@/config/log'; |
|||
|
|||
let timer = null; |
|||
const store = useStore(); |
|||
const token = computed(() => store.getters['user/token']); |
|||
const devicesAll = computed(() => { |
|||
return store.state.device.devicesAll; |
|||
}); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); |
|||
const contentHeight = ref(600); |
|||
|
|||
// 获取设备完整信息列表 |
|||
const getDevicesAllData = () => { |
|||
try { |
|||
if (token && token.value) { |
|||
store.dispatch('device/getDevicesAll'); |
|||
timer && clearTimeout(timer); |
|||
timer = null; |
|||
} else { |
|||
timer = setTimeout(() => { |
|||
getDevicesAllData(); |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
throw new Error(error); |
|||
} |
|||
}; |
|||
|
|||
getDevicesAllData(); |
|||
|
|||
// 设置表格高度 |
|||
onMounted(() => { |
|||
const winHeight = document.documentElement.clientHeight; |
|||
contentHeight.value = winHeight - 250; |
|||
}); |
|||
|
|||
/** |
|||
* 删除 |
|||
* @param {number} page 页码 |
|||
* @param {number} size 每页条数 |
|||
*/ |
|||
function onSearch(page, size = 50) { |
|||
const deviceId = currentDeviceId.value; |
|||
const params = { |
|||
deviceId, |
|||
page, |
|||
size, |
|||
}; |
|||
store.dispatch('device/getDevicesAll', params); |
|||
} |
|||
|
|||
// 当前页码变化 |
|||
const onCurrentPageChange = page => { |
|||
onSearch(page, devicesAll.value.page.size || 50); |
|||
}; |
|||
|
|||
// 每页条数变化 |
|||
const onSizeChange = size => { |
|||
onSearch(1, size); |
|||
}; |
|||
</script> |
@ -1,66 +0,0 @@ |
|||
<template> |
|||
<el-form ref="searchDeviceForm" :inline="true" :model="searchDevice"> |
|||
<el-form-item label="选择站点"> |
|||
<el-select v-model="searchDevice.deviceId" placeholder="请选择站点" @change="change"> |
|||
<el-option v-if="showAll" label="全部" value></el-option> |
|||
<el-option |
|||
v-for="item in devices" |
|||
:key="item.deviceId" |
|||
:label="`${item.address}-${item.deviceId}`" |
|||
:value="item.deviceId" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" @click="onSubmit"> |
|||
<i class="el-icon-search mr-2"></i> |
|||
查询 |
|||
</el-button> |
|||
</el-form-item> |
|||
<el-form-item v-if="showRefresh"> |
|||
<el-button type="primary" @click="$emit('refresh')"> |
|||
<i class="el-icon-refresh mr-2"></i> |
|||
刷新 |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, defineEmits, defineProps, reactive, ref, watch } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { useRouter } from 'vue-router'; |
|||
|
|||
defineProps({ showRefresh: Boolean }); |
|||
const emit = defineEmits(['getDate', 'refresh']); |
|||
const router = useRouter(); |
|||
const searchDevice = reactive({ deviceId: '' }); |
|||
|
|||
const searchDeviceForm = ref(null); // form |
|||
|
|||
const store = useStore(); |
|||
const devices = computed(() => store.state.device.devices); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
const showAll = computed(() => router.currentRoute.value.name === 'devices'); // 是否显示全部 |
|||
|
|||
// 监听currentDeviceId |
|||
watch( |
|||
() => currentDeviceId.value, |
|||
newValue => { |
|||
if (newValue && searchDevice.deviceId !== newValue) { |
|||
searchDevice.deviceId = newValue; |
|||
} |
|||
}, |
|||
{ immediate: true }, |
|||
); |
|||
|
|||
const change = e => { |
|||
store.commit('device/setCurrentDeviceId', e); |
|||
}; |
|||
|
|||
const onSubmit = () => { |
|||
searchDeviceForm.value.validate(() => { |
|||
emit('search'); |
|||
}); |
|||
}; |
|||
</script> |
@ -1,131 +0,0 @@ |
|||
<template> |
|||
<el-form ref="searchDeviceForm" :inline="true" :model="searchDevice"> |
|||
<el-form-item label="选择站点:"> |
|||
<el-select v-model="searchDevice.deviceId" placeholder="请选择站点" @change="change"> |
|||
<!-- <el-option label="全部" value></el-option> --> |
|||
<el-option |
|||
v-for="item in devices" |
|||
:key="item.deviceId" |
|||
:label="`${item.address}-${item.deviceId}`" |
|||
:value="item.deviceId" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item v-if="showTypeSelect" label="类型:"> |
|||
<el-select v-model="searchDevice.dataType" placeholder="选择查询类型"> |
|||
<el-option label="全部" value=""></el-option> |
|||
<el-option label="事件上报" value="ReportHistoryEvent"></el-option> |
|||
<el-option label="业务上报" value="ReportHistoryData"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="起始时间:"> |
|||
<el-date-picker v-model="searchDevice.date[0]" format="YYYY-MM-DD HH:mm" placeholder="起始时间" type="datetime"></el-date-picker> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="截止时间:"> |
|||
<el-date-picker v-model="searchDevice.date[1]" format="YYYY-MM-DD HH:mm" placeholder="截止时间" type="datetime"></el-date-picker> |
|||
</el-form-item> |
|||
|
|||
<el-form-item> |
|||
<el-button :loading="loadingSearch" type="primary" @click="onSubmit"> |
|||
<i class="el-icon-search mr-2"></i> |
|||
查询 |
|||
</el-button> |
|||
</el-form-item> |
|||
|
|||
<el-form-item v-if="showExport"> |
|||
<el-button :loading="loadingExport" type="success" @click="onExport"> |
|||
<i class="el-icon-download mr-2"></i> |
|||
导出 |
|||
</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, defineEmits, defineProps, reactive, ref, watch } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import dayjs from 'dayjs'; |
|||
import { exportHistory } from 'apis'; |
|||
|
|||
const emit = defineEmits(['search']); |
|||
const searchDevice = reactive({ |
|||
deviceId: '', |
|||
date: [dayjs().subtract(7, 'day').startOf('day'), new Date()], |
|||
dataType: 'ReportHistoryData', |
|||
}); |
|||
const searchDeviceForm = ref(null); // form |
|||
const store = useStore(); |
|||
const devices = computed(() => store.state.device.devices); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
const loadingExport = ref(false); |
|||
|
|||
defineProps({ |
|||
showExport: Boolean, |
|||
showTypeSelect: Boolean, |
|||
loadingSearch: Boolean, |
|||
}); |
|||
|
|||
// 监听currentDeviceId |
|||
watch( |
|||
() => currentDeviceId.value, |
|||
newValue => { |
|||
if (newValue) { |
|||
searchDevice.deviceId !== newValue && (searchDevice.deviceId = newValue); |
|||
} |
|||
}, |
|||
{ immediate: true }, |
|||
); |
|||
|
|||
const change = e => { |
|||
store.commit('device/setCurrentDeviceId', e); |
|||
}; |
|||
|
|||
// 生成查询参数 |
|||
function generateParams() { |
|||
const { deviceId, date, dataType } = searchDevice; |
|||
let params = { |
|||
deviceId, |
|||
date, |
|||
dataType, |
|||
}; |
|||
|
|||
if (date) { |
|||
const start = +dayjs(date[0]).format('x'); |
|||
const end = +dayjs(date[1]).format('x'); |
|||
const daterange = [start, end]; |
|||
params = { |
|||
deviceId, |
|||
date: daterange, |
|||
dataType, |
|||
}; |
|||
} |
|||
|
|||
return params; |
|||
} |
|||
|
|||
// 提交 |
|||
const onSubmit = () => { |
|||
searchDeviceForm.value.validate(valid => { |
|||
if (valid) { |
|||
const params = generateParams(); |
|||
emit('search', params); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// 导出 |
|||
async function onExport() { |
|||
try { |
|||
loadingExport.value = true; |
|||
const params = generateParams(); |
|||
const resData = await exportHistory(params); |
|||
loadingExport.value = false; |
|||
resData && (window.location.href = resData); |
|||
} catch (error) { |
|||
loadingExport.value = false; |
|||
throw new Error(error); |
|||
} |
|||
} |
|||
</script> |
@ -1,82 +0,0 @@ |
|||
<template> |
|||
<el-card class="box-card"> |
|||
<el-empty v-if="noData" description="暂无数据"></el-empty> |
|||
<div id="realtimeContainer"></div> |
|||
</el-card> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, defineExpose, defineProps, onMounted, ref, watchEffect } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import * as echarts from 'echarts/core'; |
|||
import { DataZoomComponent, GridComponent, LegendComponent, ToolboxComponent, TooltipComponent } from 'echarts/components'; |
|||
import { LineChart } from 'echarts/charts'; |
|||
import { UniversalTransition } from 'echarts/features'; |
|||
import { CanvasRenderer } from 'echarts/renderers'; |
|||
import { generateChartOption } from 'utils/statistical'; |
|||
|
|||
echarts.use([ |
|||
ToolboxComponent, |
|||
TooltipComponent, |
|||
GridComponent, |
|||
LegendComponent, |
|||
LineChart, |
|||
CanvasRenderer, |
|||
UniversalTransition, |
|||
DataZoomComponent, |
|||
]); |
|||
|
|||
const store = useStore(); |
|||
const props = defineProps({ searchHeight: Number }); |
|||
const realtimeData = computed(() => store.state.statistics.realtimeData); |
|||
const isMounted = ref(false); |
|||
const noData = ref(true); |
|||
let myChart = null; |
|||
|
|||
// 设置图表 |
|||
onMounted(() => { |
|||
isMounted.value = true; |
|||
window.addEventListener('resize', () => { |
|||
if (!myChart) return; |
|||
myChart.resize(); |
|||
}); |
|||
}); |
|||
|
|||
watchEffect(() => { |
|||
if (!realtimeData.value) return; |
|||
const { data } = realtimeData.value; |
|||
if (!data || !data.length || !isMounted.value) return; |
|||
initChart(data); |
|||
}); |
|||
|
|||
// 初始化chart |
|||
function initChart(data) { |
|||
const chartDom = document.getElementById('realtimeContainer'); |
|||
const canvasHeight = document.documentElement.clientHeight - props.searchHeight - 150; |
|||
chartDom.style.height = `${canvasHeight}px`; |
|||
myChart && myChart.dispose(); |
|||
myChart = echarts.init(chartDom); |
|||
render(data); |
|||
|
|||
myChart.on('legendselectchanged', event => { |
|||
render(data, event.selected); |
|||
}); |
|||
} |
|||
|
|||
function render(data, selected) { |
|||
// myChart && myChart.clear(); |
|||
if (!data || !data.length) { |
|||
noData.value = true; |
|||
return; |
|||
} |
|||
noData.value = false; |
|||
|
|||
if (!myChart) { |
|||
initChart(data); |
|||
} |
|||
const option = generateChartOption(data, selected); |
|||
option && myChart.setOption(option); |
|||
} |
|||
|
|||
defineExpose({ render }); |
|||
</script> |
@ -0,0 +1,82 @@ |
|||
<template> |
|||
<a-calendar v-model="date" :fullscreen="false" @panelChange="onPanelChange" @select="onSelectDate"> |
|||
<template #headerRender="{ value: current, onChange }"> |
|||
<div style="padding: 10px"> |
|||
<a-row type="flex" justify="space-between"> |
|||
<a-col> |
|||
<a-select |
|||
size="small" |
|||
:dropdown-match-select-width="false" |
|||
class="my-year-select" |
|||
:value="String(current.year())" |
|||
@change=" |
|||
newYear => { |
|||
onChange(current.year(newYear)); |
|||
} |
|||
" |
|||
> |
|||
<a-select-option v-for="val in getYears(current)" :key="String(val)" class="year-item"> |
|||
{{ val }} |
|||
</a-select-option> |
|||
</a-select> |
|||
</a-col> |
|||
<a-col> |
|||
<a-select |
|||
size="small" |
|||
:dropdown-match-select-width="false" |
|||
:value="String(current.month())" |
|||
@change=" |
|||
selectedMonth => { |
|||
onChange(current.month(parseInt(selectedMonth, 10))); |
|||
} |
|||
" |
|||
> |
|||
<a-select-option v-for="(val, index) in getMonths(current)" :key="String(index)" class="month-item"> |
|||
{{ val }} |
|||
</a-select-option> |
|||
</a-select> |
|||
</a-col> |
|||
</a-row> |
|||
</div> |
|||
</template> |
|||
</a-calendar> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref } from 'vue'; |
|||
|
|||
const date = ref(); |
|||
const onPanelChange = (value, mode) => { |
|||
console.log(value, mode); |
|||
}; |
|||
|
|||
const getMonths = value => { |
|||
const localeData = value.localeData(); |
|||
const months = []; |
|||
|
|||
for (let i = 0; i < 12; i++) { |
|||
months.push(localeData.monthsShort(value.month(i))); |
|||
} |
|||
|
|||
return months; |
|||
}; |
|||
|
|||
const getYears = value => { |
|||
const year = value.year(); |
|||
const years = []; |
|||
|
|||
for (let i = year - 10; i < year + 10; i += 1) { |
|||
years.push(i); |
|||
} |
|||
|
|||
return years; |
|||
}; |
|||
|
|||
function onSelectDate(event) { |
|||
console.log(event); |
|||
} |
|||
</script> |
|||
|
|||
<script> |
|||
export default { name: 'LeftCanlendar' }; |
|||
</script> |
@ -0,0 +1,13 @@ |
|||
<template> |
|||
<Calendar /> |
|||
<Projects /> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import Calendar from './Calendar.vue'; |
|||
import Projects from './Projects.vue'; |
|||
</script> |
|||
|
|||
<script> |
|||
export default { name: 'LeftIndex' }; |
|||
</script> |
@ -0,0 +1,5 @@ |
|||
<template>项目列表</template> |
|||
|
|||
<script setup></script> |
|||
|
|||
<style scoped></style> |
@ -0,0 +1,20 @@ |
|||
<template> |
|||
<menu-unfold-outlined v-if="collapsed" @click="toggleCollapse" /> |
|||
<menu-fold-outlined v-else class="trigger" @click="toggleCollapse" /> |
|||
<h1>课题数据库</h1> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
// eslint-disable-next-line import/no-extraneous-dependencies |
|||
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'; |
|||
|
|||
const store = useStore(); |
|||
const collapsed = computed(() => store.state.layout.display.left); // 是否显示左栏 |
|||
|
|||
// toggle left window display |
|||
function toggleCollapse() { |
|||
store.commit('layout/setDisplay', { prop: 'left', show: !collapsed.value }); |
|||
} |
|||
</script> |
@ -1,157 +0,0 @@ |
|||
/* eslint-disable max-len */ |
|||
export const colors = ['#5470C6', '#91CC75', '#EE6666', '#5470C6', '#91CC75', '#EE6666', '#5470C6', '#91CC75', '#5470C6']; |
|||
|
|||
export const legendData = [ |
|||
'SO2(ppb)', |
|||
'盐分阻抗(Ω)', |
|||
'盐分温度(℃)', |
|||
'环境温度(℃)', |
|||
'环境湿度(RH%)', |
|||
'锌腐蚀电流(nA)', |
|||
'铜腐蚀电流(nA)', |
|||
'铝腐蚀电流(nA)', |
|||
'钢腐蚀电流(nA)', |
|||
]; |
|||
|
|||
// 默认legend select
|
|||
export const defaultSelectedLegend = { |
|||
'钢腐蚀电流(nA)': true, |
|||
'铜腐蚀电流(nA)': true, |
|||
'铝腐蚀电流(nA)': true, |
|||
'锌腐蚀电流(nA)': true, |
|||
'SO2(ppb)': false, |
|||
'盐分阻抗(Ω)': false, |
|||
'盐分温度(℃)': false, |
|||
'环境温度(℃)': false, |
|||
'环境湿度(RH%)': false, |
|||
}; |
|||
|
|||
// y轴定义
|
|||
export const yAxisData = [ |
|||
{ |
|||
type: 'value', |
|||
name: '腐蚀电流(nA)', |
|||
offset: 0, |
|||
position: 'left', |
|||
axisLine: { |
|||
show: true, |
|||
lineStyle: { color: colors[0] }, |
|||
}, |
|||
axisLabel: { formatter: '{value}' }, |
|||
axisPointer: { show: false }, |
|||
}, |
|||
{ |
|||
type: 'value', |
|||
name: '温度(℃)', |
|||
offset: 0, |
|||
position: 'right', |
|||
axisLine: { |
|||
show: true, |
|||
lineStyle: { color: colors[1] }, |
|||
}, |
|||
axisLabel: { formatter: '{value}' }, |
|||
axisPointer: { show: false }, |
|||
}, |
|||
{ |
|||
type: 'value', |
|||
name: '湿度(RH%)', |
|||
offset: 70, |
|||
position: 'right', |
|||
axisLine: { |
|||
show: true, |
|||
lineStyle: { color: colors[2] }, |
|||
}, |
|||
axisLabel: { formatter: '{value}' }, |
|||
axisPointer: { show: false }, |
|||
}, |
|||
{ |
|||
type: 'value', |
|||
name: 'SO2(ppb)', |
|||
position: 'right', |
|||
show: false, |
|||
offset: 150, |
|||
axisLine: { |
|||
show: true, |
|||
lineStyle: { color: colors[3] }, |
|||
}, |
|||
axisLabel: { formatter: '{value}' }, |
|||
axisPointer: { show: false }, |
|||
}, |
|||
{ |
|||
type: 'value', |
|||
name: '盐分阻抗(Ω)', |
|||
show: false, |
|||
position: 'right', |
|||
offset: 220, |
|||
axisLine: { |
|||
show: true, |
|||
lineStyle: { color: colors[4] }, |
|||
}, |
|||
axisLabel: { formatter: '{value}' }, |
|||
axisPointer: { show: false }, |
|||
}, |
|||
]; |
|||
|
|||
/** |
|||
* 生成默认数据 |
|||
* @param {Object} data |
|||
* @returns {[{data: (number|[]|string|*), name: string, type: string},{data: (number|[]|BufferSource|string|*), name: string, type: string, yAxisIndex: number},{data: ([]|string|*), name: string, type: string, yAxisIndex: number},{data: ([]|string|*), name: string, type: string, yAxisIndex: number},{data: [], name: string, type: string, yAxisIndex: number},null,null,null]} |
|||
*/ |
|||
export function generateDefaultSeries(data) { |
|||
return [ |
|||
{ |
|||
name: '锌腐蚀电流(nA)', |
|||
type: 'line', |
|||
yAxisIndex: 0, |
|||
data: data.corrosionXIN, |
|||
}, |
|||
{ |
|||
name: '铜腐蚀电流(nA)', |
|||
type: 'line', |
|||
yAxisIndex: 0, |
|||
data: data.corrosionTONG, |
|||
}, |
|||
{ |
|||
name: '铝腐蚀电流(nA)', |
|||
type: 'line', |
|||
yAxisIndex: 0, |
|||
data: data.corrosionLV, |
|||
}, |
|||
{ |
|||
name: '钢腐蚀电流(nA)', |
|||
type: 'line', |
|||
yAxisIndex: 0, |
|||
data: data.corrosionGANG, |
|||
}, |
|||
{ |
|||
name: '环境温度(℃)', |
|||
type: 'line', |
|||
yAxisIndex: 1, |
|||
data: data.environmentTemperature, |
|||
}, |
|||
{ |
|||
name: '盐分温度(℃)', |
|||
type: 'line', |
|||
yAxisIndex: 1, |
|||
data: data.saltT, |
|||
}, |
|||
{ |
|||
name: '环境湿度(RH%)', |
|||
type: 'line', |
|||
yAxisIndex: 2, |
|||
data: data.environmentHumidity, |
|||
}, |
|||
{ |
|||
name: 'SO2(ppb)', |
|||
type: 'line', |
|||
yAxisIndex: 3, |
|||
data: data.so2, |
|||
}, |
|||
{ |
|||
name: '盐分阻抗(Ω)', |
|||
type: 'line', |
|||
yAxisIndex: 4, |
|||
data: data.saltR, |
|||
}, |
|||
]; |
|||
} |
@ -1,164 +0,0 @@ |
|||
// 网络参数设置
|
|||
export const networkConfig = { |
|||
ip1: '', |
|||
port1: '', |
|||
ip2: '', |
|||
port2: '', |
|||
ip3: '', |
|||
port3: '', |
|||
ipBackup: '', |
|||
portBackup: '', |
|||
account: '', |
|||
password: '', |
|||
apn: '', |
|||
status: '', |
|||
}; |
|||
|
|||
// 功能参数设置
|
|||
export const functionConfig = { |
|||
frequency: { |
|||
so2: 0, // SO2采样频率
|
|||
metal: 0, // 金属腐蚀采样频率
|
|||
th: 0, // 温湿度 采样频率
|
|||
salt: 0, // 盐雾 采样频率
|
|||
}, // 采样频率
|
|||
count: 0, // 采集个数
|
|||
time: Date.now(), // 设置时间
|
|||
batteryLow: 0, // 电池电压低阈值
|
|||
batteryHigh: 0, // 电池电压高阈值
|
|||
sunHigh: 0, // 太阳能电压高阈值
|
|||
humidityHigh: 0, // 湿度高阈值
|
|||
temperatureLow: 0, // 温度低阈值
|
|||
temperatureHigh: 0, // 温度高阈值
|
|||
securityMode: 'OPEN', // 安全模式 OPEN->不加密 ENCRYPTION->加密
|
|||
report: { |
|||
type: 'CYCLE', // 上报周期类型 0->时间点 1->周期
|
|||
timePoints: [''], // 设置时间点
|
|||
cycle: 240, // 上报周期分钟数
|
|||
}, |
|||
status: '', |
|||
}; |
|||
|
|||
// 金属腐蚀类型
|
|||
export const corrosiveTypes = [ |
|||
{ |
|||
value: 'XIN', |
|||
type: '锌', |
|||
}, |
|||
{ |
|||
value: 'LV', |
|||
type: '铝', |
|||
}, |
|||
{ |
|||
value: 'TONG', |
|||
type: '铜', |
|||
}, |
|||
{ |
|||
value: 'GANG', |
|||
type: '钢', |
|||
}, |
|||
]; |
|||
|
|||
// 添加设备
|
|||
export const deviceData = { |
|||
deviceId: '', // 设备id
|
|||
deviceFullId: '', // 设备完整id
|
|||
deviceDirection: '', // 设备朝向
|
|||
area: '', // 地区
|
|||
address: '', // 站点名称
|
|||
contact: '', // 联系人
|
|||
phone: '', // 联系人电话
|
|||
lon: '', // 经度
|
|||
lat: '', // 纬度
|
|||
head: '', // 负责人
|
|||
installLocation: '', // 安装位置
|
|||
installTime: '', // 安装时间
|
|||
runTime: '', // 正式运行时间
|
|||
linkAddress: '', // 链路地址
|
|||
probNo: '', // 探头编号
|
|||
simple: '', // 试样
|
|||
sim1: '', // sim卡1
|
|||
protocolVersion: '', // 协议版本
|
|||
joint: '', // 主站后台联调情况
|
|||
operationRecord: '', // 维修记录
|
|||
remark: '', // 备注
|
|||
type: 'IACD', // 产品类型
|
|||
}; |
|||
|
|||
// 设备添加编辑 规则
|
|||
export const deviceRules = { |
|||
deviceId: [ |
|||
{ |
|||
required: true, |
|||
message: '请输入设备ID号', |
|||
trigger: 'blur', |
|||
}, |
|||
{ |
|||
len: 6, |
|||
message: '请输入6位设备ID号', |
|||
trigger: 'blur', |
|||
}, |
|||
], |
|||
address: [ |
|||
{ |
|||
required: true, |
|||
message: '请输入站点名称', |
|||
trigger: 'blur', |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
// 下发类型
|
|||
export const PEND_TYPE = { |
|||
PENDING: { |
|||
type: 'primary', |
|||
text: '待下发', |
|||
}, |
|||
FAIL: { |
|||
type: 'danger', |
|||
text: '配置失败', |
|||
}, |
|||
SUCCESS: { |
|||
type: 'success', |
|||
text: '配置成功', |
|||
}, |
|||
}; |
|||
|
|||
// 上报类型
|
|||
export const REPORT_TYPE = { |
|||
CYCLE: '周期上报', |
|||
POINT: '定时上报', |
|||
}; |
|||
|
|||
// 下发类型
|
|||
export const PENDING_TYPE = { |
|||
DATA: { text: '业务上报' }, |
|||
EVENT: { text: '事件上报' }, |
|||
}; |
|||
|
|||
// 实时查询数据的轮询时间 1分钟
|
|||
export const REALTIME_DATA_INTERVAL = 60000 * 5; |
|||
|
|||
// 设备状态 情景
|
|||
export const STATUS_COLOR = { |
|||
FAULT: { |
|||
text: '故障', |
|||
color: '#FCA5A5', |
|||
}, |
|||
WARNING: { |
|||
text: '报警', |
|||
color: '#FCD34D', |
|||
}, |
|||
NORMAL: { |
|||
text: '正常', |
|||
color: '#BEF264', |
|||
}, |
|||
OFFLINE: { |
|||
text: '离线', |
|||
color: '#CBD5E1', |
|||
}, |
|||
ONLINE: { |
|||
text: '在线', |
|||
color: '#6EE7B7', |
|||
}, |
|||
}; |
@ -1,110 +0,0 @@ |
|||
// 功能码
|
|||
export const CODE = { |
|||
BUSINESS_REPORT: { |
|||
text: '业务上报', |
|||
code: 0, |
|||
}, |
|||
EVENT_REPORT: { |
|||
text: '事件上报', |
|||
code: 1, |
|||
}, |
|||
NET_PARAM_SETTING_OR_READING: { |
|||
code: 2, |
|||
text: '联网参数设置或读取', |
|||
}, |
|||
FUNC_PARAM_SETTING_OR_READING: { |
|||
code: 3, |
|||
text: '功能参数设置或读取', |
|||
}, |
|||
READ_HISTORY_DATA: { |
|||
code: 4, |
|||
text: '读取历史日志记录', |
|||
}, |
|||
READ_HISTORY_EVENT: { |
|||
code: 5, |
|||
text: '读取历史事件记录', |
|||
}, |
|||
UPGRADE: { |
|||
code: 6, |
|||
text: '升级启动、传输、结束', |
|||
}, |
|||
ACK: { |
|||
code: 7, |
|||
text: '应答帧(固定帧)', |
|||
}, |
|||
OVER: { |
|||
code: 8, |
|||
text: '结束帧', |
|||
}, |
|||
RESERVED: { |
|||
code: 9, |
|||
text: '保留', |
|||
}, |
|||
}; |
|||
|
|||
// 传输方向
|
|||
export const DIRECTION = { |
|||
IACD2SERVER: '上行', |
|||
SERVER2IACD: '下行', |
|||
}; |
|||
|
|||
// 传输启动
|
|||
export const PRM = { |
|||
ACK: '传输结束', |
|||
REQ: '传输启动', |
|||
}; |
|||
|
|||
// 流控
|
|||
export const DFC = { |
|||
CONTINUE: '有后续包', |
|||
OVER: '无后续包', |
|||
}; |
|||
|
|||
// 安全方式
|
|||
export const SER = { |
|||
OPEN: '不加密', |
|||
ENCRYPTION: '加密', |
|||
}; |
|||
|
|||
// 类型标识
|
|||
export const ASDU_TYPE = { |
|||
IACD: { |
|||
code: 1, |
|||
text: '大气腐蚀', |
|||
}, |
|||
OTHER: { |
|||
code: 2, |
|||
text: '其他', |
|||
}, |
|||
}; |
|||
|
|||
// 信息体序
|
|||
export const INFO_ORDER = { |
|||
NoOrder: '无序', |
|||
HasOrder: '有序', |
|||
None: '无', |
|||
}; |
|||
|
|||
// 传输原因
|
|||
export const REASON = { |
|||
None: { |
|||
code: 0, |
|||
text: '无', |
|||
}, |
|||
ReportData: { |
|||
code: 1, |
|||
text: '业务主动上报', |
|||
}, |
|||
ReportEvent: { |
|||
code: 2, |
|||
text: '事件主动上报', |
|||
}, |
|||
History: { |
|||
code: 3, |
|||
text: '历史数据记录或程序数据', |
|||
}, |
|||
HumanOperator: { |
|||
code: 4, |
|||
text: '人工上报', |
|||
}, |
|||
}; |
@ -1,19 +0,0 @@ |
|||
import { ref } from 'vue'; |
|||
|
|||
export default function useDeviceCreate() { |
|||
const display = ref(false); |
|||
|
|||
function hide() { |
|||
display.value = false; |
|||
} |
|||
|
|||
function show() { |
|||
display.value = true; |
|||
} |
|||
|
|||
return { |
|||
display, |
|||
hide, |
|||
show, |
|||
}; |
|||
} |
@ -1,163 +0,0 @@ |
|||
import { getDevices, getDevicesAll, getDevicesCount } from 'apis'; |
|||
import dayjs from 'dayjs'; |
|||
import { ElMessage } from 'element-plus'; |
|||
|
|||
const user = { |
|||
namespaced: true, |
|||
|
|||
state: { |
|||
devices: [], // 站点列表 设备列表简版
|
|||
devicesAll: { page: {}, data: [] }, // 设备列表完整版
|
|||
currentDeviceId: '', // 当前正在编辑的设备deviceId
|
|||
count: { |
|||
total: 0, // 总设备数量
|
|||
online: 0, // 在线
|
|||
offline: 0, // 离线
|
|||
warning: 0, // 警告
|
|||
fault: 0, // 故障
|
|||
}, |
|||
}, |
|||
|
|||
getters: { |
|||
// 当前正在编辑的设备的完整信息
|
|||
current({ devicesAll, currentDeviceId }) { |
|||
try { |
|||
return devicesAll.data.find(device => device.deviceId === currentDeviceId); |
|||
} catch (error) { |
|||
return null; |
|||
} |
|||
}, |
|||
}, |
|||
|
|||
mutations: { |
|||
/** |
|||
* 设置devices数据 |
|||
* @param {*} state |
|||
* @param {array} devices |
|||
*/ |
|||
setDevices(state, devices) { |
|||
if (devices && devices.length) { |
|||
state.devices = devices; |
|||
user.mutations.setCurrentDeviceId(state, devices[0].deviceId); |
|||
} else { |
|||
state.devices = []; |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 设置设备统计数据信息 |
|||
* @param {object} state |
|||
* @param {object} count // 服务端返回的对象
|
|||
* @param {number} count.total // 总设备数量
|
|||
* @param {number} count.online // 在线
|
|||
* @param {number} count.offline // 离线
|
|||
* @param {number} count.warning // 报警
|
|||
* @param {number} count.fault // 故障
|
|||
*/ |
|||
setDevicesCount(state, count) { |
|||
state.count = count; |
|||
}, |
|||
|
|||
/** |
|||
* 设置devicesAll的数据 |
|||
* @param {*} state |
|||
* @param {object} devices {page, data} |
|||
*/ |
|||
setDevicesAll(state, devices) { |
|||
if (devices && devices.data) { |
|||
for (let i = 0; i < devices.data.length; i++) { |
|||
const device = devices.data[i]; |
|||
if (device.installTime) { |
|||
devices.data[i].installTime = dayjs(new Date(+device.installTime)).format('YYYY-MM-DD HH:mm:ss'); |
|||
} |
|||
if (device.runTime) { |
|||
devices.data[i].runTime = dayjs(new Date(+device.runTime)).format('YYYY-MM-DD HH:mm:ss'); |
|||
} |
|||
} |
|||
} |
|||
state.devicesAll = devices; |
|||
}, |
|||
|
|||
/** |
|||
* 设置正则编辑的设备deviceId |
|||
* @param {*} state |
|||
* @param {string} deviceId |
|||
*/ |
|||
setCurrentDeviceId(state, deviceId) { |
|||
state.currentDeviceId = deviceId; |
|||
}, |
|||
|
|||
/** |
|||
* 更新某个设备的信息 |
|||
* @param {*} param0 |
|||
* @param {object} newData 设备更新后的信息 |
|||
*/ |
|||
updateDevice({ devicesAll }, newData) { |
|||
for (let i = 0; i < devicesAll.data.length; i++) { |
|||
const item = devicesAll.data[i]; |
|||
if (item && item.deviceId === newData.deviceId) { |
|||
newData.installTime = dayjs(new Date(+item.installTime)).format('YYYY-MM-DD HH:mm:ss'); |
|||
newData.runTime = dayjs(new Date(+item.runTime)).format('YYYY-MM-DD HH:mm:ss'); |
|||
devicesAll.data[i] = newData; |
|||
break; |
|||
} |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 删除设备 |
|||
* @param {*} param0 |
|||
* @param {string} deviceId 设备id |
|||
*/ |
|||
deleteDevice({ devicesAll }, deviceId) { |
|||
if (!devicesAll || !devicesAll.data || !devicesAll.data.length) return; |
|||
devicesAll.data = devicesAll.data.filter(item => item.deviceId !== deviceId); |
|||
}, |
|||
|
|||
/** |
|||
* 新增设备后将设备数据追加到最前边 |
|||
* @param {array} devicesAll |
|||
* @param {object} device 后台返回的新增设备信息 |
|||
*/ |
|||
unshiftDevice({ devicesAll }, device) { |
|||
devicesAll.data.unshift(device); |
|||
}, |
|||
}, |
|||
|
|||
actions: { |
|||
// 获取设备列表(站点列表)
|
|||
async getDevices({ commit }) { |
|||
try { |
|||
const data = await getDevices(); |
|||
commit('setDevices', data || []); |
|||
return data; |
|||
} catch (error) { |
|||
throw new Error(error); |
|||
} |
|||
}, |
|||
|
|||
// 查询设备数量信息
|
|||
async getDevicesCount({ commit }) { |
|||
try { |
|||
const data = await getDevicesCount(); |
|||
commit('setDevicesCount', data); |
|||
} catch (error) { |
|||
ElMessage.error(error.message || '获取设备统计信息失败'); |
|||
throw new Error(error); |
|||
} |
|||
}, |
|||
|
|||
// 获取设备列表(站点列表) 完整信息
|
|||
async getDevicesAll({ commit }, params) { |
|||
try { |
|||
const data = await getDevicesAll(params); |
|||
commit('setDevicesAll', data || null); |
|||
return data; |
|||
} catch (error) { |
|||
throw new Error(error); |
|||
} |
|||
}, |
|||
}, |
|||
}; |
|||
|
|||
export default user; |
@ -1,16 +1,5 @@ |
|||
import { createStore } from 'vuex'; |
|||
import device from './device'; |
|||
import statistics from './statistics'; |
|||
import user from './user'; |
|||
import user from './tall/user'; |
|||
import layout from './tall/layout'; |
|||
|
|||
export default createStore({ |
|||
modules: { user, device, statistics }, |
|||
state: { menu: { show: true, collapse: false } }, |
|||
getters: {}, |
|||
mutations: { |
|||
toggleCollapse(state) { |
|||
state.menu.collapse = !state.menu.collapse; |
|||
}, |
|||
}, |
|||
actions: {}, |
|||
}); |
|||
export default createStore({ modules: { user, layout } }); |
|||
|
@ -1,78 +0,0 @@ |
|||
import { getDatas, getMonthsDate } from 'apis/index'; |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
|
|||
state: { |
|||
electricData: null, |
|||
corrosionData: null, |
|||
moistTimeData: null, |
|||
realtimeData: null, |
|||
}, |
|||
|
|||
getters: {}, |
|||
|
|||
mutations: { |
|||
/** |
|||
* 设置积分电量数据 |
|||
* @param {*} state |
|||
* @param {array} data |
|||
*/ |
|||
setElectricData(state, data) { |
|||
state.electricData = data; |
|||
}, |
|||
|
|||
/** |
|||
* 设置月累计腐蚀的数据 |
|||
* @param {*} state |
|||
* @param {*} data |
|||
*/ |
|||
setCorrosionData(state, data) { |
|||
state.corrosionData = data; |
|||
}, |
|||
|
|||
/** |
|||
* 设置月累计湿润时间图的数据 |
|||
* @param {*} state |
|||
* @param {*} data |
|||
*/ |
|||
setMoistTimeData(state, data) { |
|||
state.moistTimeData = data; |
|||
}, |
|||
|
|||
/** |
|||
* 设置实时数据统计的数据 |
|||
* @param {*} state |
|||
* @param {*} data |
|||
*/ |
|||
setRealtimeData(state, data) { |
|||
state.realtimeData = data; |
|||
}, |
|||
}, |
|||
|
|||
actions: { |
|||
// 获取积分电量数据
|
|||
async getMonthsDate({ commit }, params) { |
|||
try { |
|||
const data = await getMonthsDate(params); |
|||
commit('setElectricData', data.powers || null); |
|||
commit('setCorrosionData', data.corrosions || null); |
|||
commit('setMoistTimeData', data.humids || null); |
|||
return data; |
|||
} catch (error) { |
|||
throw new Error(error); |
|||
} |
|||
}, |
|||
|
|||
// 获取实时数据统计数据
|
|||
async getRealtimeData({ commit }, params) { |
|||
try { |
|||
const data = await getDatas(params); |
|||
commit('setRealtimeData', data || null); |
|||
return data; |
|||
} catch (error) { |
|||
throw new Error(error); |
|||
} |
|||
}, |
|||
}, |
|||
}; |
@ -0,0 +1,8 @@ |
|||
import state from './state'; |
|||
import mutations from './mutations'; |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
state, |
|||
mutations, |
|||
}; |
@ -0,0 +1,13 @@ |
|||
const mutations = { |
|||
/** |
|||
* 设置显隐 |
|||
* @param { Object} state |
|||
* @param {String} prop 要设置的字段/属性名 |
|||
* @param {Boolean} show true->显示,false->隐藏 |
|||
*/ |
|||
setDisplay(state, { prop, show }) { |
|||
state.display[prop] = show; |
|||
}, |
|||
}; |
|||
|
|||
export default mutations; |
@ -0,0 +1,8 @@ |
|||
/* eslint-disable object-curly-newline */ |
|||
const state = { |
|||
display: { |
|||
left: true, // 是否显示左栏
|
|||
}, |
|||
}; |
|||
|
|||
export default state; |
@ -1,4 +1,4 @@ |
|||
import { getToken } from 'apis/index'; |
|||
import { getToken } from 'apis'; |
|||
|
|||
export default { |
|||
namespaced: true, |
@ -0,0 +1,6 @@ |
|||
import dayjs from 'dayjs'; |
|||
import 'dayjs/locale/zh-cn'; |
|||
|
|||
dayjs.locale('zh-cn'); |
|||
|
|||
export default dayjs; |
@ -1,128 +0,0 @@ |
|||
import { STATUS_COLOR } from '@/config/config'; |
|||
|
|||
/** |
|||
* 生成设备概览 数量数据 |
|||
* @param {object} count |
|||
* @param {number} count.online 在线数量 |
|||
* @param {number} count.offline 离线数量 |
|||
* @param {number} count.fault 故障数量 |
|||
* @param {number} count.warning 报警数量 |
|||
* @param {number} count.total 总设备数 |
|||
* @returns {object} {} |
|||
*/ |
|||
// eslint-disable-next-line import/prefer-default-export
|
|||
export function generateChartOption(count) { |
|||
return { |
|||
tooltip: { trigger: 'item' }, |
|||
title: { |
|||
text: '设备总数', |
|||
top: '45%', |
|||
left: '50%', |
|||
textAlign: 'center', |
|||
textVerticalAlign: 'middle', |
|||
textStyle: { fontSize: 22 }, |
|||
subtext: count.total, |
|||
subtextStyle: { fontSize: 18 }, |
|||
}, |
|||
|
|||
series: [ |
|||
{ |
|||
name: '设备统计', |
|||
type: 'pie', |
|||
radius: ['70%', '95%'], |
|||
avoidLabelOverlap: false, |
|||
itemStyle: { |
|||
borderRadius: 10, |
|||
borderColor: '#fff', |
|||
borderWidth: 2, |
|||
}, |
|||
label: { |
|||
show: true, |
|||
position: 'inside', |
|||
fontSize: 17, |
|||
color: '#323232', |
|||
// formatter: '{b}\n{c}',
|
|||
lineHeight: 40, |
|||
}, |
|||
emphasis: { |
|||
label: { |
|||
show: true, |
|||
fontSize: 19, |
|||
fontWeight: 'bold', |
|||
}, |
|||
}, |
|||
labelLine: { show: false }, |
|||
data: [ |
|||
{ |
|||
value: count.online, |
|||
name: STATUS_COLOR.ONLINE.text, |
|||
itemStyle: { color: STATUS_COLOR.ONLINE.color }, |
|||
label: { show: count.online }, |
|||
}, |
|||
{ |
|||
value: count.offline, |
|||
name: STATUS_COLOR.OFFLINE.text, |
|||
itemStyle: { color: STATUS_COLOR.OFFLINE.color }, |
|||
label: { show: count.offline }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
name: '设备统计', |
|||
type: 'pie', |
|||
radius: ['35%', '60%'], |
|||
avoidLabelOverlap: false, |
|||
itemStyle: { |
|||
borderRadius: 10, |
|||
borderColor: '#fff', |
|||
borderWidth: 2, |
|||
}, |
|||
label: { |
|||
show: true, |
|||
position: 'inside', |
|||
fontSize: 14, |
|||
color: '#323232', |
|||
// formatter: '{b}\n{c}',
|
|||
lineHeight: 40, |
|||
}, |
|||
emphasis: { |
|||
label: { |
|||
show: true, |
|||
fontSize: 16, |
|||
fontWeight: 'bold', |
|||
}, |
|||
}, |
|||
labelLine: { show: false }, |
|||
data: [ |
|||
{ |
|||
value: count.fault, |
|||
name: STATUS_COLOR.FAULT.text, |
|||
itemStyle: { color: STATUS_COLOR.FAULT.color }, |
|||
label: { show: count.fault }, |
|||
}, |
|||
{ |
|||
value: count.warning, |
|||
name: STATUS_COLOR.WARNING.text, |
|||
itemStyle: { color: STATUS_COLOR.WARNING.color }, |
|||
label: { show: count.warning }, |
|||
}, |
|||
{ |
|||
value: count.normal, |
|||
name: STATUS_COLOR.NORMAL.text, |
|||
itemStyle: { color: STATUS_COLOR.NORMAL.color }, |
|||
label: { show: count.normal }, |
|||
}, |
|||
{ |
|||
value: count.offline, |
|||
name: STATUS_COLOR.OFFLINE.text, |
|||
itemStyle: { |
|||
color: STATUS_COLOR.OFFLINE.color, |
|||
opacity: 0, |
|||
}, |
|||
label: { show: count.offline }, |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
}; |
|||
} |
@ -1,176 +0,0 @@ |
|||
/* eslint-disable max-len,object-curly-newline */ |
|||
import dayjs from 'dayjs'; |
|||
import max from 'lodash/max'; |
|||
import { colors, defaultSelectedLegend, generateDefaultSeries, legendData, yAxisData } from '@/config/chart'; |
|||
|
|||
/** |
|||
* 生成chart所需参数 |
|||
* @param {Object[]} data 服务端返回数据 |
|||
* @param {string} data[].time 时间 ms |
|||
* @param {string} data[].so2 SO2 |
|||
* @param {string} data[].saltR 盐阻 |
|||
* @param {string} data[].saltT 盐温 |
|||
* @param {string} data[].environmentTemperature 环温 |
|||
* @param {string} data[].environmentHumidity 环湿 |
|||
* @param {string} data[].corrosion1 锌 |
|||
* @param {string} data[].corrosion2 铜 |
|||
* @param {string} data[].corrosion3 铝 |
|||
* @param {string} data[].corrosion4 钢 |
|||
* @returns {{environmentTemperature: *[], corrosionXIN: *[], corrosionGANG: *[], corrosionTONG: *[], so2: *[], corrosionLV: *[], time: *[], saltT: *[], saltR: *[], environmentHumidity: *[]}} |
|||
*/ |
|||
function generateParams(data) { |
|||
const result = { |
|||
time: [], |
|||
so2: [], |
|||
saltR: [], |
|||
saltT: [], |
|||
environmentTemperature: [], |
|||
environmentHumidity: [], |
|||
corrosionXIN: [], |
|||
corrosionTONG: [], |
|||
corrosionLV: [], |
|||
corrosionGANG: [], |
|||
}; |
|||
data.forEach(item => { |
|||
result.time.push(dayjs(new Date(+item.time)).format('YY/MM/DD HH:mm')); |
|||
result.so2.push(+item.so2); |
|||
result.saltR.push(+item.saltR); |
|||
result.saltT.push(+item.saltT); |
|||
result.environmentTemperature.push(+item.environmentTemperature); |
|||
result.environmentHumidity.push(+item.environmentHumidity); |
|||
result.corrosionXIN.push(+item.corrosion1); |
|||
result.corrosionTONG.push(+item.corrosion2); |
|||
result.corrosionLV.push(+item.corrosion3); |
|||
result.corrosionGANG.push(+item.corrosion4); |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 计算Y轴的显示 |
|||
* @param {string} yName Y轴的name |
|||
* @param {Object} selectedLegend legends |
|||
* @returns {boolean} |
|||
*/ |
|||
export function computeYAxisShow(yName, selectedLegend) { |
|||
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
|||
for (const key in selectedLegend) { |
|||
if (key.includes(yName) && selectedLegend[key]) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 生产y轴内容 |
|||
* @param {Object} selectedLegend |
|||
* @returns {({axisLabel: {formatter: string}, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string}|{axisLabel: {formatter: string}, offset: number, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string}|{axisLabel: {formatter: string}, offset: number, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string}|{axisLabel: {formatter: string}, offset: number, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string}|{axisLabel: {formatter: string}, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string})[]} |
|||
*/ |
|||
export function generateYAxis(selectedLegend) { |
|||
let leftIndex = 0; |
|||
let rightIndex = 0; |
|||
yAxisData.forEach(item => { |
|||
item.show = computeYAxisShow(item.name, selectedLegend); |
|||
if (item.show) { |
|||
if (item.position === 'left') { |
|||
item.offset = 100 * leftIndex; |
|||
leftIndex += 1; |
|||
} else { |
|||
item.offset = 80 * rightIndex; |
|||
rightIndex += 1; |
|||
} |
|||
} |
|||
}); |
|||
return yAxisData; |
|||
} |
|||
|
|||
/** |
|||
* 生成series数据 |
|||
* @param {Object} data |
|||
* @param {Object[]} yAxis |
|||
* @returns {({data: ([]|number|string|*), name: string, type: string}|{data: ([]|number|BufferSource|string|*), name: string, type: string, yAxisIndex: number}|{data: ([]|string|*), name: string, type: string, yAxisIndex: number}|{data: ([]|string|*), name: string, type: string, yAxisIndex: number}|{data: [], name: string, type: string, yAxisIndex: number})[]|*[]} |
|||
*/ |
|||
function generateSeries(data, yAxis) { |
|||
const seriesArr = generateDefaultSeries(data); |
|||
const showArr = seriesArr.filter(item => yAxis.find(y => y.name === item.name)); |
|||
const hideArr = seriesArr.filter(item => !yAxis.find(y => y.name === item.name)); |
|||
const result = [...showArr, ...hideArr]; |
|||
result.forEach(item => { |
|||
if (item.name.includes('电流')) { |
|||
item.yAxisIndex = 0; |
|||
} else if (item.name.includes('温度')) { |
|||
item.yAxisIndex = 1; |
|||
} else if (item.name.includes('湿度')) { |
|||
item.yAxisIndex = 2; |
|||
} else if (item.name.includes('SO2')) { |
|||
item.yAxisIndex = 3; |
|||
} else if (item.name.includes('阻抗')) { |
|||
item.yAxisIndex = 4; |
|||
} |
|||
}); |
|||
return result || []; |
|||
} |
|||
|
|||
/** |
|||
* 计算图表grid left right值 |
|||
* @param {Object[]} yAxis |
|||
* @returns {{left: number, right: number}} |
|||
*/ |
|||
function generateGrid(yAxis) { |
|||
const left = []; |
|||
const right = []; |
|||
yAxis.forEach(item => { |
|||
if (item.show) { |
|||
if (item.position === 'left') { |
|||
left.push(item.offset || 0); |
|||
} else { |
|||
right.push(item.offset || 0); |
|||
} |
|||
} |
|||
}); |
|||
return { |
|||
left: max(left) + 100, |
|||
right: max(right) + 80, |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* 生成chart参数 |
|||
* @param {Object[]} rawData 返回段返回的data数据 |
|||
* @param {Object[]} yAxis |
|||
* @returns {{yAxis: [{axisLabel: {formatter: string}, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string},{axisLabel: {formatter: string}, offset: number, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string},{axisLabel: {formatter: string}, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string},{axisLabel: {formatter: string}, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string},{axisLabel: {formatter: string}, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string},null,null,null], xAxis: [{data: *[], axisTick: {alignWithLabel: boolean}, type: string}], color: string[], grid: {right: string}, legend: {data: string[]}, series: [{data: *[], name: string, type: string},{data: *[], name: string, type: string, yAxisIndex: number},{data: *[], name: string, type: string, yAxisIndex: number},{data: *[], name: string, type: string, yAxisIndex: number},{data: *[], name: string, type: string, yAxisIndex: number},null,null,null], tooltip: {axisPointer: {type: string}, trigger: string}, toolbox: {feature: {saveAsImage: {show: boolean}, restore: {show: boolean}, dataView: {show: boolean, readOnly: boolean}}}}} |
|||
*/ |
|||
// eslint-disable-next-line import/prefer-default-export
|
|||
export function generateChartOption(rawData, selected = defaultSelectedLegend) { |
|||
const data = generateParams(rawData); |
|||
const yAxis = generateYAxis(selected); |
|||
const series = generateSeries(data, yAxis); |
|||
const grid = generateGrid(yAxis); |
|||
const option = { |
|||
color: colors, |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
type: 'cross', |
|||
snap: true, |
|||
}, |
|||
}, |
|||
grid, |
|||
legend: { |
|||
selected, |
|||
data: legendData, |
|||
}, |
|||
dataZoom: [{ type: 'inside' }, { type: 'slider' }], |
|||
xAxis: [ |
|||
{ |
|||
type: 'category', |
|||
axisTick: { alignWithLabel: true }, |
|||
data: data.time, |
|||
}, |
|||
], |
|||
yAxis, |
|||
series, |
|||
}; |
|||
return option; |
|||
} |
@ -1,16 +0,0 @@ |
|||
import dayjs from 'dayjs'; |
|||
|
|||
/** |
|||
* 格式化时间 |
|||
* @param {string | number} time 时间戳字符串或数字 |
|||
* @param {string} formatStyle |
|||
* @returns |
|||
*/ |
|||
export function formatMsTime(time, formatStyle = 'YYYY-MM-DD HH:mm:ss') { |
|||
return dayjs(new Date(+time)).format(formatStyle); |
|||
} |
|||
|
|||
// 图表时间轴的时间格式化
|
|||
export function formatChartTime(time) { |
|||
return formatMsTime(time, 'MM/DD HH:mm'); |
|||
} |
@ -1,182 +0,0 @@ |
|||
<template> |
|||
<SearchCommands @search="onSearch" /> |
|||
<template v-if="data"> |
|||
<el-table :data="data" :max-height="contentHeight" border stripe style="width: 100%"> |
|||
<el-table-column align="center" fixed label="设备ID" min-width="100" prop="deviceId" /> |
|||
<el-table-column align="center" label="时间" min-width="200"> |
|||
<template #default="scope"> |
|||
{{ dayjs(new Date(+scope.row.createdAt)).format('YYYY-MM-DD HH:mm:ss') }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="传输方向" min-width="80"> |
|||
<template #default="scope"> |
|||
{{ log.DIRECTION[scope.row.dir] }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="传输启动" min-width="80"> |
|||
<template #default="scope"> |
|||
{{ log.PRM[scope.row.prm] }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="安全模式" min-width="80"> |
|||
<template #default="scope"> |
|||
{{ log.SER[scope.row.ser] }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="流控" min-width="80"> |
|||
<template #default="scope"> |
|||
{{ log.DFC[scope.row.dfc] }} |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column align="center" label="功能码" min-width="180"> |
|||
<template #default="scope"> |
|||
{{ log.CODE[scope.row.code].text }} |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column align="center" label="包序号" min-width="80" prop="serialNo" /> |
|||
|
|||
<el-table-column align="center" label="类型标识" min-width="80"> |
|||
<template #default="scope"> |
|||
<span v-if="scope.row.asduType && log.ASDU_TYPE[scope.row.asduType]"> |
|||
{{ log.ASDU_TYPE[scope.row.asduType].text }} |
|||
</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="信息体顺序" min-width="100"> |
|||
<template #default="scope"> |
|||
{{ log.INFO_ORDER[scope.row.infoOrder] }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="信息体个数" min-width="100" prop="infoNum" /> |
|||
|
|||
<el-table-column align="center" label="传输原因" min-width="180"> |
|||
<template #default="scope"> |
|||
{{ log.REASON[scope.row.reason].text }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="协议内容" min-width="80"> |
|||
<template #default="scope"> |
|||
<el-button type="text" @click="openContent(scope.row.content)">查看</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<el-pagination |
|||
:current-page="page.page" |
|||
:default-page-size="50" |
|||
:page-count="page.count" |
|||
:page-size="page.size" |
|||
:page-sizes="[1, 10, 20, 50, 100]" |
|||
:total="page.total" |
|||
background |
|||
class="my-3 float-right" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
@size-change="onSizeChange" |
|||
@current-change="onCurrentPageChange" |
|||
></el-pagination> |
|||
</template> |
|||
|
|||
<!-- 弹出框--> |
|||
<el-dialog v-model="showContent" title="协议内容"> |
|||
<code class="font-mono">{{ contents }}</code> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { getLog } from 'apis'; |
|||
import SearchCommands from 'components/commands/search-commands.vue'; |
|||
import dayjs from 'dayjs'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import { computed, onMounted, ref } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import * as log from '@/config/log'; |
|||
|
|||
const data = ref([]); |
|||
const page = ref({ |
|||
page: 1, |
|||
size: 50, |
|||
count: 0, |
|||
total: 0, |
|||
}); |
|||
const showContent = ref(false); // 是否显示协议内容弹出框 |
|||
const contents = ref(''); // 弹出的协议内容 |
|||
const contentHeight = ref(700); |
|||
const store = useStore(); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); |
|||
|
|||
// 设置表格高度 |
|||
onMounted(() => { |
|||
const winHeight = document.documentElement.clientHeight; |
|||
contentHeight.value = winHeight - 250; |
|||
onSearch(); |
|||
}); |
|||
|
|||
/** |
|||
* 查询 |
|||
* @param {object} options |
|||
* @returns {Promise<void>} |
|||
*/ |
|||
async function onSearch( |
|||
options = { |
|||
...page.value, |
|||
sort: [ |
|||
{ |
|||
col: 'createdAt', |
|||
order: 'DESC', |
|||
}, |
|||
], |
|||
deviceId: currentDeviceId.value, |
|||
}, |
|||
) { |
|||
try { |
|||
const resData = await getLog(options); |
|||
page.value = resData.page; |
|||
data.value = resData.data; |
|||
} catch (error) { |
|||
ElMessage.error('查询失败'); |
|||
throw new Error(error); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 打开协议内容的弹出框 并记录值 |
|||
* @param {string} content 协议内容 |
|||
*/ |
|||
function openContent(content) { |
|||
showContent.value = true; |
|||
contents.value = content; |
|||
} |
|||
|
|||
// 当前页码变化 |
|||
const onCurrentPageChange = pageEvent => { |
|||
const options = { |
|||
page: pageEvent, |
|||
size: page.value.size || 50, |
|||
sort: [ |
|||
{ |
|||
col: 'createdAt', |
|||
order: 'DESC', |
|||
}, |
|||
], |
|||
deviceId: currentDeviceId.value, |
|||
}; |
|||
onSearch(options); |
|||
}; |
|||
|
|||
// 每页条数变化 |
|||
const onSizeChange = size => { |
|||
const options = { |
|||
page: 1, |
|||
size, |
|||
sort: [ |
|||
{ |
|||
col: 'createdAt', |
|||
order: 'DESC', |
|||
}, |
|||
], |
|||
deviceId: currentDeviceId.value, |
|||
}; |
|||
onSearch(options); |
|||
}; |
|||
</script> |
@ -1,32 +0,0 @@ |
|||
<template> |
|||
<!-- 设置站点选择 和 设备下发状态 --> |
|||
<DeviceSelectAndStatus :status="status" /> |
|||
|
|||
<el-tabs v-model="activeName"> |
|||
<el-tab-pane :lazy="true" label="远动参数" name="function"> |
|||
<FunctionConfigPending v-if="activeName === 'function'" :active-name="activeName" /> |
|||
</el-tab-pane> |
|||
<el-tab-pane :lazy="true" label="网络参数" name="network"> |
|||
<NetworkConfigPending v-if="activeName === 'network'" :active-name="activeName" /> |
|||
</el-tab-pane> |
|||
<el-tab-pane :lazy="true" label="远动参数配置记录" name="function-log"> |
|||
<ConfigLogFunction v-if="activeName === 'function-log'" :active-name="activeName" /> |
|||
</el-tab-pane> |
|||
<el-tab-pane :lazy="true" label="网络参数配置记录" name="network-log"> |
|||
<ConfigLogNetwork v-if="activeName === 'network-log'" :active-name="activeName" /> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref } from 'vue'; |
|||
import DeviceSelectAndStatus from 'components/config/device-select-and-status.vue'; |
|||
import FunctionConfigPending from 'components/config/function-config-pending.vue'; |
|||
import NetworkConfigPending from 'components/config/network-config-pending.vue'; |
|||
import ConfigLogFunction from 'components/config/config-log-function.vue'; |
|||
import ConfigLogNetwork from 'components/config/config-log-network.vue'; |
|||
|
|||
const status = ref(''); |
|||
|
|||
const activeName = ref('function'); |
|||
</script> |
@ -1,185 +0,0 @@ |
|||
<template> |
|||
<SearchBar :show-refresh="true" @refresh="onRefresh" @search="getData" /> |
|||
|
|||
<template v-if="data"> |
|||
<el-table :data="data" :max-height="contentHeight" border stripe style="width: 100%"> |
|||
<el-table-column align="center" fixed label="设备ID" min-width="80" prop="deviceId" /> |
|||
<el-table-column align="center" label="采集时间" min-width="170"> |
|||
<template #default="scope"> |
|||
{{ formatTime(+scope.row.time) }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="后台接收时间" min-width="170"> |
|||
<template #default="scope"> |
|||
{{ formatTime(+scope.row.createdAt) }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="锌腐蚀电流(nA)" min-width="130" prop="corrosion1" /> |
|||
<el-table-column align="center" label="铜腐蚀电流(nA)" min-width="130" prop="corrosion2" /> |
|||
<el-table-column align="center" label="铝腐蚀电流(nA)" min-width="130" prop="corrosion3" /> |
|||
<el-table-column align="center" label="钢腐蚀电流(nA)" min-width="130" prop="corrosion4" /> |
|||
<el-table-column align="center" label="环境温度(℃)" min-width="110" prop="environmentTemperature" /> |
|||
<el-table-column align="center" label="环境湿度(RH%)" min-width="130" prop="environmentHumidity" /> |
|||
<el-table-column align="center" label="SO2(ppb)" min-width="90" prop="so2" /> |
|||
<!-- TODO:--> |
|||
<el-table-column align="center" label="盐分温度(℃)" min-width="110" prop="saltT" /> |
|||
<el-table-column align="center" label="盐分阻抗(Ω)" min-width="110" prop="saltR" /> |
|||
<el-table-column align="center" label="机箱温度(℃)" min-width="110" prop="deviceTemperature" /> |
|||
<el-table-column align="center" label="机箱湿度(RH%)" min-width="130" prop="deviceHumidity" /> |
|||
<el-table-column align="center" label="太阳能板电压(V)" min-width="140" prop="solarVoltage" /> |
|||
<el-table-column align="center" label="蓄电池电压(V)" min-width="120" prop="batteryVoltage" /> |
|||
<el-table-column align="center" label="电压百分比" min-width="94" prop="batteryVoltagePercentage" /> |
|||
<el-table-column align="center" label="剩余电量(mAH)" min-width="140" prop="batteryVoltageRemain" /> |
|||
<el-table-column align="center" label="消耗电量(mAH)" min-width="140" prop="batteryLoss" /> |
|||
<el-table-column align="center" label="ICCID" min-width="190" prop="iccid" /> |
|||
<el-table-column align="center" label="IMEI" min-width="150" prop="imei" /> |
|||
<el-table-column align="center" label="信号强度" min-width="80" prop="signal" /> |
|||
<el-table-column align="center" label="基站编号" min-width="130" prop="stationNo" /> |
|||
<el-table-column align="center" label="硬件版本" min-width="80" prop="hardwareVersion" /> |
|||
<el-table-column align="center" label="软件版本" min-width="80" prop="softwareVersion" /> |
|||
</el-table> |
|||
|
|||
<el-pagination |
|||
:current-page="page.page" |
|||
:default-page-size="50" |
|||
:page-count="page.count" |
|||
:page-size="page.size" |
|||
:page-sizes="[1, 10, 20, 50, 100]" |
|||
:total="page.total" |
|||
background |
|||
class="my-3 float-right" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
@current-change="onCurrentPageChange" |
|||
@size-change="onSizeChange" |
|||
@prev-click="onPrev" |
|||
@next-click="onNext" |
|||
></el-pagination> |
|||
</template> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, onMounted, onUnmounted, ref } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import SearchBar from 'components/realtime/search-bar.vue'; |
|||
import { getDatas } from 'apis'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import dayjs from 'dayjs'; |
|||
import { REALTIME_DATA_INTERVAL } from '@/config/config'; |
|||
|
|||
const page = ref({ |
|||
page: 1, |
|||
size: 50, |
|||
}); |
|||
let timer = null; |
|||
let apiTimer = null; |
|||
const store = useStore(); |
|||
const token = computed(() => store.getters['user/token']); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
const contentHeight = ref(600); |
|||
const loadingSearch = ref(false); |
|||
const data = ref(null); |
|||
|
|||
// 获取设备完整信息列表 |
|||
const getData = async () => { |
|||
try { |
|||
if (token && token.value) { |
|||
if (!currentDeviceId.value) { |
|||
return ElMessage.error('请选择站点'); |
|||
} |
|||
|
|||
const date = [dayjs().startOf('day').format('x'), dayjs().endOf('day').format('x')]; |
|||
const params = { |
|||
deviceId: currentDeviceId.value, |
|||
dateRange: date, |
|||
paging: true, |
|||
page: page.value.page, |
|||
size: page.value.size, |
|||
sort: [ |
|||
{ |
|||
col: 'createdAt', |
|||
order: 'DESC', |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
loadingSearch.value = true; |
|||
const resData = await getDatas(params); |
|||
loadingSearch.value = false; |
|||
|
|||
data.value = resData.data; |
|||
page.value = resData.page; |
|||
timer && clearTimeout(timer); |
|||
timer = null; |
|||
|
|||
// 定时轮询请求新数据 |
|||
if (!apiTimer) { |
|||
apiTimer = setInterval(() => { |
|||
getData(); |
|||
}, REALTIME_DATA_INTERVAL); |
|||
} |
|||
} else { |
|||
timer = setTimeout(() => { |
|||
getData(); |
|||
}, 20); |
|||
} |
|||
} catch (error) { |
|||
ElMessage.error(error.message || '获取数据失败'); |
|||
console.log('error: ', error); |
|||
} |
|||
}; |
|||
|
|||
// 设置表格高度 |
|||
onMounted(() => { |
|||
const winHeight = document.documentElement.clientHeight; |
|||
contentHeight.value = winHeight - 170; |
|||
currentDeviceId.value && getData(); |
|||
}); |
|||
|
|||
onUnmounted(() => { |
|||
apiTimer && clearInterval(apiTimer); |
|||
apiTimer = null; |
|||
}); |
|||
|
|||
/** |
|||
* 当前 码变化 |
|||
* 更新page 重新 取数据 |
|||
* @param {number} e 的页码 |
|||
*/ |
|||
const onCurrentPageChange = e => { |
|||
page.value.page = e; |
|||
getData(); |
|||
}; |
|||
|
|||
const onSizeChange = e => { |
|||
page.value.size = e; |
|||
getData(); |
|||
}; |
|||
|
|||
// 下一页 |
|||
const onNext = e => { |
|||
page.value.page = e + 1; |
|||
getData(); |
|||
}; |
|||
|
|||
// 上一页 |
|||
const onPrev = e => { |
|||
page.value.page = e - 1; |
|||
getData(); |
|||
}; |
|||
|
|||
/** |
|||
* 格式化时间 |
|||
* @param {number} time 时间戳 |
|||
* @returns {string} |
|||
*/ |
|||
function formatTime(time) { |
|||
return dayjs(new Date(time)).format('YYYY-MM-DD HH:mm:ss'); |
|||
} |
|||
|
|||
// 手动刷新 清除定时器 重新获取数据 |
|||
function onRefresh() { |
|||
apiTimer && clearInterval(apiTimer); |
|||
apiTimer = null; |
|||
getData(); |
|||
} |
|||
</script> |
@ -0,0 +1,3 @@ |
|||
<template>测试</template> |
|||
|
|||
<script setup></script> |
@ -1,192 +0,0 @@ |
|||
<template> |
|||
<el-form ref="deviceEdit" :model="data" :rules="deviceRules" label-position="top"> |
|||
<el-row :gutter="20"> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="设备ID" prop="deviceId"> |
|||
<el-input v-model="data.deviceId" disabled></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="站点名称" prop="address"> |
|||
<el-input v-model="data.address"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="产品类型" prop="type"> |
|||
<!-- <el-input v-model="data.securityMode"></el-input> --> |
|||
<el-radio v-model="data.type" label="IACD">大气腐蚀</el-radio> |
|||
<el-radio v-model="data.type" label="OTHER">其他</el-radio> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="设备完整ID" prop="deviceFullId"> |
|||
<el-input v-model="data.deviceFullId"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="地区" prop="area"> |
|||
<el-input v-model="data.area"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="联系人" prop="contact"> |
|||
<el-input v-model="data.contact"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="电话" prop="phone"> |
|||
<el-input v-model="data.phone"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="经度" prop="lon"> |
|||
<el-input v-model="data.lon"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="纬度" prop="lat"> |
|||
<el-input v-model="data.lat"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="负责人" prop="head"> |
|||
<el-input v-model="data.head"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="安装位置" prop="installLocation"> |
|||
<el-input v-model="data.installLocation"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="安装时间" prop="installTime"> |
|||
<el-date-picker v-model="data.installTime" placeholder="安装时间" style="width: 100%" type="datetime"></el-date-picker> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="正式运行时间" prop="runTime"> |
|||
<el-date-picker v-model="data.runTime" placeholder="正式运行时间" style="width: 100%" type="datetime"></el-date-picker> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="链路地址" prop="linkAddress"> |
|||
<el-input v-model="data.linkAddress"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="探头编号" prop="probNo"> |
|||
<el-input v-model="data.probNo"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="试样" prop="simple"> |
|||
<el-input v-model="data.simple"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="sim卡1" prop="sim1"> |
|||
<el-input v-model="data.sim1"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="设备朝向" prop="deviceDirection"> |
|||
<el-input v-model="data.deviceDirection"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="协议版本" prop="protocolVersion"> |
|||
<el-input v-model="data.protocolVersion"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="与主站后台联调情况" prop="joint"> |
|||
<el-input v-model="data.joint" type="textarea"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
|
|||
<el-col :lg="8" :md="12" :span="12" :xl="6" :xs="24"> |
|||
<el-form-item label="备注" prop="remark"> |
|||
<el-input v-model="data.remark" type="textarea"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row> |
|||
<el-col :span="24"> |
|||
<el-form-item label="维修记录" prop="operationRecord"> |
|||
<el-input v-model="data.operationRecord" type="textarea"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row :gutter="20" class="mt-5 pl-2"> |
|||
<el-form-item> |
|||
<el-button type="primary" @click="onSubmit">提交</el-button> |
|||
<el-button @click="$emit('cancel')">取消</el-button> |
|||
</el-form-item> |
|||
</el-row> |
|||
</el-form> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, defineEmits, reactive, ref, watch } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { updateDevice } from 'apis'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import { deviceData, deviceRules } from '@/config/config'; |
|||
|
|||
let data = reactive(deviceData); |
|||
|
|||
const deviceEdit = ref(null); // form |
|||
const store = useStore(); |
|||
const device = computed(() => store.getters['device/current']); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); |
|||
const emit = defineEmits(['cancel']); |
|||
|
|||
watch( |
|||
() => device.value, |
|||
newValue => { |
|||
data = newValue; |
|||
}, |
|||
{ |
|||
immediate: true, |
|||
deep: true, |
|||
}, |
|||
); |
|||
|
|||
// 提交表单 |
|||
const onSubmit = () => { |
|||
deviceEdit.value.validate(async valid => { |
|||
if (valid) { |
|||
if (data.installTime) { |
|||
data.installTime = new Date(data.installTime).getTime(); |
|||
} |
|||
if (data.runTime) { |
|||
data.runTime = new Date(data.runTime).getTime(); |
|||
} |
|||
try { |
|||
await updateDevice(currentDeviceId.value, data); |
|||
ElMessage.success('更新成功'); |
|||
emit('cancel'); |
|||
store.commit('device/updateDevice', data); |
|||
await store.dispatch('device/getDevices'); // 更新站点列表 |
|||
} catch (error) { |
|||
ElMessage.error('更新失败, 请稍后重试'); |
|||
throw new Error(error); |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
</script> |
@ -1,216 +0,0 @@ |
|||
<template> |
|||
<SearchBar @search="onSearch" /> |
|||
|
|||
<div class="my-3"> |
|||
<el-button type="primary" @click="deviceCreate.show()"> |
|||
<i class="el-icon-plus mr-2"></i> |
|||
添加设备 |
|||
</el-button> |
|||
</div> |
|||
|
|||
<template v-if="devicesAll && devicesAll.data"> |
|||
<el-table :data="devicesAll.data" :max-height="contentHeight" border stripe style="width: 100%"> |
|||
<el-table-column type="expand"> |
|||
<template #default="props"> |
|||
<el-row :gutter="20" class="px-6 text-gray-400"> |
|||
<el-col :span="9">链路地址:{{ props.row.linkAddress }}</el-col> |
|||
<el-col :span="9">探头编号:{{ props.row.probNo }}</el-col> |
|||
<el-col :span="9">设备朝向:{{ props.row.deviceDirection }}</el-col> |
|||
<el-col :span="9">试样:{{ props.row.simple }}</el-col> |
|||
<el-col :span="9">安装位置:{{ props.row.installLocation }}</el-col> |
|||
<el-col :span="9">sim1:{{ props.row.sim1 }}</el-col> |
|||
<el-col :span="9">协议版本:{{ props.row.protocolVersion }}</el-col> |
|||
<el-col :span="9"> |
|||
产品类型: |
|||
<span v-if="ASDU_TYPE[props.row.type]">{{ ASDU_TYPE[props.row.type].text }}</span> |
|||
</el-col> |
|||
<el-col :span="9">与主站后台联调情况:{{ props.row.joint }}</el-col> |
|||
<el-col :span="9">备注:{{ props.row.remark }}</el-col> |
|||
<el-col :span="9">维修记录:{{ props.row.operationRecord }}</el-col> |
|||
</el-row> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="设备ID" min-width="100" prop="deviceId" /> |
|||
<el-table-column align="center" label="设备完整ID" min-width="150" prop="deviceFullId" /> |
|||
<el-table-column align="left" header-align="center" label="站点" min-width="150" prop="address" /> |
|||
<el-table-column align="left" header-align="center" label="地区" min-width="120" prop="area" /> |
|||
<el-table-column align="center" label="状态" min-width="70"> |
|||
<template #default="scope"> |
|||
<el-tag v-if="scope.row.status" :color="STATUS_COLOR[scope.row.status].color" style="color: #fff"> |
|||
{{ STATUS_COLOR[scope.row.status].text }} |
|||
</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" label="联系人" min-width="100" prop="contact" /> |
|||
<el-table-column align="center" label="联系电话" min-width="150" prop="phone" /> |
|||
<el-table-column align="center" label="安装时间" min-width="200" prop="installTime" /> |
|||
<el-table-column align="center" label="正式运行时间" min-width="200" prop="runTime" /> |
|||
<el-table-column align="center" fixed="right" label="操作" min-width="180"> |
|||
<template #default="props"> |
|||
<el-tooltip class="item" content="参数配置" effect="dark" placement="top"> |
|||
<i class="el-icon-set-up text-base text-yellow-600 mx-1" @click="openPage(props.row, 'config')"></i> |
|||
</el-tooltip> |
|||
<el-tooltip class="item" content="实时数据" effect="dark" placement="top"> |
|||
<i class="el-icon-odometer text-base text-green-600 mx-1" @click="openPage(props.row, 'data-realtime')"></i> |
|||
</el-tooltip> |
|||
<el-tooltip class="item" content="历史数据" effect="dark" placement="top"> |
|||
<i class="el-icon-document-copy text-base text-green-600 mx-1" @click="openPage(props.row, 'history')"></i> |
|||
</el-tooltip> |
|||
<el-tooltip class="item" content="数据统计" effect="dark" placement="top"> |
|||
<i class="el-icon-data-line text-base text-green-600 mx-1" @click="openPage(props.row, 'statistical')"></i> |
|||
</el-tooltip> |
|||
|
|||
<el-popconfirm title="确定要删除此设备吗?" @confirm="handleDelete(props.row.deviceId)"> |
|||
<template #reference> |
|||
<i class="el-icon-delete text-base text-red-600 mx-1"></i> |
|||
<!-- <el-button type="text" plain size="mini" icon="el-icon-delete"></el-button> --> |
|||
</template> |
|||
</el-popconfirm> |
|||
|
|||
<el-tooltip class="item" content="编辑设备信息" effect="dark" placement="top"> |
|||
<i class="el-icon-edit text-base text-blue-600 mx-1" @click="handleEdit(props.row)"></i> |
|||
</el-tooltip> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<el-pagination |
|||
:current-page="devicesAll.page.page" |
|||
:default-page-size="50" |
|||
:page-count="devicesAll.page.count" |
|||
:page-size="devicesAll.page.size" |
|||
:page-sizes="[1, 10, 20, 50, 100]" |
|||
:total="devicesAll.page.total" |
|||
background |
|||
class="my-3 float-right" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
@size-change="onSizeChange" |
|||
@current-change="onCurrentPageChange" |
|||
></el-pagination> |
|||
</template> |
|||
|
|||
<!-- 编辑设备信息 --> |
|||
<DeviceEdit :show="editing" @toggle-mdoal="editing = false" /> |
|||
|
|||
<el-dialog v-model="deviceCreate.display" title="添加设备" top="30px" width="80%"> |
|||
<DeviceCreate @on-hide="deviceCreate.hide()" /> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed, onMounted, reactive, ref } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { useRoute, useRouter } from 'vue-router'; |
|||
import { deleteDevice } from 'apis'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import SearchBar from 'components/realtime/search-bar.vue'; |
|||
import DeviceEdit from 'components/device/device-edit.vue'; |
|||
import DeviceCreate from 'components/device/device-create.vue'; |
|||
import useDeviceCreate from '@/hooks/useDeviceCreate'; |
|||
import { STATUS_COLOR } from '@/config/config'; |
|||
import { ASDU_TYPE } from '@/config/log'; |
|||
|
|||
let timer = null; |
|||
const store = useStore(); |
|||
const router = useRouter(); |
|||
const route = useRoute(); |
|||
const token = computed(() => store.getters['user/token']); |
|||
const devicesAll = computed(() => { |
|||
return store.state.device.devicesAll; |
|||
}); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); |
|||
const contentHeight = ref(600); |
|||
const editing = ref(false); |
|||
const { show, display, hide } = useDeviceCreate(); |
|||
const deviceCreate = reactive({ |
|||
show, |
|||
display, |
|||
hide, |
|||
}); // 设备添加hook |
|||
|
|||
// 获取设备完整信息列表 |
|||
const getDevicesAllData = () => { |
|||
try { |
|||
if (token && token.value) { |
|||
store.dispatch('device/getDevicesAll'); |
|||
timer && clearTimeout(timer); |
|||
timer = null; |
|||
} else { |
|||
timer = setTimeout(() => { |
|||
getDevicesAllData(); |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
ElMessage.error(error); |
|||
throw new Error(error); |
|||
} |
|||
}; |
|||
|
|||
getDevicesAllData(); |
|||
|
|||
// 设置表格高度 |
|||
onMounted(() => { |
|||
const winHeight = document.documentElement.clientHeight; |
|||
contentHeight.value = winHeight - 250; |
|||
}); |
|||
|
|||
/** |
|||
* 删除 |
|||
* @param {number} page 页码 |
|||
* @param {number} size 每页条数 |
|||
*/ |
|||
function onSearch(page, size = 50) { |
|||
const deviceId = currentDeviceId.value; |
|||
const params = { |
|||
deviceId, |
|||
page, |
|||
size, |
|||
}; |
|||
store.dispatch('device/getDevicesAll', params); |
|||
} |
|||
|
|||
// 当前页码变化 |
|||
const onCurrentPageChange = page => { |
|||
onSearch(page, devicesAll.value.page.size || 50); |
|||
}; |
|||
|
|||
// 每页条数变化 |
|||
const onSizeChange = size => { |
|||
onSearch(1, size); |
|||
}; |
|||
|
|||
/** |
|||
* 删除某设备 |
|||
* @param {string} deviceId 设备id |
|||
*/ |
|||
const handleDelete = async deviceId => { |
|||
try { |
|||
await deleteDevice(deviceId); |
|||
store.commit('device/deleteDevice', deviceId); |
|||
ElMessage.success('删除成功'); |
|||
store.dispatch('device/getDevices'); // 更新站点列表 |
|||
} catch (error) { |
|||
ElMessage.error('删除失败, 请稍后重试'); |
|||
throw new Error(error); |
|||
} |
|||
}; |
|||
|
|||
// 编辑设备信息 |
|||
const handleEdit = item => { |
|||
store.commit('device/setCurrentDeviceId', item.deviceId); |
|||
editing.value = true; |
|||
}; |
|||
|
|||
/** |
|||
* 打开页面 |
|||
* @param {object} item 设备对象 |
|||
* @param {string} pageName 页面name |
|||
*/ |
|||
const openPage = (item, pageName) => { |
|||
item && store.commit('device/setCurrentDeviceId', item.deviceId); |
|||
router.push({ |
|||
path: `/corrosion/${pageName}`, |
|||
query: route.query, |
|||
}); |
|||
}; |
|||
</script> |
@ -1,22 +0,0 @@ |
|||
<template> |
|||
<el-tabs v-model="activeName"> |
|||
<el-tab-pane :lazy="true" label="本地数据" name="local"> |
|||
<Local v-if="activeName === 'local'" :active-name="activeName" /> |
|||
</el-tab-pane> |
|||
<el-tab-pane :lazy="true" label="设备数据" name="device"> |
|||
<Device v-if="activeName === 'device'" :active-name="activeName" /> |
|||
</el-tab-pane> |
|||
<el-tab-pane :lazy="true" label="补传记录" name="log"> |
|||
<HistoryLog v-if="activeName === 'log'" :active-name="activeName" /> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref } from 'vue'; |
|||
import Local from 'components/history/local.vue'; |
|||
import Device from 'components/history/device.vue'; |
|||
import HistoryLog from 'components/history/history-log.vue'; |
|||
|
|||
const activeName = ref('local'); |
|||
</script> |
@ -0,0 +1,12 @@ |
|||
<template> |
|||
<div> |
|||
<div>左边 时间轴</div> |
|||
<div> |
|||
<router-view></router-view> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup></script> |
|||
|
|||
<style scoped></style> |
@ -1,23 +0,0 @@ |
|||
<template> |
|||
<el-row :gutter="20"> |
|||
<el-col :span="8"> |
|||
<el-card shadow="hover"> |
|||
<ChartDeviceCount /> |
|||
</el-card> |
|||
</el-col> |
|||
<el-col :span="16"> |
|||
<ChartDeviceDetail /> |
|||
</el-col> |
|||
</el-row> |
|||
<DeviceTable /> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import ChartDeviceCount from 'components/overview/chart-device-count.vue'; |
|||
import ChartDeviceDetail from 'components/overview/chart-device-detail.vue'; |
|||
import DeviceTable from 'components/overview/device-table.vue'; |
|||
import { useStore } from 'vuex'; |
|||
|
|||
const store = useStore(); |
|||
store.dispatch('device/getDevicesCount'); // 获取设备数量信息 |
|||
</script> |
@ -1,80 +0,0 @@ |
|||
<template> |
|||
<SearchBar ref="searchBar" :loading-search="loadingSearch" @search="onSearch" /> |
|||
<HistoryData :search-height="searchHeight" class="mt-4" /> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import SearchBar from 'components/statistical/search-bar.vue'; |
|||
import HistoryData from 'components/statistical/stastistical-chart.vue'; |
|||
import { computed, onMounted, ref } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import dayjs from 'dayjs'; |
|||
|
|||
const searchBar = ref(null); |
|||
let timer = null; |
|||
const store = useStore(); |
|||
const token = computed(() => store.getters['user/token']); |
|||
const currentDeviceId = computed(() => store.state.device.currentDeviceId); // 正在操作的设备的id |
|||
const search = ref({}); |
|||
const loadingSearch = ref(false); |
|||
const searchHeight = ref(50); |
|||
|
|||
onMounted(() => { |
|||
searchHeight.value = searchBar.value.$el.clientHeight; |
|||
}); |
|||
|
|||
/** |
|||
* 历史数据统计 |
|||
* @param {*} deviceId // 站点 设备id |
|||
* @param {*} date // 年月时间段 |
|||
* @param {*} sort // 1 -> 按时间正序 -1->按时间倒序 |
|||
* @param {*} page // 第几页 |
|||
* @param {*} size // 每页多少条 |
|||
* @param {*} type // 0查全部 |
|||
*/ |
|||
const getData = async () => { |
|||
try { |
|||
if (token && token.value) { |
|||
if (!currentDeviceId.value) return; |
|||
loadingSearch.value = true; |
|||
const options = { ...search.value }; |
|||
const date = options && options.date ? options.date : [+dayjs().subtract(7, 'day').startOf('day').format('x'), +dayjs().format('x')]; |
|||
const params = { |
|||
deviceId: currentDeviceId.value, |
|||
gatheredDateRange: date, |
|||
paging: false, |
|||
sort: [ |
|||
{ |
|||
col: 'time', |
|||
order: 'ASC', |
|||
}, |
|||
], |
|||
}; |
|||
await store.dispatch('statistics/getRealtimeData', params); |
|||
timer && clearTimeout(timer); |
|||
timer = null; |
|||
loadingSearch.value = false; |
|||
} else { |
|||
loadingSearch.value = false; |
|||
timer = setTimeout(() => { |
|||
getData(); |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
loadingSearch.value = false; |
|||
ElMessage.error(error.message || '查询失败'); |
|||
} |
|||
}; |
|||
|
|||
getData(); |
|||
|
|||
/** |
|||
* 监听search信息 |
|||
* @param {object} payload search组件emit的数据 |
|||
*/ |
|||
const onSearch = payload => { |
|||
search.value = { ...payload }; |
|||
getData(); |
|||
}; |
|||
</script> |
@ -0,0 +1,3 @@ |
|||
<template>登录</template> |
|||
|
|||
<script setup></script> |
File diff suppressed because it is too large
Loading…
Reference in new issue