index.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <template>
  2. <div class="tsb-device-shell">
  3. <div v-if="!currentDeviceSn" class="workspace-empty app-container">
  4. <el-empty description="请从首页选择已登录设备">
  5. <el-button type="primary" size="small" @click="goHomeIndex">前往首页</el-button>
  6. </el-empty>
  7. </div>
  8. <template v-else>
  9. <div class="device-tabs-bar">
  10. <div
  11. v-for="item in openedDevices"
  12. :key="item.deviceSn"
  13. class="device-tab"
  14. :class="{ active: isActiveTab(item) }"
  15. @click="switchDevice(item)"
  16. >
  17. <span class="tab-label">SN {{ item.deviceSn }}</span>
  18. <i
  19. class="el-icon-close tab-close"
  20. @click.stop="closeDevice(item)"
  21. />
  22. </div>
  23. <el-button type="text" size="mini" class="add-device-btn" @click="goHomeIndex">
  24. <i class="el-icon-plus" /> 选择设备
  25. </el-button>
  26. </div>
  27. <div v-if="!isHomePage" class="sub-toolbar">
  28. <el-button type="text" icon="el-icon-back" @click="goDeviceHome">返回功能菜单</el-button>
  29. <span class="sub-title">{{ subPageTitle }}</span>
  30. <span class="sub-sn">设备 SN:{{ currentDeviceSn }}</span>
  31. </div>
  32. <keep-alive>
  33. <component :is="panelComponent" :key="panelCacheKey" :bound-device-sn="currentDeviceSn" />
  34. </keep-alive>
  35. </template>
  36. </div>
  37. </template>
  38. <script>
  39. import tsbWebSocket from '@/utils/tsbWebSocket'
  40. import { ensureWorkspaceTag } from '@/utils/tsbWorkspaceNav'
  41. import { buildDeviceSession, clearTsbDeviceSession, hasTsbDeviceSession, saveTsbDeviceSession } from '@/utils/tsbDeviceSession'
  42. import DeviceHome from './home'
  43. import TaxPage from '@/views/app-common/tax/index'
  44. const PANEL_COMPONENTS = {
  45. home: DeviceHome,
  46. tax: TaxPage
  47. }
  48. const PANEL_TITLES = {
  49. home: '设备功能',
  50. tax: '报税口'
  51. }
  52. export default {
  53. name: 'TsbDeviceShell',
  54. components: { DeviceHome, TaxPage },
  55. computed: {
  56. openedDevices() {
  57. return this.$store.getters.tsbOpenedDevices || []
  58. },
  59. currentDeviceSn() {
  60. return this.$store.getters.tsbCurrentDeviceSn
  61. },
  62. currentPanel() {
  63. return this.$store.getters.tsbCurrentPanel
  64. },
  65. isHomePage() {
  66. return this.currentPanel === 'home'
  67. },
  68. subPageTitle() {
  69. return PANEL_TITLES[this.currentPanel] || ''
  70. },
  71. panelComponent() {
  72. return PANEL_COMPONENTS[this.currentPanel] || DeviceHome
  73. },
  74. panelCacheKey() {
  75. return `${this.currentDeviceSn}-${this.currentPanel}`
  76. }
  77. },
  78. created() {
  79. if (!hasTsbDeviceSession() || !this.openedDevices.length) {
  80. this.$store.commit('tsb/SET_SESSION_CHECKED', false)
  81. this.$tab.closeOpenPage({ path: '/index' })
  82. return
  83. }
  84. this.ensureWsConnected()
  85. ensureWorkspaceTag()
  86. },
  87. activated() {
  88. if (!hasTsbDeviceSession() || !this.openedDevices.length) {
  89. return
  90. }
  91. this.ensureWsConnected()
  92. ensureWorkspaceTag()
  93. },
  94. methods: {
  95. ensureWsConnected() {
  96. const device = this.$store.getters.tsbCurrentDevice
  97. if (!device || device.deviceSn == null) {
  98. return
  99. }
  100. if (!tsbWebSocket.isConnected()) {
  101. tsbWebSocket.ensureCurrentDeviceConnection()
  102. }
  103. },
  104. isActiveTab(device) {
  105. return String(device.deviceSn) === String(this.currentDeviceSn)
  106. },
  107. switchDevice(device) {
  108. if (this.isActiveTab(device)) {
  109. return
  110. }
  111. tsbWebSocket.switchToDevice(device).catch(() => {})
  112. },
  113. closeDevice(device) {
  114. const closingCurrent = this.isActiveTab(device)
  115. const panelMap = { ...this.$store.state.tsb.devicePanelMap }
  116. delete panelMap[device.deviceSn]
  117. this.$store.commit('tsb/SET_DEVICE_PANEL_MAP', panelMap)
  118. this.$store.commit('tsb/REMOVE_OPENED_DEVICE', device.deviceSn)
  119. this.$store.commit('tsb/CLEAR_DEVICE_PAGE_FORM', String(device.deviceSn))
  120. tsbWebSocket.disconnectDevice(device.deviceSn)
  121. tsbWebSocket.onDeviceTabClosed(device.deviceSn)
  122. const remaining = this.$store.getters.tsbOpenedDevices
  123. if (!remaining.length) {
  124. clearTsbDeviceSession()
  125. tsbWebSocket.releaseWorkspaceSession()
  126. this.$store.commit('tsb/SET_SESSION_CHECKED', false)
  127. this.$tab.closeOpenPage({ path: '/index' })
  128. return
  129. }
  130. const current = this.$store.getters.tsbCurrentDevice
  131. saveTsbDeviceSession(buildDeviceSession(current, remaining, panelMap))
  132. if (closingCurrent) {
  133. const next = remaining[remaining.length - 1]
  134. tsbWebSocket.switchToDevice(next).catch(() => {})
  135. }
  136. },
  137. goDeviceHome() {
  138. const sn = this.currentDeviceSn
  139. if (sn != null) {
  140. this.$store.commit('tsb/SET_DEVICE_PANEL', { deviceSn: sn, panel: 'home' })
  141. this.persistPanelMap()
  142. }
  143. },
  144. goHomeIndex() {
  145. this.$router.push('/index')
  146. },
  147. persistPanelMap() {
  148. saveTsbDeviceSession(buildDeviceSession(
  149. this.$store.getters.tsbCurrentDevice,
  150. this.$store.getters.tsbOpenedDevices,
  151. this.$store.state.tsb.devicePanelMap
  152. ))
  153. }
  154. }
  155. }
  156. </script>
  157. <style scoped lang="scss">
  158. .tsb-device-shell {
  159. min-height: 100%;
  160. }
  161. .workspace-empty {
  162. padding-top: 48px;
  163. }
  164. .device-tabs-bar {
  165. display: flex;
  166. align-items: center;
  167. flex-wrap: wrap;
  168. gap: 8px;
  169. padding: 8px 12px;
  170. background: #f5f7fa;
  171. border-bottom: 1px solid #e4e7ed;
  172. }
  173. .device-tab {
  174. display: inline-flex;
  175. align-items: center;
  176. gap: 6px;
  177. padding: 6px 12px;
  178. border: 1px solid #dcdfe6;
  179. border-radius: 4px;
  180. background: #fff;
  181. cursor: pointer;
  182. font-size: 13px;
  183. transition: all 0.2s;
  184. &.active {
  185. color: #409eff;
  186. border-color: #409eff;
  187. background: #ecf5ff;
  188. }
  189. }
  190. .tab-close {
  191. font-size: 12px;
  192. color: #909399;
  193. &:hover {
  194. color: #f56c6c;
  195. }
  196. }
  197. .add-device-btn {
  198. margin-left: 4px;
  199. }
  200. .sub-toolbar {
  201. display: flex;
  202. align-items: center;
  203. gap: 12px;
  204. padding: 8px 16px;
  205. border-bottom: 1px solid #ebeef5;
  206. background: #fff;
  207. }
  208. .sub-title {
  209. font-weight: 600;
  210. font-size: 14px;
  211. }
  212. .sub-sn {
  213. margin-left: auto;
  214. color: #909399;
  215. font-size: 13px;
  216. }
  217. </style>