装备列表优化
This commit is contained in:
@@ -1,10 +1,22 @@
|
|||||||
import req from '/@/api/http/request'
|
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 () => {
|
export const getUVDeviceList = async () => {
|
||||||
let res = await req.get(`/uv/api/devices/query/list`)
|
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">
|
<div class="search-wrapper">
|
||||||
<input type="text" v-model="searchText" placeholder="按名称搜索设备" class="search-input">
|
<input type="text" v-model="searchText" placeholder="按名称搜索设备" class="search-input">
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox-wrapper">
|
<div class="checkbox-wrapper" @mousedown.stop @click.stop>
|
||||||
<input type="checkbox" id="online-only" v-model="showOnlineOnly" class="custom-checkbox">
|
<input type="checkbox" id="online-only" v-model="showOnlineOnly" class="custom-checkbox" @mousedown.stop @click.stop @change.stop="handleOnlineOnlyChange">
|
||||||
<label for="online-only">只看在线</label>
|
<label for="online-only" @mousedown.stop @click.prevent.stop="toggleOnlineOnly">只看在线</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 内容列表 -->
|
<!-- 内容列表 -->
|
||||||
<div class="ud-device-content">
|
<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>
|
<p>没有找到匹配的设备</p>
|
||||||
<span class="tip">请尝试调整筛选条件</span>
|
<span class="tip">请尝试调整筛选条件</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,10 +75,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<!-- 预览按钮 (如果满足条件) -->
|
<!-- 预览按钮 (如果满足条件) -->
|
||||||
<span v-if="(item.device.online && item.device.flvUrl)"
|
<span v-if="canPreviewDevice(item.device)"
|
||||||
class="preview-btn"
|
class="preview-btn"
|
||||||
title="预览直播"
|
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>
|
<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>
|
</span>
|
||||||
@@ -177,6 +181,7 @@ const tabOptions = ref([
|
|||||||
{ label: "无人车", type: 1 },
|
{ label: "无人车", type: 1 },
|
||||||
{ label: "其他", type: 2 },
|
{ label: "其他", type: 2 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const activeTab = ref(0);
|
const activeTab = ref(0);
|
||||||
const deviceList = ref([
|
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": "%" } ] },
|
{ "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();
|
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();
|
startOfflineTimer();
|
||||||
@@ -202,9 +268,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
//页面卸载
|
//页面卸载
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
EventBus.off('WebScoket-Message', handlerSocketMessage)
|
stopRealtimeDeviceMode();
|
||||||
stopOfflineTimer();
|
|
||||||
stopDevuceUploadTimer();
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -213,7 +277,7 @@ onUnmounted(() => {
|
|||||||
var devuceUploadTimer = null;
|
var devuceUploadTimer = null;
|
||||||
const startDevuceUploadTimer =()=>{
|
const startDevuceUploadTimer =()=>{
|
||||||
devuceUploadTimer = setInterval(()=>{
|
devuceUploadTimer = setInterval(()=>{
|
||||||
const onlineDevices = deviceList.value.filter(item => item.device.online === true);
|
const onlineDevices = getDeviceItems().filter(item => item.device.online === true);
|
||||||
onlineDevices.forEach(item=>{
|
onlineDevices.forEach(item=>{
|
||||||
const { device:deviceInfo, props: deviceProps } = item;
|
const { device:deviceInfo, props: deviceProps } = item;
|
||||||
const uploadInfo = {
|
const uploadInfo = {
|
||||||
@@ -241,7 +305,7 @@ var offlineTimer = null;
|
|||||||
const startOfflineTimer = ()=>{
|
const startOfflineTimer = ()=>{
|
||||||
offlineTimer = setInterval(()=>{
|
offlineTimer = setInterval(()=>{
|
||||||
const currentTime = new Date();
|
const currentTime = new Date();
|
||||||
deviceList.value.forEach(item => {
|
getDeviceItems().forEach(item => {
|
||||||
if (item.device.update_time) {
|
if (item.device.update_time) {
|
||||||
const updateTime = new Date(item.device.update_time);
|
const updateTime = new Date(item.device.update_time);
|
||||||
if (updateTime < new Date(currentTime - 50000)) {
|
if (updateTime < new Date(currentTime - 50000)) {
|
||||||
@@ -473,7 +537,7 @@ const handlerSocketMessage = (message) => {
|
|||||||
|
|
||||||
//找出唯一标识的无人设备
|
//找出唯一标识的无人设备
|
||||||
function findUVDeviceByCode(code,type) {
|
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的设备总数和在线数 ---
|
// --- 2. 计算属性:动态计算当前Tab的设备总数和在线数 ---
|
||||||
const currentTabCounts = computed(() => {
|
const currentTabCounts = computed(() => {
|
||||||
// 1. 先根据当前激活的Tab过滤出设备
|
// 1. 先根据当前激活的Tab过滤出设备
|
||||||
const devicesInCurrentTab = deviceList.value.filter(
|
const devicesInCurrentTab = getDeviceItems().filter(
|
||||||
item => item.device.device_type === activeTab.value
|
item => item.device.device_type === activeTab.value
|
||||||
);
|
);
|
||||||
// 2. 计算总数
|
// 2. 计算总数
|
||||||
@@ -515,7 +579,7 @@ const currentTabCounts = computed(() => {
|
|||||||
// 计算属性,实现所有筛选逻辑
|
// 计算属性,实现所有筛选逻辑
|
||||||
const filteredDeviceList = computed(() => {
|
const filteredDeviceList = computed(() => {
|
||||||
// 1. 先根据Tab、搜索框、复选框进行筛选
|
// 1. 先根据Tab、搜索框、复选框进行筛选
|
||||||
const filtered = deviceList.value.filter(item => {
|
const filtered = getDeviceItems().filter(item => {
|
||||||
const typeMatch = item.device.device_type === activeTab.value;
|
const typeMatch = item.device.device_type === activeTab.value;
|
||||||
const onlineMatch = !showOnlineOnly.value || (showOnlineOnly.value && item.device.online);
|
const onlineMatch = !showOnlineOnly.value || (showOnlineOnly.value && item.device.online);
|
||||||
const searchMatch = !searchText.value || item.device.device_name.toLowerCase().includes(searchText.value.toLowerCase());
|
const searchMatch = !searchText.value || item.device.device_name.toLowerCase().includes(searchText.value.toLowerCase());
|
||||||
@@ -536,26 +600,58 @@ const getSourceText = (source) => {
|
|||||||
return sourceMap[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) => {
|
const handlePreview = (device) => {
|
||||||
|
if (!canPreviewDevice(device)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
//播放flvUrl
|
//播放flvUrl
|
||||||
if(device.flvUrl && device.flvUrl != "Noneed"){
|
if (hasDirectPreviewUrl(device)) {
|
||||||
openLiveFlv("wrc_sn","wrc",device.flvUrl)
|
openLiveFlv(getPreviewPlayerSn(device), "wrc", device.flvUrl)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//大疆道通无人机直播
|
//大疆道通无人机直播
|
||||||
if(device.device_type === 0 && device.flvUrl === "Noneed"){
|
if (Number(device.device_type) === 0 && device.flvUrl === "Noneed") {
|
||||||
let deviceSn = device.device_code;
|
const deviceSn = getDeviceLiveSn(device);
|
||||||
let deviceRcSn = device.parent_sn?device.parent_sn:null;
|
const deviceRcSn = device.parent_sn || device.device_sn || null;
|
||||||
openLive(deviceSn,deviceRcSn,'dock');
|
if (!deviceSn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openLive(deviceSn, deviceRcSn, 'dock');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//无人机直播
|
|
||||||
const liveContent = reactive({
|
const liveContent = reactive({
|
||||||
data: {
|
data: {
|
||||||
showLiveModal: false,
|
showLiveModal: false,
|
||||||
@@ -567,14 +663,17 @@ const liveContent = reactive({
|
|||||||
const openLive = (liveSn, rcSn, liveType) => {
|
const openLive = (liveSn, rcSn, liveType) => {
|
||||||
liveContent.data.showLiveModal = false
|
liveContent.data.showLiveModal = false
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
liveContent.data.showLiveModal = true
|
|
||||||
liveContent.data.liveSn = liveSn
|
liveContent.data.liveSn = liveSn
|
||||||
liveContent.data.liveType = liveType
|
liveContent.data.liveType = liveType
|
||||||
liveContent.data.rcsn = rcSn
|
liveContent.data.rcsn = rcSn
|
||||||
|
liveContent.data.showLiveModal = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const closeLive = () => {
|
const closeLive = () => {
|
||||||
liveContent.data.showLiveModal = false
|
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) => {
|
const openLiveFlv = (liveSn,liveType,flvUrl) => {
|
||||||
liveContentJgWrc.data.showLiveModal = false
|
liveContentJgWrc.data.showLiveModal = false
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
liveContentJgWrc.data.showLiveModal = true
|
|
||||||
liveContentJgWrc.data.liveSn = liveSn
|
liveContentJgWrc.data.liveSn = liveSn
|
||||||
liveContentJgWrc.data.liveType = liveType
|
liveContentJgWrc.data.liveType = liveType
|
||||||
liveContentJgWrc.data.flvUrl = flvUrl
|
liveContentJgWrc.data.flvUrl = flvUrl
|
||||||
|
liveContentJgWrc.data.showLiveModal = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const closeLiveFlv = () => {
|
const closeLiveFlv = () => {
|
||||||
liveContentJgWrc.data.showLiveModal = false
|
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) => {
|
const handleSave = async (newDeviceData) => {
|
||||||
deviceList.value = await getUVDeviceList();
|
await syncDeviceListSource();
|
||||||
};
|
};
|
||||||
const handleUpdate = async (newDeviceData) => {
|
const handleUpdate = async (newDeviceData) => {
|
||||||
deviceList.value = await getUVDeviceList();
|
await syncDeviceListSource();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user