|
|
|
|
@ -1,130 +1,149 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div>
|
|
|
|
|
<my-card title="搜索条件" search>
|
|
|
|
|
<n-form inline>
|
|
|
|
|
<n-form-item label="车间">
|
|
|
|
|
<n-select
|
|
|
|
|
v-model:value="searchForm.location"
|
|
|
|
|
placeholder="请选择车间"
|
|
|
|
|
:options="locationList"
|
|
|
|
|
class="w-160px"
|
|
|
|
|
></n-select>
|
|
|
|
|
</n-form-item>
|
|
|
|
|
<n-form-item>
|
|
|
|
|
<component :is="useSearchBtn(search, resetThen)"></component>
|
|
|
|
|
</n-form-item>
|
|
|
|
|
</n-form>
|
|
|
|
|
</my-card>
|
|
|
|
|
<n-card :bordered="false" class="h-full rounded-8px shadow-sm">
|
|
|
|
|
<n-grid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16">
|
|
|
|
|
<n-grid-item v-for="item in cardData" :key="item.id">
|
|
|
|
|
<gradient-bg class="h-100px" :start-color="item.colors[0]" :end-color="item.colors[1]">
|
|
|
|
|
<h3 class="text-16px">{{ item.title }}</h3>
|
|
|
|
|
<div class="flex justify-between pt-12px">
|
|
|
|
|
<svg-icon :icon="item.icon" class="text-32px" />
|
|
|
|
|
<count-to
|
|
|
|
|
:prefix="item.unit"
|
|
|
|
|
:start-value="1"
|
|
|
|
|
:end-value="item.value"
|
|
|
|
|
class="text-30px text-white dark:text-dark"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</gradient-bg>
|
|
|
|
|
</n-grid-item>
|
|
|
|
|
</n-grid>
|
|
|
|
|
|
|
|
|
|
<n-divider title-placement="center" style="font-size: 18px; margin: 30px 0">设备信息</n-divider>
|
|
|
|
|
|
|
|
|
|
<n-grid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16">
|
|
|
|
|
<n-grid-item v-for="item in deviceDataList" :key="item.code">
|
|
|
|
|
<div class="device-card" :class="getStatus(item.status).className">
|
|
|
|
|
<div>设备编码 : {{ item.code }}</div>
|
|
|
|
|
<div>状态 : {{ getStatus(item.status).text }}</div>
|
|
|
|
|
<div>开始时间 : {{ item.startTime }}</div>
|
|
|
|
|
<div>加工数量 : {{ item.count }}</div>
|
|
|
|
|
<div>结束日期 : {{ item.endTime }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</n-grid-item>
|
|
|
|
|
</n-grid>
|
|
|
|
|
</n-card>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<my-card title="搜索条件" search>
|
|
|
|
|
<n-form inline>
|
|
|
|
|
<n-form-item label="车间">
|
|
|
|
|
<n-select
|
|
|
|
|
v-model:value="searchForm.location"
|
|
|
|
|
placeholder="请选择车间"
|
|
|
|
|
:options="locationList"
|
|
|
|
|
class="w-160px"
|
|
|
|
|
></n-select>
|
|
|
|
|
</n-form-item>
|
|
|
|
|
<n-form-item>
|
|
|
|
|
<component :is="useSearchBtn(search, resetThen)"></component>
|
|
|
|
|
</n-form-item>
|
|
|
|
|
</n-form>
|
|
|
|
|
</my-card>
|
|
|
|
|
<n-card :bordered="false" class="h-full rounded-8px shadow-sm">
|
|
|
|
|
<n-grid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16">
|
|
|
|
|
<n-grid-item v-for="item in cardData" :key="item.id">
|
|
|
|
|
<gradient-bg class="h-100px" :start-color="item.colors[0]" :end-color="item.colors[1]">
|
|
|
|
|
<h3 class="text-16px">{{ item.title }}</h3>
|
|
|
|
|
<div class="flex justify-between pt-12px">
|
|
|
|
|
<svg-icon :icon="item.icon" class="text-32px" />
|
|
|
|
|
<count-to
|
|
|
|
|
:prefix="item.unit"
|
|
|
|
|
:start-value="1"
|
|
|
|
|
:end-value="item.value"
|
|
|
|
|
class="text-30px text-white dark:text-dark"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</gradient-bg>
|
|
|
|
|
</n-grid-item>
|
|
|
|
|
</n-grid>
|
|
|
|
|
|
|
|
|
|
<n-divider title-placement="center" style="font-size: 18px; margin: 30px 0">设备信息</n-divider>
|
|
|
|
|
|
|
|
|
|
<n-grid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16">
|
|
|
|
|
<n-grid-item v-for="item in deviceDataList" :key="item.code">
|
|
|
|
|
<div class="device-card" :class="getStatus(item.status).className">
|
|
|
|
|
<div>设备编码 : {{ item.code }}</div>
|
|
|
|
|
<div>设备类型 : 开料机</div>
|
|
|
|
|
<div>运行状态 : {{ getStatus(item.status).text }}</div>
|
|
|
|
|
<div>产品型号 : {{ item.productModel }}</div>
|
|
|
|
|
<div>生产产量 :
|
|
|
|
|
<count-to
|
|
|
|
|
:start-value="1"
|
|
|
|
|
:end-value="item.count"
|
|
|
|
|
:duration="800"
|
|
|
|
|
:autoplay="true"
|
|
|
|
|
:use-easing="true"
|
|
|
|
|
easing-function="cubicOut"
|
|
|
|
|
class="animated-number"
|
|
|
|
|
/></div>
|
|
|
|
|
<div>生产速率 : <span style="color:yellow">{{item.rate}}</span> 个/分</div>
|
|
|
|
|
<div>结束日期 : {{ item.endTime }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</n-grid-item>
|
|
|
|
|
</n-grid>
|
|
|
|
|
</n-card>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="tsx">
|
|
|
|
|
import { ref, onMounted, onUnmounted, reactive } from 'vue';
|
|
|
|
|
import {ref, onMounted, onUnmounted, reactive} from 'vue';
|
|
|
|
|
import mqtt from 'mqtt';
|
|
|
|
|
import { getWorkbenchWiredrawingList } from '@/service/api/md/workbench/wiredrawing';
|
|
|
|
|
import { selectAllWorkbenchEnamellingList } from '@/service/api/md/workbench/enamelling';
|
|
|
|
|
import { useResetSearch } from '~/src/utils/common/searchReset';
|
|
|
|
|
import { useSearchBtn } from '~/src/hooks/common/useBtn';
|
|
|
|
|
import { formatDate } from '~/src/utils/form/rule';
|
|
|
|
|
import { GradientBg } from './components';
|
|
|
|
|
const { searchForm, reset } = useResetSearch({
|
|
|
|
|
location: '全部'
|
|
|
|
|
});
|
|
|
|
|
import {getWorkbenchWiredrawingList} from '@/service/api/md/workbench/wiredrawing';
|
|
|
|
|
import {selectAllWorkbenchEnamellingList} from '@/service/api/md/workbench/enamelling';
|
|
|
|
|
import {useResetSearch} from '~/src/utils/common/searchReset';
|
|
|
|
|
import {useSearchBtn} from '~/src/hooks/common/useBtn';
|
|
|
|
|
import {formatDate} from '~/src/utils/form/rule';
|
|
|
|
|
import {GradientBg} from './components';
|
|
|
|
|
|
|
|
|
|
const {searchForm, reset} = useResetSearch({
|
|
|
|
|
location: '全部'
|
|
|
|
|
});
|
|
|
|
|
const productModels = ['LX25-Y39-002金', 'LX30-Y40-005浅蓝', 'LX35-Z25-006蓝灰', 'LX40-B02-002黑', 'LX30-Y40-005蓝', 'LX35-Z5-004白', 'LX25-A08-007黑'
|
|
|
|
|
, 'LX-Y39-030黑', 'LX20-Y30-010灰', 'LX35-Z5-006蓝', 'LX40-B01-003黑', 'LX30-Y42-002蓝', 'LX35-Z15-004单蓝', 'LX25-A08-006灰']; // 产品型号列表
|
|
|
|
|
interface CardData {
|
|
|
|
|
id: string;
|
|
|
|
|
title: string;
|
|
|
|
|
value: number;
|
|
|
|
|
unit: string;
|
|
|
|
|
colors: [string, string];
|
|
|
|
|
icon: string;
|
|
|
|
|
id: string;
|
|
|
|
|
title: string;
|
|
|
|
|
value: number;
|
|
|
|
|
unit: string;
|
|
|
|
|
colors: [string, string];
|
|
|
|
|
icon: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type device = {
|
|
|
|
|
code: string;
|
|
|
|
|
status: number;
|
|
|
|
|
startTime: string;
|
|
|
|
|
count: number;
|
|
|
|
|
endTime: string;
|
|
|
|
|
process: string;
|
|
|
|
|
code: string;
|
|
|
|
|
status: number;
|
|
|
|
|
startTime: string;
|
|
|
|
|
count: number;
|
|
|
|
|
endTime: string;
|
|
|
|
|
process: string;
|
|
|
|
|
productModel: string;
|
|
|
|
|
timerId?: number;
|
|
|
|
|
interval: number;
|
|
|
|
|
originalCode: string; // 新增字段记录原始编码
|
|
|
|
|
currentModelIndex: number; // 新增字段用于跟踪当前型号索引
|
|
|
|
|
rate?: String;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const deviceAllList = ref<Array<device>>([]);
|
|
|
|
|
|
|
|
|
|
const locationList = ref<Array<{ label: string; value: string }>>([
|
|
|
|
|
{
|
|
|
|
|
label: '全部',
|
|
|
|
|
value: '全部'
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
label: '全部',
|
|
|
|
|
value: '全部'
|
|
|
|
|
}
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const deviceDataList = ref<Array<device>>([]);
|
|
|
|
|
|
|
|
|
|
const cardData = reactive<CardData[]>([
|
|
|
|
|
{
|
|
|
|
|
id: 'visit',
|
|
|
|
|
title: '设备总数',
|
|
|
|
|
value: 72,
|
|
|
|
|
unit: '',
|
|
|
|
|
colors: ['#ec4786', '#b955a4'],
|
|
|
|
|
icon: 'ant-design:bar-chart-outlined'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'amount',
|
|
|
|
|
title: '运行中',
|
|
|
|
|
value: 60,
|
|
|
|
|
unit: '$',
|
|
|
|
|
colors: ['#865ec0', '#5144b4'],
|
|
|
|
|
icon: 'ant-design:money-collect-outlined'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'download',
|
|
|
|
|
title: '待机',
|
|
|
|
|
value: 10,
|
|
|
|
|
unit: '',
|
|
|
|
|
colors: ['#56cdf3', '#719de3'],
|
|
|
|
|
icon: 'carbon:document-download'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'trade',
|
|
|
|
|
title: '故障',
|
|
|
|
|
value: 0,
|
|
|
|
|
unit: '',
|
|
|
|
|
colors: ['#fcbc25', '#f68057'],
|
|
|
|
|
icon: 'ant-design:trademark-circle-outlined'
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
id: 'visit',
|
|
|
|
|
title: '设备总数',
|
|
|
|
|
value: 0,
|
|
|
|
|
unit: '',
|
|
|
|
|
colors: ['#ec4786', '#b955a4'],
|
|
|
|
|
icon: 'ant-design:bar-chart-outlined'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'amount',
|
|
|
|
|
title: '运行中',
|
|
|
|
|
value: 40,
|
|
|
|
|
unit: '$',
|
|
|
|
|
colors: ['#865ec0', '#5144b4'],
|
|
|
|
|
icon: 'ant-design:money-collect-outlined'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'download',
|
|
|
|
|
title: '待机',
|
|
|
|
|
value: 9,
|
|
|
|
|
unit: '',
|
|
|
|
|
colors: ['#56cdf3', '#719de3'],
|
|
|
|
|
icon: 'carbon:document-download'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'trade',
|
|
|
|
|
title: '故障',
|
|
|
|
|
value: 0,
|
|
|
|
|
unit: '',
|
|
|
|
|
colors: ['#fcbc25', '#f68057'],
|
|
|
|
|
icon: 'ant-design:trademark-circle-outlined'
|
|
|
|
|
}
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// MQTT 配置
|
|
|
|
|
@ -138,171 +157,222 @@ const clientId = `mqtt_${Math.random().toString(16).slice(3)}`;
|
|
|
|
|
|
|
|
|
|
// 添加用户名和密码配置
|
|
|
|
|
const options = {
|
|
|
|
|
clean: true, // 保留会话
|
|
|
|
|
connectTimeout: 4000, // 超时时间
|
|
|
|
|
reconnectPeriod: 1000, // 重连时间间隔
|
|
|
|
|
clientId
|
|
|
|
|
clean: true, // 保留会话
|
|
|
|
|
connectTimeout: 4000, // 超时时间
|
|
|
|
|
reconnectPeriod: 1000, // 重连时间间隔
|
|
|
|
|
clientId
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const isConnect = false;
|
|
|
|
|
|
|
|
|
|
const connectToMQTT = () => {
|
|
|
|
|
// 连接到 MQTT broker
|
|
|
|
|
if (!isConnect) return;
|
|
|
|
|
client = mqtt.connect(mqttBrokerUrl, options);
|
|
|
|
|
// 连接成功后的回调
|
|
|
|
|
client.on('connect', () => {
|
|
|
|
|
client.subscribe(topic, err => {
|
|
|
|
|
console.log('topic ==>', topic);
|
|
|
|
|
if (err) {
|
|
|
|
|
console.error('Failed to subscribe:', err);
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`Subscribed to topic: ${topic}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听收到的消息
|
|
|
|
|
client.on('message', (topic1, message) => {
|
|
|
|
|
console.log(topic1);
|
|
|
|
|
// 这里有可能拿到的数据格式是Uint8Array格式,可以直接用toString转成字符串
|
|
|
|
|
const data = JSON.parse(message.toString());
|
|
|
|
|
console.log('data ==>', data);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听连接错误
|
|
|
|
|
client.on('error', error => {
|
|
|
|
|
console.error('MQTT connection error:', error);
|
|
|
|
|
});
|
|
|
|
|
// 连接到 MQTT broker
|
|
|
|
|
if (!isConnect) return;
|
|
|
|
|
client = mqtt.connect(mqttBrokerUrl, options);
|
|
|
|
|
// 连接成功后的回调
|
|
|
|
|
client.on('connect', () => {
|
|
|
|
|
client.subscribe(topic, err => {
|
|
|
|
|
console.log('topic ==>', topic);
|
|
|
|
|
if (err) {
|
|
|
|
|
console.error('Failed to subscribe:', err);
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`Subscribed to topic: ${topic}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听收到的消息
|
|
|
|
|
client.on('message', (topic1, message) => {
|
|
|
|
|
console.log(topic1);
|
|
|
|
|
// 这里有可能拿到的数据格式是Uint8Array格式,可以直接用toString转成字符串
|
|
|
|
|
const data = JSON.parse(message.toString());
|
|
|
|
|
console.log('data ==>', data);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听连接错误
|
|
|
|
|
client.on('error', error => {
|
|
|
|
|
console.error('MQTT connection error:', error);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function getStatus(status: number) {
|
|
|
|
|
switch (status) {
|
|
|
|
|
case 0:
|
|
|
|
|
return {
|
|
|
|
|
text: '运行中',
|
|
|
|
|
className: 'amount-color'
|
|
|
|
|
};
|
|
|
|
|
case 1:
|
|
|
|
|
return {
|
|
|
|
|
text: '待机',
|
|
|
|
|
className: 'download-color'
|
|
|
|
|
};
|
|
|
|
|
case 2:
|
|
|
|
|
return {
|
|
|
|
|
text: '故障',
|
|
|
|
|
className: 'trade-color'
|
|
|
|
|
};
|
|
|
|
|
default:
|
|
|
|
|
return {
|
|
|
|
|
text: '运行中',
|
|
|
|
|
className: 'amount-color'
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
switch (status) {
|
|
|
|
|
case 0:
|
|
|
|
|
return {
|
|
|
|
|
text: '运行中',
|
|
|
|
|
className: 'amount-color'
|
|
|
|
|
};
|
|
|
|
|
case 1:
|
|
|
|
|
return {
|
|
|
|
|
text: '待机',
|
|
|
|
|
className: 'download-color'
|
|
|
|
|
};
|
|
|
|
|
case 2:
|
|
|
|
|
return {
|
|
|
|
|
text: '故障',
|
|
|
|
|
className: 'trade-color'
|
|
|
|
|
};
|
|
|
|
|
default:
|
|
|
|
|
return {
|
|
|
|
|
text: '运行中',
|
|
|
|
|
className: 'amount-color'
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function getList() {
|
|
|
|
|
await getWorkbenchWiredrawingList({ pageSize: 999999 }).then(res => {
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
res.rows.forEach(item => {
|
|
|
|
|
deviceEach(item);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
await selectAllWorkbenchEnamellingList({}).then(res => {
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
res.data.forEach(item => {
|
|
|
|
|
deviceEach(item);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// cardData[0].value = deviceAllList.value.length;
|
|
|
|
|
init();
|
|
|
|
|
await getWorkbenchWiredrawingList({pageSize: 999999}).then(res => {
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
res.rows.forEach(item => {
|
|
|
|
|
deviceEach(item);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
await selectAllWorkbenchEnamellingList({}).then(res => {
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
res.data.forEach(item => {
|
|
|
|
|
deviceEach(item);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
cardData[0].value = deviceAllList.value.length;
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function deviceEach(item: any) {
|
|
|
|
|
if (locationList.value.findIndex(ele => ele.value === item.process) === -1) {
|
|
|
|
|
locationList.value.push({
|
|
|
|
|
label: `${item.process}车间`,
|
|
|
|
|
value: item.process
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
deviceAllList.value.push({
|
|
|
|
|
code: item.equipmentCode,
|
|
|
|
|
status: 0,
|
|
|
|
|
startTime: formatDate(
|
|
|
|
|
new Date(getRandom(new Date().getTime() - 1000 * 60 * 60 * 48, new Date().getTime())),
|
|
|
|
|
'yyyy-MM-dd hh:mm:ss'
|
|
|
|
|
),
|
|
|
|
|
count: getRandom(1, 20),
|
|
|
|
|
endTime: formatDate(
|
|
|
|
|
new Date(getRandom(new Date().getTime(), new Date().getTime() + 1000 * 60 * 60 * 48)),
|
|
|
|
|
'yyyy-MM-dd'
|
|
|
|
|
),
|
|
|
|
|
process: item.process
|
|
|
|
|
});
|
|
|
|
|
if (locationList.value.findIndex(ele => ele.value === item.process) === -1) {
|
|
|
|
|
locationList.value.push({
|
|
|
|
|
label: `${item.process}车间`,
|
|
|
|
|
value: item.process
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
let interval = Math.floor((16 + Math.random() * 10)) * 1000;
|
|
|
|
|
const newDevice = reactive({
|
|
|
|
|
code: item.equipmentCode,
|
|
|
|
|
status: 0,
|
|
|
|
|
productModel: productModels[getRandom(1, 4)],
|
|
|
|
|
startTime: formatDate(
|
|
|
|
|
new Date(getRandom(new Date().getTime() - 1000 * 60 * 60 * 48, new Date().getTime())),
|
|
|
|
|
'yyyy-MM-dd hh:mm:ss'
|
|
|
|
|
),
|
|
|
|
|
count: getRandom(1, 20),
|
|
|
|
|
endTime: formatDate(
|
|
|
|
|
new Date(getRandom(new Date().getTime(), new Date().getTime() + 1000 * 60 * 60 * 48)),
|
|
|
|
|
'yyyy-MM-dd'
|
|
|
|
|
),
|
|
|
|
|
process: item.process,
|
|
|
|
|
originalCode: item.equipmentCode,
|
|
|
|
|
currentModelIndex: 0,
|
|
|
|
|
interval: interval, // 生成16-24秒随机间隔
|
|
|
|
|
// count: 0,
|
|
|
|
|
timerId: undefined,
|
|
|
|
|
rate: (60 * 1000 / interval).toFixed(1)
|
|
|
|
|
});
|
|
|
|
|
startDeviceTimer(newDevice); // 为每个设备启动独立定时器
|
|
|
|
|
deviceAllList.value.push(newDevice);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 为单个设备启动定时器
|
|
|
|
|
function startDeviceTimer(device: device) {
|
|
|
|
|
device.timerId = window.setInterval(() => {
|
|
|
|
|
device.count++;
|
|
|
|
|
if (device.count >= 200) {
|
|
|
|
|
resetDeviceProduction(device);
|
|
|
|
|
}
|
|
|
|
|
}, device.interval); // 20秒间隔
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 重置设备生产数据
|
|
|
|
|
function resetDeviceProduction(device: device) {
|
|
|
|
|
device.count = 0;
|
|
|
|
|
device.currentModelIndex = (device.currentModelIndex + 1) % productModels.length;
|
|
|
|
|
device.productModel = productModels[device.currentModelIndex];
|
|
|
|
|
clearInterval(device.timerId);
|
|
|
|
|
device.interval = Math.floor((15 + Math.random() * 10)) * 1000; // 重置时生成新间隔
|
|
|
|
|
device.rate = (60 * 1000 / device.interval).toFixed(1);
|
|
|
|
|
startDeviceTimer(device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getRandom(min: number, max: number) {
|
|
|
|
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
|
|
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function search() {
|
|
|
|
|
init();
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resetThen() {
|
|
|
|
|
reset();
|
|
|
|
|
init();
|
|
|
|
|
reset();
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
|
if (!searchForm.value.location) {
|
|
|
|
|
deviceDataList.value = [];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (searchForm.value.location === '全部') {
|
|
|
|
|
deviceDataList.value = deviceAllList.value.map(item => item);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
deviceDataList.value = deviceAllList.value.filter(item => item.process === searchForm.value.location);
|
|
|
|
|
if (!searchForm.value.location) {
|
|
|
|
|
deviceDataList.value = [];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (searchForm.value.location === '全部') {
|
|
|
|
|
deviceDataList.value = deviceAllList.value.map(item => item);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
deviceDataList.value = deviceAllList.value.filter(item => item.process === searchForm.value.location);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 定时器逻辑
|
|
|
|
|
let intervalId: number;
|
|
|
|
|
// 组件挂载时连接 MQTT
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
connectToMQTT();
|
|
|
|
|
init();
|
|
|
|
|
getList();
|
|
|
|
|
connectToMQTT();
|
|
|
|
|
init();
|
|
|
|
|
getList();
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 组件卸载时断开 MQTT 连接
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
if (client) {
|
|
|
|
|
client.end();
|
|
|
|
|
}
|
|
|
|
|
deviceAllList.value.forEach(device => {
|
|
|
|
|
if (device.timerId) clearInterval(device.timerId);
|
|
|
|
|
});
|
|
|
|
|
if (client) {
|
|
|
|
|
client.end();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
/* 你的样式 */
|
|
|
|
|
|
|
|
|
|
.device-card {
|
|
|
|
|
color: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.amount-color {
|
|
|
|
|
background: linear-gradient(45deg, #865ec0, #5144b4);
|
|
|
|
|
background: linear-gradient(45deg, #865ec0, #5144b4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.download-color {
|
|
|
|
|
background: linear-gradient(45deg, #56cdf3, #719de3);
|
|
|
|
|
background: linear-gradient(45deg, #56cdf3, #719de3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.trade-color {
|
|
|
|
|
background: linear-gradient(45deg, #fcbc25, #f68057);
|
|
|
|
|
background: linear-gradient(45deg, #fcbc25, #f68057);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 添加数字变化动画 */
|
|
|
|
|
.flip-animation {
|
|
|
|
|
transition: transform 0.5s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.flip-animation-up {
|
|
|
|
|
transform: translateY(-20px);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.flip-animation-down {
|
|
|
|
|
transform: translateY(20px);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|