You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
481 lines
14 KiB
481 lines
14 KiB
<template> |
|
<div class="flex-col gap-16 wh-full"> |
|
<el-button type="primary" @click="onBack" class="w-150px"> |
|
<i class="i-line-md:arrow-left"></i>返回站点数据 |
|
</el-button> |
|
<EdfsWrap |
|
:title="isonLineTransfer ? `在线设备列表` : `历史设备列表`" |
|
class="flex-1" |
|
useScrollBar |
|
:shapeColor="isonLineTransfer ? '#4B9E5F' : '#F1BF63'" |
|
> |
|
<template #title-right> |
|
<template v-if="env.VITE_APP_ENV == 'local'"> |
|
<template v-if="isBatchTransfer || isBatchUpgrade"> |
|
<el-button type="primary" @click="onBatchSave"> |
|
确定{{ batchText }} |
|
</el-button> |
|
<el-button type="info" @click="onBatchCancel"> 取消</el-button> |
|
</template> |
|
<template v-else> |
|
<el-button type="primary" @click="onBatchTransfer"> |
|
<i class="i-mdi:database-arrow-right-outline mr-1"/> |
|
{{ isonLineTransfer ? '数据迁移' : '数据导出' }} |
|
</el-button> |
|
<el-button v-if="isonLineTransfer" type="primary" @click="onBatchUpgrade"> |
|
<i class="i-codicon:chip mr-1"/>批量升级 |
|
</el-button> |
|
</template> |
|
</template> |
|
</template> |
|
<div class="device-list-wrap"> |
|
<el-checkbox-group v-model="checkDeviceList"> |
|
<div class="device-item" v-for="item in devices"> |
|
<div class="device-item-header"> |
|
<div class="flex items-center"> |
|
<el-checkbox |
|
:value="item.sn" |
|
v-if="(isBatchTransfer || isBatchUpgrade) && item.status !== '离线'" |
|
> |
|
<div>设备ID: {{ item.sn }}</div> |
|
</el-checkbox> |
|
<div v-else |
|
class="h-32 leading-32px"> |
|
<div class="overflow-hidden text-ellipsis whitespace-nowrap w-140">设备ID: |
|
{{ item.sn }} |
|
</div> |
|
</div> |
|
</div> |
|
<div |
|
class="flex items-center gap-col-1" |
|
v-if="!(isBatchTransfer || isBatchUpgrade)" |
|
> |
|
<template v-if="env.VITE_APP_ENV == 'local'"> |
|
<el-tooltip |
|
:content="isonLineTransfer ? '数据迁移' : '数据导出'" |
|
v-if="isonLineTransfer ? item.status === '在线' : true" |
|
> |
|
<i |
|
class="i-mdi:database-arrow-right-outline :hover:color-[#8ACE6A] color-[#4B9E5F] cursor-pointer text-20px" |
|
@click="onBatchTransferSave([item])" |
|
/> |
|
</el-tooltip> |
|
<el-tooltip |
|
content="固件升级" |
|
v-if="isonLineTransfer && item.status === '在线'" |
|
> |
|
<i |
|
class="i-codicon:chip :hover:color-[#8ACE6A] color-[#4B9E5F] cursor-pointer text-20px" |
|
@click="onFirmwareUpload([item])" |
|
/> |
|
</el-tooltip> |
|
</template> |
|
|
|
<el-tooltip content="数据详情"> |
|
<i |
|
class="i-material-symbols:info-outline :hover:color-[#8ACE6A] color-[#4B9E5F] cursor-pointer text-20px" |
|
@click="onDeviceDetails(item)" |
|
/> |
|
</el-tooltip> |
|
<el-tooltip content="拓扑详情"> |
|
<i |
|
class="i-hugeicons:node-edit :hover:color-[#8ACE6A] color-[#4B9E5F] cursor-pointer text-20px" |
|
@click="onTopology(item)" |
|
/> |
|
</el-tooltip> |
|
|
|
</div> |
|
</div> |
|
|
|
<div class="device-item-body relative"> |
|
<template v-if="isonLineTransfer"> |
|
<template v-for="key in Object.keys(onlineDeviceMap)"> |
|
<div class="info-item" v-if="isonLineTransfer && key === 'status'"> |
|
<div>{{ onlineDeviceMap.status }}:</div> |
|
<el-tag :type="item.status === '在线' ? 'success' : 'danger'"> |
|
{{ item.status }} |
|
</el-tag> |
|
</div> |
|
<div class="info-item" v-else> |
|
<div>{{ onlineDeviceMap[key as keyof typeof onlineDeviceMap] }}:</div> |
|
<div>{{ item[key] }}</div> |
|
</div> |
|
</template> |
|
</template> |
|
<template v-else> |
|
<template v-for="key in Object.keys(offlineDeviceMap)"> |
|
<div class="info-item" v-if="key === 'create_time'"> |
|
<div> |
|
{{ offlineDeviceMap[key as keyof typeof offlineDeviceMap] }}: |
|
</div> |
|
<div>{{ dayjs(item[key]).format('YYYY-MM-DD HH:mm:ss') }}</div> |
|
</div> |
|
<div class="info-item" v-else> |
|
<div> |
|
{{ offlineDeviceMap[key as keyof typeof offlineDeviceMap] }}: |
|
</div> |
|
<div>{{ item[key] }}</div> |
|
</div> |
|
</template> |
|
</template> |
|
</div> |
|
</div> |
|
</el-checkbox-group> |
|
</div> |
|
</EdfsWrap> |
|
</div> |
|
|
|
<OnLineTransferDlg |
|
ref="onLineTransferDlgRef" |
|
@on-save="onLineDeviceTransfer" |
|
:is-batch-transfer="isBatchTransfer" |
|
/> |
|
<OffTransferDlg |
|
ref="offTransferDlgRef" |
|
:isBatchTransfer="false" |
|
:siteInfo="siteInfo" |
|
@on-save="onOffDeviceTransfer" |
|
/> |
|
<DeviceDrawer |
|
v-model="isShowDetails" |
|
ref="deviceDrawerRef" |
|
:siteInfo="siteInfo" |
|
:is-transfer="isonLineTransfer" |
|
/> |
|
</template> |
|
|
|
<script setup lang="ts"> |
|
import dayjs from 'dayjs' |
|
import OnLineTransferDlg from './components/onLineTransferDlg.vue' |
|
import OffTransferDlg from './components/offTransferDlg.vue' |
|
import { useTransferDataStore } from '@/stores/transferData' |
|
import { storeToRefs } from 'pinia' |
|
import type { IDevice, IOfflineDevice, IOnlineDevice } from '../type' |
|
import { useMessage } from '@/composables/useMessage' |
|
import { getDeviceList, type ISite } from '@/api/module/transfer' |
|
import DeviceDrawer from './components/deviceDrawer.vue' |
|
|
|
import { getFirmwarePath } from '@/api/module/firmware' |
|
import { createTask, type TaskCreateParams } from '@/api/module/taks' |
|
import EdfsWrap from "@/components/Edfs-wrap.vue"; |
|
|
|
const env = import.meta.env |
|
const onLineTransferDlgRef = ref<typeof OnLineTransferDlg>() |
|
const router = useRouter() |
|
const route = useRoute() |
|
const siteInfo = ref<ISite>( |
|
route.query.site ? JSON.parse(route.query.site as string) : null |
|
) |
|
|
|
const type = ref<'export' | 'details'>(route.query.type as 'export' | 'details') |
|
const isonLineTransfer = computed(() => type.value === 'export') |
|
const message = useMessage() |
|
|
|
const transferDataStore = useTransferDataStore() |
|
const { devicesMap } = storeToRefs(transferDataStore) |
|
|
|
const transferStatus = ref< |
|
'progress' | 'success' | 'failed' | 'timeout' | 'stop' | undefined |
|
>() |
|
|
|
const devices = computed(() => { |
|
return isonLineTransfer.value |
|
? Array.from(devicesMap.value.values()) |
|
: offLineDeviceList.value |
|
}) as Ref<any[]> |
|
|
|
// 在线设备批量迁移 |
|
function onLineDeviceTransfer() { |
|
if (isBatchTransfer.value) { |
|
onBatchCancel() |
|
} |
|
} |
|
|
|
const onlineDeviceMap: Record< |
|
keyof Omit< |
|
IOnlineDevice, |
|
'lastUpdated' | 'sn' | 'isChecked' | 'upFirmware' | 'upFirmwareStatus' | 'versions' |
|
>, |
|
string |
|
> = { |
|
status: '状态', |
|
site_id: '站点名称', |
|
clientIp: '客户端IP', |
|
footprint: '数据占用空间', |
|
} |
|
const offlineDeviceMap: Record< |
|
keyof Pick<IOfflineDevice, 'site_id' | 'db' | 'create_time'>, |
|
string |
|
> = { |
|
site_id: '站点名称', |
|
db: '数据库', |
|
create_time: '创建时间', |
|
} |
|
|
|
const checkDeviceList = ref<string[]>([]) |
|
|
|
const isBatchTransfer = ref(false) |
|
|
|
function onBatchTransfer() { |
|
checkDeviceList.value = [] |
|
isBatchTransfer.value = true |
|
} |
|
|
|
function onBatchTransferSave(checkDeviceList: IOnlineDevice[] | IOfflineDevice[]) { |
|
if (isonLineTransfer.value) { |
|
const lineDevices = checkDeviceList as IOnlineDevice[] |
|
const site = lineDevices[0]?.site_id |
|
if (!site) { |
|
message.error('请选择站点') |
|
return |
|
} |
|
const deivcesParms = lineDevices.map(item => ({ |
|
sn: item.sn, |
|
host: item.clientIp, |
|
disk: item.footprint, |
|
})) |
|
|
|
const parmas: TaskCreateParams = { |
|
site, |
|
devices: deivcesParms, |
|
mode: 'export', |
|
} |
|
onLineTransferDlgRef.value?.open(parmas) |
|
} else { |
|
const offlineDevices = checkDeviceList as IOfflineDevice[] |
|
const site = offlineDevices[0]?.site_id |
|
const deivcesParms = offlineDevices.map(item => ({ |
|
sn: item.sn, |
|
})) |
|
|
|
const parmas: TaskCreateParams = { |
|
site, |
|
devices: deivcesParms, |
|
mode: 'import', |
|
} |
|
|
|
offTransferDlgRef.value?.open(parmas) |
|
} |
|
} |
|
|
|
const isBatchUpgrade = ref(false) |
|
|
|
function onBatchUpgrade() { |
|
checkDeviceList.value = [] |
|
isBatchUpgrade.value = true |
|
} |
|
|
|
const batchText = computed(() => { |
|
return isBatchTransfer.value ? '迁移' : '升级' |
|
}) |
|
|
|
function onBatchSave() { |
|
if (!checkDeviceList.value.length) { |
|
message.error(`请选择要${batchText.value}的设备`) |
|
return |
|
} |
|
|
|
const checkList = isonLineTransfer.value |
|
? getOnlineDeviceList() |
|
: getOfflineDeviceList() |
|
|
|
if (isBatchTransfer.value) { |
|
onBatchTransferSave(checkList) |
|
} |
|
|
|
if (isBatchUpgrade.value && isonLineTransfer.value) { |
|
onFirmwareUpload(checkList as IOnlineDevice[]) |
|
} |
|
} |
|
|
|
function getOnlineDeviceList(): IOnlineDevice[] { |
|
return checkDeviceList.value |
|
.map(sn => { |
|
return devicesMap.value.get(sn) ?? undefined |
|
}) |
|
.filter(Boolean) as IOnlineDevice[] |
|
} |
|
|
|
function getOfflineDeviceList(): IOfflineDevice[] { |
|
return offLineDeviceList.value.filter(item => checkDeviceList.value.includes(item.sn)) |
|
} |
|
|
|
function onBatchCancel() { |
|
isBatchTransfer.value = false |
|
isBatchUpgrade.value = false |
|
checkDeviceList.value = [] |
|
} |
|
|
|
function onBack() { |
|
router.push('/station') |
|
} |
|
|
|
// 监听页面刷新 |
|
window.onbeforeunload = function () { |
|
stop() |
|
} |
|
|
|
onBeforeRouteLeave(async (to, from, next) => { |
|
if (transferStatus.value === 'progress') { |
|
try { |
|
await message.confirm('当前迁移尚未完成,是否确认离开?') |
|
window.location.href = to.fullPath |
|
} catch (error) { |
|
next(false) |
|
} |
|
} else { |
|
next() |
|
} |
|
}) |
|
|
|
const offLineDeviceList = ref<IOfflineDevice[]>([]) |
|
|
|
async function loadDeviceList() { |
|
const res = await getDeviceList(siteInfo.value.name) |
|
if (res.code === 200 || res.code === 0) { |
|
offLineDeviceList.value = res.data |
|
} |
|
} |
|
|
|
const firmwarePath = ref('') |
|
|
|
onMounted(async () => { |
|
if (!isonLineTransfer.value) { |
|
loadDeviceList() |
|
} else { |
|
const res = await getFirmwarePath() |
|
if (res.code === 200 || res.code === 0) { |
|
firmwarePath.value = res.data.path |
|
} |
|
} |
|
}) |
|
|
|
const isShowDetails = ref(false) |
|
const deviceDrawerRef = ref<typeof DeviceDrawer>() |
|
|
|
function onDeviceDetails(item: IOfflineDevice) { |
|
deviceDrawerRef.value?.openFullScreen() |
|
deviceDrawerRef.value?.open(item) |
|
} |
|
|
|
function onTopology(item: IDevice) { |
|
console.log(item) |
|
} |
|
|
|
const isCanFirmwareUpload = computed(() => !!firmwarePath.value && isonLineTransfer.value) |
|
|
|
const upgradeSnList = ref<string[]>([]) |
|
|
|
async function onFirmwareUpload(devices: IOnlineDevice[]) { |
|
upgradeSnList.value = [] |
|
if (!isCanFirmwareUpload.value) { |
|
message.error('升级失败,请检查固件路径') |
|
onBatchCancel() |
|
return |
|
} |
|
const site = devices[0]?.site_id |
|
if (!site) { |
|
message.error('请选择站点') |
|
return |
|
} |
|
const deivcesParms = devices.map(item => ({ |
|
sn: item.sn, |
|
host: item.clientIp, |
|
disk: item.footprint, |
|
})) |
|
|
|
const parmas: TaskCreateParams = { |
|
site, |
|
devices: deivcesParms, |
|
mode: 'update', |
|
firmware: firmwarePath.value, |
|
} |
|
const res = await createTask(parmas) |
|
if (res.code !== 0) { |
|
message.error(`任务创建失败`) |
|
} else { |
|
message.success('任务创建成功,请在任务列表中查看') |
|
} |
|
|
|
onBatchCancel() |
|
} |
|
|
|
// ================ 离线数据导入 ========= |
|
const offTransferDlgRef = ref<typeof OffTransferDlg>() |
|
|
|
function onOffDeviceTransfer() { |
|
onBatchCancel() |
|
} |
|
|
|
// ================ 离线数据导入 end ========= |
|
|
|
// const transferLogScrollbar = ref() |
|
|
|
// watch(siteTransferLogList, () => { |
|
// nextTick(() => { |
|
// if (transferLogScrollbar.value) { |
|
// const scrollbar = transferLogScrollbar.value |
|
// scrollbar.setScrollTop(scrollbar.wrap$.scrollHeight) |
|
// } |
|
// }) |
|
// }, { deep: true }) |
|
</script> |
|
|
|
<style scoped lang="scss"> |
|
.transfer-log-wrap { |
|
margin-top: 10px; |
|
@apply border-radius-8px bg-[#F9FAFB] p-10; |
|
|
|
:deep(.el-scrollbar) { |
|
height: calc(100% - 20px); |
|
} |
|
} |
|
|
|
.device-list-wrap { |
|
@apply wh-full; |
|
|
|
:deep(.el-checkbox-group) { |
|
@apply wh-full flex flex-wrap gap-col-6 gap-row-4; |
|
} |
|
|
|
:deep(.el-checkbox__inner) { |
|
width: 18px; |
|
height: 18px; |
|
} |
|
|
|
:deep(.el-checkbox__label) { |
|
@apply text-14px font-500 text-[#313131]; |
|
} |
|
|
|
:deep(.el-checkbox__input.is-checked + .el-checkbox__label) { |
|
color: var(--el-color-primary); |
|
} |
|
|
|
.device-item { |
|
@apply w-289 h-160 border border-solid border-[#E0E0E0] rounded-8px p-x-14 p-y-10 flex-col; |
|
|
|
.device-item-header { |
|
@apply w-full text-black text-18px font-500 flex items-center justify-between; |
|
|
|
.info { |
|
font-size: 14px; |
|
color: #f1bf63; |
|
cursor: pointer; |
|
text-decoration: underline; |
|
|
|
&:hover { |
|
color: #8ace6a; |
|
} |
|
} |
|
} |
|
|
|
.device-item-body { |
|
@apply flex-col m-t-10 text-sm gap-6; |
|
|
|
.info-item { |
|
@apply flex items-center gap-col-2; |
|
} |
|
} |
|
} |
|
} |
|
</style>
|
|
|