新增网络状态检测
This commit is contained in:
@@ -118,3 +118,7 @@ export function restartTikTok(data) {
|
|||||||
return postAxios({ url: 'restartTikTok', data })
|
return postAxios({ url: 'restartTikTok', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//获取设备网络状态
|
||||||
|
export function getDeviceNetStatus(data) {
|
||||||
|
return postAxios({ url: 'getDeviceNetStatus', data })
|
||||||
|
}
|
||||||
@@ -482,3 +482,65 @@ video {
|
|||||||
.video-canvas .overlay {
|
.video-canvas .overlay {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 容器要能放伪元素 */
|
||||||
|
.video-canvas.net-bad {
|
||||||
|
position: relative;
|
||||||
|
outline: 2px solid rgba(239, 68, 68, 0.6);
|
||||||
|
/* 常亮的细红框作底线 */
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 闪烁的红色光圈边框(不占据布局、不挡点击) */
|
||||||
|
.video-canvas.net-bad::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -4px;
|
||||||
|
/* 往外扩一点,避免吃掉内容 */
|
||||||
|
border-radius: 20px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 3;
|
||||||
|
/* 盖在内部 img/canvas 之上 */
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 3px rgba(239, 68, 68, 0.95),
|
||||||
|
/* 实心外圈 */
|
||||||
|
0 0 14px rgba(239, 68, 68, 0.85),
|
||||||
|
/* 强发光 */
|
||||||
|
0 0 36px rgba(239, 68, 68, 0.65);
|
||||||
|
/* 远发光 */
|
||||||
|
opacity: 0.2;
|
||||||
|
animation: alertPulse 0.9s infinite cubic-bezier(.65, .05, .36, 1);
|
||||||
|
will-change: opacity, box-shadow, transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 强烈的“脉冲闪烁”效果 */
|
||||||
|
@keyframes alertPulse {
|
||||||
|
0% {
|
||||||
|
opacity: 0.2;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 2px rgba(239, 68, 68, 0.6),
|
||||||
|
0 0 10px rgba(239, 68, 68, 0.55),
|
||||||
|
0 0 24px rgba(239, 68, 68, 0.45);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
opacity: 1;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 4px rgba(239, 68, 68, 1),
|
||||||
|
0 0 18px rgba(239, 68, 68, 0.95),
|
||||||
|
0 0 42px rgba(239, 68, 68, 0.85);
|
||||||
|
transform: scale(1.01);
|
||||||
|
/* 轻微放大,增强“警报感” */
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0.2;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 2px rgba(239, 68, 68, 0.6),
|
||||||
|
0 0 10px rgba(239, 68, 68, 0.55),
|
||||||
|
0 0 24px rgba(239, 68, 68, 0.45);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,15 +4,6 @@
|
|||||||
<div style="position: absolute;left: 20px; top: 20px;">
|
<div style="position: absolute;left: 20px; top: 20px;">
|
||||||
<el-button style="background: linear-gradient(90deg, #60a5fa, #34d399); color: azure; "
|
<el-button style="background: linear-gradient(90deg, #60a5fa, #34d399); color: azure; "
|
||||||
@click="showMyInfo = true">人设编辑</el-button>
|
@click="showMyInfo = true">人设编辑</el-button>
|
||||||
|
|
||||||
<!-- <el-button style="background: linear-gradient(90deg, #60a5fa, #34d399); color: azure;" @click="MesNewList.push({
|
|
||||||
sender: 'Alice', // 或 name/user/from
|
|
||||||
device: 'iPhone 14 Pro', // 或 deviceName/udid
|
|
||||||
text: '你好呀~', // 或 content
|
|
||||||
time: '2023-10-01 12:00:00', // 可选;有些是 'time' 会被过滤或 content
|
|
||||||
type: 'msg' // 可选;有些是 'time' 会被过滤
|
|
||||||
})">新增</el-button> -->
|
|
||||||
<!-- <el-button @click="updates([{ id: 10022, aiOperation: 1 }])">更新请求</el-button> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="center-line"> <!-- 左边栏按钮 -->
|
<div class="center-line"> <!-- 左边栏按钮 -->
|
||||||
@@ -35,8 +26,10 @@
|
|||||||
<!-- 中间手机区域 -->
|
<!-- 中间手机区域 -->
|
||||||
<div class="content" @click.self="selectedDevice = 999">
|
<div class="content" @click.self="selectedDevice = 999">
|
||||||
<div class="video-container" v-for="(device, index) in deviceInformation" :key="device.deviceId">
|
<div class="video-container" v-for="(device, index) in deviceInformation" :key="device.deviceId">
|
||||||
<div class="video-canvas" :class="{ active: selectedDevice === index }" :style="getCanvasStyle(index)"
|
<div class="video-canvas" :class="{
|
||||||
@click="selectDevice(index)">
|
active: selectedDevice === index,
|
||||||
|
'net-bad': netStatus[device.deviceId] === false
|
||||||
|
}" :style="getCanvasStyle(index)" @click="selectDevice(index)">
|
||||||
<img class="stream" :src="imgSrcMap[device.deviceId] || ''" :data-id="device.deviceId"
|
<img class="stream" :src="imgSrcMap[device.deviceId] || ''" :data-id="device.deviceId"
|
||||||
:ref="el => (imgRefs[device.deviceId] = el)" />
|
:ref="el => (imgRefs[device.deviceId] = el)" />
|
||||||
|
|
||||||
@@ -171,7 +164,8 @@ import {
|
|||||||
changeAccount,
|
changeAccount,
|
||||||
stopAllTask,
|
stopAllTask,
|
||||||
anchorList,
|
anchorList,
|
||||||
restartTikTok
|
restartTikTok,
|
||||||
|
getDeviceNetStatus
|
||||||
} from '@/api/ios';
|
} from '@/api/ios';
|
||||||
import ding from '@/assets/mes.wav'
|
import ding from '@/assets/mes.wav'
|
||||||
import { set } from "lodash";
|
import { set } from "lodash";
|
||||||
@@ -229,6 +223,23 @@ let deviceInformation = ref([])
|
|||||||
//停止中
|
//停止中
|
||||||
let stopLoading = null
|
let stopLoading = null
|
||||||
|
|
||||||
|
// 每台设备的网络状态:true=正常,false=异常
|
||||||
|
const netStatus = reactive({}) // { [deviceId]: boolean }
|
||||||
|
|
||||||
|
// —— 网络波动联动(每设备暂停/恢复)——
|
||||||
|
const offlineFlags = reactive({}) // { [deviceId]: true/false } 是否因断网而暂停过
|
||||||
|
const lastNet = reactive({}) // { [deviceId]: true/false } 上一次看到的网络状态
|
||||||
|
const resumeTimers = new Map() // deviceId -> setTimeout id
|
||||||
|
const NET_RESUME_STABLE_MS = 6000 // 断网恢复后,等待网络稳定的毫秒数
|
||||||
|
|
||||||
|
function clearResumeTimer(id) {
|
||||||
|
const t = resumeTimers.get(id)
|
||||||
|
if (t) {
|
||||||
|
clearTimeout(t)
|
||||||
|
resumeTimers.delete(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 当前是否被其它模式占用(四个互斥按钮专用)
|
// 当前是否被其它模式占用(四个互斥按钮专用)
|
||||||
const isLocked = (type) => !!runType.value && runType.value !== type
|
const isLocked = (type) => !!runType.value && runType.value !== type
|
||||||
|
|
||||||
@@ -777,11 +788,6 @@ const onCanvasUp = async (udid, e, index) => {
|
|||||||
if (isTap) {
|
if (isTap) {
|
||||||
await tapAction({ udid, x: endDevXY.x, y: endDevXY.y })
|
await tapAction({ udid, x: endDevXY.x, y: endDevXY.y })
|
||||||
} else {
|
} else {
|
||||||
// //通过方向判断code 1234
|
|
||||||
// const rotation = Number((deviceInformation.value[index] || {}).rotation || 0)
|
|
||||||
// const code = getSwipeCodeWithRotation(dx, dy, rotation)
|
|
||||||
// await swipeAction({ udid, direction: code })
|
|
||||||
|
|
||||||
//通过自定义滑动坐标和时间传参
|
//通过自定义滑动坐标和时间传参
|
||||||
await swipeAction({ udid, sx: startDevXY.x, sy: startDevXY.y, ex: endDevXY.x, ey: endDevXY.y, duration: elapsed / 1000 })
|
await swipeAction({ udid, sx: startDevXY.x, sy: startDevXY.y, ex: endDevXY.x, ey: endDevXY.y, duration: elapsed / 1000 })
|
||||||
}
|
}
|
||||||
@@ -922,60 +928,9 @@ function onDialogConfirm(result, type, index, data) {
|
|||||||
comonList = result
|
comonList = result
|
||||||
setContentList(result)
|
setContentList(result)
|
||||||
common.value = data.common
|
common.value = data.common
|
||||||
// dialogTitle.value = '私信';
|
|
||||||
// setTimeout(() => {
|
|
||||||
// showDialog.value = true;
|
|
||||||
|
|
||||||
// initialTextStr.value = getContentpriListMultiline();
|
|
||||||
// }, 500)
|
|
||||||
transDlgType.value = '私信'
|
transDlgType.value = '私信'
|
||||||
showtransDlg.value = true
|
showtransDlg.value = true
|
||||||
} else if (type == '私信') {
|
|
||||||
// runType.value = 'follow'
|
|
||||||
// setContentpriList(result)
|
|
||||||
|
|
||||||
// if (isAlliance.value) {
|
|
||||||
// followAndGreetUnion(
|
|
||||||
// {
|
|
||||||
// deviceList: deviceInformation.value.map(item => item.deviceId),
|
|
||||||
// anchorList: hostList.map(item => ({
|
|
||||||
// anchorId: item.id,
|
|
||||||
// country: item.country,
|
|
||||||
// invitationType: item.invitationType,
|
|
||||||
// state: stateByInvType(item.invitationType),
|
|
||||||
// })),
|
|
||||||
// prologueList: result,
|
|
||||||
// needReply: data.auto,
|
|
||||||
// needTranslate: data.needTranslate,
|
|
||||||
// }
|
|
||||||
// ).then((res) => {
|
|
||||||
// ElMessage({ type: 'success', message: '任务开启成功' });
|
|
||||||
|
|
||||||
// hostList = []
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// passAnchorData(
|
|
||||||
// {
|
|
||||||
// deviceList: deviceInformation.value.map(item => item.deviceId),
|
|
||||||
// anchorList: hostList.map(item => ({
|
|
||||||
// anchorId: item.id,
|
|
||||||
// country: item.country,
|
|
||||||
// invitationType: item.invitationType,
|
|
||||||
// state: stateByInvType(item.invitationType),
|
|
||||||
// })),
|
|
||||||
// prologueList: result,
|
|
||||||
// comment: comonList,
|
|
||||||
// needReply: data.auto,
|
|
||||||
// needTranslate: data.needTranslate,
|
|
||||||
// isComment: data.common
|
|
||||||
// }
|
|
||||||
// ).then((res) => {
|
|
||||||
// ElMessage({ type: 'success', message: '任务开启成功' });
|
|
||||||
|
|
||||||
// hostList = []
|
|
||||||
// })
|
|
||||||
|
|
||||||
// }
|
|
||||||
} else if (type == '视频评论') {
|
} else if (type == '视频评论') {
|
||||||
runType.value = 'like'
|
runType.value = 'like'
|
||||||
deviceInformation.value.forEach((item) => growAccount({ udid: item.deviceId, comment: result, isComment: data.common }))
|
deviceInformation.value.forEach((item) => growAccount({ udid: item.deviceId, comment: result, isComment: data.common }))
|
||||||
@@ -1016,11 +971,10 @@ onMounted(async () => {
|
|||||||
|
|
||||||
window.electronAPI.startMq(userdata.tenantId, userdata.id)
|
window.electronAPI.startMq(userdata.tenantId, userdata.id)
|
||||||
// 初始化时获取设备列表
|
// 初始化时获取设备列表
|
||||||
|
|
||||||
//每3秒获取一次设备列表 消息列表 和 查询主播列表是否还有主播
|
//每3秒获取一次设备列表 消息列表 和 查询主播列表是否还有主播
|
||||||
getListtimer = setInterval(async () => {
|
getListtimer = setInterval(async () => {
|
||||||
getDeviceListFun()
|
getDeviceListFun() //获取设备列表
|
||||||
selectLastFun()
|
selectLastFun() //获取手机网络状态
|
||||||
const hostsList = await getStoredHostList()
|
const hostsList = await getStoredHostList()
|
||||||
// console.log(hostsList.length)
|
// console.log(hostsList.length)
|
||||||
//当私信主播时,主播列表没有数据了,提示列表空了 并且关闭私信
|
//当私信主播时,主播列表没有数据了,提示列表空了 并且关闭私信
|
||||||
@@ -1042,8 +996,10 @@ onMounted(async () => {
|
|||||||
|
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
await checkVPN()
|
await checkVPN()
|
||||||
|
await refreshNetStatus()
|
||||||
}, 1000 * 20)
|
}, 1000 * 20)
|
||||||
|
|
||||||
|
|
||||||
if (!await isAiConfig()) {
|
if (!await isAiConfig()) {
|
||||||
showMyInfo.value = true
|
showMyInfo.value = true
|
||||||
}
|
}
|
||||||
@@ -1114,20 +1070,14 @@ onUnmounted(() => {
|
|||||||
let isStartLac = false
|
let isStartLac = false
|
||||||
const getDeviceListFun = () => {
|
const getDeviceListFun = () => {
|
||||||
getDeviceList().then((res) => {
|
getDeviceList().then((res) => {
|
||||||
// console.log('返回', res.length)
|
|
||||||
if (res && res.length > 0 && deviceInformation.value.length !== res.length) {
|
if (res && res.length > 0 && deviceInformation.value.length !== res.length) {
|
||||||
console.log("设备变更")
|
console.log("设备变更")
|
||||||
deviceInformation.value = res
|
deviceInformation.value = res
|
||||||
// refreshAllStopImgs()
|
|
||||||
// reloadImg()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.length == 0) {
|
if (res.length == 0) {
|
||||||
deviceInformation.value = []
|
deviceInformation.value = []
|
||||||
// refreshAllStopImgs()
|
|
||||||
// reloadImg()
|
|
||||||
}
|
}
|
||||||
// deviceInformation.value = ['', '', '', '', '', '',]
|
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (isStartLac) {
|
if (isStartLac) {
|
||||||
ElMessage.error(`IOSAI服务错误`)
|
ElMessage.error(`IOSAI服务错误`)
|
||||||
@@ -1210,37 +1160,68 @@ function runTask(key, deviceId, type) {
|
|||||||
console.log("进入follow", scheduleEnabled.value)
|
console.log("进入follow", scheduleEnabled.value)
|
||||||
if (scheduleEnabled.value) {
|
if (scheduleEnabled.value) {
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
await stopAll(100)
|
await stopAll(5000)
|
||||||
} else {
|
} else {
|
||||||
passAnchorData(
|
if (isAlliance.value) {
|
||||||
{
|
followAndGreetUnion(
|
||||||
deviceList: [deviceId],
|
{
|
||||||
anchorList: [],
|
deviceList: [deviceId],
|
||||||
prologueList: getContentpriList(),
|
anchorList: [],
|
||||||
comment: comonList,
|
prologueList: getContentpriList(),
|
||||||
needReply: auto.value
|
needReply: auto.value,
|
||||||
}
|
}
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
hostList = []
|
hostList = []
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
passAnchorData(
|
||||||
|
{
|
||||||
|
deviceList: [deviceId],
|
||||||
|
anchorList: [],
|
||||||
|
prologueList: getContentpriList(),
|
||||||
|
comment: comonList,
|
||||||
|
needReply: auto.value,
|
||||||
|
isComment: common.value //是否评论
|
||||||
|
|
||||||
|
}
|
||||||
|
).then((res) => {
|
||||||
|
hostList = []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//第一个小时结束后,第二轮开始的时候,直接进入follow
|
//第一个小时结束后,第二轮开始的时候,直接进入follow
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
runType.value = 'follow'
|
runType.value = 'follow'
|
||||||
|
if (isAlliance.value) {
|
||||||
|
followAndGreetUnion(
|
||||||
|
{
|
||||||
|
deviceList: deviceInformation.value.map(item => item.deviceId),
|
||||||
|
anchorList: [],
|
||||||
|
prologueList: getContentpriList(),
|
||||||
|
needReply: auto.value
|
||||||
|
}
|
||||||
|
).then((res) => {
|
||||||
|
hostList = []
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
passAnchorData(
|
||||||
|
{
|
||||||
|
deviceList: deviceInformation.value.map(item => item.deviceId),
|
||||||
|
anchorList: [],
|
||||||
|
prologueList: getContentpriList(),
|
||||||
|
comment: comonList,
|
||||||
|
needReply: auto.value,
|
||||||
|
isComment: common.value //是否评论
|
||||||
|
|
||||||
|
}
|
||||||
|
).then((res) => {
|
||||||
|
hostList = []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
passAnchorData(
|
|
||||||
{
|
|
||||||
deviceList: deviceInformation.value.map(item => item.deviceId),
|
|
||||||
anchorList: [],
|
|
||||||
prologueList: getContentpriList(),
|
|
||||||
comment: comonList,
|
|
||||||
needReply: auto.value
|
|
||||||
}
|
|
||||||
).then((res) => {
|
|
||||||
hostList = []
|
|
||||||
})
|
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -1254,7 +1235,7 @@ function runTask(key, deviceId, type) {
|
|||||||
} else if (key === 'like') {
|
} else if (key === 'like') {
|
||||||
|
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
await stopAll(100)
|
await stopAll(5000)
|
||||||
} else {
|
} else {
|
||||||
growAccount({ udid: deviceId })
|
growAccount({ udid: deviceId })
|
||||||
return
|
return
|
||||||
@@ -1268,7 +1249,7 @@ function runTask(key, deviceId, type) {
|
|||||||
|
|
||||||
} else if (key === 'brushLive') {
|
} else if (key === 'brushLive') {
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
await stopAll(100)
|
await stopAll(5000)
|
||||||
} else {
|
} else {
|
||||||
watchLiveForGrowth({ udid: deviceId })
|
watchLiveForGrowth({ udid: deviceId })
|
||||||
return
|
return
|
||||||
@@ -1282,7 +1263,7 @@ function runTask(key, deviceId, type) {
|
|||||||
runType.value = 'brushLive'
|
runType.value = 'brushLive'
|
||||||
} else if (key === 'listen') {
|
} else if (key === 'listen') {
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
await stopAll(100)
|
await stopAll(5000)
|
||||||
} else {
|
} else {
|
||||||
monitorMessages({ udid: deviceId })
|
monitorMessages({ udid: deviceId })
|
||||||
return
|
return
|
||||||
@@ -1402,7 +1383,7 @@ function startScheduleLoop() {
|
|||||||
pauseSnapshot = { index: scheduleState.index, elapsedBeforePause }
|
pauseSnapshot = { index: scheduleState.index, elapsedBeforePause }
|
||||||
|
|
||||||
// 停掉当前片段
|
// 停掉当前片段
|
||||||
await stopAll(100)
|
await stopAll(5000)
|
||||||
|
|
||||||
|
|
||||||
// 执行中断任务(带重试)
|
// 执行中断任务(带重试)
|
||||||
@@ -1590,13 +1571,6 @@ const checkVPN = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 语言选项(也可以使用内置默认)
|
|
||||||
// const languages = [
|
|
||||||
// { label: '简体中文 (zh-CN)', value: 'zh-CN' },
|
|
||||||
// { label: 'English (en)', value: 'en' },
|
|
||||||
// { label: '日本語 (ja)', value: 'ja' },
|
|
||||||
// ]
|
|
||||||
|
|
||||||
// 模拟翻译函数:你可以接入自己后端或第三方接口 sentences文本 targetLang语言
|
// 模拟翻译函数:你可以接入自己后端或第三方接口 sentences文本 targetLang语言
|
||||||
async function doTranslate(sentences, targetLang) {
|
async function doTranslate(sentences, targetLang) {
|
||||||
const str = arrayToString(sentences)
|
const str = arrayToString(sentences)
|
||||||
@@ -1680,6 +1654,83 @@ function arrayToString(arr) {
|
|||||||
// 过滤空项并用 \n 连接
|
// 过滤空项并用 \n 连接
|
||||||
return arr.filter(Boolean).join(' \n')
|
return arr.filter(Boolean).join(' \n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// —— 每 20s 刷新网络状态 ——
|
||||||
|
// 写一个小函数专门刷新网络状态,方便复用
|
||||||
|
async function refreshNetStatus() {
|
||||||
|
const list = [...(deviceInformation.value || [])]
|
||||||
|
|
||||||
|
// 并发请求每台设备网络状态(安全处理错误)
|
||||||
|
const settled = await Promise.allSettled(
|
||||||
|
list.map(d => getDeviceNetStatus({ udid: d.deviceId }))
|
||||||
|
)
|
||||||
|
|
||||||
|
settled.forEach((s, i) => {
|
||||||
|
const id = list[i].deviceId
|
||||||
|
netStatus[id] = (s.status === 'fulfilled' && s.value === true)
|
||||||
|
})
|
||||||
|
console.log("设备在线状态", settled)
|
||||||
|
|
||||||
|
// 清理已下线设备的状态,避免残留
|
||||||
|
const aliveIds = new Set(list.map(d => d.deviceId))
|
||||||
|
Object.keys(netStatus).forEach(id => {
|
||||||
|
if (!aliveIds.has(id)) {
|
||||||
|
delete netStatus[id]
|
||||||
|
delete lastNet[id]
|
||||||
|
delete offlineFlags[id]
|
||||||
|
clearResumeTimer(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// —— 在这里做“网络→任务联动”(只在有运行模式时触发)——
|
||||||
|
if (runType.value) {
|
||||||
|
list.forEach(({ deviceId: id }) => {
|
||||||
|
const prev = lastNet[id] // 之前的网络状态
|
||||||
|
const curr = netStatus[id] // 当前的网络状态(true/false/undefined)
|
||||||
|
if (typeof curr === 'undefined') return
|
||||||
|
|
||||||
|
// 边沿触发:true -> false 断网,false -> true 恢复
|
||||||
|
if (prev !== curr) {
|
||||||
|
// 断网:立刻停止该设备任务(只停这一台)
|
||||||
|
if (curr === false) {
|
||||||
|
clearResumeTimer(id) // 避免有遗留恢复定时器
|
||||||
|
if (!offlineFlags[id]) {
|
||||||
|
console.log(`[NET] ${id} 离线,停止该设备任务`)
|
||||||
|
offlineFlags[id] = true
|
||||||
|
// 这里调用你已有的“只停一台”的方法
|
||||||
|
stopOne(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复:等待网络稳定后再恢复该设备任务
|
||||||
|
if (curr === true && offlineFlags[id]) {
|
||||||
|
clearResumeTimer(id)
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
// 二次确认:还在运行模式里、该设备仍在线
|
||||||
|
if (runType.value && netStatus[id] === true) {
|
||||||
|
console.log(`[NET] ${id} 恢复在线,重新启动当前模式: ${runType.value}`)
|
||||||
|
// 仅启动这一台
|
||||||
|
runTask(runType.value, id)
|
||||||
|
// 标记已恢复
|
||||||
|
offlineFlags[id] = false
|
||||||
|
}
|
||||||
|
resumeTimers.delete(id)
|
||||||
|
}, NET_RESUME_STABLE_MS)
|
||||||
|
resumeTimers.set(id, timer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 last
|
||||||
|
lastNet[id] = curr
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 不在运行模式时,清理“暂停标记”和恢复定时器,保持干净
|
||||||
|
Object.keys(offlineFlags).forEach(id => { offlineFlags[id] = false })
|
||||||
|
Array.from(resumeTimers.keys()).forEach(id => clearResumeTimer(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|||||||
Reference in New Issue
Block a user