|
|
@@ -0,0 +1,477 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import { reactive, ref } from 'vue'
|
|
|
+import { Search, RefreshLeft, Cpu, Aim } from '@element-plus/icons-vue'
|
|
|
+
|
|
|
+// --- 表单数据模型 ---
|
|
|
+const queryForm = reactive({
|
|
|
+ orgNode: [], // 级联选择结果
|
|
|
+ deviceCode: '',
|
|
|
+ deviceName: '',
|
|
|
+ deviceStatus: '',
|
|
|
+ selectedDevices: [], // 设备多选结果
|
|
|
+ timeRange: []
|
|
|
+})
|
|
|
+
|
|
|
+// --- 模拟数据 (与之前相同) ---
|
|
|
+const orgOptions = [
|
|
|
+ { value: 'factory-1', label: '第一工厂', children: [{ value: 'workshop-a', label: '总装车间' }] },
|
|
|
+ { value: 'factory-2', label: '第二工厂' }
|
|
|
+]
|
|
|
+
|
|
|
+const statusOptions = [
|
|
|
+ { label: '运行中 (RUNNING)', value: 'running', color: '#00f2ea' }, // 青色
|
|
|
+ { label: '故障 (ERROR)', value: 'error', color: '#ff0055' }, // 玫红
|
|
|
+ { label: '待机 (IDLE)', value: 'stopped', color: '#f59e0b' } // 橙色
|
|
|
+]
|
|
|
+
|
|
|
+const deviceOptions = ref([
|
|
|
+ { label: '[CNC-001] 数控机床核心单元', value: 'cnc-001' },
|
|
|
+ { label: '[ROBOT-A] 焊接机械臂一号', value: 'robot-a' },
|
|
|
+ { label: '[AGV-102] 智能物流载具', value: 'agv-102' }
|
|
|
+])
|
|
|
+
|
|
|
+// --- 级联配置 ---
|
|
|
+const cascaderProps = { checkStrictly: true, expandTrigger: 'hover' as const }
|
|
|
+
|
|
|
+// --- 事件处理 ---
|
|
|
+const handleQueryConditionChange = () => {
|
|
|
+ console.log('>> 系统提示: 前置条件变更,正在重新计算目标设备列表...')
|
|
|
+ // 模拟 API 调用延迟和数据更新动画效果
|
|
|
+ const originalOptions = [...deviceOptions.value]
|
|
|
+ deviceOptions.value = []
|
|
|
+ setTimeout(() => {
|
|
|
+ deviceOptions.value = originalOptions
|
|
|
+ console.log('>> 系统提示: 设备列表更新完毕。')
|
|
|
+ }, 500)
|
|
|
+}
|
|
|
+
|
|
|
+const handleSearch = () => {
|
|
|
+ console.log('执行指令: 检索数据', queryForm)
|
|
|
+}
|
|
|
+const handleReset = () => {
|
|
|
+ queryForm.orgNode = []
|
|
|
+ queryForm.deviceCode = ''
|
|
|
+ queryForm.deviceName = ''
|
|
|
+ queryForm.deviceStatus = ''
|
|
|
+ queryForm.selectedDevices = []
|
|
|
+ queryForm.timeRange = []
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div class="cyber-bg min-h-[400px] w-full p-6 flex flex-col gap-6 relative overflow-hidden">
|
|
|
+ <div
|
|
|
+ class="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiBvcGFjaXR5PSIwLjEiPjxwYXRoIGQ9Ik0wIDBoNDB2NDBIMHoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwZjJlYSIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48L3N2Zz4=')] opacity-20 pointer-events-none"
|
|
|
+ ></div>
|
|
|
+
|
|
|
+ <div class="cyber-panel relative p-5 rounded-sm backdrop-blur-xl border border-[#00f2ea]/30">
|
|
|
+ <div class="tech-corner top-left"></div>
|
|
|
+ <div class="tech-corner top-right"></div>
|
|
|
+ <div class="tech-corner bottom-left"></div>
|
|
|
+ <div class="tech-corner bottom-right"></div>
|
|
|
+
|
|
|
+ <div class="flex items-center justify-between mb-6 border-b border-[#00f2ea]/20 pb-3">
|
|
|
+ <div class="flex items-center gap-3">
|
|
|
+ <div class="p-2 bg-[#00f2ea]/10 rounded-sm border border-[#00f2ea]/50">
|
|
|
+ <el-icon class="text-[#00f2ea] text-xl animate-pulse"><Cpu /></el-icon>
|
|
|
+ </div>
|
|
|
+ <h2
|
|
|
+ class="text-xl font-oxanium text-transparent bg-clip-text bg-gradient-to-r from-[#00f2ea] to-[#bd00ff] tracking-wider font-bold uppercase u-text-glow"
|
|
|
+ >
|
|
|
+ 设备数据检索终端 // DATA RETRIEVAL TERMINAL
|
|
|
+ </h2>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center gap-2 text-xs text-[#00f2ea]/70 font-mono">
|
|
|
+ <span class="relative flex h-3 w-3">
|
|
|
+ <span
|
|
|
+ class="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#00f2ea] opacity-75"
|
|
|
+ ></span>
|
|
|
+ <span class="relative inline-flex rounded-full h-3 w-3 bg-[#00f2ea]"></span>
|
|
|
+ </span>
|
|
|
+ SYSTEM STATUS: ONLINE
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-form :inline="true" :model="queryForm" class="cyber-form flex flex-wrap gap-x-6 gap-y-4">
|
|
|
+ <div
|
|
|
+ class="w-full flex flex-wrap gap-4 items-end border-l-2 border-[#00f2ea]/30 pl-4 bg-gradient-to-r from-[#00f2ea]/5 to-transparent py-2"
|
|
|
+ >
|
|
|
+ <el-form-item label="节点层级 / NODE LEVEL">
|
|
|
+ <el-cascader
|
|
|
+ v-model="queryForm.orgNode"
|
|
|
+ :options="orgOptions"
|
|
|
+ :props="cascaderProps"
|
|
|
+ placeholder="SELECT LEVEL >"
|
|
|
+ clearable
|
|
|
+ class="cyber-input w-48"
|
|
|
+ :show-all-levels="false"
|
|
|
+ popper-class="cyber-popper"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="设备编码 / CODE">
|
|
|
+ <el-input
|
|
|
+ v-model="queryForm.deviceCode"
|
|
|
+ placeholder="INPUT CODE >"
|
|
|
+ @change="handleQueryConditionChange"
|
|
|
+ class="cyber-input w-40"
|
|
|
+ >
|
|
|
+ <template #prefix
|
|
|
+ ><el-icon><Aim /></el-icon
|
|
|
+ ></template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="设备名称 / NAME">
|
|
|
+ <el-input
|
|
|
+ v-model="queryForm.deviceName"
|
|
|
+ placeholder="INPUT NAME >"
|
|
|
+ @change="handleQueryConditionChange"
|
|
|
+ class="cyber-input w-40"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="运行状态 / STATUS">
|
|
|
+ <el-select
|
|
|
+ v-model="queryForm.deviceStatus"
|
|
|
+ placeholder="SELECT STATUS >"
|
|
|
+ clearable
|
|
|
+ @change="handleQueryConditionChange"
|
|
|
+ class="cyber-input w-44"
|
|
|
+ popper-class="cyber-popper"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in statusOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ >
|
|
|
+ <span class="flex items-center gap-2 font-mono text-sm">
|
|
|
+ <span
|
|
|
+ :style="{ backgroundColor: item.color, boxShadow: `0 0 8px ${item.color}` }"
|
|
|
+ class="h-1.5 w-1.5 rounded-full inline-block"
|
|
|
+ ></span>
|
|
|
+ {{ item.label }}
|
|
|
+ </span>
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="w-full flex flex-wrap gap-4 items-end mt-2 pl-4">
|
|
|
+ <el-form-item label="目标设备锁定 / TARGET DEVICES" class="flex-grow">
|
|
|
+ <el-select
|
|
|
+ v-model="queryForm.selectedDevices"
|
|
|
+ multiple
|
|
|
+ collapse-tags
|
|
|
+ collapse-tags-tooltip
|
|
|
+ placeholder="MULTI-SELECT DEVICES >"
|
|
|
+ class="cyber-input w-full md:w-[400px]"
|
|
|
+ popper-class="cyber-popper"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in deviceOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ >
|
|
|
+ <span class="font-mono">{{ item.label }}</span>
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="时间跨度 / TIME SPAN">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="queryForm.timeRange"
|
|
|
+ type="datetimerange"
|
|
|
+ range-separator="TO"
|
|
|
+ start-placeholder="START TIME"
|
|
|
+ end-placeholder="END TIME"
|
|
|
+ class="cyber-date-picker !w-[380px]"
|
|
|
+ popper-class="cyber-popper"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <div class="ml-auto flex gap-3">
|
|
|
+ <el-button class="cyber-btn-primary" @click="handleSearch">
|
|
|
+ <el-icon class="mr-2"><Search /></el-icon> 初始化查询
|
|
|
+ </el-button>
|
|
|
+ <el-button class="cyber-btn-secondary" @click="handleReset">
|
|
|
+ <el-icon class="mr-2"><RefreshLeft /></el-icon> 重置条件
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="flex-grow cyber-panel relative p-6 rounded-sm backdrop-blur-md border border-[#00f2ea]/20 min-h-[300px] flex items-center justify-center flex-col gap-4"
|
|
|
+ >
|
|
|
+ <div class="scan-line absolute inset-0 pointer-events-none z-0"></div>
|
|
|
+ <el-icon class="text-6xl text-[#00f2ea]/30"><Cpu /></el-icon>
|
|
|
+ <p class="text-[#00f2ea]/70 font-mono tracking-[0.2em] z-10"
|
|
|
+ >等待数据接入 // AWAITING DATA STREAM...</p
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* --- 引入科幻感字体 (可选,如果你的环境支持联网) --- */
|
|
|
+@import url('https://fonts.googleapis.com/css2?family=Oxanium:wght@400;700&family=Roboto+Mono:wght@400;500&display=swap');
|
|
|
+
|
|
|
+/* --- 自定义工具类 --- */
|
|
|
+.font-oxanium {
|
|
|
+ font-family: Oxanium, cursive, sans-serif;
|
|
|
+}
|
|
|
+
|
|
|
+.font-mono {
|
|
|
+ font-family: 'Roboto Mono', monospace;
|
|
|
+}
|
|
|
+
|
|
|
+.u-text-glow {
|
|
|
+ text-shadow: 0 0 10px rgb(0 242 234 / 50%);
|
|
|
+}
|
|
|
+
|
|
|
+/* --- 核心背景 --- */
|
|
|
+.cyber-bg {
|
|
|
+ color: #e0f2fe;
|
|
|
+ background: radial-gradient(circle at center top, #1a1a2e 0%, #050505 100%);
|
|
|
+}
|
|
|
+
|
|
|
+/* --- 面板样式 (HUD Container) --- */
|
|
|
+.cyber-panel {
|
|
|
+ background: rgb(10 10 20 / 70%);
|
|
|
+ box-shadow:
|
|
|
+ inset 0 0 30px rgb(0 242 234 / 5%),
|
|
|
+ 0 0 10px rgb(0 0 0 / 50%);
|
|
|
+}
|
|
|
+
|
|
|
+/* --- 装饰性角落 (Tech Corners) --- */
|
|
|
+.tech-corner {
|
|
|
+ position: absolute;
|
|
|
+ z-index: 1;
|
|
|
+ width: 15px;
|
|
|
+ height: 15px;
|
|
|
+ border-color: rgb(0 242 234 / 60%);
|
|
|
+}
|
|
|
+
|
|
|
+.tech-corner.top-left {
|
|
|
+ top: -1px;
|
|
|
+ left: -1px;
|
|
|
+ border-top-width: 2px;
|
|
|
+ border-left-width: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+.tech-corner.top-right {
|
|
|
+ top: -1px;
|
|
|
+ right: -1px;
|
|
|
+ border-top-width: 2px;
|
|
|
+ border-right-width: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+.tech-corner.bottom-left {
|
|
|
+ bottom: -1px;
|
|
|
+ left: -1px;
|
|
|
+ border-bottom-width: 2px;
|
|
|
+ border-left-width: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+.tech-corner.bottom-right {
|
|
|
+ right: -1px;
|
|
|
+ bottom: -1px;
|
|
|
+ border-right-width: 2px;
|
|
|
+ border-bottom-width: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+/* =========================================
|
|
|
+ Element Plus 深度定制 (The Magic Happens Here)
|
|
|
+ ========================================= */
|
|
|
+
|
|
|
+/* 1. Form Label 定制 */
|
|
|
+:deep(.el-form-item__label) {
|
|
|
+ padding-bottom: 4px;
|
|
|
+ font-family: 'Roboto Mono', monospace;
|
|
|
+ font-size: 0.75rem; /* text-xs */
|
|
|
+ font-weight: bold;
|
|
|
+ letter-spacing: 0.05em;
|
|
|
+ color: rgb(0 242 234 / 70%) !important; /* 青色文字 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 2. 输入框与选择器容器 (Wrapper) 定制 */
|
|
|
+:deep(.cyber-input .el-input__wrapper),
|
|
|
+:deep(.cyber-input .el-select__wrapper),
|
|
|
+:deep(.cyber-date-picker.el-input__wrapper) {
|
|
|
+ padding-top: 4px;
|
|
|
+ padding-bottom: 4px;
|
|
|
+ background-color: rgb(0 242 234 / 5%) !important; /* 极淡的青色背景 */
|
|
|
+ border: 1px solid rgb(0 242 234 / 20%); /* 基础边框 */
|
|
|
+ border-radius: 2px; /* 硬朗的圆角 */
|
|
|
+ box-shadow: none !important; /* 移除默认阴影 */
|
|
|
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
+}
|
|
|
+
|
|
|
+/* 悬停与聚焦状态:发光增强 */
|
|
|
+:deep(.cyber-input .el-input__wrapper:hover),
|
|
|
+:deep(.cyber-input .el-select__wrapper:hover),
|
|
|
+:deep(.cyber-input .el-input__wrapper.is-focus),
|
|
|
+:deep(.cyber-input .el-select__wrapper.is-focused) {
|
|
|
+ border-color: rgb(0 242 234 / 80%);
|
|
|
+ box-shadow:
|
|
|
+ 0 0 15px rgb(0 242 234 / 30%),
|
|
|
+ inset 0 0 10px rgb(0 242 234 / 10%) !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 输入框内部文字 */
|
|
|
+:deep(.el-input__inner) {
|
|
|
+ font-family: 'Roboto Mono', monospace;
|
|
|
+ font-size: 0.9rem;
|
|
|
+ color: #fff !important;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-input__inner::placeholder) {
|
|
|
+ color: rgb(0 242 234 / 30%);
|
|
|
+}
|
|
|
+
|
|
|
+/* 图标颜色 */
|
|
|
+:deep(.el-input__prefix-inner .el-icon),
|
|
|
+:deep(.el-select__caret),
|
|
|
+:deep(.el-date-editor .el-range-separator) {
|
|
|
+ color: rgb(0 242 234 / 60%) !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 日期选择器文字 */
|
|
|
+:deep(.el-date-editor .el-range-input) {
|
|
|
+ font-family: 'Roboto Mono', monospace;
|
|
|
+ color: #fff !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* Tag 标签定制 (用于多选和级联) */
|
|
|
+:deep(.el-tag.el-tag--info) {
|
|
|
+ color: #e0c0ff;
|
|
|
+ background-color: rgb(189 0 255 / 20%); /* 紫色背景 */
|
|
|
+ border-color: rgb(189 0 255 / 50%);
|
|
|
+ border-radius: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-tag .el-tag__close) {
|
|
|
+ color: #e0c0ff;
|
|
|
+}
|
|
|
+
|
|
|
+/* =========================================
|
|
|
+ 按钮定制 (Cyber Buttons)
|
|
|
+ ========================================= */
|
|
|
+.cyber-btn-primary {
|
|
|
+ font-family: Oxanium, sans-serif;
|
|
|
+ font-weight: bold;
|
|
|
+ letter-spacing: 1px;
|
|
|
+ color: #000 !important;
|
|
|
+ background: linear-gradient(135deg, #00f2ea 0%, #00a8c6 100%) !important;
|
|
|
+ border: none !important;
|
|
|
+ transition: all 0.3s;
|
|
|
+ clip-path: polygon(10% 0, 100% 0, 100% 70%, 90% 100%, 0 100%, 0 30%); /* 斜切角造型 */
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-btn-primary:hover {
|
|
|
+ filter: brightness(1.2);
|
|
|
+ transform: translateY(-1px);
|
|
|
+ box-shadow: 0 0 20px rgb(0 242 234 / 60%);
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-btn-secondary {
|
|
|
+ font-family: Oxanium, sans-serif;
|
|
|
+ color: rgb(0 242 234 / 80%) !important;
|
|
|
+ background: transparent !important;
|
|
|
+ border: 1px solid rgb(0 242 234 / 50%) !important;
|
|
|
+ clip-path: polygon(0 0, 90% 0, 100% 30%, 100% 100%, 10% 100%, 0 70%);
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-btn-secondary:hover {
|
|
|
+ color: #fff !important;
|
|
|
+ background: rgb(0 242 234 / 10%) !important;
|
|
|
+ border-color: #00f2ea !important;
|
|
|
+ box-shadow: inset 0 0 10px rgb(0 242 234 / 30%);
|
|
|
+}
|
|
|
+
|
|
|
+/* =========================================
|
|
|
+ 下拉弹出层定制 (Global Styles needed for Poppers)
|
|
|
+ ========================================= */
|
|
|
+
|
|
|
+/* 注意:这部分通常需要放在全局样式文件中,或者去掉 scoped,
|
|
|
+ 为了方便演示,我这里使用了特殊的处理方式。
|
|
|
+ 在实际项目中,请确保这些样式能作用于 body 下的弹出层。
|
|
|
+*/
|
|
|
+</style>
|
|
|
+
|
|
|
+<style>
|
|
|
+@keyframes scan {
|
|
|
+ 0% {
|
|
|
+ transform: translateY(-100%);
|
|
|
+ }
|
|
|
+
|
|
|
+ 100% {
|
|
|
+ transform: translateY(100%);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-popper.el-popper {
|
|
|
+ background: rgb(10 10 25 / 95%) !important;
|
|
|
+ border: 1px solid rgb(0 242 234 / 30%) !important;
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-popper .el-popper__arrow::before {
|
|
|
+ background: rgb(10 10 25 / 95%) !important;
|
|
|
+ border: 1px solid rgb(0 242 234 / 30%) !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 下拉选项 Item */
|
|
|
+.cyber-popper .el-select-dropdown__item,
|
|
|
+.cyber-popper .el-cascader-node {
|
|
|
+ font-family: 'Roboto Mono', monospace;
|
|
|
+ color: #a0aec0 !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 选项悬停/选中状态 */
|
|
|
+.cyber-popper .el-select-dropdown__item.hover,
|
|
|
+.cyber-popper .el-select-dropdown__item.selected,
|
|
|
+.cyber-popper .el-cascader-node.is-active,
|
|
|
+.cyber-popper .el-cascader-node:hover {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #00f2ea !important;
|
|
|
+ background: rgb(0 242 234 / 15%) !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 日期选择器面板特殊处理 */
|
|
|
+.cyber-popper .el-picker-panel {
|
|
|
+ color: #e0f2fe;
|
|
|
+ background: transparent !important;
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-popper .el-date-table td.available:hover {
|
|
|
+ color: #00f2ea;
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-popper .el-date-table td.today span {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #00f2ea;
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-popper .el-date-table td.current:not(.disabled) span {
|
|
|
+ color: #000;
|
|
|
+ background-color: #00f2ea;
|
|
|
+}
|
|
|
+
|
|
|
+.cyber-popper .el-date-range-picker__content.is-left {
|
|
|
+ border-right: 1px solid rgb(0 242 234 / 20%);
|
|
|
+}
|
|
|
+
|
|
|
+/* 扫描线动画 */
|
|
|
+.scan-line {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: linear-gradient(to bottom, transparent, rgb(0 242 234 / 20%), transparent);
|
|
|
+ opacity: 0.3;
|
|
|
+ animation: scan 3s linear infinite;
|
|
|
+}
|
|
|
+</style>
|