You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

682 lines
15 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
  1. <template>
  2. <div class="picking-board-screen">
  3. <!-- 装饰背景 -->
  4. <div class="bg-decoration">
  5. <div class="decoration-line line-1"></div>
  6. <div class="decoration-line line-2"></div>
  7. <div class="decoration-line line-3"></div>
  8. <div class="decoration-circle circle-1"></div>
  9. <div class="decoration-circle circle-2"></div>
  10. </div>
  11. <!-- 顶部标题栏 -->
  12. <div class="screen-header">
  13. <!-- CCL Logo -->
  14. <div class="header-logo">
  15. <img src="~@/assets/img/cclbai.png" alt="CCL Logo" class="logo-img">
  16. </div>
  17. <div class="header-decoration left"></div>
  18. <div class="header-center">
  19. <div class="title-glow"></div>
  20. <h1 class="screen-title">人工拣选实时看板</h1>
  21. <div class="title-subtitle">Manual Picking Real-time Dashboard</div>
  22. </div>
  23. <div class="header-decoration right"></div>
  24. <div class="header-time">
  25. <div class="time-icon"></div>
  26. <div class="time-text">{{ currentTime }}</div>
  27. </div>
  28. </div>
  29. <!-- 主内容区 -->
  30. <div class="screen-content">
  31. <!-- 工单卡片 -->
  32. <div
  33. class="picking-panel"
  34. v-for="(order, index) in workOrders"
  35. :key="order.workOrderNo"
  36. >
  37. <!-- 面板标题 -->
  38. <div class="panel-title-bar">
  39. <div class="title-left">
  40. <div class="title-icon"></div>
  41. <div class="title-text">
  42. <span class="title-main">人工拣选{{ index + 1 }}</span>
  43. <span class="title-divider">|</span>
  44. <span class="title-sub">工单号码: <strong>{{ order.workOrderNo }}</strong></span>
  45. <span class="title-divider">|</span>
  46. <span class="title-sub">产品名称: <strong>{{ order.materialName }}</strong></span>
  47. <span class="title-divider"></span>
  48. <span class="title-sub">工单时间: <strong>{{ order.workOrderTime }}</strong></span>
  49. </div>
  50. </div>
  51. <!-- <div class="title-right">
  52. <div class="progress-display">
  53. <div class="progress-numbers">
  54. <span class="num-current">{{ order.completedCount }}</span>
  55. <span class="num-divider">/</span>
  56. <span class="num-total">{{ order.totalCount }}</span>
  57. </div>
  58. <div class="progress-bar-container">
  59. <div class="progress-bar-bg">
  60. <div
  61. class="progress-bar-fill"
  62. :style="{ width: (order.completedCount / order.totalCount * 100) + '%' }"
  63. ></div>
  64. </div>
  65. <div class="progress-percent">{{ Math.round(order.completedCount / order.totalCount * 100) }}%</div>
  66. </div>
  67. </div>
  68. </div>-->
  69. </div>
  70. <!-- 数据表格 -->
  71. <div class="panel-table">
  72. <table class="data-table">
  73. <thead>
  74. <tr>
  75. <th style="width: 10px;">No.</th>
  76. <th style="width: 110px;">拣选托盘码</th>
  77. <th style="width: 180px;">拣选物料名称</th>
  78. <th style="width: 120px;">RFID</th>
  79. <th style="width: 130px;">状态</th>
  80. <th style="width: 80px;">存放位置</th>
  81. </tr>
  82. </thead>
  83. <tbody>
  84. <tr v-for="(item, idx) in order.details" :key="idx">
  85. <td class="text-center">{{ idx + 1 }}</td>
  86. <td class="text-center">{{ item.pickingBatchNo }}</td>
  87. <td class="text-center">{{ item.pickingMaterialName }}</td>
  88. <td class="text-right">{{ item.rfidBarcode }}</td>
  89. <td class="text-center">
  90. <span :class="['status-badge', getStatusClass(item.status)]">
  91. {{ item.status }}
  92. </span>
  93. </td>
  94. <td class="text-center">{{ item.storageLocation }}</td>
  95. </tr>
  96. </tbody>
  97. </table>
  98. </div>
  99. </div>
  100. </div>
  101. <!-- 底部装饰 -->
  102. <div class="screen-footer">
  103. <div class="footer-line"></div>
  104. </div>
  105. </div>
  106. </template>
  107. <script>
  108. import {manualPicking} from '@/api/dashboard/dashboard.js'
  109. export default {
  110. name: 'PickingBoard',
  111. data() {
  112. return {
  113. currentTime: '',
  114. timeInterval: null,
  115. dataInterval: null,
  116. // 模拟工单数据
  117. workOrders: [
  118. {
  119. workOrderNo: '',
  120. materialName: '',
  121. workOrderTime: '',
  122. completedCount: '',
  123. totalCount: '',
  124. details: []
  125. },
  126. {
  127. workOrderNo: '',
  128. materialName: '',
  129. workOrderTime: '',
  130. completedCount: '',
  131. totalCount: '',
  132. details: []
  133. }
  134. ]
  135. }
  136. },
  137. mounted() {
  138. this.updateTime()
  139. this.timeInterval = setInterval(() => {
  140. this.updateTime()
  141. }, 1000)
  142. // 首次加载数据
  143. this.getDataList()
  144. // 每10秒刷新一次数据
  145. this.dataInterval = setInterval(() => {
  146. this.getDataList()
  147. }, 10000)
  148. },
  149. beforeDestroy() {
  150. if (this.timeInterval) {
  151. clearInterval(this.timeInterval)
  152. }
  153. if (this.dataInterval) {
  154. clearInterval(this.dataInterval)
  155. }
  156. },
  157. methods: {
  158. // 获取数据列表
  159. getDataList() {
  160. manualPicking({}).then(({data}) => {
  161. if (data && data.code === 200) {
  162. console.log('获取数据成功:', data.data)
  163. // TODO: 处理返回的数据,覆盖而非追加,避免内存累积
  164. // if (data.data) {
  165. // this.leftPanelList = data.data.leftPanelList || []
  166. // this.rightPanelList = data.data.rightPanelList || []
  167. // }
  168. }
  169. }).catch(error => {
  170. console.error('获取数据失败:', error)
  171. })
  172. },
  173. /**
  174. * 更新当前时间
  175. */
  176. updateTime() {
  177. const now = new Date()
  178. const year = now.getFullYear()
  179. const month = String(now.getMonth() + 1).padStart(2, '0')
  180. const day = String(now.getDate()).padStart(2, '0')
  181. const hours = String(now.getHours()).padStart(2, '0')
  182. const minutes = String(now.getMinutes()).padStart(2, '0')
  183. const seconds = String(now.getSeconds()).padStart(2, '0')
  184. const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
  185. const weekDay = weekDays[now.getDay()]
  186. this.currentTime = `${year}-${month}-${day} ${weekDay} ${hours}:${minutes}:${seconds}`
  187. },
  188. /**
  189. * 根据状态获取样式类名
  190. */
  191. getStatusClass(status) {
  192. const statusMap = {
  193. '完成': 'status-success',
  194. '分拣中': 'status-warning',
  195. '等待分拣': 'status-pending'
  196. }
  197. return statusMap[status] || 'status-pending'
  198. }
  199. }
  200. }
  201. </script>
  202. <style scoped lang="scss">
  203. /* ===== 整体容器 ===== */
  204. .picking-board-screen {
  205. width: 100vw;
  206. height: 100vh;
  207. background: linear-gradient(135deg, #5f8cc3 0%, #749cc8 100%);
  208. position: relative;
  209. overflow: hidden;
  210. font-family: 'Microsoft YaHei', 'PingFang SC', Arial, sans-serif;
  211. }
  212. /* ===== 装饰背景 ===== */
  213. .bg-decoration {
  214. display: none;
  215. }
  216. /* ===== 顶部标题区 ===== */
  217. .screen-header {
  218. position: relative;
  219. height: 60px;
  220. display: flex;
  221. align-items: center;
  222. justify-content: center;
  223. padding: 0 40px;
  224. z-index: 10;
  225. border-bottom: 2px solid rgba(23, 179, 163, 0.4);
  226. background: linear-gradient(180deg, rgba(23, 179, 163, 0.08) 0%, transparent 100%);
  227. }
  228. /* CCL Logo */
  229. .header-logo {
  230. position: absolute;
  231. left: 20px;
  232. top: 50%;
  233. transform: translateY(-50%);
  234. z-index: 20;
  235. }
  236. .logo-img {
  237. height: 40px;
  238. width: auto;
  239. filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.3));
  240. transition: all 0.3s ease;
  241. &:hover {
  242. filter: drop-shadow(0 4px 12px rgba(23, 179, 163, 0.5));
  243. transform: scale(1.05);
  244. }
  245. }
  246. .header-decoration {
  247. display: none;
  248. }
  249. .header-center {
  250. position: relative;
  251. text-align: center;
  252. }
  253. .title-glow {
  254. display: none;
  255. }
  256. .screen-title {
  257. position: relative;
  258. font-size: 28px;
  259. font-weight: bold;
  260. color: #ffffff;
  261. margin: 0;
  262. letter-spacing: 3px;
  263. text-shadow: 0 0 20px rgba(23, 179, 163, 0.5);
  264. }
  265. .title-subtitle {
  266. font-size: 10px;
  267. color: rgba(255, 255, 255, 0.8);
  268. letter-spacing: 2px;
  269. margin-top: 3px;
  270. font-family: Arial, sans-serif;
  271. text-transform: uppercase;
  272. }
  273. .header-time {
  274. position: absolute;
  275. right: 10px;
  276. top: 50%;
  277. transform: translateY(-50%);
  278. display: flex;
  279. align-items: center;
  280. gap: 12px;
  281. background: rgba(23, 179, 163, 0.15);
  282. padding: 12px 20px;
  283. border-radius: 8px;
  284. border: 1px solid rgba(23, 179, 163, 0.4);
  285. backdrop-filter: blur(10px);
  286. }
  287. .time-icon {
  288. font-size: 14px;
  289. color: #17B3A3;
  290. }
  291. .time-text {
  292. font-size: 16px;
  293. color: #ffffff;
  294. font-family: 'Consolas', 'Courier New', monospace;
  295. font-weight: 500;
  296. letter-spacing: 1px;
  297. }
  298. /* ===== 主内容区 ===== */
  299. .screen-content {
  300. position: relative;
  301. z-index: 1;
  302. padding: 10px 5px;
  303. height: calc(100vh - 60px);
  304. overflow-y: auto;
  305. display: flex;
  306. gap: 30px;
  307. &::-webkit-scrollbar {
  308. width: 6px;
  309. }
  310. &::-webkit-scrollbar-track {
  311. background: rgba(23, 179, 163, 0.1);
  312. }
  313. &::-webkit-scrollbar-thumb {
  314. background: rgba(23, 179, 163, 0.5);
  315. border-radius: 3px;
  316. &:hover {
  317. background: rgba(23, 179, 163, 0.7);
  318. }
  319. }
  320. }
  321. /* ===== 拣选面板 ===== */
  322. .picking-panel {
  323. margin-left: 5px;
  324. flex: 1;
  325. background: rgba(70, 90, 120, 0.9);
  326. backdrop-filter: blur(10px);
  327. border: 1px solid rgba(23, 179, 163, 0.5);
  328. border-radius: 12px;
  329. box-shadow:
  330. 0 8px 32px rgba(0, 0, 0, 0.4),
  331. inset 0 1px 0 rgba(255, 255, 255, 0.1);
  332. overflow: hidden;
  333. transition: all 0.3s ease;
  334. display: flex;
  335. flex-direction: column;
  336. &:hover {
  337. border-color: rgba(23, 179, 163, 0.5);
  338. box-shadow:
  339. 0 12px 48px rgba(0, 0, 0, 0.5),
  340. 0 0 30px rgba(23, 179, 163, 0.2);
  341. transform: translateY(-2px);
  342. }
  343. }
  344. /* ===== 面板标题栏 ===== */
  345. .panel-title-bar {
  346. background: linear-gradient(135deg, rgba(23, 179, 163, 0.3) 0%, rgba(23, 179, 163, 0.15) 100%);
  347. border-bottom: 1px solid rgba(23, 179, 163, 0.3);
  348. padding: 10px 16px;
  349. display: flex;
  350. justify-content: space-between;
  351. align-items: center;
  352. }
  353. .title-left {
  354. display: flex;
  355. align-items: center;
  356. gap: 15px;
  357. flex: 1;
  358. }
  359. .title-icon {
  360. font-size: 16px;
  361. color: #64D8CB;
  362. }
  363. .title-text {
  364. display: flex;
  365. align-items: center;
  366. gap: 8px;
  367. flex-wrap: wrap;
  368. }
  369. .title-main {
  370. font-size: 18px;
  371. font-weight: bold;
  372. color: #ffffff;
  373. text-shadow: 0 0 10px rgba(100, 216, 203, 0.5);
  374. }
  375. .title-divider {
  376. color: rgba(255, 255, 255, 0.4);
  377. font-size: 11px;
  378. }
  379. .title-sub {
  380. font-size: 16px;
  381. color: rgba(255, 255, 255, 0.8);
  382. strong {
  383. color: #ffffff;
  384. font-weight: 600;
  385. font-family: 'Consolas', monospace;
  386. }
  387. }
  388. .title-right {
  389. min-width: 280px;
  390. }
  391. /* ===== 进度显示 ===== */
  392. .progress-display {
  393. display: flex;
  394. align-items: center;
  395. gap: 15px;
  396. }
  397. .progress-numbers {
  398. font-size: 18px;
  399. font-weight: bold;
  400. font-family: 'Arial', sans-serif;
  401. white-space: nowrap;
  402. .num-current {
  403. color: #FFD54F;
  404. text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  405. }
  406. .num-divider {
  407. color: rgba(255, 255, 255, 0.7);
  408. margin: 0 3px;
  409. }
  410. .num-total {
  411. color: #ffffff;
  412. }
  413. }
  414. .progress-bar-container {
  415. flex: 1;
  416. display: flex;
  417. align-items: center;
  418. gap: 6px;
  419. }
  420. .progress-bar-bg {
  421. flex: 1;
  422. height: 6px;
  423. background: rgba(0, 0, 0, 0.3);
  424. border-radius: 3px;
  425. overflow: hidden;
  426. box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
  427. }
  428. .progress-bar-fill {
  429. height: 100%;
  430. background: linear-gradient(90deg, #17B3A3, #64D8CB);
  431. border-radius: 3px;
  432. transition: width 0.5s ease;
  433. box-shadow: 0 0 15px rgba(23, 179, 163, 0.6);
  434. position: relative;
  435. }
  436. .progress-percent {
  437. font-size: 11px;
  438. color: #64D8CB;
  439. font-weight: bold;
  440. min-width: 38px;
  441. text-align: right;
  442. text-shadow: 0 0 10px rgba(100, 216, 203, 0.5);
  443. }
  444. /* ===== 数据表格 ===== */
  445. .panel-table {
  446. padding: 5px 5px;
  447. flex: 1;
  448. overflow-y: auto;
  449. &::-webkit-scrollbar {
  450. width: 4px;
  451. }
  452. &::-webkit-scrollbar-track {
  453. background: rgba(23, 179, 163, 0.05);
  454. }
  455. &::-webkit-scrollbar-thumb {
  456. background: rgba(23, 179, 163, 0.3);
  457. border-radius: 2px;
  458. &:hover {
  459. background: rgba(23, 179, 163, 0.5);
  460. }
  461. }
  462. }
  463. .data-table {
  464. width: 100%;
  465. border-collapse: separate;
  466. border-spacing: 0;
  467. thead {
  468. tr {
  469. background: linear-gradient(135deg, rgba(23, 179, 163, 0.25) 0%, rgba(23, 179, 163, 0.15) 100%);
  470. }
  471. th {
  472. padding: 8px 2px;
  473. color: #fcfdfd;
  474. font-size: 10px;
  475. font-weight: bold;
  476. text-align: center;
  477. border-bottom: 2px solid rgba(23, 179, 163, 0.4);
  478. text-shadow: 0 0 10px rgba(100, 216, 203, 0.5);
  479. white-space: nowrap;
  480. &:first-child {
  481. border-top-left-radius: 8px;
  482. }
  483. &:last-child {
  484. border-top-right-radius: 8px;
  485. }
  486. }
  487. }
  488. tbody {
  489. tr {
  490. background: rgba(60, 80, 105, 0.6);
  491. transition: all 0.3s ease;
  492. &:nth-child(even) {
  493. background: rgba(70, 90, 115, 0.7);
  494. }
  495. &:hover {
  496. background: rgba(23, 179, 163, 0.15);
  497. box-shadow: 0 4px 12px rgba(23, 179, 163, 0.2);
  498. }
  499. &:last-child {
  500. td:first-child {
  501. border-bottom-left-radius: 8px;
  502. }
  503. td:last-child {
  504. border-bottom-right-radius: 8px;
  505. }
  506. }
  507. }
  508. td {
  509. padding: 7px 2px;
  510. color: rgba(255, 255, 255, 0.9);
  511. font-size: 12px;
  512. border-bottom: 1px solid rgba(23, 179, 163, 0.15);
  513. &.text-center {
  514. text-align: center;
  515. }
  516. &.text-left {
  517. text-align: left;
  518. }
  519. &.text-right {
  520. text-align: right;
  521. }
  522. }
  523. }
  524. }
  525. /* ===== 状态徽章 ===== */
  526. .status-badge {
  527. display: inline-block;
  528. padding: 3px 8px;
  529. border-radius: 12px;
  530. font-size: 10px;
  531. font-weight: bold;
  532. text-align: center;
  533. min-width: 60px;
  534. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  535. &.status-success {
  536. background: linear-gradient(135deg, #10b981, #34d399);
  537. color: #ffffff;
  538. box-shadow: 0 0 15px rgba(16, 185, 129, 0.5);
  539. }
  540. &.status-warning {
  541. background: linear-gradient(135deg, #f59e0b, #fbbf24);
  542. color: #ffffff;
  543. box-shadow: 0 0 15px rgba(245, 158, 11, 0.5);
  544. }
  545. &.status-pending {
  546. background: linear-gradient(135deg, #6b7280, #9ca3af);
  547. color: #ffffff;
  548. box-shadow: 0 0 15px rgba(107, 114, 128, 0.5);
  549. }
  550. }
  551. /* ===== 底部装饰 ===== */
  552. .screen-footer {
  553. display: none;
  554. }
  555. /* ===== 响应式适配 ===== */
  556. @media screen and (max-width: 1600px) {
  557. .screen-title {
  558. font-size: 26px;
  559. letter-spacing: 3px;
  560. }
  561. .title-main {
  562. font-size: 18px;
  563. }
  564. .title-sub {
  565. font-size: 16px;
  566. }
  567. .progress-numbers {
  568. font-size: 16px;
  569. }
  570. }
  571. @media screen and (min-width: 2560px) {
  572. .screen-title {
  573. font-size: 32px;
  574. }
  575. .panel-title-bar {
  576. padding: 12px 20px;
  577. }
  578. .title-main {
  579. font-size: 18px;
  580. }
  581. .data-table {
  582. thead th {
  583. font-size: 12px;
  584. padding: 9px 5px;
  585. }
  586. tbody td {
  587. font-size: 14px;
  588. padding: 8px 12px;
  589. }
  590. }
  591. }
  592. </style>