装备列表优化
This commit is contained in:
@@ -1,10 +1,22 @@
|
||||
import req from '/@/api/http/request'
|
||||
|
||||
const normalizeDeviceList = (payload) => {
|
||||
if (Array.isArray(payload)) return payload
|
||||
if (Array.isArray(payload?.data)) return payload.data
|
||||
if (Array.isArray(payload?.rows)) return payload.rows
|
||||
if (Array.isArray(payload?.list)) return payload.list
|
||||
if (Array.isArray(payload?.records)) return payload.records
|
||||
if (Array.isArray(payload?.data?.rows)) return payload.data.rows
|
||||
if (Array.isArray(payload?.data?.list)) return payload.data.list
|
||||
if (Array.isArray(payload?.data?.records)) return payload.data.records
|
||||
return []
|
||||
}
|
||||
|
||||
|
||||
//获取无人设备列表
|
||||
export const getUVDeviceList = async () => {
|
||||
let res = await req.get(`/uv/api/devices/query/list`)
|
||||
return res.data
|
||||
return normalizeDeviceList(res.data)
|
||||
}
|
||||
|
||||
//修改无人设备
|
||||
|
||||
@@ -35,15 +35,18 @@
|
||||
<div class="search-wrapper">
|
||||
<input type="text" v-model="searchText" placeholder="按名称搜索设备" class="search-input">
|
||||
</div>
|
||||
<div class="checkbox-wrapper">
|
||||
<input type="checkbox" id="online-only" v-model="showOnlineOnly" class="custom-checkbox">
|
||||
<label for="online-only">只看在线</label>
|
||||
<div class="checkbox-wrapper" @mousedown.stop @click.stop>
|
||||
<input type="checkbox" id="online-only" v-model="showOnlineOnly" class="custom-checkbox" @mousedown.stop @click.stop @change.stop="handleOnlineOnlyChange">
|
||||
<label for="online-only" @mousedown.stop @click.prevent.stop="toggleOnlineOnly">只看在线</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容列表 -->
|
||||
<div class="ud-device-content">
|
||||
<div v-if="filteredDeviceList.length === 0" class="empty-state">
|
||||
<div v-if="isDeviceListLoading" class="empty-state">
|
||||
<p>加载设备中...</p>
|
||||
</div>
|
||||
<div v-else-if="filteredDeviceList.length === 0" class="empty-state">
|
||||
<p>没有找到匹配的设备</p>
|
||||
<span class="tip">请尝试调整筛选条件</span>
|
||||
</div>
|
||||
@@ -72,10 +75,11 @@
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<!-- 预览按钮 (如果满足条件) -->
|
||||
<span v-if="(item.device.online && item.device.flvUrl)"
|
||||
<span v-if="canPreviewDevice(item.device)"
|
||||
class="preview-btn"
|
||||
title="预览直播"
|
||||
@click="handlePreview(item.device)"
|
||||
@mousedown.stop
|
||||
@click.stop="handlePreview(item.device)"
|
||||
>
|
||||
<svg viewBox="0 0 1024 1024" width="20" height="20"><path d="M512 96a416 416 0 1 0 0 832 416 416 0 0 0 0-832z m0 736a320 320 0 1 1 0-640 320 320 0 0 1 0 640z m-160-320a160 160 0 1 0 320 0 160 160 0 0 0-320 0z m160-64a64 64 0 1 1 0 128 64 64 0 0 1 0-128z" fill="currentColor"></path></svg>
|
||||
</span>
|
||||
@@ -177,6 +181,7 @@ const tabOptions = ref([
|
||||
{ label: "无人车", type: 1 },
|
||||
{ label: "其他", type: 2 },
|
||||
]);
|
||||
|
||||
const activeTab = ref(0);
|
||||
const deviceList = ref([
|
||||
{ "device": { "id": 1, "device_name": "道通龙鱼", "device_type": 0, "device_source": 0, "longitude": 118.099366, "latitude": 32.6895, "online": true, "device_logo": "/3dScreen/img/flyDevices.jpg", "flvUrl": "http://example.com/live/1.flv" }, "props": [ { "prop_name": "高度", "prop_value": "120", "prop_unit": "米" }, { "prop_name": "速度", "prop_value": "15", "prop_unit": "米/秒" }, { "prop_name": "电量", "prop_value": "88", "prop_unit": "%" } ] },
|
||||
@@ -189,9 +194,70 @@ const deviceList = ref([
|
||||
|
||||
|
||||
//页面装载
|
||||
onMounted(async () => {
|
||||
const mockDeviceListSnapshot = JSON.stringify(deviceList.value);
|
||||
const isDeviceListLoading = ref(false);
|
||||
let isRealtimeDeviceMode = false;
|
||||
|
||||
const getDeviceItems = () => {
|
||||
return Array.isArray(deviceList.value) ? deviceList.value : [];
|
||||
};
|
||||
|
||||
const createMockDeviceList = () => {
|
||||
return JSON.parse(mockDeviceListSnapshot);
|
||||
};
|
||||
|
||||
const startRealtimeDeviceMode = () => {
|
||||
if (isRealtimeDeviceMode) {
|
||||
return;
|
||||
}
|
||||
EventBus.on("WebScoket-Message", handlerSocketMessage);
|
||||
startOfflineTimer();
|
||||
startDevuceUploadTimer();
|
||||
isRealtimeDeviceMode = true;
|
||||
};
|
||||
|
||||
const stopRealtimeDeviceMode = () => {
|
||||
if (!isRealtimeDeviceMode) {
|
||||
return;
|
||||
}
|
||||
EventBus.off('WebScoket-Message', handlerSocketMessage);
|
||||
stopOfflineTimer();
|
||||
stopDevuceUploadTimer();
|
||||
isRealtimeDeviceMode = false;
|
||||
};
|
||||
|
||||
const loadApiDeviceList = async () => {
|
||||
isDeviceListLoading.value = true;
|
||||
deviceList.value = [];
|
||||
try {
|
||||
deviceList.value = await getUVDeviceList();
|
||||
EventBus.on("WebScoket-Message", handlerSocketMessage)
|
||||
} finally {
|
||||
isDeviceListLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const syncDeviceListSource = async () => {
|
||||
if (showOnlineOnly.value) {
|
||||
await loadApiDeviceList();
|
||||
startRealtimeDeviceMode();
|
||||
return;
|
||||
}
|
||||
stopRealtimeDeviceMode();
|
||||
isDeviceListLoading.value = false;
|
||||
deviceList.value = createMockDeviceList();
|
||||
};
|
||||
|
||||
const handleOnlineOnlyChange = async () => {
|
||||
await syncDeviceListSource();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (!showOnlineOnly.value) {
|
||||
deviceList.value = createMockDeviceList();
|
||||
return;
|
||||
}
|
||||
deviceList.value = createMockDeviceList();
|
||||
return;
|
||||
|
||||
//开启离线检测
|
||||
startOfflineTimer();
|
||||
@@ -202,9 +268,7 @@ onMounted(async () => {
|
||||
|
||||
//页面卸载
|
||||
onUnmounted(() => {
|
||||
EventBus.off('WebScoket-Message', handlerSocketMessage)
|
||||
stopOfflineTimer();
|
||||
stopDevuceUploadTimer();
|
||||
stopRealtimeDeviceMode();
|
||||
})
|
||||
|
||||
|
||||
@@ -213,7 +277,7 @@ onUnmounted(() => {
|
||||
var devuceUploadTimer = null;
|
||||
const startDevuceUploadTimer =()=>{
|
||||
devuceUploadTimer = setInterval(()=>{
|
||||
const onlineDevices = deviceList.value.filter(item => item.device.online === true);
|
||||
const onlineDevices = getDeviceItems().filter(item => item.device.online === true);
|
||||
onlineDevices.forEach(item=>{
|
||||
const { device:deviceInfo, props: deviceProps } = item;
|
||||
const uploadInfo = {
|
||||
@@ -241,7 +305,7 @@ var offlineTimer = null;
|
||||
const startOfflineTimer = ()=>{
|
||||
offlineTimer = setInterval(()=>{
|
||||
const currentTime = new Date();
|
||||
deviceList.value.forEach(item => {
|
||||
getDeviceItems().forEach(item => {
|
||||
if (item.device.update_time) {
|
||||
const updateTime = new Date(item.device.update_time);
|
||||
if (updateTime < new Date(currentTime - 50000)) {
|
||||
@@ -473,7 +537,7 @@ const handlerSocketMessage = (message) => {
|
||||
|
||||
//找出唯一标识的无人设备
|
||||
function findUVDeviceByCode(code,type) {
|
||||
return deviceList.value.find(item => item.device.device_code === code && item.device.device_type === type) || null;
|
||||
return getDeviceItems().find(item => item.device.device_code === code && item.device.device_type === type) || null;
|
||||
}
|
||||
|
||||
//设置属性值
|
||||
@@ -500,7 +564,7 @@ function setUVDevicePropsValue(props, data) {
|
||||
// --- 2. 计算属性:动态计算当前Tab的设备总数和在线数 ---
|
||||
const currentTabCounts = computed(() => {
|
||||
// 1. 先根据当前激活的Tab过滤出设备
|
||||
const devicesInCurrentTab = deviceList.value.filter(
|
||||
const devicesInCurrentTab = getDeviceItems().filter(
|
||||
item => item.device.device_type === activeTab.value
|
||||
);
|
||||
// 2. 计算总数
|
||||
@@ -515,7 +579,7 @@ const currentTabCounts = computed(() => {
|
||||
// 计算属性,实现所有筛选逻辑
|
||||
const filteredDeviceList = computed(() => {
|
||||
// 1. 先根据Tab、搜索框、复选框进行筛选
|
||||
const filtered = deviceList.value.filter(item => {
|
||||
const filtered = getDeviceItems().filter(item => {
|
||||
const typeMatch = item.device.device_type === activeTab.value;
|
||||
const onlineMatch = !showOnlineOnly.value || (showOnlineOnly.value && item.device.online);
|
||||
const searchMatch = !searchText.value || item.device.device_name.toLowerCase().includes(searchText.value.toLowerCase());
|
||||
@@ -536,26 +600,58 @@ const getSourceText = (source) => {
|
||||
return sourceMap[source] || "未知";
|
||||
};
|
||||
|
||||
const toggleOnlineOnly = async () => {
|
||||
showOnlineOnly.value = !showOnlineOnly.value;
|
||||
await syncDeviceListSource();
|
||||
};
|
||||
|
||||
const getDeviceLiveSn = (device) => {
|
||||
return device?.device_code || device?.device_sn || "";
|
||||
};
|
||||
|
||||
const getPreviewPlayerSn = (device) => {
|
||||
return getDeviceLiveSn(device) || (device?.id != null ? String(device.id) : "wrc_sn");
|
||||
};
|
||||
|
||||
const hasDirectPreviewUrl = (device) => {
|
||||
const flvUrl = typeof device?.flvUrl === "string" ? device.flvUrl.trim() : "";
|
||||
return flvUrl !== "" && flvUrl !== "Noneed";
|
||||
};
|
||||
|
||||
const canPreviewDevice = (device) => {
|
||||
if (!device?.online) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasDirectPreviewUrl(device)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Number(device?.device_type) === 0 && device?.flvUrl === "Noneed" && !!getDeviceLiveSn(device);
|
||||
};
|
||||
|
||||
// 预览按钮点击事件处理
|
||||
const handlePreview = (device) => {
|
||||
if (!canPreviewDevice(device)) {
|
||||
return;
|
||||
}
|
||||
//播放flvUrl
|
||||
if(device.flvUrl && device.flvUrl != "Noneed"){
|
||||
openLiveFlv("wrc_sn","wrc",device.flvUrl)
|
||||
if (hasDirectPreviewUrl(device)) {
|
||||
openLiveFlv(getPreviewPlayerSn(device), "wrc", device.flvUrl)
|
||||
return;
|
||||
}
|
||||
|
||||
//大疆道通无人机直播
|
||||
if(device.device_type === 0 && device.flvUrl === "Noneed"){
|
||||
let deviceSn = device.device_code;
|
||||
let deviceRcSn = device.parent_sn?device.parent_sn:null;
|
||||
openLive(deviceSn,deviceRcSn,'dock');
|
||||
if (Number(device.device_type) === 0 && device.flvUrl === "Noneed") {
|
||||
const deviceSn = getDeviceLiveSn(device);
|
||||
const deviceRcSn = device.parent_sn || device.device_sn || null;
|
||||
if (!deviceSn) {
|
||||
return;
|
||||
}
|
||||
openLive(deviceSn, deviceRcSn, 'dock');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//无人机直播
|
||||
const liveContent = reactive({
|
||||
data: {
|
||||
showLiveModal: false,
|
||||
@@ -567,14 +663,17 @@ const liveContent = reactive({
|
||||
const openLive = (liveSn, rcSn, liveType) => {
|
||||
liveContent.data.showLiveModal = false
|
||||
nextTick(() => {
|
||||
liveContent.data.showLiveModal = true
|
||||
liveContent.data.liveSn = liveSn
|
||||
liveContent.data.liveType = liveType
|
||||
liveContent.data.rcsn = rcSn
|
||||
liveContent.data.showLiveModal = true
|
||||
})
|
||||
}
|
||||
const closeLive = () => {
|
||||
liveContent.data.showLiveModal = false
|
||||
liveContent.data.liveSn = ""
|
||||
liveContent.data.liveType = ""
|
||||
liveContent.data.rcsn = ""
|
||||
}
|
||||
|
||||
|
||||
@@ -592,14 +691,17 @@ const liveContentJgWrc = reactive({
|
||||
const openLiveFlv = (liveSn,liveType,flvUrl) => {
|
||||
liveContentJgWrc.data.showLiveModal = false
|
||||
nextTick(() => {
|
||||
liveContentJgWrc.data.showLiveModal = true
|
||||
liveContentJgWrc.data.liveSn = liveSn
|
||||
liveContentJgWrc.data.liveType = liveType
|
||||
liveContentJgWrc.data.flvUrl = flvUrl
|
||||
liveContentJgWrc.data.showLiveModal = true
|
||||
})
|
||||
}
|
||||
const closeLiveFlv = () => {
|
||||
liveContentJgWrc.data.showLiveModal = false
|
||||
liveContentJgWrc.data.liveSn = ""
|
||||
liveContentJgWrc.data.liveType = ""
|
||||
liveContentJgWrc.data.flvUrl = ""
|
||||
}
|
||||
|
||||
|
||||
@@ -662,10 +764,10 @@ const editUVDevice = ref(null);
|
||||
|
||||
// 处理保存和修改事件
|
||||
const handleSave = async (newDeviceData) => {
|
||||
deviceList.value = await getUVDeviceList();
|
||||
await syncDeviceListSource();
|
||||
};
|
||||
const handleUpdate = async (newDeviceData) => {
|
||||
deviceList.value = await getUVDeviceList();
|
||||
await syncDeviceListSource();
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user