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.
- package/assets/styles/base/_functions.scss +0 -0
- package/assets/styles/base/_mixins.scss +1 -1
- package/assets/styles/global/_button.scss +10 -17
- package/assets/styles/global/_form.scss +2 -2
- package/assets/styles/global/_labeled-input.scss +2 -6
- package/assets/styles/global/_select.scss +7 -6
- package/assets/styles/global/_table.scss +2 -3
- package/assets/styles/global/_tooltip.scss +1 -8
- package/assets/styles/themes/_dark.scss +0 -2
- package/assets/styles/themes/_light.scss +2 -5
- package/assets/styles/vendor/vue-select.scss +1 -2
- package/assets/translations/en-us.yaml +3 -1
- package/assets/translations/zh-hans.yaml +28 -51
- package/components/ActionDropdown.vue +0 -1
- package/components/ActionMenuShell.vue +3 -6
- package/components/BrandImage.vue +0 -22
- package/components/ClusterIconMenu.vue +1 -1
- package/components/CodeMirror.vue +0 -1
- package/components/CruResource.vue +1 -1
- package/components/CruResourceFooter.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +24 -4
- package/components/GlobalRoleBindings.vue +48 -112
- package/components/IndentedPanel.vue +10 -4
- package/components/PromptRemove.vue +3 -3
- package/components/ResourceDetail/Masthead.vue +242 -190
- package/components/ResourceDetail/index.vue +5 -20
- package/components/ResourceList/Masthead.vue +84 -146
- package/components/ResourceList/ResourceLoadingIndicator.vue +2 -5
- package/components/ResourceTable.vue +1 -76
- package/components/SideNav.vue +29 -66
- package/components/SortableTable/THead.vue +0 -6
- package/components/SortableTable/index.vue +388 -481
- package/components/Tabbed/index.vue +5 -4
- package/components/auth/Principal.vue +2 -3
- package/components/auth/RoleDetailEdit.vue +5 -58
- package/components/auth/SelectPrincipal.vue +0 -1
- package/components/form/BannerSettings.vue +16 -18
- package/components/form/ChangePassword.vue +4 -4
- package/components/form/ColorInput.vue +8 -32
- package/components/form/Footer.vue +1 -1
- package/components/form/InputWithSelect.vue +0 -2
- package/components/form/KeyValue.vue +7 -31
- package/components/form/LabeledSelect.vue +178 -178
- package/components/form/Members/ClusterPermissionsEditor.vue +2 -1
- package/components/form/Members/MembershipEditor.vue +1 -1
- package/components/form/NameNsDescription.vue +11 -24
- package/components/form/Password.vue +2 -6
- package/components/form/ResourceQuota/Namespace.vue +1 -1
- package/components/form/ResourceQuota/NamespaceRow.vue +10 -13
- package/components/form/ResourceQuota/ProjectRow.vue +1 -0
- package/components/form/Select.vue +2 -2
- package/components/nav/Favorite.vue +1 -5
- package/components/nav/Group.vue +23 -69
- package/components/nav/Header.vue +17 -82
- package/components/nav/HeaderPageActionMenu.vue +0 -1
- package/components/nav/NamespaceFilter.vue +3 -0
- package/components/nav/TopLevelMenu.vue +119 -182
- package/components/nav/Type.vue +11 -48
- package/components/rancherResourceDetail/Masthead.vue +769 -0
- package/components/rancherResourceDetail/__tests__/Masthead.test.ts +65 -0
- package/components/rancherResourceDetail/index.vue +591 -0
- package/components/rancherResourceList/Masthead.vue +375 -0
- package/components/rancherResourceList/ResourceLoadingIndicator.vue +140 -0
- package/components/rancherResourceList/index.vue +307 -0
- package/components/rancherResourceList/resource-list.config.js +7 -0
- package/components/rancherResourceTable.vue +783 -0
- package/components/rancherSortableTable/THead.vue +561 -0
- package/components/rancherSortableTable/actions.js +153 -0
- package/components/rancherSortableTable/advanced-filtering.js +272 -0
- package/components/rancherSortableTable/debug.js +117 -0
- package/components/rancherSortableTable/filtering.js +290 -0
- package/components/rancherSortableTable/grouping.js +48 -0
- package/components/rancherSortableTable/index.vue +2712 -0
- package/components/rancherSortableTable/paging.js +155 -0
- package/components/rancherSortableTable/selection.js +629 -0
- package/components/rancherSortableTable/sortable-config.ts +4 -0
- package/components/rancherSortableTable/sorting.js +129 -0
- package/composables/useClickOutside.ts +1 -1
- package/config/product/auth.js +7 -16
- package/config/product/explorer.js +1 -1
- package/config/product/settings.js +8 -17
- package/config/settings.ts +0 -28
- package/edit/management.cattle.io.user.vue +4 -17
- package/edit/networking.k8s.io.ingress/RulePath.vue +1 -1
- package/edit/token.vue +1 -1
- package/list/harvesterhci.io.management.cluster.vue +0 -17
- package/list/management.cattle.io.setting.vue +13 -22
- package/list/management.cattle.io.user.vue +14 -25
- package/list/provisioning.cattle.io.cluster.vue +7 -6
- package/mixins/brand.js +0 -17
- package/package.json +1 -1
- package/pages/auth/login.vue +29 -84
- package/pages/c/_cluster/auth/roles/index.vue +14 -61
- package/pages/c/_cluster/settings/banners.vue +101 -174
- package/pages/c/_cluster/settings/brand.vue +301 -348
- package/pages/c/_cluster/settings/performance.vue +38 -61
- package/pages/home.vue +21 -70
- package/pages/prefs.vue +23 -25
- package/pkg/tsconfig.json +9 -9
- package/pkg/vue.config.js +1 -1
- package/promptRemove/mixin/roleDeletionCheck.js +2 -2
- package/scripts/clean +0 -0
- package/scripts/extension/bundle +0 -0
- package/scripts/extension/helm/scripts/package +0 -0
- package/scripts/extension/helm/scripts/patch +0 -0
- package/scripts/extension/helm/scripts/version +0 -0
- package/scripts/extension/helmpatch +0 -0
- package/scripts/extension/parse-tag-name +0 -0
- package/scripts/extension/publish +0 -0
- package/scripts/publish-shell.sh +60 -86
- package/scripts/serve-pkgs +0 -0
- package/scripts/sync-shell-deps +0 -0
- package/scripts/typegen.sh +28 -44
- package/store/i18n.js +5 -5
- package/store/prefs.js +5 -17
- package/store/type-map.js +1 -2
- package/types/cloud-shell/index.d.ts +11014 -0
- package/types/shell/index.d.ts +1 -1
- package/utils/error.js +0 -4
- package/utils/router.js +3 -3
- package/vue.config.js +6 -1
- package/assets/images/action.svg +0 -6
- package/assets/images/pl/logo.png +0 -0
- /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>
|