2025-07-31 11:43:31 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<el-row class="container" :gutter="20">
|
|
|
|
|
|
<el-col :span="4" class="device-panel">
|
|
|
|
|
|
<div class="panel-header">
|
|
|
|
|
|
<h3>设备列表</h3>
|
2025-08-15 11:42:31 +08:00
|
|
|
|
<el-button size="mini" type="primary" @click="refreshDevices">刷新</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="device-stats">
|
|
|
|
|
|
<span>总设备: {{ deviceStats.totalCount || 0 }}</span>
|
|
|
|
|
|
<span>活跃设备: {{ deviceStats.activeCount || 0 }}</span>
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<ul class="device-list">
|
2025-08-15 11:42:31 +08:00
|
|
|
|
<li v-for="item in deviceList" :key="item.id" class="device-item" :class="{ active: item.isActive }">
|
|
|
|
|
|
<div class="device-status" :class="{ online: item.isActive }"></div>
|
|
|
|
|
|
<div class="device-info">
|
|
|
|
|
|
<span class="device-name">{{ item.name }}</span>
|
|
|
|
|
|
<span class="device-id">{{ item.id }}</span>
|
|
|
|
|
|
<span class="device-ip">{{ item.ip }}</span>
|
|
|
|
|
|
</div>
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</el-col>
|
2025-08-22 11:30:23 +08:00
|
|
|
|
<!-- <el-col :span="20" class="main-panel">
|
2025-07-31 11:43:31 +08:00
|
|
|
|
<div class="form-panel">
|
2025-08-16 16:18:38 +08:00
|
|
|
|
<el-form inline>
|
2025-08-22 11:30:23 +08:00
|
|
|
|
<el-form-item label="目标库位" class="form-item">
|
2025-08-16 16:18:38 +08:00
|
|
|
|
<WarehouseSelect size="mini" style="width: 200px;" v-model="defaultForm.warehouseId" />
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</el-form-item>
|
2025-07-31 18:07:51 +08:00
|
|
|
|
<el-form-item label="挂载单据" class="form-item">
|
2025-08-22 11:30:23 +08:00
|
|
|
|
<el-select size="mini" filterable v-model="defaultForm.stockIoId" placeholder="请选择挂载单据" clearable class="form-input">
|
2025-07-31 11:43:31 +08:00
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="item in masterList"
|
|
|
|
|
|
:key="item.stockIoId"
|
|
|
|
|
|
:label="item.stockIoCode"
|
|
|
|
|
|
:value="item.stockIoId"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="数量" class="form-item">
|
2025-08-16 16:18:38 +08:00
|
|
|
|
<el-input size="mini" v-model="defaultForm.quantity" class="form-input" />
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="记录类型" class="form-item">
|
2025-08-16 16:18:38 +08:00
|
|
|
|
<el-select size="mini" v-model="defaultForm.ioType" placeholder="请选择操作类型" clearable class="form-input">
|
2025-08-16 10:08:38 +08:00
|
|
|
|
<el-option label="入库" value="in" />
|
|
|
|
|
|
<el-option label="出库" value="out" />
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="批次号" class="form-item">
|
2025-08-16 16:18:38 +08:00
|
|
|
|
<el-input size="mini" v-model="defaultForm.batchNo" class="form-input" />
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-button type="primary" :disabled="selectedList.length === 0" @click="handleBatchConfirm">批量确认</el-button>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="table-panel">
|
2025-08-27 16:47:33 +08:00
|
|
|
|
<KLPTable height="100%" :data="messageList" style="width: 100%" class="message-table" stripe @selection-change="handleSelectionChange">
|
2025-07-31 11:43:31 +08:00
|
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
2025-08-22 11:30:23 +08:00
|
|
|
|
<el-table-column prop="time" label="时间" width="150" align="center" />
|
2025-07-31 11:43:31 +08:00
|
|
|
|
<el-table-column prop="itemId" label="物料" align="center">
|
|
|
|
|
|
<template #default="scope">
|
2025-08-11 15:04:35 +08:00
|
|
|
|
<ProductInfo v-if="scope.row.itemType == 'product' || scope.row.itemType == 'semi'" :productId="scope.row.itemId" />
|
2025-07-31 11:43:31 +08:00
|
|
|
|
<RawMaterialInfo v-else :materialId="scope.row.itemId" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="存储位置" align="center">
|
|
|
|
|
|
<template #default="scope">
|
2025-08-16 10:22:11 +08:00
|
|
|
|
<ELWarehouseSelect v-model="scope.row.warehouseId" />
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2025-07-31 18:07:51 +08:00
|
|
|
|
<el-table-column prop="stockIoId" label="挂载单据" align="center">
|
2025-07-31 11:43:31 +08:00
|
|
|
|
<template #default="scope">
|
2025-08-22 11:30:23 +08:00
|
|
|
|
<el-select v-model="scope.row.stockIoId" filterable placeholder="请选择挂载单据" clearable class="table-select">
|
2025-07-31 11:43:31 +08:00
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="item in masterList"
|
|
|
|
|
|
:key="item.stockIoId"
|
|
|
|
|
|
:label="item.stockIoCode"
|
|
|
|
|
|
:value="item.stockIoId"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="quantity" label="数量" align="center">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model="scope.row.quantity" class="table-input" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="ioType" label="操作类型" align="center">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-select v-model="scope.row.ioType" placeholder="请选择操作类型" clearable class="table-select">
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="dict in dict.type.stock_io_type"
|
|
|
|
|
|
:key="dict.value"
|
|
|
|
|
|
:label="dict.label"
|
|
|
|
|
|
:value="dict.value"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2025-08-16 10:08:38 +08:00
|
|
|
|
<el-table-column prop="batchNo" label="批次号" align="center">
|
2025-07-31 11:43:31 +08:00
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model="scope.row.batchNo" class="table-input" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2025-08-22 11:30:23 +08:00
|
|
|
|
<el-table-column label="操作" align="center" width="140" fixed="right">
|
2025-07-31 11:43:31 +08:00
|
|
|
|
<template #default="scope">
|
2025-08-22 11:30:23 +08:00
|
|
|
|
<el-button size="mini" type="text" @click="handleDelete(scope.row)">删除</el-button>
|
|
|
|
|
|
<el-button size="mini" type="text" @click="handleConfirm(scope.row)">确认</el-button>
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2025-08-27 16:47:33 +08:00
|
|
|
|
</KLPTable>
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</div>
|
2025-08-22 11:30:23 +08:00
|
|
|
|
</el-col> -->
|
2025-07-31 11:43:31 +08:00
|
|
|
|
</el-row>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import { listStockIo, scanInStock, scanOutStock } from '@/api/wms/stockIo';
|
|
|
|
|
|
import { addStockIoDetail } from '@/api/wms/stockIoDetail';
|
|
|
|
|
|
import WarehouseSelect from '@/components/WarehouseSelect/index.vue';
|
2025-08-16 10:22:11 +08:00
|
|
|
|
import ELWarehouseSelect from '@/components/KLPService/WarehouseSelect/index.vue';
|
2025-07-31 11:43:31 +08:00
|
|
|
|
import ProductInfo from '@/components/KLPService/Renderer/ProductInfo.vue';
|
|
|
|
|
|
import RawMaterialInfo from '@/components/KLPService/Renderer/RawMaterialInfo.vue';
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
components: {
|
|
|
|
|
|
WarehouseSelect,
|
2025-08-16 10:22:11 +08:00
|
|
|
|
ELWarehouseSelect,
|
2025-07-31 11:43:31 +08:00
|
|
|
|
ProductInfo,
|
|
|
|
|
|
RawMaterialInfo,
|
|
|
|
|
|
},
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
deviceList: [],
|
2025-08-15 11:42:31 +08:00
|
|
|
|
deviceStats: {
|
|
|
|
|
|
totalCount: 0,
|
|
|
|
|
|
activeCount: 0
|
|
|
|
|
|
},
|
2025-07-31 11:43:31 +08:00
|
|
|
|
messageList: [],
|
|
|
|
|
|
socket: null,
|
|
|
|
|
|
defaultForm: {
|
|
|
|
|
|
ioType: '',
|
|
|
|
|
|
stockIoId: '',
|
|
|
|
|
|
quantity: 1,
|
|
|
|
|
|
warehouseId: '',
|
|
|
|
|
|
batchNo: '',
|
|
|
|
|
|
},
|
|
|
|
|
|
masterList: [],
|
|
|
|
|
|
selectedList: [],
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
dicts: ['stock_io_type'],
|
|
|
|
|
|
mounted() {
|
|
|
|
|
|
this.initSocket();
|
|
|
|
|
|
this.fetchMaster();
|
|
|
|
|
|
},
|
2025-08-15 11:42:31 +08:00
|
|
|
|
beforeDestroy() {
|
|
|
|
|
|
// 组件销毁时关闭WebSocket连接
|
|
|
|
|
|
if (this.socket) {
|
|
|
|
|
|
this.socket.close();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-07-31 11:43:31 +08:00
|
|
|
|
methods: {
|
|
|
|
|
|
initSocket() {
|
2025-08-15 11:42:31 +08:00
|
|
|
|
// 处理WebSocket连接
|
2025-07-31 11:43:31 +08:00
|
|
|
|
this.socket = new WebSocket("ws://localhost:9000/ws");
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
this.socket.onopen = () => {
|
|
|
|
|
|
console.log("Socket 连接已建立");
|
|
|
|
|
|
};
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
this.socket.onmessage = (event) => {
|
2025-08-15 11:42:31 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const data = JSON.parse(event.data);
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-08-15 11:42:31 +08:00
|
|
|
|
// 处理设备列表数据
|
|
|
|
|
|
if (data.type === "allDevices") {
|
|
|
|
|
|
console.log("获取设备列表", data);
|
|
|
|
|
|
this.deviceList = data.devices || [];
|
|
|
|
|
|
this.deviceStats = {
|
|
|
|
|
|
totalCount: data.totalCount || 0,
|
|
|
|
|
|
activeCount: data.activeCount || 0
|
|
|
|
|
|
};
|
2025-08-16 10:08:38 +08:00
|
|
|
|
}
|
2025-08-15 11:42:31 +08:00
|
|
|
|
// 处理扫描消息
|
|
|
|
|
|
else if (data.type === "scanMessage") {
|
|
|
|
|
|
console.log("获取扫描消息", data);
|
|
|
|
|
|
this.messageList.push({
|
|
|
|
|
|
time: new Date().toLocaleString(),
|
|
|
|
|
|
itemId: data.itemId,
|
|
|
|
|
|
itemType: data.itemType,
|
|
|
|
|
|
stockIoId: this.defaultForm.stockIoId,
|
|
|
|
|
|
quantity: this.defaultForm.quantity,
|
|
|
|
|
|
ioType: this.defaultForm.ioType,
|
|
|
|
|
|
warehouseId: this.defaultForm.warehouseId,
|
|
|
|
|
|
batchNo: this.defaultForm.batchNo,
|
|
|
|
|
|
unit: '个'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("解析WebSocket消息失败", error);
|
2025-07-31 11:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
this.socket.onclose = () => {
|
|
|
|
|
|
console.log("Socket 连接已关闭");
|
2025-08-15 11:42:31 +08:00
|
|
|
|
// 连接关闭后尝试重连
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.initSocket();
|
|
|
|
|
|
}, 5000);
|
|
|
|
|
|
};
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-08-15 11:42:31 +08:00
|
|
|
|
this.socket.onerror = (error) => {
|
|
|
|
|
|
console.error("Socket 错误", error);
|
2025-07-31 11:43:31 +08:00
|
|
|
|
};
|
|
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-08-15 11:42:31 +08:00
|
|
|
|
// 刷新设备列表
|
|
|
|
|
|
refreshDevices() {
|
|
|
|
|
|
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
|
|
|
|
this.socket.send(JSON.stringify({ type: "refreshDevices" }));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.$message.warning("WebSocket连接未建立,无法刷新设备列表");
|
|
|
|
|
|
// 尝试重新连接
|
|
|
|
|
|
this.initSocket();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
fetchMaster() {
|
|
|
|
|
|
listStockIo({ pageSize: 9999, pageNum: 1 }).then(res => {
|
2025-08-15 11:42:31 +08:00
|
|
|
|
console.log("获取挂载单据", res);
|
|
|
|
|
|
this.masterList = res.rows || [];
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error("获取挂载单据失败", error);
|
|
|
|
|
|
this.$message.error("获取挂载单据失败");
|
2025-07-31 11:43:31 +08:00
|
|
|
|
});
|
|
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
handleDeviceChange(item) {
|
|
|
|
|
|
this.socket.send(
|
|
|
|
|
|
JSON.stringify({
|
|
|
|
|
|
type: "toggleDevice",
|
|
|
|
|
|
deviceId: item.id,
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
handleBatchConfirm() {
|
|
|
|
|
|
// 汇总会导致的库存变更,需要确认
|
2025-08-15 11:42:31 +08:00
|
|
|
|
console.log("批量确认", this.selectedList);
|
|
|
|
|
|
if (this.selectedList.length === 0) {
|
|
|
|
|
|
this.$message.warning("请选择需要确认的记录");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-08-15 11:42:31 +08:00
|
|
|
|
// 批量处理逻辑
|
|
|
|
|
|
Promise.all(this.selectedList.map(item => this.processRecord(item)))
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
this.$message.success("批量确认成功");
|
|
|
|
|
|
// 从列表中移除已确认的项
|
|
|
|
|
|
this.messageList = this.messageList.filter(
|
|
|
|
|
|
item => !this.selectedList.some(selected => selected.time === item.time)
|
|
|
|
|
|
);
|
|
|
|
|
|
this.selectedList = [];
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
console.error("批量确认失败", error);
|
|
|
|
|
|
this.$message.error("批量确认失败");
|
|
|
|
|
|
});
|
2025-07-31 11:43:31 +08:00
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
handleSelectionChange(selection) {
|
|
|
|
|
|
this.selectedList = selection;
|
|
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
handleDelete(row) {
|
|
|
|
|
|
this.messageList = this.messageList.filter(item => item.time !== row.time);
|
|
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
async handleConfirm(row) {
|
2025-08-15 11:42:31 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await this.processRecord(row);
|
|
|
|
|
|
this.handleDelete(row);
|
|
|
|
|
|
this.$message.success('确认成功');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("确认失败", error);
|
|
|
|
|
|
this.$message.error('确认失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-08-15 11:42:31 +08:00
|
|
|
|
// 处理单条记录的确认逻辑
|
|
|
|
|
|
async processRecord(row) {
|
|
|
|
|
|
// 插入记录
|
|
|
|
|
|
await this.insertRecord({...row, recordType: 1});
|
|
|
|
|
|
// 更新库存
|
|
|
|
|
|
await this.updateStock(row);
|
2025-07-31 11:43:31 +08:00
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
insertRecord(row) {
|
2025-08-15 11:42:31 +08:00
|
|
|
|
return addStockIoDetail(row);
|
2025-07-31 11:43:31 +08:00
|
|
|
|
},
|
2025-08-16 10:08:38 +08:00
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
updateStock(row) {
|
|
|
|
|
|
if (row.ioType === 'in') {
|
2025-08-15 11:42:31 +08:00
|
|
|
|
return scanInStock(row);
|
2025-07-31 11:43:31 +08:00
|
|
|
|
} else {
|
2025-08-15 11:42:31 +08:00
|
|
|
|
return scanOutStock(row);
|
2025-07-31 11:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.container {
|
|
|
|
|
|
height: calc(100vh - 100px);
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.device-panel {
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
padding: 0 !important;
|
|
|
|
|
|
height: calc(100vh - 40px);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.panel-header {
|
|
|
|
|
|
padding: 15px 20px;
|
|
|
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
|
|
background-color: #f8fafc;
|
|
|
|
|
|
border-radius: 8px 8px 0 0;
|
2025-08-15 11:42:31 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
2025-07-31 11:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.panel-header h3 {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-15 11:42:31 +08:00
|
|
|
|
/* 设备统计信息 */
|
|
|
|
|
|
.device-stats {
|
|
|
|
|
|
padding: 10px 20px;
|
|
|
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 11:43:31 +08:00
|
|
|
|
.device-list {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.device-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 12px 20px;
|
|
|
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
|
|
transition: background-color 0.3s;
|
2025-08-15 11:42:31 +08:00
|
|
|
|
cursor: pointer;
|
2025-07-31 11:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.device-item:hover {
|
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-15 11:42:31 +08:00
|
|
|
|
/* 设备状态指示器 */
|
|
|
|
|
|
.device-status {
|
|
|
|
|
|
width: 10px;
|
|
|
|
|
|
height: 10px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background-color: #ccc;
|
|
|
|
|
|
margin-right: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.device-status.online {
|
|
|
|
|
|
background-color: #42b983; /* 绿色表示在线 */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.device-info {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.device-name {
|
|
|
|
|
|
display: block;
|
2025-07-31 11:43:31 +08:00
|
|
|
|
font-weight: 500;
|
2025-08-15 11:42:31 +08:00
|
|
|
|
color: #303133;
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.device-id {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 12px;
|
2025-07-31 11:43:31 +08:00
|
|
|
|
color: #606266;
|
2025-08-15 11:42:31 +08:00
|
|
|
|
margin-bottom: 2px;
|
2025-07-31 11:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-15 11:42:31 +08:00
|
|
|
|
.device-ip {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 12px;
|
2025-07-31 11:43:31 +08:00
|
|
|
|
color: #909399;
|
2025-08-15 11:42:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 活跃设备高亮 */
|
|
|
|
|
|
.device-item.active {
|
|
|
|
|
|
background-color: #f0f9eb;
|
2025-07-31 11:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-panel {
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
padding: 15px 20px;
|
|
|
|
|
|
margin-bottom: 20px;
|
2025-08-16 16:18:38 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
2025-07-31 11:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.filter-form {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-item {
|
|
|
|
|
|
margin-bottom: 0 !important;
|
|
|
|
|
|
margin-right: 15px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-input {
|
|
|
|
|
|
width: 180px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.table-panel {
|
|
|
|
|
|
flex: 1;
|
2025-08-22 11:30:23 +08:00
|
|
|
|
height: calc(100vh - 220px);
|
2025-07-31 11:43:31 +08:00
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.message-table {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.table-select {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.table-input {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-table::before {
|
|
|
|
|
|
height: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-table th {
|
|
|
|
|
|
background-color: #f8fafc !important;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-table td, .el-table th {
|
|
|
|
|
|
padding: 12px 0;
|
|
|
|
|
|
}
|
2025-08-16 10:08:38 +08:00
|
|
|
|
</style>
|