vue2-client 1.2.0 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +65 -65
  2. package/babel.config.js +1 -0
  3. package/docs/notice.md +22 -0
  4. package/index.js +28 -28
  5. package/package.json +1 -1
  6. package/src/App.vue +93 -93
  7. package/src/assets/img/SunClientDownload.png +0 -0
  8. package/src/assets/img/SunClientManual/1.png +0 -0
  9. package/src/assets/img/SunClientManual/2.png +0 -0
  10. package/src/assets/img/SunClientManual/3.png +0 -0
  11. package/src/assets/img/SunClientManual/4.png +0 -0
  12. package/src/assets/img/ToDeskDownload.png +0 -0
  13. package/src/assets/img/ToDeskManual/1.png +0 -0
  14. package/src/assets/img/ToDeskManual/2.png +0 -0
  15. package/src/assets/img/ToDeskManual/3.png +0 -0
  16. package/src/assets/img/ToDeskManual/4.png +0 -0
  17. package/src/assets/sound/newNote.mp3 +0 -0
  18. package/src/base-client/all.js +57 -57
  19. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +1157 -1159
  20. package/src/base-client/components/common/CreateQuery/index.md +42 -42
  21. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQuery.vue +540 -540
  22. package/src/base-client/components/common/CreateSimpleFormQuery/index.md +42 -42
  23. package/src/base-client/components/common/CustomColumnsDrawer/index.md +46 -46
  24. package/src/base-client/components/common/FormGroupEdit/FormGroupEdit.vue +150 -150
  25. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  26. package/src/base-client/components/common/FormGroupQuery/index.md +43 -43
  27. package/src/base-client/components/common/XAddForm/XAddForm.vue +323 -323
  28. package/src/base-client/components/common/XAddForm/index.md +60 -60
  29. package/src/base-client/components/common/XBadge/index.md +39 -39
  30. package/src/base-client/components/common/XCard/index.md +43 -43
  31. package/src/base-client/components/common/XForm/XForm.vue +275 -275
  32. package/src/base-client/components/common/XForm/XFormItem.vue +217 -217
  33. package/src/base-client/components/common/XForm/index.md +196 -196
  34. package/src/base-client/components/common/XFormCol/index.md +35 -35
  35. package/src/base-client/components/common/XFormTable/XFormTable.vue +407 -405
  36. package/src/base-client/components/common/XFormTable/index.md +89 -89
  37. package/src/base-client/components/common/XTable/XTable.vue +262 -262
  38. package/src/base-client/components/common/XTable/index.md +255 -255
  39. package/src/base-client/components/common/XTreeOne/XTreeOne.vue +105 -105
  40. package/src/base-client/components/iot/CustomerDetailsView/CustomerDetailsView.vue +226 -226
  41. package/src/base-client/components/iot/CustomerDetailsView/index.md +41 -41
  42. package/src/base-client/components/iot/DataAnalysisUser/DataAnalysisUser.vue +127 -127
  43. package/src/base-client/components/iot/DataAnalysisView/DataAnalysisView.vue +244 -250
  44. package/src/base-client/components/iot/DataAnalysisViewGD/DataAnalysisViewGD.vue +548 -548
  45. package/src/base-client/components/iot/DeviceBrandDetailsView/DeviceBrandDetailsView.vue +453 -453
  46. package/src/base-client/components/iot/DeviceDetailsView/DeviceDetailsView.vue +231 -231
  47. package/src/base-client/components/iot/DeviceDetailsView/index.md +43 -43
  48. package/src/base-client/components/iot/DeviceDetailsView/part/DeviceDetailsCount.vue +330 -330
  49. package/src/base-client/components/iot/DeviceDetailsView/part/DeviceDetailsException.vue +57 -57
  50. package/src/base-client/components/iot/DeviceDetailsView/part/DeviceDetailsInstruct.vue +122 -122
  51. package/src/base-client/components/iot/DeviceDetailsView/part/DeviceDetailsInstructOperate.vue +122 -122
  52. package/src/base-client/components/iot/DeviceDetailsView/part/DeviceDetailsMain.vue +225 -225
  53. package/src/base-client/components/iot/DeviceDetailsView/part/DeviceDetailsRead.vue +135 -135
  54. package/src/base-client/components/iot/DeviceTypeDetailsView/DeviceTypeDetailsView.vue +277 -277
  55. package/src/base-client/components/iot/InstructDetailsView/InstructDetailsView.vue +472 -472
  56. package/src/base-client/components/iot/InstructDetailsView/index.md +45 -45
  57. package/src/base-client/components/iot/LogDetailsView/LogDetailsView.vue +380 -380
  58. package/src/base-client/components/iot/LogDetailsView/index.md +43 -43
  59. package/src/base-client/components/iot/MeterDetailsView/MeterDetailsView.vue +360 -360
  60. package/src/base-client/components/iot/MeterDetailsView/index.md +43 -43
  61. package/src/base-client/components/iot/MeterDetailsView/part/MeterDetailsCount.vue +335 -335
  62. package/src/base-client/components/iot/MeterDetailsView/part/MeterDetailsException.vue +185 -185
  63. package/src/base-client/components/iot/MeterDetailsView/part/MeterDetailsHandPlan.vue +292 -292
  64. package/src/base-client/components/iot/MeterDetailsView/part/MeterDetailsInstruct.vue +237 -237
  65. package/src/base-client/components/iot/MeterDetailsView/part/MeterDetailsMain.vue +257 -257
  66. package/src/base-client/components/iot/MeterDetailsView/part/MeterDetailsSellGas.vue +190 -190
  67. package/src/base-client/components/iot/WebmeterAnalysisView/WebmeterAnalysisView.vue +723 -723
  68. package/src/base-client/components/iot/WebmeterAnalysisView/index.md +48 -48
  69. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  70. package/src/base-client/components/system/DictionaryDetailsView/index.md +41 -41
  71. package/src/base-client/components/system/QueryParamsDetailsView/QueryParamsDetailsView.vue +248 -248
  72. package/src/base-client/components/ticket/EmployeeDetailsView/EmployeeDetailsView.vue +371 -406
  73. package/src/base-client/components/ticket/TicketDetailsView/TicketDetailsView.vue +838 -486
  74. package/src/base-client/components/ticket/TicketDetailsView/part/TicketDetailsFlow.vue +261 -184
  75. package/src/base-client/components/ticket/TicketSubmitSuccessView/TicketSubmitSuccessView.vue +528 -303
  76. package/src/base-client/plugins/AppData.js +69 -69
  77. package/src/base-client/plugins/GetLoginInfoService.js +221 -221
  78. package/src/base-client/plugins/index.js +21 -21
  79. package/src/bootstrap.js +27 -27
  80. package/src/components/Ellipsis/Ellipsis.vue +64 -64
  81. package/src/components/Ellipsis/index.md +38 -38
  82. package/src/components/NumberInfo/index.md +43 -43
  83. package/src/components/STable/README.md +341 -341
  84. package/src/components/Trend/index.md +45 -45
  85. package/src/components/cache/AKeepAlive.js +172 -172
  86. package/src/components/checkbox/index.js +7 -7
  87. package/src/components/index.js +36 -36
  88. package/src/components/menu/menu.js +273 -273
  89. package/src/components/setting/Setting.vue +237 -237
  90. package/src/components/table/advance/AdvanceTable.vue +275 -275
  91. package/src/components/transition/PageToggleTransition.vue +97 -97
  92. package/src/layouts/CommonLayout.vue +42 -42
  93. package/src/layouts/PageLayout.vue +151 -151
  94. package/src/layouts/SinglePageView.vue +74 -74
  95. package/src/layouts/header/AdminHeader.vue +109 -109
  96. package/src/layouts/header/HeaderAvatar.vue +60 -60
  97. package/src/layouts/header/HeaderNotice.vue +97 -97
  98. package/src/layouts/tabs/TabsHead.vue +190 -190
  99. package/src/layouts/tabs/TabsView.vue +355 -355
  100. package/src/main.js +20 -20
  101. package/src/mock/goods/index.js +108 -108
  102. package/src/mock/index.js +12 -12
  103. package/src/mock/project/index.js +17 -17
  104. package/src/mock/user/current.js +13 -13
  105. package/src/mock/user/login.js +39 -39
  106. package/src/mock/workplace/index.js +15 -15
  107. package/src/pages/exception/403.vue +25 -25
  108. package/src/pages/exception/404.vue +25 -25
  109. package/src/pages/exception/500.vue +25 -25
  110. package/src/pages/login/Login.vue +194 -194
  111. package/src/pages/report/ReportTableHome.vue +28 -28
  112. package/src/pages/resourceManage/resourceManageMain.vue +55 -55
  113. package/src/pages/system/applyInstallView/Core.vue +570 -570
  114. package/src/pages/system/applyInstallView/index.vue +34 -34
  115. package/src/pages/system/dictionary/index.vue +41 -41
  116. package/src/pages/system/queryParams/index.vue +41 -41
  117. package/src/router/async/config.async.js +25 -25
  118. package/src/router/async/router.map.js +59 -59
  119. package/src/router/guards.js +104 -104
  120. package/src/router/index.js +27 -27
  121. package/src/services/api/EmployeeDetailsViewApi.js +3 -1
  122. package/src/services/api/TicketDetailsViewApi.js +9 -1
  123. package/src/services/api/common.js +37 -39
  124. package/src/services/api/restTools.js +23 -23
  125. package/src/services/dataSource.js +12 -12
  126. package/src/services/user.js +34 -34
  127. package/src/store/modules/setting.js +114 -114
  128. package/src/utils/i18n.js +80 -80
  129. package/src/utils/indexedDB.js +146 -146
  130. package/src/utils/request.js +197 -197
  131. package/src/utils/routerUtil.js +15 -2
  132. package/tests/unit/ReportTable.spec.js +15 -15
  133. package/vue.config.js +153 -153
  134. package/webpack.config.js +12 -12
@@ -1,355 +1,355 @@
1
- <template>
2
- <admin-layout>
3
- <contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
4
- <tabs-head
5
- v-if="multiPage"
6
- :active="activePage"
7
- :page-list="pageList"
8
- @change="changePage"
9
- @close="remove"
10
- @refresh="refresh"
11
- @contextmenu="onContextmenu"
12
- />
13
- <div :class="['tabs-view-content', layout, pageWidth]" :style="`margin-top: ${multiPage ? -24 : 0}px`">
14
- <page-toggle-transition v-show="!$route.meta.singlePage" :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
15
- <a-keep-alive :exclude-keys="excludeKeys" v-if="multiPage && cachePage" v-model="clearCaches">
16
- <router-view v-if="!refreshing" ref="tabContent" :key="$route.path" />
17
- </a-keep-alive>
18
- <router-view ref="tabContent" v-else-if="!refreshing" />
19
- </page-toggle-transition>
20
- <div v-show="$route.meta.singlePage && this.single.length > 0">
21
- <template v-for="page in single">
22
- <single-page-view v-show="page.meta.singlePage === $route.meta.singlePage" :single-page-url="{url:page.meta.singlePage}" :key="page.meta.singlePage"></single-page-view>
23
- </template>
24
- </div>
25
- </div>
26
- </admin-layout>
27
- </template>
28
-
29
- <script>
30
- import AdminLayout from '@vue2-client/layouts/AdminLayout'
31
- import Contextmenu from '@vue2-client/components/menu/Contextmenu'
32
- import PageToggleTransition from '@vue2-client/components/transition/PageToggleTransition'
33
- import { mapState, mapMutations } from 'vuex'
34
- import { getI18nKey } from '@vue2-client/utils/routerUtil'
35
- import AKeepAlive from '@vue2-client/components/cache/AKeepAlive'
36
- import TabsHead from '@vue2-client/layouts/tabs/TabsHead'
37
- import SinglePageView from '../SinglePageView'
38
-
39
- export default {
40
- name: 'TabsView',
41
- i18n: require('./i18n'),
42
- components: { SinglePageView, TabsHead, PageToggleTransition, Contextmenu, AdminLayout, AKeepAlive },
43
- data () {
44
- return {
45
- clearCaches: [],
46
- pageList: [],
47
- activePage: '',
48
- menuVisible: false,
49
- refreshing: false,
50
- allSinglePages: [],
51
- excludeKeys: []
52
- }
53
- },
54
- computed: {
55
- ...mapState('setting', ['multiPage', 'cachePage', 'animate', 'layout', 'pageWidth']),
56
- ...mapState('account', ['single']),
57
- menuItemList () {
58
- return [
59
- { key: '1', icon: 'vertical-right', text: this.$t('closeLeft') },
60
- { key: '2', icon: 'vertical-left', text: this.$t('closeRight') },
61
- { key: '3', icon: 'close', text: this.$t('closeOthers') },
62
- { key: '4', icon: 'sync', text: this.$t('refresh') }
63
- ]
64
- },
65
- tabsOffset () {
66
- return this.multiPage ? 24 : 0
67
- }
68
- },
69
- created () {
70
- this.loadCacheConfig(this.$router?.options?.routes)
71
- this.loadCachedTabs()
72
- const route = this.$route
73
- if (this.pageList.findIndex(item => item.path === route.path) === -1) {
74
- this.pageList.push(this.createPage(route))
75
- }
76
- this.activePage = route.path
77
- if (this.multiPage) {
78
- this.$nextTick(() => {
79
- this.setCachedKey(route)
80
- })
81
- this.addListener()
82
- }
83
- },
84
- mounted () {
85
- this.correctPageMinHeight(-this.tabsOffset)
86
- },
87
- beforeDestroy () {
88
- this.removeListener()
89
- this.correctPageMinHeight(this.tabsOffset)
90
- },
91
- watch: {
92
- '$router.options.routes': function (val) {
93
- this.excludeKeys = []
94
- this.loadCacheConfig(val)
95
- },
96
- '$route': function (newRoute) {
97
- this.activePage = newRoute.path
98
- const page = this.pageList.find(item => item.path === newRoute.path)
99
- if (!this.multiPage) {
100
- this.pageList = [this.createPage(newRoute)]
101
- } else if (page) {
102
- page.fullPath = newRoute.fullPath
103
- } else if (!page) {
104
- this.pageList.push(this.createPage(newRoute))
105
- }
106
- if (this.multiPage) {
107
- this.$nextTick(() => {
108
- this.setCachedKey(newRoute)
109
- })
110
- }
111
- },
112
- 'multiPage': function (newVal) {
113
- if (!newVal) {
114
- this.pageList = [this.createPage(this.$route)]
115
- this.removeListener()
116
- } else {
117
- this.addListener()
118
- }
119
- },
120
- tabsOffset (newVal, oldVal) {
121
- this.correctPageMinHeight(oldVal - newVal)
122
- }
123
- },
124
- methods: {
125
- changePage (key) {
126
- this.activePage = key
127
- const page = this.pageList.find(item => item.path === key)
128
- this.$router.push(page.fullPath)
129
- },
130
- remove (key, next) {
131
- this.setSingle({ path: key, delete: true })
132
- if (this.pageList.length === 1) {
133
- return this.$message.warning(this.$t('warn'))
134
- }
135
- // 清除缓存
136
- let index = this.pageList.findIndex(item => item.path === key)
137
- this.clearCaches = this.pageList.splice(index, 1).map(page => page.cachedKey)
138
- if (next) {
139
- this.$router.push(next)
140
- } else if (key === this.activePage) {
141
- index = index >= this.pageList.length ? this.pageList.length - 1 : index
142
- this.activePage = this.pageList[index].path
143
- this.$router.push(this.activePage)
144
- }
145
- },
146
- refresh (key, page) {
147
- page = page || this.pageList.find(item => item.path === key)
148
- page.loading = true
149
- this.clearCache(page)
150
- if (key === this.activePage) {
151
- // eslint-disable-next-line no-return-assign
152
- this.reloadContent(() => page.loading = false)
153
- } else {
154
- // 其实刷新很快,加这个延迟纯粹为了 loading 状态多展示一会儿,让用户感知刷新这一过程
155
- // eslint-disable-next-line no-return-assign
156
- setTimeout(() => page.loading = false, 500)
157
- }
158
- },
159
- onContextmenu (pageKey, e) {
160
- if (pageKey) {
161
- e.preventDefault()
162
- e.meta = pageKey
163
- this.menuVisible = true
164
- }
165
- },
166
- onMenuSelect (key, target, pageKey) {
167
- switch (key) {
168
- case '1': this.closeLeft(pageKey); break
169
- case '2': this.closeRight(pageKey); break
170
- case '3': this.closeOthers(pageKey); break
171
- case '4': this.refresh(pageKey); break
172
- default: break
173
- }
174
- },
175
- closeOthers (pageKey) {
176
- // 清除缓存
177
- const clearPages = this.pageList.filter(item => item.path !== pageKey && !item.unclose)
178
- this.clearCaches = clearPages.map(item => item.cachedKey)
179
- this.pageList = this.pageList.filter(item => !clearPages.includes(item))
180
- // 判断跳转
181
- if (this.activePage != pageKey) {
182
- this.activePage = pageKey
183
- this.$router.push(this.activePage)
184
- }
185
- },
186
- closeLeft (pageKey) {
187
- const index = this.pageList.findIndex(item => item.path === pageKey)
188
- // 清除缓存
189
- const clearPages = this.pageList.filter((item, i) => i < index && !item.unclose)
190
- this.clearCaches = clearPages.map(item => item.cachedKey)
191
- this.pageList = this.pageList.filter(item => !clearPages.includes(item))
192
- // 判断跳转
193
- if (!this.pageList.find(item => item.path === this.activePage)) {
194
- this.activePage = pageKey
195
- this.$router.push(this.activePage)
196
- }
197
- },
198
- closeRight (pageKey) {
199
- // 清除缓存
200
- const index = this.pageList.findIndex(item => item.path === pageKey)
201
- const clearPages = this.pageList.filter((item, i) => i > index && !item.unclose)
202
- this.clearCaches = clearPages.map(item => item.cachedKey)
203
- this.pageList = this.pageList.filter(item => !clearPages.includes(item))
204
- // 判断跳转
205
- if (!this.pageList.find(item => item.path === this.activePage)) {
206
- this.activePage = pageKey
207
- this.$router.push(this.activePage)
208
- }
209
- },
210
- clearCache (page) {
211
- page._init_ = false
212
- this.clearCaches = [page.cachedKey]
213
- },
214
- reloadContent (onLoaded) {
215
- this.refreshing = true
216
- setTimeout(() => {
217
- this.refreshing = false
218
- this.$nextTick(() => {
219
- this.setCachedKey(this.$route)
220
- if (typeof onLoaded === 'function') {
221
- onLoaded.apply(this, [])
222
- }
223
- })
224
- }, 200)
225
- },
226
- pageName (page) {
227
- return this.$t(getI18nKey(page.keyPath))
228
- },
229
- /**
230
- * 添加监听器
231
- */
232
- addListener () {
233
- window.addEventListener('page:close', this.closePageListener)
234
- window.addEventListener('page:refresh', this.refreshPageListener)
235
- window.addEventListener('unload', this.unloadListener)
236
- },
237
- /**
238
- * 移出监听器
239
- */
240
- removeListener () {
241
- window.removeEventListener('page:close', this.closePageListener)
242
- window.removeEventListener('page:refresh', this.refreshPageListener)
243
- window.removeEventListener('unload', this.unloadListener)
244
- },
245
- /**
246
- * 页签关闭事件监听
247
- * @param event 页签关闭事件
248
- */
249
- closePageListener (event) {
250
- const { closeRoute, nextRoute } = event.detail
251
- const closePath = typeof closeRoute === 'string' ? closeRoute : closeRoute.path
252
- const path = closePath && closePath.split('?')[0]
253
- this.remove(path, nextRoute)
254
- },
255
- /**
256
- * 页面刷新事件监听
257
- * @param event 页签关闭事件
258
- */
259
- refreshPageListener (event) {
260
- const { pageKey } = event.detail
261
- const path = pageKey && pageKey.split('?')[0]
262
- this.refresh(path)
263
- },
264
- /**
265
- * 页面 unload 事件监听器,添加页签到 session 缓存,用于刷新时保留页签
266
- */
267
- unloadListener () {
268
- const tabs = this.pageList.map(item => ({ ...item, _init_: false }))
269
- sessionStorage.setItem(process.env.VUE_APP_TBAS_KEY, JSON.stringify(tabs))
270
- },
271
- createPage (route) {
272
- return {
273
- keyPath: route.matched[route.matched.length - 1].path,
274
- fullPath: route.fullPath,
275
- loading: false,
276
- path: route.path,
277
- title: route.meta && route.meta.page && route.meta.page.title,
278
- unclose: route.meta && route.meta.page && (route.meta.page.closable === false)
279
- }
280
- },
281
- /**
282
- * 设置页面缓存的key
283
- * @param route 页面对应的路由
284
- */
285
- setCachedKey (route) {
286
- const page = this.pageList.find(item => item.path === route.path)
287
- page.unclose = route.meta && route.meta.page && (route.meta.page.closable === false)
288
- if (!page._init_) {
289
- const vnode = this.$refs.tabContent.$vnode
290
- page.cachedKey = vnode.key + vnode.componentOptions.Ctor.cid
291
- page._init_ = true
292
- }
293
- },
294
- /**
295
- * 加载缓存的 tabs
296
- */
297
- loadCachedTabs () {
298
- const cachedTabsStr = sessionStorage.getItem(process.env.VUE_APP_TBAS_KEY)
299
- if (cachedTabsStr) {
300
- try {
301
- // 加载缓存的单页面
302
- const allCachedPath = JSON.parse(cachedTabsStr).map(str => { return str.fullPath })
303
- this.allSinglePages.map(item => {
304
- if (allCachedPath.includes(item.fullPath)) {
305
- // 如果缓存中有单页面 加载到 state.single
306
- this.setSingle(item)
307
- }
308
- })
309
- const cachedTabs = JSON.parse(cachedTabsStr)
310
- if (cachedTabs.length > 0) {
311
- this.pageList = cachedTabs
312
- }
313
- } catch (e) {
314
- console.warn('failed to load cached tabs, got exception:', e)
315
- } finally {
316
- sessionStorage.removeItem(process.env.VUE_APP_TBAS_KEY)
317
- }
318
- }
319
- },
320
- loadCacheConfig (routes, pCache = true) {
321
- routes.forEach(item => {
322
- const cacheAble = item.meta?.page?.cacheAble ?? pCache ?? true
323
- if (item.meta && item.meta.singlePage) {
324
- // 找到所有单页面添加到 allSinglePages 中
325
- this.allSinglePages.push(item)
326
- }
327
- if (!cacheAble) {
328
- this.excludeKeys.push(new RegExp(`${item.path}\\d+$`))
329
- }
330
- if (item.children) {
331
- this.loadCacheConfig(item.children, cacheAble)
332
- }
333
- })
334
- },
335
- ...mapMutations('setting', ['correctPageMinHeight']),
336
- ...mapMutations('account', ['setSingle'])
337
- }
338
- }
339
- </script>
340
-
341
- <style scoped lang="less">
342
- .tabs-view{
343
- margin: -16px auto 8px;
344
- &.head.fixed{
345
- max-width: 1400px;
346
- }
347
- }
348
- .tabs-view-content{
349
- position: relative;
350
- &.head.fixed{
351
- width: 1400px;
352
- margin: 0 auto;
353
- }
354
- }
355
- </style>
1
+ <template>
2
+ <admin-layout>
3
+ <contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
4
+ <tabs-head
5
+ v-if="multiPage"
6
+ :active="activePage"
7
+ :page-list="pageList"
8
+ @change="changePage"
9
+ @close="remove"
10
+ @refresh="refresh"
11
+ @contextmenu="onContextmenu"
12
+ />
13
+ <div :class="['tabs-view-content', layout, pageWidth]" :style="`margin-top: ${multiPage ? -24 : 0}px`">
14
+ <page-toggle-transition v-show="!$route.meta.singlePage" :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
15
+ <a-keep-alive :exclude-keys="excludeKeys" v-if="multiPage && cachePage" v-model="clearCaches">
16
+ <router-view v-if="!refreshing" ref="tabContent" :key="$route.path" />
17
+ </a-keep-alive>
18
+ <router-view ref="tabContent" v-else-if="!refreshing" />
19
+ </page-toggle-transition>
20
+ <div v-show="$route.meta.singlePage && this.single.length > 0">
21
+ <template v-for="page in single">
22
+ <single-page-view v-show="page.meta.singlePage === $route.meta.singlePage" :single-page-url="{url:page.meta.singlePage}" :key="page.meta.singlePage"></single-page-view>
23
+ </template>
24
+ </div>
25
+ </div>
26
+ </admin-layout>
27
+ </template>
28
+
29
+ <script>
30
+ import AdminLayout from '@vue2-client/layouts/AdminLayout'
31
+ import Contextmenu from '@vue2-client/components/menu/Contextmenu'
32
+ import PageToggleTransition from '@vue2-client/components/transition/PageToggleTransition'
33
+ import { mapState, mapMutations } from 'vuex'
34
+ import { getI18nKey } from '@vue2-client/utils/routerUtil'
35
+ import AKeepAlive from '@vue2-client/components/cache/AKeepAlive'
36
+ import TabsHead from '@vue2-client/layouts/tabs/TabsHead'
37
+ import SinglePageView from '../SinglePageView'
38
+
39
+ export default {
40
+ name: 'TabsView',
41
+ i18n: require('./i18n'),
42
+ components: { SinglePageView, TabsHead, PageToggleTransition, Contextmenu, AdminLayout, AKeepAlive },
43
+ data () {
44
+ return {
45
+ clearCaches: [],
46
+ pageList: [],
47
+ activePage: '',
48
+ menuVisible: false,
49
+ refreshing: false,
50
+ allSinglePages: [],
51
+ excludeKeys: []
52
+ }
53
+ },
54
+ computed: {
55
+ ...mapState('setting', ['multiPage', 'cachePage', 'animate', 'layout', 'pageWidth']),
56
+ ...mapState('account', ['single']),
57
+ menuItemList () {
58
+ return [
59
+ { key: '1', icon: 'vertical-right', text: this.$t('closeLeft') },
60
+ { key: '2', icon: 'vertical-left', text: this.$t('closeRight') },
61
+ { key: '3', icon: 'close', text: this.$t('closeOthers') },
62
+ { key: '4', icon: 'sync', text: this.$t('refresh') }
63
+ ]
64
+ },
65
+ tabsOffset () {
66
+ return this.multiPage ? 24 : 0
67
+ }
68
+ },
69
+ created () {
70
+ this.loadCacheConfig(this.$router?.options?.routes)
71
+ this.loadCachedTabs()
72
+ const route = this.$route
73
+ if (this.pageList.findIndex(item => item.path === route.path) === -1) {
74
+ this.pageList.push(this.createPage(route))
75
+ }
76
+ this.activePage = route.path
77
+ if (this.multiPage) {
78
+ this.$nextTick(() => {
79
+ this.setCachedKey(route)
80
+ })
81
+ this.addListener()
82
+ }
83
+ },
84
+ mounted () {
85
+ this.correctPageMinHeight(-this.tabsOffset)
86
+ },
87
+ beforeDestroy () {
88
+ this.removeListener()
89
+ this.correctPageMinHeight(this.tabsOffset)
90
+ },
91
+ watch: {
92
+ '$router.options.routes': function (val) {
93
+ this.excludeKeys = []
94
+ this.loadCacheConfig(val)
95
+ },
96
+ '$route': function (newRoute) {
97
+ this.activePage = newRoute.path
98
+ const page = this.pageList.find(item => item.path === newRoute.path)
99
+ if (!this.multiPage) {
100
+ this.pageList = [this.createPage(newRoute)]
101
+ } else if (page) {
102
+ page.fullPath = newRoute.fullPath
103
+ } else if (!page) {
104
+ this.pageList.push(this.createPage(newRoute))
105
+ }
106
+ if (this.multiPage) {
107
+ this.$nextTick(() => {
108
+ this.setCachedKey(newRoute)
109
+ })
110
+ }
111
+ },
112
+ 'multiPage': function (newVal) {
113
+ if (!newVal) {
114
+ this.pageList = [this.createPage(this.$route)]
115
+ this.removeListener()
116
+ } else {
117
+ this.addListener()
118
+ }
119
+ },
120
+ tabsOffset (newVal, oldVal) {
121
+ this.correctPageMinHeight(oldVal - newVal)
122
+ }
123
+ },
124
+ methods: {
125
+ changePage (key) {
126
+ this.activePage = key
127
+ const page = this.pageList.find(item => item.path === key)
128
+ this.$router.push(page.fullPath)
129
+ },
130
+ remove (key, next) {
131
+ this.setSingle({ path: key, delete: true })
132
+ if (this.pageList.length === 1) {
133
+ return this.$message.warning(this.$t('warn'))
134
+ }
135
+ // 清除缓存
136
+ let index = this.pageList.findIndex(item => item.path === key)
137
+ this.clearCaches = this.pageList.splice(index, 1).map(page => page.cachedKey)
138
+ if (next) {
139
+ this.$router.push(next)
140
+ } else if (key === this.activePage) {
141
+ index = index >= this.pageList.length ? this.pageList.length - 1 : index
142
+ this.activePage = this.pageList[index].path
143
+ this.$router.push(this.activePage)
144
+ }
145
+ },
146
+ refresh (key, page) {
147
+ page = page || this.pageList.find(item => item.path === key)
148
+ page.loading = true
149
+ this.clearCache(page)
150
+ if (key === this.activePage) {
151
+ // eslint-disable-next-line no-return-assign
152
+ this.reloadContent(() => page.loading = false)
153
+ } else {
154
+ // 其实刷新很快,加这个延迟纯粹为了 loading 状态多展示一会儿,让用户感知刷新这一过程
155
+ // eslint-disable-next-line no-return-assign
156
+ setTimeout(() => page.loading = false, 500)
157
+ }
158
+ },
159
+ onContextmenu (pageKey, e) {
160
+ if (pageKey) {
161
+ e.preventDefault()
162
+ e.meta = pageKey
163
+ this.menuVisible = true
164
+ }
165
+ },
166
+ onMenuSelect (key, target, pageKey) {
167
+ switch (key) {
168
+ case '1': this.closeLeft(pageKey); break
169
+ case '2': this.closeRight(pageKey); break
170
+ case '3': this.closeOthers(pageKey); break
171
+ case '4': this.refresh(pageKey); break
172
+ default: break
173
+ }
174
+ },
175
+ closeOthers (pageKey) {
176
+ // 清除缓存
177
+ const clearPages = this.pageList.filter(item => item.path !== pageKey && !item.unclose)
178
+ this.clearCaches = clearPages.map(item => item.cachedKey)
179
+ this.pageList = this.pageList.filter(item => !clearPages.includes(item))
180
+ // 判断跳转
181
+ if (this.activePage != pageKey) {
182
+ this.activePage = pageKey
183
+ this.$router.push(this.activePage)
184
+ }
185
+ },
186
+ closeLeft (pageKey) {
187
+ const index = this.pageList.findIndex(item => item.path === pageKey)
188
+ // 清除缓存
189
+ const clearPages = this.pageList.filter((item, i) => i < index && !item.unclose)
190
+ this.clearCaches = clearPages.map(item => item.cachedKey)
191
+ this.pageList = this.pageList.filter(item => !clearPages.includes(item))
192
+ // 判断跳转
193
+ if (!this.pageList.find(item => item.path === this.activePage)) {
194
+ this.activePage = pageKey
195
+ this.$router.push(this.activePage)
196
+ }
197
+ },
198
+ closeRight (pageKey) {
199
+ // 清除缓存
200
+ const index = this.pageList.findIndex(item => item.path === pageKey)
201
+ const clearPages = this.pageList.filter((item, i) => i > index && !item.unclose)
202
+ this.clearCaches = clearPages.map(item => item.cachedKey)
203
+ this.pageList = this.pageList.filter(item => !clearPages.includes(item))
204
+ // 判断跳转
205
+ if (!this.pageList.find(item => item.path === this.activePage)) {
206
+ this.activePage = pageKey
207
+ this.$router.push(this.activePage)
208
+ }
209
+ },
210
+ clearCache (page) {
211
+ page._init_ = false
212
+ this.clearCaches = [page.cachedKey]
213
+ },
214
+ reloadContent (onLoaded) {
215
+ this.refreshing = true
216
+ setTimeout(() => {
217
+ this.refreshing = false
218
+ this.$nextTick(() => {
219
+ this.setCachedKey(this.$route)
220
+ if (typeof onLoaded === 'function') {
221
+ onLoaded.apply(this, [])
222
+ }
223
+ })
224
+ }, 200)
225
+ },
226
+ pageName (page) {
227
+ return this.$t(getI18nKey(page.keyPath))
228
+ },
229
+ /**
230
+ * 添加监听器
231
+ */
232
+ addListener () {
233
+ window.addEventListener('page:close', this.closePageListener)
234
+ window.addEventListener('page:refresh', this.refreshPageListener)
235
+ window.addEventListener('unload', this.unloadListener)
236
+ },
237
+ /**
238
+ * 移出监听器
239
+ */
240
+ removeListener () {
241
+ window.removeEventListener('page:close', this.closePageListener)
242
+ window.removeEventListener('page:refresh', this.refreshPageListener)
243
+ window.removeEventListener('unload', this.unloadListener)
244
+ },
245
+ /**
246
+ * 页签关闭事件监听
247
+ * @param event 页签关闭事件
248
+ */
249
+ closePageListener (event) {
250
+ const { closeRoute, nextRoute } = event.detail
251
+ const closePath = typeof closeRoute === 'string' ? closeRoute : closeRoute.path
252
+ const path = closePath && closePath.split('?')[0]
253
+ this.remove(path, nextRoute)
254
+ },
255
+ /**
256
+ * 页面刷新事件监听
257
+ * @param event 页签关闭事件
258
+ */
259
+ refreshPageListener (event) {
260
+ const { pageKey } = event.detail
261
+ const path = pageKey && pageKey.split('?')[0]
262
+ this.refresh(path)
263
+ },
264
+ /**
265
+ * 页面 unload 事件监听器,添加页签到 session 缓存,用于刷新时保留页签
266
+ */
267
+ unloadListener () {
268
+ const tabs = this.pageList.map(item => ({ ...item, _init_: false }))
269
+ sessionStorage.setItem(process.env.VUE_APP_TBAS_KEY, JSON.stringify(tabs))
270
+ },
271
+ createPage (route) {
272
+ return {
273
+ keyPath: route.matched[route.matched.length - 1].path,
274
+ fullPath: route.fullPath,
275
+ loading: false,
276
+ path: route.path,
277
+ title: route.meta && route.meta.page && route.meta.page.title,
278
+ unclose: route.meta && route.meta.page && (route.meta.page.closable === false)
279
+ }
280
+ },
281
+ /**
282
+ * 设置页面缓存的key
283
+ * @param route 页面对应的路由
284
+ */
285
+ setCachedKey (route) {
286
+ const page = this.pageList.find(item => item.path === route.path)
287
+ page.unclose = route.meta && route.meta.page && (route.meta.page.closable === false)
288
+ if (!page._init_) {
289
+ const vnode = this.$refs.tabContent.$vnode
290
+ page.cachedKey = vnode.key + vnode.componentOptions.Ctor.cid
291
+ page._init_ = true
292
+ }
293
+ },
294
+ /**
295
+ * 加载缓存的 tabs
296
+ */
297
+ loadCachedTabs () {
298
+ const cachedTabsStr = sessionStorage.getItem(process.env.VUE_APP_TBAS_KEY)
299
+ if (cachedTabsStr) {
300
+ try {
301
+ // 加载缓存的单页面
302
+ const allCachedPath = JSON.parse(cachedTabsStr).map(str => { return str.fullPath })
303
+ this.allSinglePages.map(item => {
304
+ if (allCachedPath.includes(item.fullPath)) {
305
+ // 如果缓存中有单页面 加载到 state.single
306
+ this.setSingle(item)
307
+ }
308
+ })
309
+ const cachedTabs = JSON.parse(cachedTabsStr)
310
+ if (cachedTabs.length > 0) {
311
+ this.pageList = cachedTabs
312
+ }
313
+ } catch (e) {
314
+ console.warn('failed to load cached tabs, got exception:', e)
315
+ } finally {
316
+ sessionStorage.removeItem(process.env.VUE_APP_TBAS_KEY)
317
+ }
318
+ }
319
+ },
320
+ loadCacheConfig (routes, pCache = true) {
321
+ routes.forEach(item => {
322
+ const cacheAble = item.meta?.page?.cacheAble ?? pCache ?? true
323
+ if (item.meta && item.meta.singlePage) {
324
+ // 找到所有单页面添加到 allSinglePages 中
325
+ this.allSinglePages.push(item)
326
+ }
327
+ if (!cacheAble) {
328
+ this.excludeKeys.push(new RegExp(`${item.path}\\d+$`))
329
+ }
330
+ if (item.children) {
331
+ this.loadCacheConfig(item.children, cacheAble)
332
+ }
333
+ })
334
+ },
335
+ ...mapMutations('setting', ['correctPageMinHeight']),
336
+ ...mapMutations('account', ['setSingle'])
337
+ }
338
+ }
339
+ </script>
340
+
341
+ <style scoped lang="less">
342
+ .tabs-view{
343
+ margin: -16px auto 8px;
344
+ &.head.fixed{
345
+ max-width: 1400px;
346
+ }
347
+ }
348
+ .tabs-view-content{
349
+ position: relative;
350
+ &.head.fixed{
351
+ width: 1400px;
352
+ margin: 0 auto;
353
+ }
354
+ }
355
+ </style>