vue2-client 1.5.13 → 1.5.15

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 (112) hide show
  1. package/CHANGELOG.md +513 -504
  2. package/index.js +30 -30
  3. package/package.json +78 -78
  4. package/src/base-client/components/common/AddressSearchCombobox/AddressSearchCombobox.vue +225 -225
  5. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +667 -667
  6. package/src/base-client/components/common/CreateQuery/CreateQueryItem.vue +713 -713
  7. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQuery.vue +457 -457
  8. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQueryItem.vue +508 -508
  9. package/src/base-client/components/common/FormGroupEdit/FormGroupEdit.vue +140 -140
  10. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +165 -165
  11. package/src/base-client/components/common/JSONToTree/jsontotree.vue +275 -275
  12. package/src/base-client/components/common/XAddForm/XAddForm.vue +316 -316
  13. package/src/base-client/components/common/XAddNativeForm/XAddNativeForm.vue +278 -277
  14. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  15. package/src/base-client/components/common/XForm/XForm.vue +185 -185
  16. package/src/base-client/components/common/XForm/XFormItem.vue +502 -485
  17. package/src/base-client/components/common/XForm/index.md +177 -177
  18. package/src/base-client/components/common/XFormTable/XFormTable.vue +325 -324
  19. package/src/base-client/components/common/XFormTable/index.md +96 -96
  20. package/src/base-client/components/common/XTable/XTable.vue +505 -505
  21. package/src/base-client/components/common/XTreeOne/XTreeOne.vue +109 -109
  22. package/src/base-client/components/system/QueryParamsDetailsView/QueryParamsDetailsView.vue +281 -281
  23. package/src/base-client/components/ticket/TicketDetailsView/TicketDetailsView.vue +807 -807
  24. package/src/base-client/components/ticket/TicketDetailsView/index.md +29 -29
  25. package/src/base-client/components/ticket/TicketDetailsView/part/TicketDetailsFlow.vue +260 -260
  26. package/src/base-client/components/ticket/TicketSubmitSuccessView/TicketSubmitSuccessView.vue +532 -532
  27. package/src/base-client/components/ticket/TicketSubmitSuccessView/index.md +29 -29
  28. package/src/base-client/plugins/AppData.js +70 -70
  29. package/src/base-client/plugins/GetLoginInfoService.js +179 -179
  30. package/src/base-client/plugins/PagedList.js +177 -177
  31. package/src/base-client/plugins/compatible/LoginServiceOA.js +20 -20
  32. package/src/base-client/plugins/i18n-extend.js +32 -32
  33. package/src/components/Ellipsis/Ellipsis.vue +65 -65
  34. package/src/components/Ellipsis/index.md +38 -38
  35. package/src/components/NumberInfo/index.md +43 -43
  36. package/src/components/STable/README.md +341 -341
  37. package/src/components/STable/index.js +318 -318
  38. package/src/components/Trend/index.md +45 -45
  39. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  40. package/src/components/checkbox/ImgCheckbox.vue +163 -163
  41. package/src/components/exception/ExceptionPage.vue +70 -70
  42. package/src/components/form/FormRow.vue +52 -52
  43. package/src/components/index.js +36 -36
  44. package/src/components/menu/SideMenu.vue +62 -62
  45. package/src/components/menu/menu.js +273 -273
  46. package/src/components/page/header/index.less +40 -40
  47. package/src/components/setting/Setting.vue +235 -235
  48. package/src/components/table/StandardTable.vue +141 -141
  49. package/src/components/table/advance/ActionColumns.vue +158 -158
  50. package/src/components/table/advance/SearchArea.vue +355 -355
  51. package/src/components/tool/AStepItem.vue +60 -60
  52. package/src/components/tool/AvatarList.vue +68 -68
  53. package/src/components/tool/Drawer.vue +142 -142
  54. package/src/components/tool/TagSelect.vue +83 -83
  55. package/src/components/transition/PageToggleTransition.vue +97 -97
  56. package/src/config/CreateQueryConfig.js +307 -307
  57. package/src/config/default/admin.config.js +18 -18
  58. package/src/config/default/setting.config.js +44 -44
  59. package/src/config/replacer/resolve.config.js +67 -67
  60. package/src/layouts/AdminLayout.vue +174 -174
  61. package/src/layouts/CommonLayout.vue +42 -42
  62. package/src/layouts/ComponentLayoutOne.vue +47 -47
  63. package/src/layouts/PageLayout.vue +151 -151
  64. package/src/layouts/SinglePageView.vue +111 -111
  65. package/src/layouts/footer/PageFooter.vue +49 -49
  66. package/src/layouts/header/AdminHeader.vue +104 -104
  67. package/src/layouts/header/HeaderAvatar.vue +61 -61
  68. package/src/layouts/header/HeaderNotice.vue +176 -176
  69. package/src/layouts/header/HeaderSearch.vue +67 -67
  70. package/src/layouts/header/InstitutionDetail.vue +181 -181
  71. package/src/layouts/header/index.less +92 -92
  72. package/src/layouts/tabs/TabsHead.vue +190 -190
  73. package/src/layouts/tabs/TabsView.vue +379 -379
  74. package/src/mock/goods/index.js +108 -108
  75. package/src/pages/exception/404.vue +25 -25
  76. package/src/pages/login/Login.vue +363 -363
  77. package/src/pages/report/ReportTable.js +124 -124
  78. package/src/pages/report/ReportTableHome.vue +28 -28
  79. package/src/pages/resourceManage/orgListManage.vue +98 -98
  80. package/src/pages/system/dictionary/index.vue +43 -43
  81. package/src/pages/system/file/index.vue +317 -317
  82. package/src/pages/system/monitor/loginInfor/index.vue +36 -36
  83. package/src/pages/system/monitor/operLog/index.vue +36 -36
  84. package/src/pages/system/queryParams/index.vue +43 -43
  85. package/src/router/async/config.async.js +27 -27
  86. package/src/router/async/router.map.js +68 -68
  87. package/src/router/guards.js +113 -113
  88. package/src/router/index.js +27 -27
  89. package/src/services/api/DictionaryDetailsViewApi.js +6 -6
  90. package/src/services/api/LogDetailsViewApi.js +10 -10
  91. package/src/services/api/QueryParamsDetailsViewApi.js +6 -6
  92. package/src/services/api/TicketDetailsViewApi.js +34 -34
  93. package/src/services/api/cas.js +79 -79
  94. package/src/services/api/commonTempTable.js +10 -10
  95. package/src/services/api/index.js +17 -17
  96. package/src/services/api/logininfor/index.js +6 -6
  97. package/src/services/api/manage.js +8 -8
  98. package/src/services/apiService.js +13 -13
  99. package/src/services/user.js +53 -53
  100. package/src/store/modules/index.js +4 -4
  101. package/src/theme/default/nprogress.less +76 -76
  102. package/src/theme/default/style.less +47 -47
  103. package/src/utils/colors.js +107 -107
  104. package/src/utils/excel/Blob.js +180 -180
  105. package/src/utils/excel/Export2Excel.js +141 -141
  106. package/src/utils/formatter.js +68 -68
  107. package/src/utils/i18n.js +80 -80
  108. package/src/utils/indexedDB.js +145 -145
  109. package/src/utils/theme-color-replacer-extend.js +91 -91
  110. package/src/utils/themeUtil.js +100 -100
  111. package/src/utils/util.js +230 -230
  112. package/vue.config.js +106 -106
@@ -1,379 +1,379 @@
1
- <template>
2
- <div id="TabsView">
3
- <admin-layout>
4
- <contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
5
- <tabs-head
6
- v-if="multiPage"
7
- :active="activePage"
8
- :page-list="pageList"
9
- @change="changePage"
10
- @close="remove"
11
- @refresh="refresh"
12
- @contextmenu="onContextmenu"
13
- />
14
- <div :class="['tabs-view-content', layout, pageWidth]" :style="`margin-top: ${multiPage ? -24 : 0}px`">
15
- <page-toggle-transition v-show="!$route.meta.singlePage" :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
16
- <a-keep-alive :exclude-keys="excludeKeys" v-if="multiPage && cachePage" v-model="clearCaches">
17
- <router-view v-if="!refreshing" ref="tabContent" :key="$route.path" />
18
- </a-keep-alive>
19
- <router-view ref="tabContent" v-else-if="!refreshing" />
20
- </page-toggle-transition>
21
- <template v-for="page in single">
22
- <single-page-view
23
- v-show="page.meta.singlePage === $route.meta.singlePage"
24
- :single-page-url="page.meta.singlePage"
25
- :full-path="page.fullPath"
26
- :key="page.meta.singlePage"
27
- @load="onSinglePageLoad" />
28
- </template>
29
- </div>
30
- </admin-layout>
31
- </div>
32
- </template>
33
-
34
- <script>
35
- import AdminLayout from '@vue2-client/layouts/AdminLayout'
36
- import Contextmenu from '@vue2-client/components/menu/Contextmenu'
37
- import PageToggleTransition from '@vue2-client/components/transition/PageToggleTransition'
38
- import { mapState, mapMutations } from 'vuex'
39
- import { getI18nKey } from '@vue2-client/utils/routerUtil'
40
- import AKeepAlive from '@vue2-client/components/cache/AKeepAlive'
41
- import TabsHead from '@vue2-client/layouts/tabs/TabsHead'
42
- import SinglePageView from '../SinglePageView'
43
-
44
- export default {
45
- name: 'TabsView',
46
- i18n: require('./i18n'),
47
- components: { SinglePageView, TabsHead, PageToggleTransition, Contextmenu, AdminLayout, AKeepAlive },
48
- data () {
49
- return {
50
- clearCaches: [],
51
- pageList: [],
52
- activePage: '',
53
- menuVisible: false,
54
- refreshing: false,
55
- allSinglePages: [],
56
- excludeKeys: []
57
- }
58
- },
59
- computed: {
60
- ...mapState('setting', ['multiPage', 'cachePage', 'animate', 'layout', 'pageWidth']),
61
- ...mapState('account', ['single']),
62
- menuItemList () {
63
- return [
64
- { key: '1', icon: 'vertical-right', text: this.$t('closeLeft') },
65
- { key: '2', icon: 'vertical-left', text: this.$t('closeRight') },
66
- { key: '3', icon: 'close', text: this.$t('closeOthers') },
67
- { key: '4', icon: 'sync', text: this.$t('refresh') }
68
- ]
69
- },
70
- tabsOffset () {
71
- return this.multiPage ? 24 : 0
72
- }
73
- },
74
- created () {
75
- this.loadCacheConfig(this.$router?.options?.routes)
76
- this.loadCachedTabs()
77
- const route = this.$route
78
- if (this.pageList.findIndex(item => item.path === route.path) === -1) {
79
- this.pageList.push(this.createPage(route))
80
- }
81
- this.activePage = route.path
82
- if (this.multiPage) {
83
- this.$nextTick(() => {
84
- this.setCachedKey(route)
85
- })
86
- this.addListener()
87
- }
88
- },
89
- mounted () {
90
- this.correctPageMinHeight(-this.tabsOffset)
91
- },
92
- beforeDestroy () {
93
- this.removeListener()
94
- this.correctPageMinHeight(this.tabsOffset)
95
- },
96
- watch: {
97
- '$router.options.routes': function (val) {
98
- this.excludeKeys = []
99
- this.loadCacheConfig(val)
100
- },
101
- '$route': function (newRoute) {
102
- this.activePage = newRoute.path
103
- const page = this.pageList.find(item => item.path === newRoute.path)
104
- if (!this.multiPage) {
105
- this.pageList = [this.createPage(newRoute)]
106
- } else if (page) {
107
- page.fullPath = newRoute.fullPath
108
- } else if (!page) {
109
- this.pageList.push(this.createPage(newRoute))
110
- }
111
- if (this.multiPage) {
112
- this.$nextTick(() => {
113
- this.setCachedKey(newRoute)
114
- })
115
- }
116
- },
117
- 'multiPage': function (newVal) {
118
- if (!newVal) {
119
- this.pageList = [this.createPage(this.$route)]
120
- this.removeListener()
121
- } else {
122
- this.addListener()
123
- }
124
- },
125
- tabsOffset (newVal, oldVal) {
126
- this.correctPageMinHeight(oldVal - newVal)
127
- }
128
- },
129
- methods: {
130
- changePage (key) {
131
- this.activePage = key
132
- const page = this.pageList.find(item => item.path === key)
133
- this.$router.push(page.fullPath)
134
- },
135
- remove (key, next) {
136
- this.setSingle({ path: key, delete: true })
137
- if (this.pageList.length === 1) {
138
- return this.$message.warning(this.$t('warn'))
139
- }
140
- // 清除缓存
141
- let index = this.pageList.findIndex(item => item.path === key)
142
- this.clearCaches = this.pageList.splice(index, 1).map(page => page.cachedKey)
143
- if (next) {
144
- this.$router.push(next)
145
- } else if (key === this.activePage) {
146
- index = index >= this.pageList.length ? this.pageList.length - 1 : index
147
- this.activePage = this.pageList[index].path
148
- this.$router.push(this.activePage)
149
- }
150
- },
151
- refresh (key, page) {
152
- page = page || this.pageList.find(item => item.path === key)
153
- page.loading = true
154
- // 单页面刷新
155
- const singlePage = this.allSinglePages.find(item => item.fullPath === key)
156
- if (singlePage) {
157
- window.frames[singlePage.fullPath].location.reload()
158
- return
159
- }
160
- this.clearCache(page)
161
- if (key === this.activePage) {
162
- // eslint-disable-next-line no-return-assign
163
- this.reloadContent(() => page.loading = false)
164
- } else {
165
- // 其实刷新很快,加这个延迟纯粹为了 loading 状态多展示一会儿,让用户感知刷新这一过程
166
- // eslint-disable-next-line no-return-assign
167
- setTimeout(() => page.loading = false, 500)
168
- }
169
- },
170
- onSinglePageLoad (fullPath) {
171
- const page = this.pageList.find(item => item.fullPath === fullPath)
172
- page.loading = false
173
- },
174
- onContextmenu (pageKey, e) {
175
- if (pageKey) {
176
- e.preventDefault()
177
- e.meta = pageKey
178
- this.menuVisible = true
179
- }
180
- },
181
- onMenuSelect (key, target, pageKey) {
182
- switch (key) {
183
- case '1': this.closeLeft(pageKey); break
184
- case '2': this.closeRight(pageKey); break
185
- case '3': this.closeOthers(pageKey); break
186
- case '4': this.refresh(pageKey); break
187
- default: break
188
- }
189
- },
190
- closeOthers (pageKey) {
191
- // 清除缓存
192
- const clearPages = this.pageList.filter(item => item.path !== pageKey && !item.unclose)
193
- this.clearCaches = clearPages.map(item => item.cachedKey)
194
- this.pageList = this.pageList.filter(item => !clearPages.includes(item))
195
- // 判断跳转
196
- if (this.activePage !== pageKey) {
197
- this.activePage = pageKey
198
- this.$router.push(this.activePage)
199
- }
200
- },
201
- closeLeft (pageKey) {
202
- const index = this.pageList.findIndex(item => item.path === pageKey)
203
- // 清除缓存
204
- const clearPages = this.pageList.filter((item, i) => i < index && !item.unclose)
205
- this.clearCaches = clearPages.map(item => item.cachedKey)
206
- this.pageList = this.pageList.filter(item => !clearPages.includes(item))
207
- // 判断跳转
208
- if (!this.pageList.find(item => item.path === this.activePage)) {
209
- this.activePage = pageKey
210
- this.$router.push(this.activePage)
211
- }
212
- },
213
- closeRight (pageKey) {
214
- // 清除缓存
215
- const index = this.pageList.findIndex(item => item.path === pageKey)
216
- const clearPages = this.pageList.filter((item, i) => i > index && !item.unclose)
217
- this.clearCaches = clearPages.map(item => item.cachedKey)
218
- this.pageList = this.pageList.filter(item => !clearPages.includes(item))
219
- // 判断跳转
220
- if (!this.pageList.find(item => item.path === this.activePage)) {
221
- this.activePage = pageKey
222
- this.$router.push(this.activePage)
223
- }
224
- },
225
- clearCache (page) {
226
- page._init_ = false
227
- this.clearCaches = [page.cachedKey]
228
- },
229
- reloadContent (onLoaded) {
230
- this.refreshing = true
231
- setTimeout(() => {
232
- this.refreshing = false
233
- this.$nextTick(() => {
234
- this.setCachedKey(this.$route)
235
- if (typeof onLoaded === 'function') {
236
- onLoaded.apply(this, [])
237
- }
238
- })
239
- }, 200)
240
- },
241
- pageName (page) {
242
- return this.$t(getI18nKey(page.keyPath))
243
- },
244
- /**
245
- * 添加监听器
246
- */
247
- addListener () {
248
- window.addEventListener('page:close', this.closePageListener)
249
- window.addEventListener('page:refresh', this.refreshPageListener)
250
- window.addEventListener('unload', this.unloadListener)
251
- },
252
- /**
253
- * 移出监听器
254
- */
255
- removeListener () {
256
- window.removeEventListener('page:close', this.closePageListener)
257
- window.removeEventListener('page:refresh', this.refreshPageListener)
258
- window.removeEventListener('unload', this.unloadListener)
259
- },
260
- /**
261
- * 页签关闭事件监听
262
- * @param event 页签关闭事件
263
- */
264
- closePageListener (event) {
265
- const { closeRoute, nextRoute } = event.detail
266
- const closePath = typeof closeRoute === 'string' ? closeRoute : closeRoute.path
267
- const path = closePath && closePath.split('?')[0]
268
- this.remove(path, nextRoute)
269
- },
270
- /**
271
- * 页面刷新事件监听
272
- * @param event 页签关闭事件
273
- */
274
- refreshPageListener (event) {
275
- const { pageKey } = event.detail
276
- const path = pageKey && pageKey.split('?')[0]
277
- this.refresh(path)
278
- },
279
- /**
280
- * 页面 unload 事件监听器,添加页签到 session 缓存,用于刷新时保留页签
281
- */
282
- unloadListener () {
283
- const tabs = this.pageList.map(item => ({ ...item, _init_: false }))
284
- sessionStorage.setItem(process.env.VUE_APP_TBAS_KEY, JSON.stringify(tabs))
285
- },
286
- createPage (route) {
287
- return {
288
- keyPath: route.matched[route.matched.length - 1].path,
289
- fullPath: route.fullPath,
290
- loading: false,
291
- path: route.path,
292
- title: route.meta && route.meta.page && route.meta.page.title,
293
- unclose: route.meta && route.meta.page && (route.meta.page.closable === false)
294
- }
295
- },
296
- /**
297
- * 设置页面缓存的key
298
- * @param route 页面对应的路由
299
- */
300
- setCachedKey (route) {
301
- const page = this.pageList.find(item => item.path === route.path)
302
- page.unclose = route.meta && route.meta.page && (route.meta.page.closable === false)
303
- if (!page._init_) {
304
- const vnode = this.$refs.tabContent.$vnode
305
- page.cachedKey = vnode.key + vnode.componentOptions.Ctor.cid
306
- page._init_ = true
307
- }
308
- },
309
- /**
310
- * 加载缓存的 tabs
311
- */
312
- loadCachedTabs () {
313
- const cachedTabsStr = sessionStorage.getItem(process.env.VUE_APP_TBAS_KEY)
314
- if (cachedTabsStr) {
315
- try {
316
- // 加载缓存的单页面
317
- const allCachedPath = JSON.parse(cachedTabsStr).map(str => { return str.fullPath })
318
- this.allSinglePages.map(item => {
319
- if (allCachedPath.includes(item.fullPath)) {
320
- // 如果缓存中有单页面 加载到 state.single
321
- this.setSingle(item)
322
- }
323
- })
324
- const cachedTabs = JSON.parse(cachedTabsStr)
325
- if (cachedTabs.length > 0) {
326
- this.pageList = cachedTabs
327
- }
328
- } catch (e) {
329
- console.warn('failed to load cached tabs, got exception:', e)
330
- } finally {
331
- sessionStorage.removeItem(process.env.VUE_APP_TBAS_KEY)
332
- }
333
- }
334
- },
335
- loadCacheConfig (routes, pCache = true) {
336
- routes.forEach(item => {
337
- const cacheAble = item.meta?.page?.cacheAble ?? pCache ?? true
338
- if (item.meta && item.meta.singlePage) {
339
- // 找到所有单页面添加到 allSinglePages 中
340
- this.allSinglePages.push(item)
341
- }
342
- if (!cacheAble) {
343
- this.excludeKeys.push(new RegExp(`${item.path}\\d+$`))
344
- }
345
- if (item.children) {
346
- this.loadCacheConfig(item.children, cacheAble)
347
- }
348
- })
349
- },
350
- ...mapMutations('setting', ['correctPageMinHeight']),
351
- ...mapMutations('account', ['setSingle'])
352
- }
353
- }
354
- </script>
355
-
356
- <style lang="less">
357
- #TabsView {
358
- .tabs-view {
359
- margin: -16px auto 8px;
360
- &.head.fixed {
361
- max-width: 100%;
362
- }
363
- }
364
- .tabs-view-content {
365
- position: relative;
366
- &.head.fixed {
367
- width: 100%;
368
- margin: 0 auto;
369
- }
370
- > div > div > .ant-card:first-of-type > .ant-card-body {
371
- max-height: calc(100vh - 64px - 48px - 24px);
372
- overflow-y: auto;
373
- &::-webkit-scrollbar {
374
- display: none;
375
- }
376
- }
377
- }
378
- }
379
- </style>
1
+ <template>
2
+ <div id="TabsView">
3
+ <admin-layout>
4
+ <contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
5
+ <tabs-head
6
+ v-if="multiPage"
7
+ :active="activePage"
8
+ :page-list="pageList"
9
+ @change="changePage"
10
+ @close="remove"
11
+ @refresh="refresh"
12
+ @contextmenu="onContextmenu"
13
+ />
14
+ <div :class="['tabs-view-content', layout, pageWidth]" :style="`margin-top: ${multiPage ? -24 : 0}px`">
15
+ <page-toggle-transition v-show="!$route.meta.singlePage" :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
16
+ <a-keep-alive :exclude-keys="excludeKeys" v-if="multiPage && cachePage" v-model="clearCaches">
17
+ <router-view v-if="!refreshing" ref="tabContent" :key="$route.path" />
18
+ </a-keep-alive>
19
+ <router-view ref="tabContent" v-else-if="!refreshing" />
20
+ </page-toggle-transition>
21
+ <template v-for="page in single">
22
+ <single-page-view
23
+ v-show="page.meta.singlePage === $route.meta.singlePage"
24
+ :single-page-url="page.meta.singlePage"
25
+ :full-path="page.fullPath"
26
+ :key="page.meta.singlePage"
27
+ @load="onSinglePageLoad" />
28
+ </template>
29
+ </div>
30
+ </admin-layout>
31
+ </div>
32
+ </template>
33
+
34
+ <script>
35
+ import AdminLayout from '@vue2-client/layouts/AdminLayout'
36
+ import Contextmenu from '@vue2-client/components/menu/Contextmenu'
37
+ import PageToggleTransition from '@vue2-client/components/transition/PageToggleTransition'
38
+ import { mapState, mapMutations } from 'vuex'
39
+ import { getI18nKey } from '@vue2-client/utils/routerUtil'
40
+ import AKeepAlive from '@vue2-client/components/cache/AKeepAlive'
41
+ import TabsHead from '@vue2-client/layouts/tabs/TabsHead'
42
+ import SinglePageView from '../SinglePageView'
43
+
44
+ export default {
45
+ name: 'TabsView',
46
+ i18n: require('./i18n'),
47
+ components: { SinglePageView, TabsHead, PageToggleTransition, Contextmenu, AdminLayout, AKeepAlive },
48
+ data () {
49
+ return {
50
+ clearCaches: [],
51
+ pageList: [],
52
+ activePage: '',
53
+ menuVisible: false,
54
+ refreshing: false,
55
+ allSinglePages: [],
56
+ excludeKeys: []
57
+ }
58
+ },
59
+ computed: {
60
+ ...mapState('setting', ['multiPage', 'cachePage', 'animate', 'layout', 'pageWidth']),
61
+ ...mapState('account', ['single']),
62
+ menuItemList () {
63
+ return [
64
+ { key: '1', icon: 'vertical-right', text: this.$t('closeLeft') },
65
+ { key: '2', icon: 'vertical-left', text: this.$t('closeRight') },
66
+ { key: '3', icon: 'close', text: this.$t('closeOthers') },
67
+ { key: '4', icon: 'sync', text: this.$t('refresh') }
68
+ ]
69
+ },
70
+ tabsOffset () {
71
+ return this.multiPage ? 24 : 0
72
+ }
73
+ },
74
+ created () {
75
+ this.loadCacheConfig(this.$router?.options?.routes)
76
+ this.loadCachedTabs()
77
+ const route = this.$route
78
+ if (this.pageList.findIndex(item => item.path === route.path) === -1) {
79
+ this.pageList.push(this.createPage(route))
80
+ }
81
+ this.activePage = route.path
82
+ if (this.multiPage) {
83
+ this.$nextTick(() => {
84
+ this.setCachedKey(route)
85
+ })
86
+ this.addListener()
87
+ }
88
+ },
89
+ mounted () {
90
+ this.correctPageMinHeight(-this.tabsOffset)
91
+ },
92
+ beforeDestroy () {
93
+ this.removeListener()
94
+ this.correctPageMinHeight(this.tabsOffset)
95
+ },
96
+ watch: {
97
+ '$router.options.routes': function (val) {
98
+ this.excludeKeys = []
99
+ this.loadCacheConfig(val)
100
+ },
101
+ '$route': function (newRoute) {
102
+ this.activePage = newRoute.path
103
+ const page = this.pageList.find(item => item.path === newRoute.path)
104
+ if (!this.multiPage) {
105
+ this.pageList = [this.createPage(newRoute)]
106
+ } else if (page) {
107
+ page.fullPath = newRoute.fullPath
108
+ } else if (!page) {
109
+ this.pageList.push(this.createPage(newRoute))
110
+ }
111
+ if (this.multiPage) {
112
+ this.$nextTick(() => {
113
+ this.setCachedKey(newRoute)
114
+ })
115
+ }
116
+ },
117
+ 'multiPage': function (newVal) {
118
+ if (!newVal) {
119
+ this.pageList = [this.createPage(this.$route)]
120
+ this.removeListener()
121
+ } else {
122
+ this.addListener()
123
+ }
124
+ },
125
+ tabsOffset (newVal, oldVal) {
126
+ this.correctPageMinHeight(oldVal - newVal)
127
+ }
128
+ },
129
+ methods: {
130
+ changePage (key) {
131
+ this.activePage = key
132
+ const page = this.pageList.find(item => item.path === key)
133
+ this.$router.push(page.fullPath)
134
+ },
135
+ remove (key, next) {
136
+ this.setSingle({ path: key, delete: true })
137
+ if (this.pageList.length === 1) {
138
+ return this.$message.warning(this.$t('warn'))
139
+ }
140
+ // 清除缓存
141
+ let index = this.pageList.findIndex(item => item.path === key)
142
+ this.clearCaches = this.pageList.splice(index, 1).map(page => page.cachedKey)
143
+ if (next) {
144
+ this.$router.push(next)
145
+ } else if (key === this.activePage) {
146
+ index = index >= this.pageList.length ? this.pageList.length - 1 : index
147
+ this.activePage = this.pageList[index].path
148
+ this.$router.push(this.activePage)
149
+ }
150
+ },
151
+ refresh (key, page) {
152
+ page = page || this.pageList.find(item => item.path === key)
153
+ page.loading = true
154
+ // 单页面刷新
155
+ const singlePage = this.allSinglePages.find(item => item.fullPath === key)
156
+ if (singlePage) {
157
+ window.frames[singlePage.fullPath].location.reload()
158
+ return
159
+ }
160
+ this.clearCache(page)
161
+ if (key === this.activePage) {
162
+ // eslint-disable-next-line no-return-assign
163
+ this.reloadContent(() => page.loading = false)
164
+ } else {
165
+ // 其实刷新很快,加这个延迟纯粹为了 loading 状态多展示一会儿,让用户感知刷新这一过程
166
+ // eslint-disable-next-line no-return-assign
167
+ setTimeout(() => page.loading = false, 500)
168
+ }
169
+ },
170
+ onSinglePageLoad (fullPath) {
171
+ const page = this.pageList.find(item => item.fullPath === fullPath)
172
+ page.loading = false
173
+ },
174
+ onContextmenu (pageKey, e) {
175
+ if (pageKey) {
176
+ e.preventDefault()
177
+ e.meta = pageKey
178
+ this.menuVisible = true
179
+ }
180
+ },
181
+ onMenuSelect (key, target, pageKey) {
182
+ switch (key) {
183
+ case '1': this.closeLeft(pageKey); break
184
+ case '2': this.closeRight(pageKey); break
185
+ case '3': this.closeOthers(pageKey); break
186
+ case '4': this.refresh(pageKey); break
187
+ default: break
188
+ }
189
+ },
190
+ closeOthers (pageKey) {
191
+ // 清除缓存
192
+ const clearPages = this.pageList.filter(item => item.path !== pageKey && !item.unclose)
193
+ this.clearCaches = clearPages.map(item => item.cachedKey)
194
+ this.pageList = this.pageList.filter(item => !clearPages.includes(item))
195
+ // 判断跳转
196
+ if (this.activePage !== pageKey) {
197
+ this.activePage = pageKey
198
+ this.$router.push(this.activePage)
199
+ }
200
+ },
201
+ closeLeft (pageKey) {
202
+ const index = this.pageList.findIndex(item => item.path === pageKey)
203
+ // 清除缓存
204
+ const clearPages = this.pageList.filter((item, i) => i < index && !item.unclose)
205
+ this.clearCaches = clearPages.map(item => item.cachedKey)
206
+ this.pageList = this.pageList.filter(item => !clearPages.includes(item))
207
+ // 判断跳转
208
+ if (!this.pageList.find(item => item.path === this.activePage)) {
209
+ this.activePage = pageKey
210
+ this.$router.push(this.activePage)
211
+ }
212
+ },
213
+ closeRight (pageKey) {
214
+ // 清除缓存
215
+ const index = this.pageList.findIndex(item => item.path === pageKey)
216
+ const clearPages = this.pageList.filter((item, i) => i > index && !item.unclose)
217
+ this.clearCaches = clearPages.map(item => item.cachedKey)
218
+ this.pageList = this.pageList.filter(item => !clearPages.includes(item))
219
+ // 判断跳转
220
+ if (!this.pageList.find(item => item.path === this.activePage)) {
221
+ this.activePage = pageKey
222
+ this.$router.push(this.activePage)
223
+ }
224
+ },
225
+ clearCache (page) {
226
+ page._init_ = false
227
+ this.clearCaches = [page.cachedKey]
228
+ },
229
+ reloadContent (onLoaded) {
230
+ this.refreshing = true
231
+ setTimeout(() => {
232
+ this.refreshing = false
233
+ this.$nextTick(() => {
234
+ this.setCachedKey(this.$route)
235
+ if (typeof onLoaded === 'function') {
236
+ onLoaded.apply(this, [])
237
+ }
238
+ })
239
+ }, 200)
240
+ },
241
+ pageName (page) {
242
+ return this.$t(getI18nKey(page.keyPath))
243
+ },
244
+ /**
245
+ * 添加监听器
246
+ */
247
+ addListener () {
248
+ window.addEventListener('page:close', this.closePageListener)
249
+ window.addEventListener('page:refresh', this.refreshPageListener)
250
+ window.addEventListener('unload', this.unloadListener)
251
+ },
252
+ /**
253
+ * 移出监听器
254
+ */
255
+ removeListener () {
256
+ window.removeEventListener('page:close', this.closePageListener)
257
+ window.removeEventListener('page:refresh', this.refreshPageListener)
258
+ window.removeEventListener('unload', this.unloadListener)
259
+ },
260
+ /**
261
+ * 页签关闭事件监听
262
+ * @param event 页签关闭事件
263
+ */
264
+ closePageListener (event) {
265
+ const { closeRoute, nextRoute } = event.detail
266
+ const closePath = typeof closeRoute === 'string' ? closeRoute : closeRoute.path
267
+ const path = closePath && closePath.split('?')[0]
268
+ this.remove(path, nextRoute)
269
+ },
270
+ /**
271
+ * 页面刷新事件监听
272
+ * @param event 页签关闭事件
273
+ */
274
+ refreshPageListener (event) {
275
+ const { pageKey } = event.detail
276
+ const path = pageKey && pageKey.split('?')[0]
277
+ this.refresh(path)
278
+ },
279
+ /**
280
+ * 页面 unload 事件监听器,添加页签到 session 缓存,用于刷新时保留页签
281
+ */
282
+ unloadListener () {
283
+ const tabs = this.pageList.map(item => ({ ...item, _init_: false }))
284
+ sessionStorage.setItem(process.env.VUE_APP_TBAS_KEY, JSON.stringify(tabs))
285
+ },
286
+ createPage (route) {
287
+ return {
288
+ keyPath: route.matched[route.matched.length - 1].path,
289
+ fullPath: route.fullPath,
290
+ loading: false,
291
+ path: route.path,
292
+ title: route.meta && route.meta.page && route.meta.page.title,
293
+ unclose: route.meta && route.meta.page && (route.meta.page.closable === false)
294
+ }
295
+ },
296
+ /**
297
+ * 设置页面缓存的key
298
+ * @param route 页面对应的路由
299
+ */
300
+ setCachedKey (route) {
301
+ const page = this.pageList.find(item => item.path === route.path)
302
+ page.unclose = route.meta && route.meta.page && (route.meta.page.closable === false)
303
+ if (!page._init_) {
304
+ const vnode = this.$refs.tabContent.$vnode
305
+ page.cachedKey = vnode.key + vnode.componentOptions.Ctor.cid
306
+ page._init_ = true
307
+ }
308
+ },
309
+ /**
310
+ * 加载缓存的 tabs
311
+ */
312
+ loadCachedTabs () {
313
+ const cachedTabsStr = sessionStorage.getItem(process.env.VUE_APP_TBAS_KEY)
314
+ if (cachedTabsStr) {
315
+ try {
316
+ // 加载缓存的单页面
317
+ const allCachedPath = JSON.parse(cachedTabsStr).map(str => { return str.fullPath })
318
+ this.allSinglePages.map(item => {
319
+ if (allCachedPath.includes(item.fullPath)) {
320
+ // 如果缓存中有单页面 加载到 state.single
321
+ this.setSingle(item)
322
+ }
323
+ })
324
+ const cachedTabs = JSON.parse(cachedTabsStr)
325
+ if (cachedTabs.length > 0) {
326
+ this.pageList = cachedTabs
327
+ }
328
+ } catch (e) {
329
+ console.warn('failed to load cached tabs, got exception:', e)
330
+ } finally {
331
+ sessionStorage.removeItem(process.env.VUE_APP_TBAS_KEY)
332
+ }
333
+ }
334
+ },
335
+ loadCacheConfig (routes, pCache = true) {
336
+ routes.forEach(item => {
337
+ const cacheAble = item.meta?.page?.cacheAble ?? pCache ?? true
338
+ if (item.meta && item.meta.singlePage) {
339
+ // 找到所有单页面添加到 allSinglePages 中
340
+ this.allSinglePages.push(item)
341
+ }
342
+ if (!cacheAble) {
343
+ this.excludeKeys.push(new RegExp(`${item.path}\\d+$`))
344
+ }
345
+ if (item.children) {
346
+ this.loadCacheConfig(item.children, cacheAble)
347
+ }
348
+ })
349
+ },
350
+ ...mapMutations('setting', ['correctPageMinHeight']),
351
+ ...mapMutations('account', ['setSingle'])
352
+ }
353
+ }
354
+ </script>
355
+
356
+ <style lang="less">
357
+ #TabsView {
358
+ .tabs-view {
359
+ margin: -16px auto 8px;
360
+ &.head.fixed {
361
+ max-width: 100%;
362
+ }
363
+ }
364
+ .tabs-view-content {
365
+ position: relative;
366
+ &.head.fixed {
367
+ width: 100%;
368
+ margin: 0 auto;
369
+ }
370
+ > div > div > .ant-card:first-of-type > .ant-card-body {
371
+ max-height: calc(100vh - 64px - 48px - 24px);
372
+ overflow-y: auto;
373
+ &::-webkit-scrollbar {
374
+ display: none;
375
+ }
376
+ }
377
+ }
378
+ }
379
+ </style>