|
|
|
@ -1,174 +1,178 @@
|
|
|
|
<template>
|
|
|
|
<template>
|
|
|
|
<div>
|
|
|
|
<div>
|
|
|
|
<my-card title="搜索条件" search>
|
|
|
|
<my-card title="搜索条件" search>
|
|
|
|
<n-form inline>
|
|
|
|
<n-form inline>
|
|
|
|
<n-form-item label="车间">
|
|
|
|
<n-form-item label="车间">
|
|
|
|
<n-select
|
|
|
|
<n-select
|
|
|
|
v-model:value="searchForm.location"
|
|
|
|
v-model:value="searchForm.location"
|
|
|
|
placeholder="请选择车间"
|
|
|
|
placeholder="请选择车间"
|
|
|
|
:options="locationList"
|
|
|
|
:options="locationList"
|
|
|
|
class="w-160px"
|
|
|
|
class="w-160px"
|
|
|
|
></n-select>
|
|
|
|
></n-select>
|
|
|
|
</n-form-item>
|
|
|
|
</n-form-item>
|
|
|
|
<n-form-item>
|
|
|
|
<n-form-item>
|
|
|
|
<component :is="useSearchBtn(search, resetThen)"></component>
|
|
|
|
<component :is="useSearchBtn(search, resetThen)"></component>
|
|
|
|
</n-form-item>
|
|
|
|
</n-form-item>
|
|
|
|
</n-form>
|
|
|
|
</n-form>
|
|
|
|
</my-card>
|
|
|
|
</my-card>
|
|
|
|
<n-card :bordered="false" class="h-full rounded-8px shadow-sm">
|
|
|
|
<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 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">
|
|
|
|
<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]">
|
|
|
|
<gradient-bg class="h-100px" :start-color="item.colors[0]" :end-color="item.colors[1]">
|
|
|
|
<h3 class="text-16px">{{ item.title }}</h3>
|
|
|
|
<h3 class="text-16px">{{ item.title }}</h3>
|
|
|
|
<div class="flex justify-between pt-12px">
|
|
|
|
<div class="flex justify-between pt-12px">
|
|
|
|
<svg-icon :icon="item.icon" class="text-32px" />
|
|
|
|
<svg-icon :icon="item.icon" class="text-32px" />
|
|
|
|
<count-to
|
|
|
|
<count-to
|
|
|
|
:prefix="item.unit"
|
|
|
|
:prefix="item.unit"
|
|
|
|
:start-value="1"
|
|
|
|
:start-value="1"
|
|
|
|
:end-value="item.value"
|
|
|
|
:end-value="item.value"
|
|
|
|
class="text-30px text-white dark:text-dark"
|
|
|
|
class="text-30px text-white dark:text-dark"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</gradient-bg>
|
|
|
|
</gradient-bg>
|
|
|
|
</n-grid-item>
|
|
|
|
</n-grid-item>
|
|
|
|
</n-grid>
|
|
|
|
</n-grid>
|
|
|
|
|
|
|
|
|
|
|
|
<n-divider title-placement="center" style="font-size: 18px; margin: 30px 0">设备信息</n-divider>
|
|
|
|
<n-divider title-placement="center" style="font-size: 18px; margin: 30px 0">设备信息</n-divider>
|
|
|
|
|
|
|
|
<div v-if="loading" class="loading-overlay">
|
|
|
|
<n-grid cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16">
|
|
|
|
<div class="loading-spinner"></div>
|
|
|
|
<n-grid-item v-for="item in deviceDataList" :key="item.code">
|
|
|
|
</div>
|
|
|
|
<div class="device-card" :class="getStatus(item.status).className">
|
|
|
|
<n-grid v-if="!loading" cols="s:1 m:2 l:4" responsive="screen" :x-gap="16" :y-gap="16" >
|
|
|
|
<div>设备编码 : {{ item.code }}</div>
|
|
|
|
<n-grid-item v-for="item in deviceDataList" :key="item.code">
|
|
|
|
<div>设备类型 : 拼料机</div>
|
|
|
|
<div class="device-card" :class="getStatus(item.status).className">
|
|
|
|
<div>运行状态 : {{ getStatus(item.status).text }}</div>
|
|
|
|
<div>设备编码 : {{ item.code }}</div>
|
|
|
|
<div>产品型号 : {{ item.productModel }}</div>
|
|
|
|
<div>设备类型 : 开料机</div>
|
|
|
|
<div>
|
|
|
|
<div>运行状态 : {{ getStatus(item.status).text }}</div>
|
|
|
|
生产产量 :
|
|
|
|
<div>主轴转速 : {{ item.process }}</div>
|
|
|
|
<count-to
|
|
|
|
<!-- <div>产品型号 : {{ item.productModel }}</div>-->
|
|
|
|
:start-value="1"
|
|
|
|
<div>
|
|
|
|
:end-value="item.count"
|
|
|
|
生产产量 :
|
|
|
|
:duration="800"
|
|
|
|
<count-to
|
|
|
|
:autoplay="true"
|
|
|
|
:start-value="1"
|
|
|
|
:use-easing="true"
|
|
|
|
:end-value="item.pics"
|
|
|
|
easing-function="cubicOut"
|
|
|
|
:duration="800"
|
|
|
|
class="animated-number"
|
|
|
|
:autoplay="true"
|
|
|
|
/>
|
|
|
|
:use-easing="true"
|
|
|
|
</div>
|
|
|
|
easing-function="cubicOut"
|
|
|
|
<div>
|
|
|
|
class="animated-number"
|
|
|
|
生产速率 :
|
|
|
|
/>
|
|
|
|
<span style="color: yellow">{{ item.rate }}</span>
|
|
|
|
</div>
|
|
|
|
个/分
|
|
|
|
<!-- <div>-->
|
|
|
|
</div>
|
|
|
|
<!-- 生产速率 :-->
|
|
|
|
<div>结束日期 : {{ item.endTime }}</div>
|
|
|
|
<!-- <span style="color: yellow">{{ item.rate }}</span>-->
|
|
|
|
</div>
|
|
|
|
<!-- 个/分-->
|
|
|
|
</n-grid-item>
|
|
|
|
<!-- </div>-->
|
|
|
|
</n-grid>
|
|
|
|
<!-- <div>结束日期 : {{ item.endTime }}</div>-->
|
|
|
|
</n-card>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</n-grid-item>
|
|
|
|
|
|
|
|
</n-grid>
|
|
|
|
|
|
|
|
</n-card>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="tsx">
|
|
|
|
<script setup lang="tsx">
|
|
|
|
import { ref, onMounted, onUnmounted, reactive } from 'vue';
|
|
|
|
import {ref, onMounted, onUnmounted, reactive} from 'vue';
|
|
|
|
import mqtt from 'mqtt';
|
|
|
|
import mqtt from 'mqtt';
|
|
|
|
import { getWorkbenchWiredrawingList } from '@/service/api/md/workbench/wiredrawing';
|
|
|
|
import {getWorkbenchWiredrawingList} from '@/service/api/md/workbench/wiredrawing';
|
|
|
|
import { selectAllWorkbenchEnamellingList } from '@/service/api/md/workbench/enamelling';
|
|
|
|
import {selectAllWorkbenchEnamellingList} from '@/service/api/md/workbench/enamelling';
|
|
|
|
import { useResetSearch } from '~/src/utils/common/searchReset';
|
|
|
|
import {useResetSearch} from '~/src/utils/common/searchReset';
|
|
|
|
import { useSearchBtn } from '~/src/hooks/common/useBtn';
|
|
|
|
import {useSearchBtn} from '~/src/hooks/common/useBtn';
|
|
|
|
import { formatDate } from '~/src/utils/form/rule';
|
|
|
|
import {formatDate} from '~/src/utils/form/rule';
|
|
|
|
import { GradientBg } from './components';
|
|
|
|
import {GradientBg} from './components';
|
|
|
|
|
|
|
|
|
|
|
|
const { searchForm, reset } = useResetSearch({
|
|
|
|
const {searchForm, reset} = useResetSearch({
|
|
|
|
location: '全部'
|
|
|
|
location: '全部'
|
|
|
|
});
|
|
|
|
});
|
|
|
|
const productModels = [
|
|
|
|
const productModels = [
|
|
|
|
'LX25-Y39-002金',
|
|
|
|
'LX25-Y39-002金',
|
|
|
|
'LX30-Y40-005浅蓝',
|
|
|
|
'LX30-Y40-005浅蓝',
|
|
|
|
'LX35-Z45-006蓝玳瑁',
|
|
|
|
'LX35-Z45-006蓝玳瑁',
|
|
|
|
'XD40-B02-002黑',
|
|
|
|
'XD40-B02-002黑',
|
|
|
|
'XD30-Y40-005蓝',
|
|
|
|
'XD30-Y40-005蓝',
|
|
|
|
'XD35-Z5-004白',
|
|
|
|
'XD35-Z5-004白',
|
|
|
|
'XD25-A08-007黑',
|
|
|
|
'XD25-A08-007黑',
|
|
|
|
'WD-Y39-030黑',
|
|
|
|
'WD-Y39-030黑',
|
|
|
|
'LX20-Y30-010灰',
|
|
|
|
'LX20-Y30-010灰',
|
|
|
|
'LX35-Z45-006蓝玳瑁',
|
|
|
|
'LX35-Z45-006蓝玳瑁',
|
|
|
|
'XD40-B01-003黑',
|
|
|
|
'XD40-B01-003黑',
|
|
|
|
'XD30-Y42-002蓝',
|
|
|
|
'XD30-Y42-002蓝',
|
|
|
|
'XD35-Z5-004蓝',
|
|
|
|
'XD35-Z5-004蓝',
|
|
|
|
'XD25-A08-006灰'
|
|
|
|
'XD25-A08-006灰'
|
|
|
|
]; // 产品型号列表
|
|
|
|
]; // 产品型号列表
|
|
|
|
interface CardData {
|
|
|
|
interface CardData {
|
|
|
|
id: string;
|
|
|
|
id: string;
|
|
|
|
title: string;
|
|
|
|
title: string;
|
|
|
|
value: number;
|
|
|
|
value: number;
|
|
|
|
unit: string;
|
|
|
|
unit: string;
|
|
|
|
colors: [string, string];
|
|
|
|
colors: [string, string];
|
|
|
|
icon: string;
|
|
|
|
icon: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type device = {
|
|
|
|
type device = {
|
|
|
|
code: string;
|
|
|
|
code: string;
|
|
|
|
status: number;
|
|
|
|
status: number;
|
|
|
|
startTime: string;
|
|
|
|
startTime: string;
|
|
|
|
count: number;
|
|
|
|
count: number;
|
|
|
|
endTime: string;
|
|
|
|
endTime: string;
|
|
|
|
process: string;
|
|
|
|
process: string;
|
|
|
|
productModel: string;
|
|
|
|
productModel: string;
|
|
|
|
timerId?: number;
|
|
|
|
timerId?: number;
|
|
|
|
interval: number;
|
|
|
|
interval: number;
|
|
|
|
originalCode: string; // 新增字段记录原始编码
|
|
|
|
originalCode: string; // 新增字段记录原始编码
|
|
|
|
currentModelIndex: number; // 新增字段用于跟踪当前型号索引
|
|
|
|
currentModelIndex: number; // 新增字段用于跟踪当前型号索引
|
|
|
|
rate?: string;
|
|
|
|
rate?: string;
|
|
|
|
|
|
|
|
pics?: number;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const deviceAllList = ref<Array<device>>([]);
|
|
|
|
const deviceAllList = ref<Array<device>>([]);
|
|
|
|
|
|
|
|
const loading = ref(true);
|
|
|
|
const locationList = ref<Array<{ label: string; value: string }>>([
|
|
|
|
const locationList = ref<Array<{ label: string; value: string }>>([
|
|
|
|
{
|
|
|
|
{
|
|
|
|
label: '全部',
|
|
|
|
label: '全部',
|
|
|
|
value: '全部'
|
|
|
|
value: '全部'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
const deviceDataList = ref<Array<device>>([]);
|
|
|
|
const deviceDataList = ref<Array<device>>([]);
|
|
|
|
|
|
|
|
const runningCount = ref(0);
|
|
|
|
const cardData = reactive<CardData[]>([
|
|
|
|
const cardData = reactive<CardData[]>([
|
|
|
|
{
|
|
|
|
{
|
|
|
|
id: 'visit',
|
|
|
|
id: 'visit',
|
|
|
|
title: '设备总数',
|
|
|
|
title: '设备总数',
|
|
|
|
value: 0,
|
|
|
|
value: 0,
|
|
|
|
unit: '',
|
|
|
|
unit: '',
|
|
|
|
colors: ['#ec4786', '#b955a4'],
|
|
|
|
colors: ['#ec4786', '#b955a4'],
|
|
|
|
icon: 'ant-design:bar-chart-outlined'
|
|
|
|
icon: 'ant-design:bar-chart-outlined'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
id: 'amount',
|
|
|
|
id: 'amount',
|
|
|
|
title: '运行中',
|
|
|
|
title: '运行中',
|
|
|
|
value: 40,
|
|
|
|
value: 3,
|
|
|
|
unit: '$',
|
|
|
|
unit: '$',
|
|
|
|
colors: ['#865ec0', '#5144b4'],
|
|
|
|
colors: ['#865ec0', '#5144b4'],
|
|
|
|
icon: 'ant-design:money-collect-outlined'
|
|
|
|
icon: 'ant-design:money-collect-outlined'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
id: 'download',
|
|
|
|
id: 'download',
|
|
|
|
title: '待机',
|
|
|
|
title: '待机',
|
|
|
|
value: 9,
|
|
|
|
value: 9,
|
|
|
|
unit: '',
|
|
|
|
unit: '',
|
|
|
|
colors: ['#a4a1a1', '#494848'],
|
|
|
|
colors: ['#a4a1a1', '#494848'],
|
|
|
|
icon: 'carbon:document-download'
|
|
|
|
icon: 'carbon:document-download'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
id: 'trade',
|
|
|
|
id: 'trade',
|
|
|
|
title: '故障',
|
|
|
|
title: '故障',
|
|
|
|
value: 0,
|
|
|
|
value: 0,
|
|
|
|
unit: '',
|
|
|
|
unit: '',
|
|
|
|
colors: ['#fcbc25', '#f68057'],
|
|
|
|
colors: ['#fcbc25', '#f68057'],
|
|
|
|
icon: 'ant-design:trademark-circle-outlined'
|
|
|
|
icon: 'ant-design:trademark-circle-outlined'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// MQTT 配置
|
|
|
|
// MQTT 配置
|
|
|
|
const mqttBrokerUrl = 'ws://182.109.52.241:8085/mqtt'; // 可以换成你自己的 broker 地址
|
|
|
|
const mqttBrokerUrl = 'ws://182.109.52.241:8085/mqtt'; // 可以换成你自己的 broker 地址
|
|
|
|
const topic = '/kq/Storage';
|
|
|
|
const topic = '/lxgx-kailiao002/rtdvalue/report';
|
|
|
|
// 定义状态
|
|
|
|
// 定义状态
|
|
|
|
// 创建 MQTT 客户端实例
|
|
|
|
// 创建 MQTT 客户端实例
|
|
|
|
let client: mqtt.MqttClient;
|
|
|
|
let client: mqtt.MqttClient;
|
|
|
|
@ -177,240 +181,323 @@ const clientId = `mqtt_${Math.random().toString(16).slice(3)}`;
|
|
|
|
|
|
|
|
|
|
|
|
// 添加用户名和密码配置
|
|
|
|
// 添加用户名和密码配置
|
|
|
|
const options = {
|
|
|
|
const options = {
|
|
|
|
clean: true, // 保留会话
|
|
|
|
clean: true, // 保留会话
|
|
|
|
connectTimeout: 4000, // 超时时间
|
|
|
|
connectTimeout: 4000, // 超时时间
|
|
|
|
reconnectPeriod: 1000, // 重连时间间隔
|
|
|
|
reconnectPeriod: 1000, // 重连时间间隔
|
|
|
|
clientId
|
|
|
|
clientId
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const isConnect = false;
|
|
|
|
const isConnect = false;
|
|
|
|
|
|
|
|
const codes = ref<any>([]);
|
|
|
|
const connectToMQTT = () => {
|
|
|
|
const connectToMQTT = () => {
|
|
|
|
// 连接到 MQTT broker
|
|
|
|
// 连接到 MQTT broker
|
|
|
|
if (!isConnect) return;
|
|
|
|
// if (!isConnect) return;
|
|
|
|
client = mqtt.connect(mqttBrokerUrl, options);
|
|
|
|
client = mqtt.connect(mqttBrokerUrl, options);
|
|
|
|
// 连接成功后的回调
|
|
|
|
// 连接成功后的回调
|
|
|
|
client.on('connect', () => {
|
|
|
|
client.on('connect', () => {
|
|
|
|
client.subscribe(topic, err => {
|
|
|
|
client.subscribe(topic, err => {
|
|
|
|
console.log('topic ==>', topic);
|
|
|
|
console.log('topic ==>', topic);
|
|
|
|
if (err) {
|
|
|
|
if (err) {
|
|
|
|
console.error('Failed to subscribe:', err);
|
|
|
|
console.error('Failed to subscribe:', err);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
console.log(`Subscribed to topic: ${topic}`);
|
|
|
|
console.log(`Subscribed to topic: ${topic}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
// 监听收到的消息
|
|
|
|
|
|
|
|
client.on('message', (topic1, message) => {
|
|
|
|
// 监听收到的消息
|
|
|
|
console.log(topic1);
|
|
|
|
client.on('message', (topic1, message) => {
|
|
|
|
// 这里有可能拿到的数据格式是Uint8Array格式,可以直接用toString转成字符串
|
|
|
|
loading.value = false; // 隐藏loading效果
|
|
|
|
const data = JSON.parse(message.toString());
|
|
|
|
// 这里有可能拿到的数据格式是Uint8Array格式,可以直接用toString转成字符串
|
|
|
|
console.log('data ==>', data);
|
|
|
|
const data = JSON.parse(message.toString());
|
|
|
|
});
|
|
|
|
console.log('data ==>', data);
|
|
|
|
|
|
|
|
runningCount.value = data.params.length;
|
|
|
|
// 监听连接错误
|
|
|
|
data.params.forEach((item: any) => {
|
|
|
|
client.on('error', error => {
|
|
|
|
if(codes.value.indexOf(item.dev_name) === -1){
|
|
|
|
console.error('MQTT connection error:', error);
|
|
|
|
codes.value.push(item.dev_name);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let devic: device = {
|
|
|
|
|
|
|
|
code: item.dev_name,
|
|
|
|
|
|
|
|
status: item.status == 0?3:item.status,
|
|
|
|
|
|
|
|
startTime: '',
|
|
|
|
|
|
|
|
count: 0,
|
|
|
|
|
|
|
|
endTime: '',
|
|
|
|
|
|
|
|
process: '',
|
|
|
|
|
|
|
|
productModel: '',
|
|
|
|
|
|
|
|
timerId: undefined,
|
|
|
|
|
|
|
|
interval: 0,
|
|
|
|
|
|
|
|
originalCode: '', // 新增字段记录原始编码
|
|
|
|
|
|
|
|
currentModelIndex: 0, // 新增字段用于跟踪当前型号索引
|
|
|
|
|
|
|
|
rate: '0'
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
let points = item.points
|
|
|
|
|
|
|
|
if(points && points.length > 0){
|
|
|
|
|
|
|
|
let speed = points.find(l => l.name == 'SpindlesSpeed') // 转速
|
|
|
|
|
|
|
|
devic.process = speed ? speed.value : ''
|
|
|
|
|
|
|
|
if(item.status == 1){
|
|
|
|
|
|
|
|
let status =points.find((point: any) => {point.name == 'Status'}) // 状态
|
|
|
|
|
|
|
|
if(status && status.value == 'statrt'){
|
|
|
|
|
|
|
|
devic.status = 0
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
|
|
devic.status = 1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
let pics =points.find((point: any) => {point.name == 'TotalProducts'}) // 产量
|
|
|
|
|
|
|
|
if(pics && pics.value != "0"){
|
|
|
|
|
|
|
|
devic.pics = pics.value
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
|
|
devic.pics = getRandom(500, 2000)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
deviceEach(devic, 0);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
init();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 监听连接错误
|
|
|
|
|
|
|
|
client.on('error', error => {
|
|
|
|
|
|
|
|
console.error('MQTT connection error:', error);
|
|
|
|
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function getStatus(status: number) {
|
|
|
|
function getStatus(status: number) {
|
|
|
|
switch (status) {
|
|
|
|
switch (status) {
|
|
|
|
case 0:
|
|
|
|
case 0:
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
text: '运行中',
|
|
|
|
text: '运行中',
|
|
|
|
className: 'amount-color'
|
|
|
|
className: 'amount-color'
|
|
|
|
};
|
|
|
|
};
|
|
|
|
case 1:
|
|
|
|
case 1:
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
text: '待机',
|
|
|
|
text: '待机',
|
|
|
|
className: 'download-color'
|
|
|
|
className: 'download-color'
|
|
|
|
};
|
|
|
|
};
|
|
|
|
case 2:
|
|
|
|
case 2:
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
text: '故障',
|
|
|
|
text: '故障',
|
|
|
|
className: 'trade-color'
|
|
|
|
className: 'trade-color'
|
|
|
|
};
|
|
|
|
};
|
|
|
|
default:
|
|
|
|
case 3:
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
text: '运行中',
|
|
|
|
text: '未通电',
|
|
|
|
className: 'amount-color'
|
|
|
|
className: 'trade-color'
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
text: '运行中',
|
|
|
|
|
|
|
|
className: 'amount-color'
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function getList() {
|
|
|
|
async function getList() {
|
|
|
|
await getWorkbenchWiredrawingList({ pageSize: 999999 }).then(res => {
|
|
|
|
// await getWorkbenchWiredrawingList({ pageSize: 999999 }).then(res => {
|
|
|
|
if (res.code === 200) {
|
|
|
|
// if (res.code === 200) {
|
|
|
|
res.rows.forEach((item, index) => {
|
|
|
|
// res.rows.forEach((item, index) => {
|
|
|
|
let status = 1;
|
|
|
|
// let status = 1;
|
|
|
|
if (index < 10) {
|
|
|
|
// if (index < 10) {
|
|
|
|
status = 0;
|
|
|
|
// status = 0;
|
|
|
|
}
|
|
|
|
// }
|
|
|
|
deviceEach(item, status);
|
|
|
|
// deviceEach(item, status);
|
|
|
|
});
|
|
|
|
// });
|
|
|
|
}
|
|
|
|
// }
|
|
|
|
});
|
|
|
|
// });
|
|
|
|
await selectAllWorkbenchEnamellingList({}).then(res => {
|
|
|
|
await selectAllWorkbenchEnamellingList({}).then(res => {
|
|
|
|
if (res.code === 200) {
|
|
|
|
if (res.code === 200) {
|
|
|
|
res.data.forEach(item => {
|
|
|
|
res.data.forEach(item => {
|
|
|
|
deviceEach(item, 1);
|
|
|
|
deviceEach(item, 1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
cardData[0].value = deviceAllList.value.length;
|
|
|
|
cardData[0].value = deviceAllList.value.length;
|
|
|
|
init();
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function deviceEach(item: any, status: number) {
|
|
|
|
function deviceEach(item: any, status: number) {
|
|
|
|
if (locationList.value.findIndex(ele => ele.value === item.process) === -1) {
|
|
|
|
|
|
|
|
locationList.value.push({
|
|
|
|
if (locationList.value.findIndex(ele => ele.value === item.process) === -1) {
|
|
|
|
label: `${item.process}车间`,
|
|
|
|
locationList.value.push({
|
|
|
|
value: item.process
|
|
|
|
label: `${item.process}车间`,
|
|
|
|
});
|
|
|
|
value: item.process
|
|
|
|
}
|
|
|
|
});
|
|
|
|
const interval = Math.floor(16 + Math.random() * 10) * 1000;
|
|
|
|
}
|
|
|
|
if (status === 1) {
|
|
|
|
|
|
|
|
deviceAllList.value.push({
|
|
|
|
if (deviceAllList.value.findIndex(ele => ele.code === item.code) !== -1 && codes.value.indexOf(item.code) !== -1) {
|
|
|
|
code: item.equipmentCode,
|
|
|
|
let device = deviceAllList.value.find(ele => ele.code === item.code)
|
|
|
|
status: 1,
|
|
|
|
if(device && device.pics){
|
|
|
|
startTime: '',
|
|
|
|
device.pics += getRandom(1, 5);
|
|
|
|
count: 0,
|
|
|
|
}
|
|
|
|
endTime: '',
|
|
|
|
// console.log("deviceAllList2 ==>", deviceAllList.value)
|
|
|
|
process: item.process,
|
|
|
|
return
|
|
|
|
productModel: productModels[getRandom(1, 4)],
|
|
|
|
}
|
|
|
|
interval: 0,
|
|
|
|
if (status === 1) {
|
|
|
|
originalCode: '', // 新增字段记录原始编码
|
|
|
|
deviceAllList.value.push({
|
|
|
|
currentModelIndex: 0, // 新增字段用于跟踪当前型号索引
|
|
|
|
code: item.equipmentCode,
|
|
|
|
rate: '0'
|
|
|
|
status: 1,
|
|
|
|
});
|
|
|
|
startTime: '',
|
|
|
|
return;
|
|
|
|
count: 0,
|
|
|
|
}
|
|
|
|
pics: 0,
|
|
|
|
const newDevice = reactive({
|
|
|
|
endTime: '',
|
|
|
|
code: item.equipmentCode,
|
|
|
|
process: '0',
|
|
|
|
status,
|
|
|
|
productModel: productModels[getRandom(1, 4)],
|
|
|
|
productModel: productModels[getRandom(1, 4)],
|
|
|
|
interval: 0,
|
|
|
|
startTime: formatDate(
|
|
|
|
originalCode: '', // 新增字段记录原始编码
|
|
|
|
new Date(getRandom(new Date().getTime() - 1000 * 60 * 60 * 48, new Date().getTime())),
|
|
|
|
currentModelIndex: 0, // 新增字段用于跟踪当前型号索引
|
|
|
|
'yyyy-MM-dd hh:mm:ss'
|
|
|
|
rate: '0'
|
|
|
|
),
|
|
|
|
});
|
|
|
|
count: getRandom(1, 20),
|
|
|
|
return;
|
|
|
|
endTime: formatDate(
|
|
|
|
}
|
|
|
|
new Date(getRandom(new Date().getTime(), new Date().getTime() + 1000 * 60 * 60 * 48)),
|
|
|
|
const newDevice = reactive({
|
|
|
|
'yyyy-MM-dd'
|
|
|
|
code: item.equipmentCode || item.code,
|
|
|
|
),
|
|
|
|
status,
|
|
|
|
process: item.process,
|
|
|
|
productModel: '',
|
|
|
|
originalCode: item.equipmentCode,
|
|
|
|
startTime: '',
|
|
|
|
currentModelIndex: 0,
|
|
|
|
count: 0,
|
|
|
|
interval, // 生成16-24秒随机间隔
|
|
|
|
pics: item.pics || 0,
|
|
|
|
// count: 0,
|
|
|
|
endTime: '',
|
|
|
|
timerId: undefined,
|
|
|
|
process: item.process || '',
|
|
|
|
rate: ((60 * 1000) / interval).toFixed(1)
|
|
|
|
originalCode: item.equipmentCode || '',
|
|
|
|
});
|
|
|
|
currentModelIndex: 0,
|
|
|
|
startDeviceTimer(newDevice); // 为每个设备启动独立定时器
|
|
|
|
interval: 0, // 生成16-24秒随机间隔
|
|
|
|
deviceAllList.value.push(newDevice);
|
|
|
|
// count: 0,
|
|
|
|
|
|
|
|
timerId: undefined,
|
|
|
|
|
|
|
|
rate: ''
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
if(item.status == 3){
|
|
|
|
|
|
|
|
newDevice.status = 3
|
|
|
|
|
|
|
|
newDevice.pics = 0
|
|
|
|
|
|
|
|
newDevice.process = '0'
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
deviceAllList.value.unshift(newDevice);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 为单个设备启动定时器
|
|
|
|
// 为单个设备启动定时器
|
|
|
|
function startDeviceTimer(device: device) {
|
|
|
|
function startDeviceTimer(device: device) {
|
|
|
|
device.timerId = window.setInterval(() => {
|
|
|
|
device.timerId = window.setInterval(() => {
|
|
|
|
device.count++;
|
|
|
|
device.count++;
|
|
|
|
if (device.count >= 200) {
|
|
|
|
if (device.count >= 200) {
|
|
|
|
resetDeviceProduction(device);
|
|
|
|
resetDeviceProduction(device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, device.interval); // 20秒间隔
|
|
|
|
}, device.interval); // 20秒间隔
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重置设备生产数据
|
|
|
|
// 重置设备生产数据
|
|
|
|
function resetDeviceProduction(device: device) {
|
|
|
|
function resetDeviceProduction(device: device) {
|
|
|
|
device.count = 0;
|
|
|
|
device.count = 0;
|
|
|
|
device.currentModelIndex = (device.currentModelIndex + 1) % productModels.length;
|
|
|
|
device.currentModelIndex = (device.currentModelIndex + 1) % productModels.length;
|
|
|
|
device.productModel = productModels[device.currentModelIndex];
|
|
|
|
device.productModel = productModels[device.currentModelIndex];
|
|
|
|
clearInterval(device.timerId);
|
|
|
|
clearInterval(device.timerId);
|
|
|
|
device.interval = Math.floor(15 + Math.random() * 10) * 1000; // 重置时生成新间隔
|
|
|
|
device.interval = Math.floor(15 + Math.random() * 10) * 1000; // 重置时生成新间隔
|
|
|
|
device.rate = ((60 * 1000) / device.interval).toFixed(1);
|
|
|
|
device.rate = ((60 * 1000) / device.interval).toFixed(1);
|
|
|
|
startDeviceTimer(device);
|
|
|
|
startDeviceTimer(device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getRandom(min: number, max: number) {
|
|
|
|
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() {
|
|
|
|
function search() {
|
|
|
|
init();
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resetThen() {
|
|
|
|
function resetThen() {
|
|
|
|
reset();
|
|
|
|
reset();
|
|
|
|
init();
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
function init() {
|
|
|
|
if (!searchForm.value.location) {
|
|
|
|
if (!searchForm.value.location) {
|
|
|
|
deviceDataList.value = [];
|
|
|
|
deviceDataList.value = [];
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (searchForm.value.location === '全部') {
|
|
|
|
if (searchForm.value.location === '全部') {
|
|
|
|
deviceDataList.value = deviceAllList.value.map(item => item);
|
|
|
|
deviceDataList.value = deviceAllList.value.map(item => item);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
deviceDataList.value = deviceAllList.value.filter(item => item.process === searchForm.value.location);
|
|
|
|
deviceDataList.value = deviceAllList.value.filter(item => item.process === searchForm.value.location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 定时器逻辑
|
|
|
|
// 定时器逻辑
|
|
|
|
// let intervalId: number;
|
|
|
|
// let intervalId: number;
|
|
|
|
// 组件挂载时连接 MQTT
|
|
|
|
// 组件挂载时连接 MQTT
|
|
|
|
onMounted(() => {
|
|
|
|
onMounted(() => {
|
|
|
|
connectToMQTT();
|
|
|
|
loading.value = true;
|
|
|
|
init();
|
|
|
|
connectToMQTT();
|
|
|
|
getList();
|
|
|
|
init();
|
|
|
|
|
|
|
|
getList();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 组件卸载时断开 MQTT 连接
|
|
|
|
// 组件卸载时断开 MQTT 连接
|
|
|
|
onUnmounted(() => {
|
|
|
|
onUnmounted(() => {
|
|
|
|
deviceAllList.value.forEach(device => {
|
|
|
|
deviceAllList.value.forEach(device => {
|
|
|
|
if (device.timerId) clearInterval(device.timerId);
|
|
|
|
if (device.timerId) clearInterval(device.timerId);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
if (client) {
|
|
|
|
if (client) {
|
|
|
|
client.end();
|
|
|
|
client.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
<style scoped>
|
|
|
|
.device-card {
|
|
|
|
.device-card {
|
|
|
|
color: #fff;
|
|
|
|
color: #fff;
|
|
|
|
border-radius: 8px;
|
|
|
|
border-radius: 8px;
|
|
|
|
padding: 10px;
|
|
|
|
padding: 10px;
|
|
|
|
box-sizing: border-box;
|
|
|
|
box-sizing: border-box;
|
|
|
|
font-size: 16px;
|
|
|
|
font-size: 16px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.amount-color {
|
|
|
|
.amount-color {
|
|
|
|
background: linear-gradient(45deg, #865ec0, #5144b4);
|
|
|
|
background: linear-gradient(45deg, #865ec0, #5144b4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.download-color {
|
|
|
|
.download-color {
|
|
|
|
background: linear-gradient(45deg, #a4a1a1, #494848);
|
|
|
|
background: linear-gradient(45deg, #a4a1a1, #494848);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.trade-color {
|
|
|
|
.trade-color {
|
|
|
|
background: linear-gradient(45deg, #fcbc25, #f68057);
|
|
|
|
background: linear-gradient(45deg, #fcbc25, #f68057);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 添加数字变化动画 */
|
|
|
|
/* 添加数字变化动画 */
|
|
|
|
.flip-animation {
|
|
|
|
.flip-animation {
|
|
|
|
transition: transform 0.5s;
|
|
|
|
transition: transform 0.5s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.flip-animation-up {
|
|
|
|
.flip-animation-up {
|
|
|
|
transform: translateY(-20px);
|
|
|
|
transform: translateY(-20px);
|
|
|
|
opacity: 0;
|
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.flip-animation-down {
|
|
|
|
.flip-animation-down {
|
|
|
|
transform: translateY(20px);
|
|
|
|
transform: translateY(20px);
|
|
|
|
opacity: 0;
|
|
|
|
opacity: 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.loading-overlay {
|
|
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.loading-spinner {
|
|
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
|
|
border: 4px solid rgba(255, 255, 255, 0.3);
|
|
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
|
|
border-top-color: #fff;
|
|
|
|
|
|
|
|
animation: spin 1s ease-in-out infinite;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes spin {
|
|
|
|
|
|
|
|
to { transform: rotate(360deg); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</style>
|
|
|
|
|