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.

232 lines
12 KiB

​
​
5 months ago
​
​
​
​
​
​
​
​
4 months ago
​
​
2 months ago
​
​
​
​
​
​
​
​
​
4 months ago
​
​
​
​
​
​
​
​
​
​
​
​
4 months ago
​
3 months ago
2 months ago
​
​
​
​
​
​
​
2 months ago
​
5 months ago
2 months ago
​
​
4 months ago
​
2 months ago
​
​
​
​
​
​
​
​
​
​
​
5 months ago
4 months ago
5 months ago
​
4 months ago
5 months ago
2 months ago
​
​
5 months ago
​
​
​
​
​
​
​
2 months ago
​
​
​
4 months ago
2 months ago
​
​
5 months ago
3 months ago
​
​
2 months ago
​
​
2 months ago
​
2 months ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
3 months ago
4 months ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
5 months ago
​
​
​
​
​
​
​
​
​
4 months ago
​
​
4 months ago
​
​
4 months ago
2 months ago
​
​
​
4 months ago
​
​
​
4 months ago
​
​
4 months ago
​
3 months ago
4 months ago
​
​
​
  1. <template>
  2. <div class="screen-wrap">
  3. <div class="top-bar">
  4. <div class="header-left"><img class="site-navbar__brand-logo" src="~@/assets/img/lc.png" alt="龙闯电梯"></div>
  5. <div class="header-center">
  6. <div class="title">生产进度管理看板 · 家用电梯</div>
  7. </div>
  8. <div class="tools">
  9. <span class="time">{{ currentTime }}</span>
  10. </div>
  11. </div>
  12. <div class="legend-row">
  13. <span class="legend-item"><i class="dot done"></i>已完成报工</span>
  14. <span class="legend-item"><i class="dot todo"></i>未开始报工</span>
  15. </div>
  16. <div class="section-header">
  17. <span class="section-title">整梯订单进度</span>
  18. <span class="section-count">第 {{ boardPage + 1 }}/{{ boardPageCount }} 页 · 显示 {{ displayBoardList.length }} / 共 {{ boardList.length }} 条</span>
  19. </div>
  20. <el-table class="board-table" :data="displayBoardList" :height="tableHeight" border stripe>
  21. <el-table-column type="index" :index="boardPage * rowLimit + 1" label="序号" width="60" align="center"></el-table-column>
  22. <el-table-column prop="projectNo" label="项目号" width="140" align="center"></el-table-column>
  23. <el-table-column prop="modelNo" label="型号" width="130" align="center"></el-table-column>
  24. <el-table-column prop="color" label="颜色" width="90" align="center"></el-table-column>
  25. <el-table-column prop="floorCount" label="层数" width="80" align="center"></el-table-column>
  26. <el-table-column prop="specialRequirement" label="特殊要求" min-width="160" align="center"></el-table-column>
  27. <el-table-column prop="planDeliveryDate" label="计划发货日期" width="130" align="center"></el-table-column>
  28. <el-table-column label="仓库配料" width="105" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'stocking')"></span></template></el-table-column>
  29. <el-table-column label="平台组装/调试" width="130" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'platformDebug')"></span></template></el-table-column>
  30. <el-table-column label="背景墙/吊顶组装" width="140" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'bgCeiling')"></span></template></el-table-column>
  31. <el-table-column label="门组装" width="95" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'doorAssy')"></span></template></el-table-column>
  32. <el-table-column label="打包" width="90" align="center"><template slot-scope="scope"><span :class="getNodeCellClass(scope.row, 'pack')"></span></template></el-table-column>
  33. <el-table-column prop="status" label="订单状态" width="100" align="center"><template slot-scope="scope"><el-tag class="board-tag" :class="getOrderStatusClass(scope.row.status)" size="small">{{ scope.row.status }}</el-tag></template></el-table-column>
  34. </el-table>
  35. </div>
  36. </template>
  37. <script>
  38. import { getHomeLiftOrderList } from '@/api/longchuang/productionPlan'
  39. import { getScreenInnerHeight } from '@/utils/screenAdapt'
  40. const NODE_TEMPLATE = ['stocking', 'platformDebug', 'bgCeiling', 'doorAssy', 'pack']
  41. const DISPLAY_STATUS_LIST = ['已排产', '进行中']
  42. const DEFAULT_ROW_LIMIT = 16
  43. const CAROUSEL_INTERVAL_MS = 8000
  44. export default {
  45. name: 'ScreenWholeLiftProgress',
  46. data() {
  47. return {
  48. loading: false,
  49. tableHeight: 640,
  50. rowLimit: DEFAULT_ROW_LIMIT,
  51. currentTime: '',
  52. timerId: null,
  53. boardTimerId: null,
  54. carouselTimerId: null,
  55. boardPage: 0,
  56. carouselIntervalMs: CAROUSEL_INTERVAL_MS,
  57. boardList: []
  58. }
  59. },
  60. computed: {
  61. boardPageCount() {
  62. return this.getPageCount(this.boardList.length, this.rowLimit)
  63. },
  64. displayBoardList() {
  65. return this.getPageList(this.boardList, this.boardPage, this.rowLimit)
  66. }
  67. },
  68. mounted() {
  69. this.setTableHeight()
  70. this.loadBoardData()
  71. this.updateTime()
  72. this.timerId = setInterval(this.updateTime, 1000)
  73. // 看板数据60秒刷新一次
  74. this.boardTimerId = setInterval(() => {
  75. this.loadBoardData()
  76. }, 60000)
  77. this.startCarousel()
  78. window.addEventListener('resize', this.setTableHeight)
  79. },
  80. beforeDestroy() {
  81. if (this.timerId) {
  82. clearInterval(this.timerId)
  83. }
  84. if (this.boardTimerId) {
  85. clearInterval(this.boardTimerId)
  86. }
  87. this.stopCarousel()
  88. window.removeEventListener('resize', this.setTableHeight)
  89. },
  90. methods: {
  91. setTableHeight() {
  92. this.tableHeight = Math.max(420, getScreenInnerHeight() - 280)
  93. this.rowLimit = DEFAULT_ROW_LIMIT
  94. this.normalizeCarouselPage()
  95. },
  96. loadBoardData() {
  97. if (this.loading) return
  98. this.loading = true
  99. getHomeLiftOrderList({ page: 1, limit: 300, statusList: DISPLAY_STATUS_LIST }).then(({data}) => {
  100. this.loading = false
  101. const source = (data && data.code === 0 && data.page && data.page.list) ? data.page.list : this.getMockList()
  102. this.boardList = this.buildBoardList(source)
  103. this.normalizeCarouselPage()
  104. }).catch(() => {
  105. this.loading = false
  106. this.boardList = this.buildBoardList(this.getMockList())
  107. this.normalizeCarouselPage()
  108. })
  109. },
  110. getPageCount(total, pageSize) {
  111. const safeSize = Math.max(1, pageSize || DEFAULT_ROW_LIMIT)
  112. return Math.max(1, Math.ceil(total / safeSize))
  113. },
  114. getPageList(list, pageIndex, pageSize) {
  115. const safeSize = Math.max(1, pageSize || DEFAULT_ROW_LIMIT)
  116. const count = this.getPageCount((list || []).length, safeSize)
  117. const safePage = Math.min(Math.max(pageIndex, 0), count - 1)
  118. const start = safePage * safeSize
  119. return (list || []).slice(start, start + safeSize)
  120. },
  121. normalizeCarouselPage() {
  122. this.boardPage = Math.min(this.boardPage, this.boardPageCount - 1)
  123. this.boardPage = Math.max(this.boardPage, 0)
  124. },
  125. nextPage(page, pageCount) {
  126. if (pageCount <= 1) return 0
  127. return (page + 1) % pageCount
  128. },
  129. startCarousel() {
  130. this.stopCarousel()
  131. this.carouselTimerId = setInterval(() => {
  132. this.boardPage = this.nextPage(this.boardPage, this.boardPageCount)
  133. }, this.carouselIntervalMs)
  134. },
  135. stopCarousel() {
  136. if (!this.carouselTimerId) return
  137. clearInterval(this.carouselTimerId)
  138. this.carouselTimerId = null
  139. },
  140. buildBoardList(sourceList) {
  141. const statusAllow = DISPLAY_STATUS_LIST
  142. return sourceList.filter(item => statusAllow.includes(item.status)).map(item => ({
  143. projectNo: item.projectNo || '',
  144. modelNo: item.modelNo || '',
  145. color: item.color || '',
  146. floorCount: item.floorCount || '',
  147. specialRequirement: item.specialRequirement || '',
  148. planDeliveryDate: item.planDeliveryDate || '',
  149. status: item.status || '',
  150. finishDate: item.finishDate || '',
  151. nodeStatusMap: this.toNodeStatusMap(item.nodeList)
  152. }))
  153. },
  154. toNodeStatusMap(nodeList) {
  155. const map = {}
  156. NODE_TEMPLATE.forEach(code => { map[code] = '未开始' })
  157. ;(nodeList || []).forEach(node => { map[node.nodeCode] = node.status || '未开始' })
  158. return map
  159. },
  160. getNodeCellText(row, code) {
  161. return row.nodeStatusMap[code] || '未开始'
  162. },
  163. getNodeCellClass(row, code) {
  164. const status = this.getNodeCellText(row, code)
  165. if (status === '已完成') return 'node-chip done'
  166. return 'node-chip todo'
  167. },
  168. getOrderStatusClass(status) {
  169. if (status === '已完成') return 'status-done'
  170. if (status === '进行中') return 'status-doing'
  171. return 'status-planned'
  172. },
  173. updateTime() {
  174. const now = new Date()
  175. const weekList = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
  176. const d = this.dayjs(now)
  177. this.currentTime = `${d.format('YYYY/MM/DD')} ${weekList[now.getDay()]} ${d.format('HH:mm:ss')}`
  178. },
  179. getMockList() {
  180. return []
  181. }
  182. }
  183. }
  184. </script>
  185. <style>
  186. .screen-wrap{height:100vh;padding:16px;background:linear-gradient(165deg,#0a1f36 0%,#0f2b47 50%,#0a2037 100%);display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden}
  187. .top-bar{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-radius:12px;border:1px solid rgba(109,167,219,.24);background:rgba(9,29,49,.82)}
  188. .top-bar{position:relative}
  189. .header-left{display:flex;align-items:center;min-width:110px;z-index:2}
  190. .logo-box{width:86px;height:30px;border-radius:6px;border:1px solid rgba(128,198,255,.45);color:#d8edff;font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;background:rgba(36,82,122,.35)}
  191. .header-center{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;max-width:70%;width:auto;padding:6px 24px 8px;border-radius:8px;border:1px solid rgba(96,170,232,.34);background:linear-gradient(180deg,rgba(33,73,116,.52),rgba(16,44,77,.42))}
  192. .header-center::before{content:'';position:absolute;left:50%;transform:translateX(-50%);top:-8px;width:68%;height:1px;background:linear-gradient(90deg,rgba(87,164,230,0),rgba(87,164,230,.9),rgba(87,164,230,0))}
  193. .top-bar .tools{margin-left:auto;z-index:2}
  194. .title{color:#8fe7ff;font-size:32px;font-weight:800;letter-spacing:3px;line-height:1.05;text-shadow:0 0 14px rgba(79,179,255,.36)}
  195. .subtitle{margin-top:4px;color:#c7e8ff;font-size:11px;letter-spacing:1px}
  196. .tools{display:flex;align-items:center;gap:12px}.time{color:#89d7ff;font-size:22px;font-weight:600}
  197. .legend-row{display:flex;gap:16px;margin:10px 0 10px 2px}.legend-item{display:inline-flex;align-items:center;color:#c7dff4;font-size:13px}
  198. .dot{width:10px;height:10px;border-radius:50%;margin-right:6px;display:inline-block}.dot.done{background:#69e4a4}.dot.doing{background:#ffd35d}.dot.todo{background:#dbe3ee}
  199. .section-header{display:flex;align-items:center;justify-content:space-between;margin:0 2px 10px}
  200. .section-title{color:#d7ebff;font-size:18px;font-weight:600}
  201. .section-count{color:#9ec6e9;font-size:14px}
  202. .board-table{border-radius:10px;overflow:hidden;border:1px solid rgba(86,140,190,.35);flex:1;min-height:0}
  203. .board-table .cell {
  204. line-height: 36px;
  205. font-size: 17px;
  206. height: 36px;
  207. }
  208. .screen-wrap .board-table .el-table,.screen-wrap .board-table .el-table__expanded-cell,.screen-wrap .board-table .el-table__body-wrapper,.screen-wrap .board-table .el-table__empty-block,.screen-wrap .board-table .el-table__fixed-body-wrapper{background:rgba(12,39,64,.96)!important;color:#fff!important}
  209. .screen-wrap .board-table .el-table__header-wrapper th,.screen-wrap .board-table .el-table__fixed-header-wrapper th{background:#123a5e!important;color:#d9e9f8!important;border-color:rgba(80,133,181,.6)!important;font-size:15px!important;padding:14px 0!important}
  210. .screen-wrap .board-table .el-table__body tr>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr>td,.screen-wrap .board-table .el-table__fixed-right .el-table__fixed-body-wrapper tr>td{background:rgba(20,52,83,.96)!important;border-color:rgba(88,139,187,.4)!important;color:#fff!important;height:55px!important;vertical-align:middle!important}
  211. .screen-wrap .board-table .el-table--striped .el-table__body tr.el-table__row--striped>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr.el-table__row--striped>td{background:rgba(29,66,102,.96)!important}
  212. .screen-wrap .board-table .el-table--enable-row-hover .el-table__body tr:hover>td,.screen-wrap .board-table .el-table__fixed-body-wrapper tr:hover>td{background:rgba(39,81,123,.96)!important}
  213. .screen-wrap .board-table .el-table .cell{color:#fff!important;line-height:29px!important;overflow:visible!important}
  214. .screen-wrap .board-table .el-table__empty-text{color:#9ac2e7!important}
  215. .node-chip{display:inline-block!important;width:46px;height:14px;margin:0 auto;border-radius:2px}
  216. .node-chip.done{background:#05f66d}.node-chip.todo{background:#dce4ee}
  217. .board-tag{display:inline-flex!important;align-items:center!important;justify-content:center!important;min-width:80px!important;height:29px!important;padding:0 10px!important;border-radius:14px!important;border:1px solid transparent!important;font-size:13px!important;font-weight:600!important;line-height:27px!important;box-sizing:border-box!important}
  218. .board-tag.status-planned{color:#b9c7d8!important;background:rgba(96,118,141,.28)!important;border-color:rgba(185,199,216,.34)!important}
  219. .board-tag.status-doing{color:#ffd774!important;background:rgba(120,92,27,.32)!important;border-color:rgba(255,215,116,.45)!important}
  220. .board-tag.status-done{color:#83f3be!important;background:rgba(31,110,84,.32)!important;border-color:rgba(131,243,190,.45)!important}
  221. </style>