1.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. <script setup lang="ts">
  2. import { reactive, ref } from 'vue'
  3. import { Search, RefreshLeft, Cpu, Aim } from '@element-plus/icons-vue'
  4. // --- 表单数据模型 ---
  5. const queryForm = reactive({
  6. orgNode: [], // 级联选择结果
  7. deviceCode: '',
  8. deviceName: '',
  9. deviceStatus: '',
  10. selectedDevices: [], // 设备多选结果
  11. timeRange: []
  12. })
  13. // --- 模拟数据 (与之前相同) ---
  14. const orgOptions = [
  15. { value: 'factory-1', label: '第一工厂', children: [{ value: 'workshop-a', label: '总装车间' }] },
  16. { value: 'factory-2', label: '第二工厂' }
  17. ]
  18. const statusOptions = [
  19. { label: '运行中 (RUNNING)', value: 'running', color: '#00f2ea' }, // 青色
  20. { label: '故障 (ERROR)', value: 'error', color: '#ff0055' }, // 玫红
  21. { label: '待机 (IDLE)', value: 'stopped', color: '#f59e0b' } // 橙色
  22. ]
  23. const deviceOptions = ref([
  24. { label: '[CNC-001] 数控机床核心单元', value: 'cnc-001' },
  25. { label: '[ROBOT-A] 焊接机械臂一号', value: 'robot-a' },
  26. { label: '[AGV-102] 智能物流载具', value: 'agv-102' }
  27. ])
  28. // --- 级联配置 ---
  29. const cascaderProps = { checkStrictly: true, expandTrigger: 'hover' as const }
  30. // --- 事件处理 ---
  31. const handleQueryConditionChange = () => {
  32. console.log('>> 系统提示: 前置条件变更,正在重新计算目标设备列表...')
  33. // 模拟 API 调用延迟和数据更新动画效果
  34. const originalOptions = [...deviceOptions.value]
  35. deviceOptions.value = []
  36. setTimeout(() => {
  37. deviceOptions.value = originalOptions
  38. console.log('>> 系统提示: 设备列表更新完毕。')
  39. }, 500)
  40. }
  41. const handleSearch = () => {
  42. console.log('执行指令: 检索数据', queryForm)
  43. }
  44. const handleReset = () => {
  45. queryForm.orgNode = []
  46. queryForm.deviceCode = ''
  47. queryForm.deviceName = ''
  48. queryForm.deviceStatus = ''
  49. queryForm.selectedDevices = []
  50. queryForm.timeRange = []
  51. }
  52. </script>
  53. <template>
  54. <div class="cyber-bg min-h-[400px] w-full p-6 flex flex-col gap-6 relative overflow-hidden">
  55. <div
  56. class="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiBvcGFjaXR5PSIwLjEiPjxwYXRoIGQ9Ik0wIDBoNDB2NDBIMHoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwZjJlYSIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48L3N2Zz4=')] opacity-20 pointer-events-none"
  57. ></div>
  58. <div class="cyber-panel relative p-5 rounded-sm backdrop-blur-xl border border-[#00f2ea]/30">
  59. <div class="tech-corner top-left"></div>
  60. <div class="tech-corner top-right"></div>
  61. <div class="tech-corner bottom-left"></div>
  62. <div class="tech-corner bottom-right"></div>
  63. <div class="flex items-center justify-between mb-6 border-b border-[#00f2ea]/20 pb-3">
  64. <div class="flex items-center gap-3">
  65. <div class="p-2 bg-[#00f2ea]/10 rounded-sm border border-[#00f2ea]/50">
  66. <el-icon class="text-[#00f2ea] text-xl animate-pulse"><Cpu /></el-icon>
  67. </div>
  68. <h2
  69. 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"
  70. >
  71. 设备数据检索终端 // DATA RETRIEVAL TERMINAL
  72. </h2>
  73. </div>
  74. <div class="flex items-center gap-2 text-xs text-[#00f2ea]/70 font-mono">
  75. <span class="relative flex h-3 w-3">
  76. <span
  77. class="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#00f2ea] opacity-75"
  78. ></span>
  79. <span class="relative inline-flex rounded-full h-3 w-3 bg-[#00f2ea]"></span>
  80. </span>
  81. SYSTEM STATUS: ONLINE
  82. </div>
  83. </div>
  84. <el-form :inline="true" :model="queryForm" class="cyber-form flex flex-wrap gap-x-6 gap-y-4">
  85. <div
  86. 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"
  87. >
  88. <el-form-item label="节点层级 / NODE LEVEL">
  89. <el-cascader
  90. v-model="queryForm.orgNode"
  91. :options="orgOptions"
  92. :props="cascaderProps"
  93. placeholder="SELECT LEVEL >"
  94. clearable
  95. class="cyber-input w-48"
  96. :show-all-levels="false"
  97. popper-class="cyber-popper"
  98. />
  99. </el-form-item>
  100. <el-form-item label="设备编码 / CODE">
  101. <el-input
  102. v-model="queryForm.deviceCode"
  103. placeholder="INPUT CODE >"
  104. @change="handleQueryConditionChange"
  105. class="cyber-input w-40"
  106. >
  107. <template #prefix
  108. ><el-icon><Aim /></el-icon
  109. ></template>
  110. </el-input>
  111. </el-form-item>
  112. <el-form-item label="设备名称 / NAME">
  113. <el-input
  114. v-model="queryForm.deviceName"
  115. placeholder="INPUT NAME >"
  116. @change="handleQueryConditionChange"
  117. class="cyber-input w-40"
  118. />
  119. </el-form-item>
  120. <el-form-item label="运行状态 / STATUS">
  121. <el-select
  122. v-model="queryForm.deviceStatus"
  123. placeholder="SELECT STATUS >"
  124. clearable
  125. @change="handleQueryConditionChange"
  126. class="cyber-input w-44"
  127. popper-class="cyber-popper"
  128. >
  129. <el-option
  130. v-for="item in statusOptions"
  131. :key="item.value"
  132. :label="item.label"
  133. :value="item.value"
  134. >
  135. <span class="flex items-center gap-2 font-mono text-sm">
  136. <span
  137. :style="{ backgroundColor: item.color, boxShadow: `0 0 8px ${item.color}` }"
  138. class="h-1.5 w-1.5 rounded-full inline-block"
  139. ></span>
  140. {{ item.label }}
  141. </span>
  142. </el-option>
  143. </el-select>
  144. </el-form-item>
  145. </div>
  146. <div class="w-full flex flex-wrap gap-4 items-end mt-2 pl-4">
  147. <el-form-item label="目标设备锁定 / TARGET DEVICES" class="flex-grow">
  148. <el-select
  149. v-model="queryForm.selectedDevices"
  150. multiple
  151. collapse-tags
  152. collapse-tags-tooltip
  153. placeholder="MULTI-SELECT DEVICES >"
  154. class="cyber-input w-full md:w-[400px]"
  155. popper-class="cyber-popper"
  156. >
  157. <el-option
  158. v-for="item in deviceOptions"
  159. :key="item.value"
  160. :label="item.label"
  161. :value="item.value"
  162. >
  163. <span class="font-mono">{{ item.label }}</span>
  164. </el-option>
  165. </el-select>
  166. </el-form-item>
  167. <el-form-item label="时间跨度 / TIME SPAN">
  168. <el-date-picker
  169. v-model="queryForm.timeRange"
  170. type="datetimerange"
  171. range-separator="TO"
  172. start-placeholder="START TIME"
  173. end-placeholder="END TIME"
  174. class="cyber-date-picker !w-[380px]"
  175. popper-class="cyber-popper"
  176. />
  177. </el-form-item>
  178. <div class="ml-auto flex gap-3">
  179. <el-button class="cyber-btn-primary" @click="handleSearch">
  180. <el-icon class="mr-2"><Search /></el-icon> 初始化查询
  181. </el-button>
  182. <el-button class="cyber-btn-secondary" @click="handleReset">
  183. <el-icon class="mr-2"><RefreshLeft /></el-icon> 重置条件
  184. </el-button>
  185. </div>
  186. </div>
  187. </el-form>
  188. </div>
  189. <div
  190. 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"
  191. >
  192. <div class="scan-line absolute inset-0 pointer-events-none z-0"></div>
  193. <el-icon class="text-6xl text-[#00f2ea]/30"><Cpu /></el-icon>
  194. <p class="text-[#00f2ea]/70 font-mono tracking-[0.2em] z-10"
  195. >等待数据接入 // AWAITING DATA STREAM...</p
  196. >
  197. </div>
  198. </div>
  199. </template>
  200. <style scoped>
  201. /* --- 引入科幻感字体 (可选,如果你的环境支持联网) --- */
  202. @import url('https://fonts.googleapis.com/css2?family=Oxanium:wght@400;700&family=Roboto+Mono:wght@400;500&display=swap');
  203. /* --- 自定义工具类 --- */
  204. .font-oxanium {
  205. font-family: Oxanium, cursive, sans-serif;
  206. }
  207. .font-mono {
  208. font-family: 'Roboto Mono', monospace;
  209. }
  210. .u-text-glow {
  211. text-shadow: 0 0 10px rgb(0 242 234 / 50%);
  212. }
  213. /* --- 核心背景 --- */
  214. .cyber-bg {
  215. color: #e0f2fe;
  216. background: radial-gradient(circle at center top, #1a1a2e 0%, #050505 100%);
  217. }
  218. /* --- 面板样式 (HUD Container) --- */
  219. .cyber-panel {
  220. background: rgb(10 10 20 / 70%);
  221. box-shadow:
  222. inset 0 0 30px rgb(0 242 234 / 5%),
  223. 0 0 10px rgb(0 0 0 / 50%);
  224. }
  225. /* --- 装饰性角落 (Tech Corners) --- */
  226. .tech-corner {
  227. position: absolute;
  228. z-index: 1;
  229. width: 15px;
  230. height: 15px;
  231. border-color: rgb(0 242 234 / 60%);
  232. }
  233. .tech-corner.top-left {
  234. top: -1px;
  235. left: -1px;
  236. border-top-width: 2px;
  237. border-left-width: 2px;
  238. }
  239. .tech-corner.top-right {
  240. top: -1px;
  241. right: -1px;
  242. border-top-width: 2px;
  243. border-right-width: 2px;
  244. }
  245. .tech-corner.bottom-left {
  246. bottom: -1px;
  247. left: -1px;
  248. border-bottom-width: 2px;
  249. border-left-width: 2px;
  250. }
  251. .tech-corner.bottom-right {
  252. right: -1px;
  253. bottom: -1px;
  254. border-right-width: 2px;
  255. border-bottom-width: 2px;
  256. }
  257. /* =========================================
  258. Element Plus 深度定制 (The Magic Happens Here)
  259. ========================================= */
  260. /* 1. Form Label 定制 */
  261. :deep(.el-form-item__label) {
  262. padding-bottom: 4px;
  263. font-family: 'Roboto Mono', monospace;
  264. font-size: 0.75rem; /* text-xs */
  265. font-weight: bold;
  266. letter-spacing: 0.05em;
  267. color: rgb(0 242 234 / 70%) !important; /* 青色文字 */
  268. }
  269. /* 2. 输入框与选择器容器 (Wrapper) 定制 */
  270. :deep(.cyber-input .el-input__wrapper),
  271. :deep(.cyber-input .el-select__wrapper),
  272. :deep(.cyber-date-picker.el-input__wrapper) {
  273. padding-top: 4px;
  274. padding-bottom: 4px;
  275. background-color: rgb(0 242 234 / 5%) !important; /* 极淡的青色背景 */
  276. border: 1px solid rgb(0 242 234 / 20%); /* 基础边框 */
  277. border-radius: 2px; /* 硬朗的圆角 */
  278. box-shadow: none !important; /* 移除默认阴影 */
  279. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  280. }
  281. /* 悬停与聚焦状态:发光增强 */
  282. :deep(.cyber-input .el-input__wrapper:hover),
  283. :deep(.cyber-input .el-select__wrapper:hover),
  284. :deep(.cyber-input .el-input__wrapper.is-focus),
  285. :deep(.cyber-input .el-select__wrapper.is-focused) {
  286. border-color: rgb(0 242 234 / 80%);
  287. box-shadow:
  288. 0 0 15px rgb(0 242 234 / 30%),
  289. inset 0 0 10px rgb(0 242 234 / 10%) !important;
  290. }
  291. /* 输入框内部文字 */
  292. :deep(.el-input__inner) {
  293. font-family: 'Roboto Mono', monospace;
  294. font-size: 0.9rem;
  295. color: #fff !important;
  296. }
  297. :deep(.el-input__inner::placeholder) {
  298. color: rgb(0 242 234 / 30%);
  299. }
  300. /* 图标颜色 */
  301. :deep(.el-input__prefix-inner .el-icon),
  302. :deep(.el-select__caret),
  303. :deep(.el-date-editor .el-range-separator) {
  304. color: rgb(0 242 234 / 60%) !important;
  305. }
  306. /* 日期选择器文字 */
  307. :deep(.el-date-editor .el-range-input) {
  308. font-family: 'Roboto Mono', monospace;
  309. color: #fff !important;
  310. }
  311. /* Tag 标签定制 (用于多选和级联) */
  312. :deep(.el-tag.el-tag--info) {
  313. color: #e0c0ff;
  314. background-color: rgb(189 0 255 / 20%); /* 紫色背景 */
  315. border-color: rgb(189 0 255 / 50%);
  316. border-radius: 2px;
  317. }
  318. :deep(.el-tag .el-tag__close) {
  319. color: #e0c0ff;
  320. }
  321. /* =========================================
  322. 按钮定制 (Cyber Buttons)
  323. ========================================= */
  324. .cyber-btn-primary {
  325. font-family: Oxanium, sans-serif;
  326. font-weight: bold;
  327. letter-spacing: 1px;
  328. color: #000 !important;
  329. background: linear-gradient(135deg, #00f2ea 0%, #00a8c6 100%) !important;
  330. border: none !important;
  331. transition: all 0.3s;
  332. clip-path: polygon(10% 0, 100% 0, 100% 70%, 90% 100%, 0 100%, 0 30%); /* 斜切角造型 */
  333. }
  334. .cyber-btn-primary:hover {
  335. filter: brightness(1.2);
  336. transform: translateY(-1px);
  337. box-shadow: 0 0 20px rgb(0 242 234 / 60%);
  338. }
  339. .cyber-btn-secondary {
  340. font-family: Oxanium, sans-serif;
  341. color: rgb(0 242 234 / 80%) !important;
  342. background: transparent !important;
  343. border: 1px solid rgb(0 242 234 / 50%) !important;
  344. clip-path: polygon(0 0, 90% 0, 100% 30%, 100% 100%, 10% 100%, 0 70%);
  345. }
  346. .cyber-btn-secondary:hover {
  347. color: #fff !important;
  348. background: rgb(0 242 234 / 10%) !important;
  349. border-color: #00f2ea !important;
  350. box-shadow: inset 0 0 10px rgb(0 242 234 / 30%);
  351. }
  352. /* =========================================
  353. 下拉弹出层定制 (Global Styles needed for Poppers)
  354. ========================================= */
  355. /* 注意:这部分通常需要放在全局样式文件中,或者去掉 scoped,
  356. 为了方便演示,我这里使用了特殊的处理方式。
  357. 在实际项目中,请确保这些样式能作用于 body 下的弹出层。
  358. */
  359. </style>
  360. <style>
  361. @keyframes scan {
  362. 0% {
  363. transform: translateY(-100%);
  364. }
  365. 100% {
  366. transform: translateY(100%);
  367. }
  368. }
  369. .cyber-popper.el-popper {
  370. background: rgb(10 10 25 / 95%) !important;
  371. border: 1px solid rgb(0 242 234 / 30%) !important;
  372. backdrop-filter: blur(10px);
  373. }
  374. .cyber-popper .el-popper__arrow::before {
  375. background: rgb(10 10 25 / 95%) !important;
  376. border: 1px solid rgb(0 242 234 / 30%) !important;
  377. }
  378. /* 下拉选项 Item */
  379. .cyber-popper .el-select-dropdown__item,
  380. .cyber-popper .el-cascader-node {
  381. font-family: 'Roboto Mono', monospace;
  382. color: #a0aec0 !important;
  383. }
  384. /* 选项悬停/选中状态 */
  385. .cyber-popper .el-select-dropdown__item.hover,
  386. .cyber-popper .el-select-dropdown__item.selected,
  387. .cyber-popper .el-cascader-node.is-active,
  388. .cyber-popper .el-cascader-node:hover {
  389. font-weight: bold;
  390. color: #00f2ea !important;
  391. background: rgb(0 242 234 / 15%) !important;
  392. }
  393. /* 日期选择器面板特殊处理 */
  394. .cyber-popper .el-picker-panel {
  395. color: #e0f2fe;
  396. background: transparent !important;
  397. }
  398. .cyber-popper .el-date-table td.available:hover {
  399. color: #00f2ea;
  400. }
  401. .cyber-popper .el-date-table td.today span {
  402. font-weight: bold;
  403. color: #00f2ea;
  404. }
  405. .cyber-popper .el-date-table td.current:not(.disabled) span {
  406. color: #000;
  407. background-color: #00f2ea;
  408. }
  409. .cyber-popper .el-date-range-picker__content.is-left {
  410. border-right: 1px solid rgb(0 242 234 / 20%);
  411. }
  412. /* 扫描线动画 */
  413. .scan-line {
  414. width: 100%;
  415. height: 100%;
  416. background: linear-gradient(to bottom, transparent, rgb(0 242 234 / 20%), transparent);
  417. opacity: 0.3;
  418. animation: scan 3s linear infinite;
  419. }
  420. </style>