dashboard-shell-shell 1.0.1000000117 → 1.0.1000000118

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 (124) hide show
  1. package/assets/styles/base/_functions.scss +0 -0
  2. package/assets/styles/base/_mixins.scss +1 -1
  3. package/assets/styles/global/_button.scss +10 -17
  4. package/assets/styles/global/_form.scss +2 -2
  5. package/assets/styles/global/_labeled-input.scss +2 -6
  6. package/assets/styles/global/_select.scss +7 -6
  7. package/assets/styles/global/_table.scss +2 -3
  8. package/assets/styles/global/_tooltip.scss +1 -8
  9. package/assets/styles/themes/_dark.scss +0 -2
  10. package/assets/styles/themes/_light.scss +2 -5
  11. package/assets/styles/vendor/vue-select.scss +1 -2
  12. package/assets/translations/en-us.yaml +3 -1
  13. package/assets/translations/zh-hans.yaml +28 -51
  14. package/components/ActionDropdown.vue +0 -1
  15. package/components/ActionMenuShell.vue +3 -6
  16. package/components/BrandImage.vue +0 -22
  17. package/components/ClusterIconMenu.vue +1 -1
  18. package/components/CodeMirror.vue +0 -1
  19. package/components/CruResource.vue +1 -1
  20. package/components/CruResourceFooter.vue +1 -1
  21. package/components/ExplorerProjectsNamespaces.vue +24 -4
  22. package/components/GlobalRoleBindings.vue +48 -112
  23. package/components/IndentedPanel.vue +10 -4
  24. package/components/PromptRemove.vue +3 -3
  25. package/components/ResourceDetail/Masthead.vue +242 -190
  26. package/components/ResourceDetail/index.vue +5 -20
  27. package/components/ResourceList/Masthead.vue +84 -146
  28. package/components/ResourceList/ResourceLoadingIndicator.vue +2 -5
  29. package/components/ResourceTable.vue +1 -76
  30. package/components/SideNav.vue +29 -66
  31. package/components/SortableTable/THead.vue +0 -6
  32. package/components/SortableTable/index.vue +388 -481
  33. package/components/Tabbed/index.vue +5 -4
  34. package/components/auth/Principal.vue +2 -3
  35. package/components/auth/RoleDetailEdit.vue +5 -58
  36. package/components/auth/SelectPrincipal.vue +0 -1
  37. package/components/form/BannerSettings.vue +16 -18
  38. package/components/form/ChangePassword.vue +4 -4
  39. package/components/form/ColorInput.vue +8 -32
  40. package/components/form/Footer.vue +1 -1
  41. package/components/form/InputWithSelect.vue +0 -2
  42. package/components/form/KeyValue.vue +7 -31
  43. package/components/form/LabeledSelect.vue +178 -178
  44. package/components/form/Members/ClusterPermissionsEditor.vue +2 -1
  45. package/components/form/Members/MembershipEditor.vue +1 -1
  46. package/components/form/NameNsDescription.vue +11 -24
  47. package/components/form/Password.vue +2 -6
  48. package/components/form/ResourceQuota/Namespace.vue +1 -1
  49. package/components/form/ResourceQuota/NamespaceRow.vue +10 -13
  50. package/components/form/ResourceQuota/ProjectRow.vue +1 -0
  51. package/components/form/Select.vue +2 -2
  52. package/components/nav/Favorite.vue +1 -5
  53. package/components/nav/Group.vue +23 -69
  54. package/components/nav/Header.vue +17 -82
  55. package/components/nav/HeaderPageActionMenu.vue +0 -1
  56. package/components/nav/NamespaceFilter.vue +3 -0
  57. package/components/nav/TopLevelMenu.vue +119 -182
  58. package/components/nav/Type.vue +11 -48
  59. package/components/rancherResourceDetail/Masthead.vue +769 -0
  60. package/components/rancherResourceDetail/__tests__/Masthead.test.ts +65 -0
  61. package/components/rancherResourceDetail/index.vue +591 -0
  62. package/components/rancherResourceList/Masthead.vue +375 -0
  63. package/components/rancherResourceList/ResourceLoadingIndicator.vue +140 -0
  64. package/components/rancherResourceList/index.vue +307 -0
  65. package/components/rancherResourceList/resource-list.config.js +7 -0
  66. package/components/rancherResourceTable.vue +783 -0
  67. package/components/rancherSortableTable/THead.vue +561 -0
  68. package/components/rancherSortableTable/actions.js +153 -0
  69. package/components/rancherSortableTable/advanced-filtering.js +272 -0
  70. package/components/rancherSortableTable/debug.js +117 -0
  71. package/components/rancherSortableTable/filtering.js +290 -0
  72. package/components/rancherSortableTable/grouping.js +48 -0
  73. package/components/rancherSortableTable/index.vue +2712 -0
  74. package/components/rancherSortableTable/paging.js +155 -0
  75. package/components/rancherSortableTable/selection.js +629 -0
  76. package/components/rancherSortableTable/sortable-config.ts +4 -0
  77. package/components/rancherSortableTable/sorting.js +129 -0
  78. package/composables/useClickOutside.ts +1 -1
  79. package/config/product/auth.js +7 -16
  80. package/config/product/explorer.js +1 -1
  81. package/config/product/settings.js +8 -17
  82. package/config/settings.ts +0 -28
  83. package/edit/management.cattle.io.user.vue +4 -17
  84. package/edit/networking.k8s.io.ingress/RulePath.vue +1 -1
  85. package/edit/token.vue +1 -1
  86. package/list/harvesterhci.io.management.cluster.vue +0 -17
  87. package/list/management.cattle.io.setting.vue +13 -22
  88. package/list/management.cattle.io.user.vue +14 -25
  89. package/list/provisioning.cattle.io.cluster.vue +7 -6
  90. package/mixins/brand.js +0 -17
  91. package/package.json +1 -1
  92. package/pages/auth/login.vue +29 -84
  93. package/pages/c/_cluster/auth/roles/index.vue +14 -61
  94. package/pages/c/_cluster/settings/banners.vue +101 -174
  95. package/pages/c/_cluster/settings/brand.vue +301 -348
  96. package/pages/c/_cluster/settings/performance.vue +38 -61
  97. package/pages/home.vue +21 -70
  98. package/pages/prefs.vue +23 -25
  99. package/pkg/tsconfig.json +9 -9
  100. package/pkg/vue.config.js +1 -1
  101. package/promptRemove/mixin/roleDeletionCheck.js +2 -2
  102. package/scripts/clean +0 -0
  103. package/scripts/extension/bundle +0 -0
  104. package/scripts/extension/helm/scripts/package +0 -0
  105. package/scripts/extension/helm/scripts/patch +0 -0
  106. package/scripts/extension/helm/scripts/version +0 -0
  107. package/scripts/extension/helmpatch +0 -0
  108. package/scripts/extension/parse-tag-name +0 -0
  109. package/scripts/extension/publish +0 -0
  110. package/scripts/publish-shell.sh +60 -86
  111. package/scripts/serve-pkgs +0 -0
  112. package/scripts/sync-shell-deps +0 -0
  113. package/scripts/typegen.sh +28 -44
  114. package/store/i18n.js +5 -5
  115. package/store/prefs.js +5 -17
  116. package/store/type-map.js +1 -2
  117. package/types/cloud-shell/index.d.ts +11014 -0
  118. package/types/shell/index.d.ts +1 -1
  119. package/utils/error.js +0 -4
  120. package/utils/router.js +3 -3
  121. package/vue.config.js +6 -1
  122. package/assets/images/action.svg +0 -6
  123. package/assets/images/pl/logo.png +0 -0
  124. /package/components/{ResourceList → rancherResourceList}/Masthead-btn.vue +0 -0
@@ -0,0 +1,783 @@
1
+ <script>
2
+ import { mapGetters } from 'vuex';
3
+ import { get } from '@shell/utils/object';
4
+ import { mapPref, GROUP_RESOURCES } from '@shell/store/prefs';
5
+ import ButtonGroup from '@shell/components/ButtonGroup';
6
+ import SortableTable from '@shell/components/rancherSortableTable';
7
+ import MastheadBtn from '@shell/components/rancherResourceList/Masthead-btn.vue';
8
+ import { NAMESPACE, AGE } from '@shell/config/table-headers';
9
+ import { findBy } from '@shell/utils/array';
10
+ import { ExtensionPoint, TableColumnLocation } from '@shell/core/types';
11
+ import { getApplicableExtensionEnhancements } from '@shell/core/plugin-helpers';
12
+
13
+ // Default group-by in the case the group stored in the preference does not apply
14
+ const DEFAULT_GROUP = 'namespace';
15
+
16
+ export const defaultTableSortGenerationFn = (schema, $store) => {
17
+ if ( !schema ) {
18
+ return null;
19
+ }
20
+
21
+ const resource = schema.id;
22
+ let sortKey = resource;
23
+
24
+ const inStore = $store.getters['currentStore'](resource);
25
+ const generation = $store.getters[`${ inStore }/currentGeneration`]?.(resource);
26
+
27
+ if ( generation ) {
28
+ sortKey += `/${ generation }`;
29
+ }
30
+
31
+ const nsFilterKey = $store.getters['activeNamespaceCacheKey'];
32
+
33
+ if ( nsFilterKey ) {
34
+ return `${ sortKey }/${ nsFilterKey }`;
35
+ }
36
+
37
+ // covers case where we have no current cluster's ns cache
38
+ return sortKey;
39
+ };
40
+
41
+ export default {
42
+
43
+ name: 'ResourceTable',
44
+
45
+ emits: ['clickedActionButton'],
46
+
47
+ components: { ButtonGroup, SortableTable, MastheadBtn },
48
+
49
+ props: {
50
+ schema: {
51
+ type: Object,
52
+ default: null,
53
+ },
54
+
55
+ schemaBtn: {
56
+ type: Object,
57
+ default: null,
58
+ },
59
+
60
+ rows: {
61
+ type: Array,
62
+ required: true
63
+ },
64
+
65
+ loading: {
66
+ type: Boolean,
67
+ required: false
68
+ },
69
+
70
+ altLoading: {
71
+ type: Boolean,
72
+ required: false
73
+ },
74
+
75
+ keyField: {
76
+ // Field that is unique for each row.
77
+ type: String,
78
+ default: '_key',
79
+ },
80
+
81
+ headers: {
82
+ type: Array,
83
+ default: null,
84
+ },
85
+
86
+ namespaced: {
87
+ type: Boolean,
88
+ default: null, // Automatic from schema
89
+ },
90
+
91
+ search: {
92
+ // Show search input to filter rows
93
+ type: Boolean,
94
+ default: true
95
+ },
96
+
97
+ tableActions: {
98
+ // Show bulk table actions
99
+ type: [Boolean, null],
100
+ default: null
101
+ },
102
+
103
+ pagingLabel: {
104
+ type: String,
105
+ default: 'sortableTable.paging.resource',
106
+ },
107
+
108
+ /**
109
+ * Additional params to pass to the pagingLabel translation
110
+ */
111
+ pagingParams: {
112
+ type: Object,
113
+ default: null,
114
+ },
115
+
116
+ rowActions: {
117
+ type: Boolean,
118
+ default: true,
119
+ },
120
+
121
+ /**
122
+ * Field to group rows by, row[groupBy] must be something that can be a map key
123
+ */
124
+ groupBy: {
125
+ type: String,
126
+ default: null
127
+ },
128
+
129
+ /**
130
+ * Override any product based group options
131
+ */
132
+ groupOptions: {
133
+ type: Array,
134
+ default: null
135
+ },
136
+
137
+ groupable: {
138
+ type: Boolean,
139
+ default: null, // Null: auto based on namespaced and type custom groupings
140
+ },
141
+
142
+ /**
143
+ * If the current preference for group isn't applicable, or not set, use this instead
144
+ */
145
+ groupDefault: {
146
+ type: String,
147
+ default: DEFAULT_GROUP,
148
+ },
149
+
150
+ groupTooltip: {
151
+ type: String,
152
+ default: 'resourceTable.groupBy.namespace',
153
+ },
154
+
155
+ overflowX: {
156
+ type: Boolean,
157
+ default: false
158
+ },
159
+ overflowY: {
160
+ type: Boolean,
161
+ default: false
162
+ },
163
+ sortGenerationFn: {
164
+ type: Function,
165
+ default: null,
166
+ },
167
+ getCustomDetailLink: {
168
+ type: Function,
169
+ default: null
170
+ },
171
+ ignoreFilter: {
172
+ type: Boolean,
173
+ default: false
174
+ },
175
+ hasAdvancedFiltering: {
176
+ type: Boolean,
177
+ default: false
178
+ },
179
+ advFilterHideLabelsAsCols: {
180
+ type: Boolean,
181
+ default: false
182
+ },
183
+ advFilterPreventFilteringLabels: {
184
+ type: Boolean,
185
+ default: false
186
+ },
187
+ /**
188
+ * Allows for the usage of a query param to work for simple filtering (q)
189
+ */
190
+ useQueryParamsForSimpleFiltering: {
191
+ type: Boolean,
192
+ default: false
193
+ },
194
+ /**
195
+ * Manual force the update of live and delayed cells. Change this number to kick off the update
196
+ */
197
+ forceUpdateLiveAndDelayed: {
198
+ type: Number,
199
+ default: 0
200
+ },
201
+
202
+ externalPaginationEnabled: {
203
+ type: Boolean,
204
+ default: false
205
+ },
206
+
207
+ externalPaginationResult: {
208
+ type: Object,
209
+ default: null
210
+ },
211
+
212
+ rowsPerPage: {
213
+ type: Number,
214
+ default: null, // Default comes from the user preference
215
+ },
216
+ searchPlaceholder: {
217
+ // search框内的输入提示
218
+ type: String,
219
+ default: '名称'
220
+ },
221
+
222
+ resource: {
223
+ type: String,
224
+ required: true,
225
+ },
226
+
227
+ typeDisplay: {
228
+ type: String,
229
+ default: null,
230
+ },
231
+ isCreatable: {
232
+ type: Boolean,
233
+ default: null,
234
+ },
235
+ isYamlCreatable: {
236
+ type: Boolean,
237
+ default: null,
238
+ },
239
+ createLocation: {
240
+ type: Object,
241
+ default: null,
242
+ },
243
+ yamlCreateLocation: {
244
+ type: Object,
245
+ default: null,
246
+ },
247
+ createButtonLabel: {
248
+ type: String,
249
+ default: null
250
+ },
251
+ /**
252
+ * Inherited global identifier prefix for tests
253
+ * Define a term based on the parent component to avoid conflicts on multiple components
254
+ */
255
+ componentTestid: {
256
+ type: String,
257
+ default: 'masthead'
258
+ },
259
+
260
+ mainButtonVisible: {
261
+ type: Boolean,
262
+ default: false
263
+ },
264
+ },
265
+
266
+ data() {
267
+ // Confirm which store we're in, if schema isn't available we're probably showing a list with different types
268
+ const inStore = this.schema?.id ? this.$store.getters['currentStore'](this.schema.id) : undefined;
269
+
270
+ return {
271
+ inStore,
272
+ /**
273
+ * Override the sortGenerationFn given changes in the rows we pass through to sortable table
274
+ *
275
+ * Primary purpose is to directly connect an iteration of `rows` with a sortGeneration string. This avoids
276
+ * reactivity issues where `rows` hasn't yet changed but something like workspaces has (stale values stored against fresh key)
277
+ */
278
+ sortGeneration: undefined
279
+ };
280
+ },
281
+
282
+ watch: {
283
+ filteredRows: {
284
+ handler() {
285
+ // This is only prevalent in fleet world and the workspace switcher
286
+ // - it's singular (a --> b --> c) instead of namespace switchers additive (a --> a+b --> a)
287
+ // - this means it's much more likely to switch between resource sets containing the same mount of rows
288
+ //
289
+ if (this.currentProduct.showWorkspaceSwitcher) {
290
+ this.sortGeneration = this.safeSortGenerationFn(this.schema, this.$store);
291
+ }
292
+ },
293
+ immediate: true
294
+ }
295
+ },
296
+
297
+ computed: {
298
+ options() {
299
+ return this.$store.getters[`type-map/optionsFor`](this.schema, this.externalPaginationEnabled);
300
+ },
301
+
302
+ _listGroupMapped() {
303
+ return this.options?.listGroups?.reduce((acc, grp) => {
304
+ acc[grp.value] = grp;
305
+
306
+ return acc;
307
+ }, {});
308
+ },
309
+
310
+ _mandatorySort() {
311
+ return this.options?.listMandatorySort;
312
+ },
313
+
314
+ ...mapGetters(['currentProduct']),
315
+
316
+ isNamespaced() {
317
+ if ( this.namespaced !== null ) {
318
+ return this.namespaced;
319
+ }
320
+
321
+ return !!get( this.schema, 'attributes.namespaced');
322
+ },
323
+
324
+ showNamespaceColumn() {
325
+ const groupNamespaces = this.group === 'namespace';
326
+ const out = !this.showGrouping || !groupNamespaces;
327
+
328
+ return out;
329
+ },
330
+
331
+ _showBulkActions() {
332
+ if (this.tableActions !== null) {
333
+ return this.tableActions;
334
+ } else if (this.schema) {
335
+ const hideTableActions = this.$store.getters['type-map/hideBulkActionsFor'](this.schema);
336
+
337
+ return !hideTableActions;
338
+ }
339
+
340
+ return false;
341
+ },
342
+
343
+ _headers() {
344
+ let headers;
345
+ const showNamespace = this.showNamespaceColumn;
346
+
347
+ if ( this.headers ) {
348
+ headers = this.headers.slice();
349
+ } else {
350
+ headers = this.$store.getters['type-map/headersFor'](this.schema, this.externalPaginationEnabled);
351
+ }
352
+
353
+ // add custom table columns provided by the extensions ExtensionPoint.TABLE_COL hook
354
+ // gate it so that we prevent errors on older versions of dashboard
355
+ if (this.$store.$plugin?.getUIConfig) {
356
+ const extensionCols = getApplicableExtensionEnhancements(this, ExtensionPoint.TABLE_COL, TableColumnLocation.RESOURCE, this.$route);
357
+
358
+ // Try and insert the columns before the Age column
359
+ let insertPosition = headers.length;
360
+
361
+ if (headers.length > 0) {
362
+ const ageColIndex = headers.findIndex((h) => h.name === AGE.name);
363
+
364
+ if (ageColIndex >= 0) {
365
+ insertPosition = ageColIndex;
366
+ } else {
367
+ // we've found some labels with ' ', which isn't necessarily empty (explore action/button)
368
+ // if we are to add cols, let's push them before these so that the UI doesn't look weird
369
+ const lastViableColIndex = headers.findIndex((h) => (!h.label || !h.label?.trim()) && (!h.labelKey || !h.labelKey?.trim()));
370
+
371
+ if (lastViableColIndex >= 0) {
372
+ insertPosition = lastViableColIndex;
373
+ }
374
+ }
375
+ }
376
+
377
+ // adding extension defined cols to the correct header config
378
+ extensionCols.forEach((col) => {
379
+ // we need the 'value' prop to be populated in order for the rows to show the values
380
+ if (!col.value && col.getValue) {
381
+ col.value = col.getValue;
382
+ }
383
+ headers.splice(insertPosition, 0, col);
384
+ });
385
+ }
386
+
387
+ // If only one namespace is selected, hide the namespace column
388
+ if ( !showNamespace ) {
389
+ const idx = headers.findIndex((header) => header.name === NAMESPACE.name);
390
+
391
+ if ( idx >= 0 ) {
392
+ headers.splice(idx, 1);
393
+ }
394
+ }
395
+
396
+ // If we are grouping by a custom group, it may specify that we hide a specific column
397
+ const custom = this._listGroupMapped?.[this.group];
398
+
399
+ let hideColumn;
400
+
401
+ if (custom?.hideColumn) {
402
+ hideColumn = custom.hideColumn;
403
+ } else {
404
+ const componentCustom = this.groupOptions?.find((go) => go.value === this.group);
405
+
406
+ hideColumn = componentCustom?.hideColumn;
407
+ }
408
+
409
+ if (hideColumn) {
410
+ const idx = headers.findIndex((header) => header.name === hideColumn);
411
+
412
+ if ( idx >= 0 ) {
413
+ headers.splice(idx, 1);
414
+ }
415
+ }
416
+
417
+ return headers;
418
+ },
419
+
420
+ /**
421
+ * Take rows and filter out entries given the namespace filter
422
+ */
423
+ filteredRows() {
424
+ const isAll = this.$store.getters['isAllNamespaces'];
425
+
426
+ // Do we need to filter by namespace like things?
427
+ if (
428
+ !this.isNamespaced || // Resource type isn't namespaced
429
+ this.ignoreFilter || // Component owner strictly states no filtering
430
+ this.externalPaginationEnabled ||
431
+ (isAll && !this.currentProduct?.hideSystemResources) || // Need all
432
+ (this.inStore ? this.$store.getters[`${ this.inStore }/haveNamespace`](this.schema.id)?.length : false)// Store reports type has namespace filter, so rows already contain the correctly filtered resources
433
+ ) {
434
+ return this.rows || [];
435
+ }
436
+
437
+ const includedNamespaces = this.$store.getters['namespaces']();
438
+
439
+ // Shouldn't happen, but does for resources like management.cattle.io.preference
440
+ if (!this.rows) {
441
+ return [];
442
+ }
443
+
444
+ const haveAllNamespace = this.$store.getters['haveAllNamespace'];
445
+
446
+ return this.rows.filter((row) => {
447
+ if (this.currentProduct?.hideSystemResources && this.isNamespaced) {
448
+ return !!includedNamespaces[row.metadata.namespace] && !row.isSystemResource;
449
+ } else if (!this.isNamespaced) {
450
+ return true;
451
+ } else if (haveAllNamespace) {
452
+ // `rows` only contains resource from a single namespace
453
+ return true;
454
+ } else {
455
+ return !!includedNamespaces[row.metadata.namespace];
456
+ }
457
+ });
458
+ },
459
+
460
+ _group: mapPref(GROUP_RESOURCES),
461
+
462
+ // The group stored in the preference (above) might not be valid for this resource table - so ensure we
463
+ // choose a group that is applicable (the default)
464
+ // This saves us from having to store a group preference per resource type - given that custom groupings aer not used much
465
+ // and it feels like a good UX to be able to keep the namespace/flat grouping across tables
466
+ group: {
467
+ get() {
468
+ // Check group is valid
469
+ const exists = this._groupOptions.find((g) => g.value === this._group);
470
+
471
+ if (!exists) {
472
+ // Attempt to find the default option in available options...
473
+ // if not use the first value in the options collection...
474
+ // and if not that just fall back to the default
475
+ if (this._groupOptions.find((g) => g.value === this.groupDefault)) {
476
+ return this.groupDefault;
477
+ }
478
+
479
+ return this._groupOptions[0]?.value || this.groupDefault || DEFAULT_GROUP;
480
+ }
481
+
482
+ return this._group;
483
+ },
484
+ set(value) {
485
+ console.log('_group:===='+value);
486
+
487
+ this._group = value;
488
+ }
489
+ },
490
+
491
+ showGrouping() {
492
+ if ( this.groupable === null ) {
493
+ const namespaceGroupable = this.$store.getters['isMultipleNamespaces'] && this.isNamespaced;
494
+ const customGroupable = !!this.options?.listGroups?.length;
495
+
496
+ // sshkey去掉分组按钮
497
+ if(this.parsedPagingParams.singularLabel === 'SSH Key' || this.parsedPagingParams.singularLabel === '负载均衡器'){
498
+ return false
499
+ }
500
+
501
+ return namespaceGroupable || customGroupable || this.groupOptions?.length;
502
+ }
503
+ return this.groupable || false;
504
+ },
505
+
506
+ computedGroupBy() {
507
+ // If we're not showing grouping options we shouldn't have a group by property
508
+ if (!this.showGrouping) {
509
+ return null;
510
+ }
511
+
512
+ if ( this.groupBy ) {
513
+ // This probably comes from the type-map config for the resource (see ResourceList)
514
+ return this.groupBy;
515
+ }
516
+
517
+ if ( this.group === 'namespace' ) {
518
+ // This switches to group rows by a key which is the label for the group (??)
519
+ return 'groupByLabel';
520
+ }
521
+
522
+ const custom = this._listGroupMapped?.[this.group];
523
+
524
+ if (custom?.field) {
525
+ // Override the normal filtering
526
+ return custom.field;
527
+ }
528
+
529
+ const componentCustom = this.groupOptions?.find((go) => go.value === this.group);
530
+
531
+ if (componentCustom?.field) {
532
+ return componentCustom.field;
533
+ }
534
+
535
+ return null;
536
+ },
537
+
538
+ _groupOptions() {
539
+ if (this.groupOptions) {
540
+ return this.groupOptions;
541
+ }
542
+
543
+ // Ignore the defaults below, we have an override set of groups
544
+ // REPLACE (instead of SUPPLEMENT) defaults with listGroups (given listGroupsWillOverride is true)
545
+ if (this.options?.listGroupsWillOverride && !!this.options?.listGroups?.length) {
546
+ return this.options?.listGroups;
547
+ }
548
+
549
+ const standard = [
550
+ {
551
+ tooltipKey: 'resourceTable.groupBy.none',
552
+ icon: 'icon-list-flat',
553
+ value: 'none',
554
+ }
555
+ ];
556
+
557
+ if (!this.options?.hiddenNamespaceGroupButton) {
558
+ standard.push( {
559
+ tooltipKey: this.groupTooltip,
560
+ icon: 'icon-folder',
561
+ value: 'namespace',
562
+ });
563
+ }
564
+
565
+ // SUPPLEMENT (instead of REPLACE) defaults with listGroups (given listGroupsWillOverride is false)
566
+ if (!!this.options?.listGroups?.length) {
567
+ return standard.concat(this.options.listGroups);
568
+ }
569
+
570
+ return standard;
571
+ },
572
+
573
+ parsedPagingParams() {
574
+ if (this.pagingParams) {
575
+ return this.pagingParams;
576
+ }
577
+
578
+ if ( !this.schema ) {
579
+ return {
580
+ singularLabel: '',
581
+ pluralLabel: ''
582
+ };
583
+ }
584
+
585
+ return {
586
+ singularLabel: this.$store.getters['type-map/labelFor'](this.schema),
587
+ pluralLabel: this.$store.getters['type-map/labelFor'](this.schema, 99),
588
+ };
589
+ },
590
+
591
+ },
592
+
593
+ methods: {
594
+ keyAction(action) {
595
+ const table = this.$refs.table;
596
+
597
+ if ( !table ) {
598
+ return;
599
+ }
600
+
601
+ const selection = table.selectedRows;
602
+
603
+ if ( action === 'remove' ) {
604
+ const act = findBy(table.availableActions, 'action', 'promptRemove');
605
+
606
+ if ( act ) {
607
+ table.setBulkActionOfInterest(act);
608
+ table.applyTableAction(act);
609
+ }
610
+
611
+ return;
612
+ }
613
+
614
+ if ( selection.length !== 1 ) {
615
+ return;
616
+ }
617
+
618
+ switch ( action ) {
619
+ case 'detail':
620
+ selection[0].goToDetail();
621
+ break;
622
+ case 'edit':
623
+ selection[0].goToEdit();
624
+ break;
625
+ case 'yaml':
626
+ selection[0].goToViewYaml();
627
+ break;
628
+ }
629
+ },
630
+
631
+ clearSelection() {
632
+ this.$refs.table.clearSelection();
633
+ },
634
+
635
+ safeSortGenerationFn() {
636
+ if (this.sortGenerationFn) {
637
+ return this.sortGenerationFn(this.schema, this.$store);
638
+ }
639
+
640
+ return defaultTableSortGenerationFn(this.schema, this.$store);
641
+ },
642
+
643
+ handleActionButtonClick(event) {
644
+ this.$emit('clickedActionButton', event);
645
+ },
646
+
647
+ handleEnterKeyPress(event) {
648
+ if (event.key === 'Enter') {
649
+ this.keyAction('detail');
650
+ }
651
+ }
652
+ },
653
+ };
654
+ </script>
655
+
656
+ <template>
657
+ <SortableTable
658
+ ref="table"
659
+ v-bind="$attrs"
660
+ :headers="_headers"
661
+ :rows="filteredRows"
662
+ :loading="loading"
663
+ :search-placeholder="searchPlaceholder"
664
+ :alt-loading="altLoading"
665
+ :group-by="computedGroupBy"
666
+ :group="group"
667
+ :group-options="_groupOptions"
668
+ :search="search"
669
+ :paging="true"
670
+ :paging-params="parsedPagingParams"
671
+ :paging-label="pagingLabel"
672
+ :rows-per-page="rowsPerPage"
673
+ :row-actions="rowActions"
674
+ :table-actions="_showBulkActions"
675
+ :overflow-x="overflowX"
676
+ :overflow-y="overflowY"
677
+ :get-custom-detail-link="getCustomDetailLink"
678
+ :has-advanced-filtering="hasAdvancedFiltering"
679
+ :adv-filter-hide-labels-as-cols="advFilterHideLabelsAsCols"
680
+ :adv-filter-prevent-filtering-labels="advFilterPreventFilteringLabels"
681
+ :key-field="keyField"
682
+ :sortGeneration="sortGeneration"
683
+ :sort-generation-fn="safeSortGenerationFn"
684
+ :use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
685
+ :force-update-live-and-delayed="forceUpdateLiveAndDelayed"
686
+ :external-pagination-enabled="externalPaginationEnabled"
687
+ :external-pagination-result="externalPaginationResult"
688
+ :mandatory-sort="_mandatorySort"
689
+ @clickedActionButton="handleActionButtonClick"
690
+ @group-value-change="group = $event"
691
+ @enter="handleEnterKeyPress"
692
+ >
693
+
694
+ <template #search-main-button>
695
+ <MastheadBtn
696
+ v-if="mainButtonVisible"
697
+ :schema="schemaBtn"
698
+ :resource="resource"
699
+ :create-button-label="createButtonLabel"
700
+ :yaml-create-button-label="yamlCreateLocation"
701
+ :create-location="createLocation"
702
+ :is-yaml-creatable="isYamlCreatable"
703
+ :is-creatable="isCreatable"
704
+ :type-display="typeDisplay"
705
+ :component-testid="componentTestid"
706
+ >
707
+
708
+ <template #extraActions>
709
+ <slot name="extraActions" />
710
+ </template>
711
+
712
+ <template #createButton>
713
+ <slot name="createButton" />
714
+ </template>
715
+
716
+ </ MastheadBtn>
717
+ </template>
718
+
719
+ <template
720
+ v-if="showGrouping && _groupOptions.length > 1"
721
+ #header-middle
722
+ >
723
+ <slot name="more-header-middle" />
724
+
725
+ <ButtonGroup
726
+ v-model:value="group"
727
+ :options="_groupOptions"
728
+ v-if="parsedPagingParams.singularLabel !== 'PCI设备'"
729
+ />
730
+ </template>
731
+
732
+ <template
733
+ v-if="showGrouping"
734
+ #header-right
735
+ >
736
+ <slot name="header-right" />
737
+ </template>
738
+
739
+ <template #group-by="{group: thisGroup}">
740
+ <div
741
+ v-clean-html="thisGroup.ref"
742
+ class="group-tab"
743
+ />
744
+ </template>
745
+
746
+ <!-- Pass down templates provided by the caller -->
747
+ <template
748
+ v-for="(_, slot) of $slots"
749
+ :key="slot"
750
+ v-slot:[slot]="scope"
751
+ >
752
+ <slot
753
+ :name="slot"
754
+ v-bind="scope"
755
+ />
756
+ </template>
757
+
758
+ <template #shortkeys>
759
+ <button
760
+ v-shortkey.once="['e']"
761
+ class="hide"
762
+ @shortkey="keyAction('edit')"
763
+ />
764
+ <!-- <button
765
+ v-shortkey.once="['y']"
766
+ class="hide"
767
+ @shortkey="keyAction('yaml')"
768
+ /> -->
769
+ <button
770
+ v-if="_showBulkActions"
771
+ v-shortkey.once="['del']"
772
+ class="hide"
773
+ @shortkey="keyAction('remove')"
774
+ />
775
+ <button
776
+ v-if="_showBulkActions"
777
+ v-shortkey.once="['backspace']"
778
+ class="hide"
779
+ @shortkey="keyAction('remove')"
780
+ />
781
+ </template>
782
+ </SortableTable>
783
+ </template>