dashboard-shell-shell 3.0.5-test.4 → 3.0.5-test.6

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 (198) hide show
  1. package/assets/brand/harvester/favicon.png +0 -0
  2. package/assets/brand/suse/favicon.png +0 -0
  3. package/assets/images/pl/half-logo.svg +23 -2
  4. package/assets/images/pl/harvester.png +0 -0
  5. package/assets/styles/app.scss +0 -4
  6. package/assets/styles/base/_basic.scss +2 -2
  7. package/assets/styles/base/_mixins.scss +1 -1
  8. package/assets/styles/base/_typography.scss +1 -2
  9. package/assets/styles/base/_variables.scss +4 -11
  10. package/assets/styles/global/_button.scss +27 -45
  11. package/assets/styles/global/_columns.scss +1 -3
  12. package/assets/styles/global/_form.scss +13 -45
  13. package/assets/styles/global/_labeled-input.scss +26 -54
  14. package/assets/styles/global/_layout.scss +3 -8
  15. package/assets/styles/global/_select.scss +17 -25
  16. package/assets/styles/global/_table.scss +1 -7
  17. package/assets/styles/global/_tooltip.scss +6 -54
  18. package/assets/styles/themes/_dark.scss +0 -3
  19. package/assets/styles/themes/_light.scss +42 -63
  20. package/assets/styles/vendor/vue-select.scss +9 -22
  21. package/assets/translations/en-us.yaml +4 -28
  22. package/assets/translations/zh-hans.yaml +189 -376
  23. package/components/ActionDropdown.vue +1 -2
  24. package/components/ActionMenu.vue +2 -2
  25. package/components/ActionMenuShell.vue +0 -2
  26. package/components/AppModal.vue +5 -46
  27. package/components/BrandImage.vue +0 -1
  28. package/components/ButtonDropdown.vue +4 -26
  29. package/components/ButtonMultiAction.vue +0 -1
  30. package/components/ClusterIconMenu.vue +1 -1
  31. package/components/CodeMirror.vue +6 -20
  32. package/components/ConsumptionGauge.vue +5 -24
  33. package/components/CruResource.vue +8 -9
  34. package/components/CruResourceFooter.vue +2 -2
  35. package/components/DashboardOptions.vue +17 -29
  36. package/components/ExplorerProjectsNamespaces.vue +5 -19
  37. package/components/GlobalRoleBindings.vue +48 -112
  38. package/components/GrafanaDashboard.vue +4 -4
  39. package/components/GrowlManager.vue +1 -3
  40. package/components/HardwareResourceGauge.vue +3 -39
  41. package/components/IndentedPanel.vue +10 -4
  42. package/components/InfoBox.vue +3 -3
  43. package/components/InputOrDisplay.vue +2 -28
  44. package/components/LabelValue.vue +1 -20
  45. package/components/ModalWithCard.vue +3 -12
  46. package/components/PodSecurityAdmission.vue +1 -1
  47. package/components/PromptModal.vue +1 -1
  48. package/components/PromptRemove.vue +11 -30
  49. package/components/ResourceDetail/Masthead/legacy.vue +38 -181
  50. package/components/ResourceDetail/legacy.vue +13 -29
  51. package/components/ResourceList/Masthead.vue +54 -226
  52. package/components/ResourceList/ResourceLoadingIndicator.vue +2 -5
  53. package/components/ResourceTable.vue +2 -24
  54. package/components/SideNav.vue +20 -74
  55. package/components/SortableTable/THead.vue +3 -33
  56. package/components/SortableTable/index.vue +464 -1017
  57. package/components/SortableTable/paging.js +16 -26
  58. package/components/SortableTable/selection.js +2 -2
  59. package/components/Tabbed/Tab.vue +3 -3
  60. package/components/Tabbed/index.vue +29 -47
  61. package/components/YamlEditor.vue +1 -0
  62. package/components/auth/Principal.vue +12 -36
  63. package/components/auth/RoleDetailEdit.vue +7 -58
  64. package/components/auth/SelectPrincipal.vue +0 -1
  65. package/components/form/ArrayList.vue +33 -41
  66. package/components/form/ArrayListGrouped.vue +2 -10
  67. package/components/form/ArrayListSelect.vue +1 -1
  68. package/components/form/BannerSettings.vue +59 -64
  69. package/components/form/ChangePassword.vue +4 -4
  70. package/components/form/ColorInput.vue +8 -32
  71. package/components/form/Footer.vue +8 -11
  72. package/components/form/InputWithSelect.vue +5 -8
  73. package/components/form/KeyValue.vue +7 -47
  74. package/components/form/LabeledSelect.vue +241 -212
  75. package/components/form/Labels.vue +3 -3
  76. package/components/form/MatchExpressions.vue +7 -24
  77. package/components/form/Members/ClusterPermissionsEditor.vue +2 -1
  78. package/components/form/Members/MembershipEditor.vue +1 -1
  79. package/components/form/NameNsDescription.vue +20 -59
  80. package/components/form/Password.vue +7 -16
  81. package/components/form/PodAffinity.vue +5 -4
  82. package/components/form/ResourceQuota/Namespace.vue +4 -4
  83. package/components/form/ResourceQuota/NamespaceRow.vue +17 -18
  84. package/components/form/ResourceQuota/Project.vue +4 -4
  85. package/components/form/ResourceQuota/ProjectRow.vue +6 -3
  86. package/components/form/Select.vue +2 -5
  87. package/components/form/SimpleSecretSelector.vue +9 -29
  88. package/components/form/UnitInput.vue +3 -8
  89. package/components/formatter/BadgeStateFormatter.vue +5 -8
  90. package/components/formatter/LiveDate.vue +3 -3
  91. package/components/nav/Favorite.vue +1 -5
  92. package/components/nav/Group.vue +99 -132
  93. package/components/nav/Header.vue +27 -124
  94. package/components/nav/HeaderPageActionMenu.vue +0 -1
  95. package/components/nav/NamespaceFilter.vue +15 -19
  96. package/components/nav/TopLevelMenu.vue +119 -182
  97. package/components/nav/Type.vue +41 -63
  98. package/composables/useClickOutside.ts +1 -1
  99. package/config/private-label.js +11 -15
  100. package/config/product/auth.js +7 -17
  101. package/config/product/settings.js +9 -19
  102. package/config/settings.ts +0 -28
  103. package/config/table-headers.js +2 -3
  104. package/dialog/ForceMachineRemoveDialog.vue +2 -2
  105. package/dialog/ScalePoolDownDialog.vue +2 -2
  106. package/edit/management.cattle.io.user.vue +4 -17
  107. package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +19 -19
  108. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +31 -31
  109. package/edit/monitoring.coreos.com.alertmanagerconfig/routeConfig.vue +12 -36
  110. package/edit/monitoring.coreos.com.alertmanagerconfig/types/email.vue +6 -6
  111. package/edit/monitoring.coreos.com.alertmanagerconfig/types/opsgenie.vue +10 -10
  112. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +4 -4
  113. package/edit/monitoring.coreos.com.alertmanagerconfig/types/slack.vue +4 -4
  114. package/edit/monitoring.coreos.com.alertmanagerconfig/types/webhook.vue +1 -1
  115. package/edit/monitoring.coreos.com.receiver/types/email.vue +6 -6
  116. package/edit/monitoring.coreos.com.receiver/types/opsgenie.vue +10 -10
  117. package/edit/monitoring.coreos.com.receiver/types/pagerduty.vue +5 -5
  118. package/edit/monitoring.coreos.com.receiver/types/slack.vue +4 -4
  119. package/edit/namespace.vue +2 -1
  120. package/edit/token.vue +12 -31
  121. package/edit/workload/index.vue +1 -1
  122. package/list/management.cattle.io.setting.vue +13 -22
  123. package/list/management.cattle.io.user.vue +3 -7
  124. package/list/namespace.vue +0 -3
  125. package/list/provisioning.cattle.io.cluster.vue +7 -6
  126. package/mixins/brand.js +0 -17
  127. package/package.json +1 -1
  128. package/pages/account/index.vue +12 -74
  129. package/pages/auth/login.vue +51 -214
  130. package/pages/auth/setup.vue +19 -142
  131. package/pages/c/_cluster/_product/namespaces.vue +4 -4
  132. package/pages/c/_cluster/auth/roles/index.vue +1 -19
  133. package/pages/c/_cluster/monitoring/monitor/index.vue +2 -2
  134. package/pages/c/_cluster/settings/banners.vue +102 -174
  135. package/pages/c/_cluster/settings/brand.vue +302 -350
  136. package/pages/c/_cluster/settings/performance.vue +38 -61
  137. package/pages/home.vue +30 -70
  138. package/pages/prefs.vue +25 -27
  139. package/promptRemove/mixin/roleDeletionCheck.js +2 -2
  140. package/public/index.html +4 -4
  141. package/rancher-components/BadgeState/BadgeState.vue +4 -6
  142. package/rancher-components/Banner/Banner.vue +8 -12
  143. package/rancher-components/Card/Card.vue +8 -7
  144. package/rancher-components/Form/Checkbox/Checkbox.vue +0 -4
  145. package/rancher-components/Form/LabeledInput/LabeledInput.vue +3 -42
  146. package/rancher-components/Form/Radio/RadioButton.vue +11 -35
  147. package/rancher-components/Form/Radio/RadioGroup.vue +5 -13
  148. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +3 -3
  149. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +0 -1
  150. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +4 -12
  151. package/rancher-components/RcDropdown/RcDropdown.vue +7 -35
  152. package/rancher-components/RcDropdown/RcDropdownItem.vue +2 -2
  153. package/rancher-components/RcDropdown/RcDropdownMenu.vue +4 -9
  154. package/rancher-components/RcDropdown/types.ts +0 -1
  155. package/rancher-components/StringList/StringList.vue +1 -1
  156. package/static/favicon.ico +0 -0
  157. package/static/favicon.png +0 -0
  158. package/static/loading-indicator.html +3 -3
  159. package/store/i18n.js +2 -2
  160. package/store/modal.ts +3 -3
  161. package/store/prefs.js +4 -11
  162. package/store/type-map.js +2 -32
  163. package/types/shell/index.d.ts +67 -74
  164. package/utils/error.js +8 -87
  165. package/utils/router.js +0 -21
  166. package/utils/select.js +3 -26
  167. package/utils/string.js +5 -8
  168. package/utils/title.ts +1 -1
  169. package/assets/icons/demo.css +0 -539
  170. package/assets/icons/demo.css:Zone.Identifier +0 -0
  171. package/assets/icons/demo_index.html +0 -1131
  172. package/assets/icons/demo_index.html:Zone.Identifier +0 -0
  173. package/assets/icons/iconfont.css +0 -216
  174. package/assets/icons/iconfont.css:Zone.Identifier +0 -0
  175. package/assets/icons/iconfont.js +0 -1
  176. package/assets/icons/iconfont.js:Zone.Identifier +0 -0
  177. package/assets/icons/iconfont.json +0 -324
  178. package/assets/icons/iconfont.json:Zone.Identifier +0 -0
  179. package/assets/icons/iconfont.ttf +0 -0
  180. package/assets/icons/iconfont.ttf:Zone.Identifier +0 -0
  181. package/assets/icons/iconfont.woff +0 -0
  182. package/assets/icons/iconfont.woff2 +0 -0
  183. package/assets/icons/iconfont.woff2:Zone.Identifier +0 -0
  184. package/assets/icons/iconfont.woff:Zone.Identifier +0 -0
  185. package/assets/images/API.svg +0 -3
  186. package/assets/images/action.svg +0 -6
  187. package/assets/images/login/password.svg +0 -20
  188. package/assets/images/login/user.svg +0 -6
  189. package/assets/images/login-bg.png +0 -0
  190. package/assets/images/login-left.png +0 -0
  191. package/assets/images/login-logo.svg +0 -19
  192. package/assets/images/logo.png +0 -0
  193. package/assets/images/pl/logo.png +0 -0
  194. package/assets/images/promp-yellow.svg +0 -5
  195. package/assets/images/user.png +0 -0
  196. package/assets/styles/all.scss +0 -63
  197. package/components/DotState.vue +0 -84
  198. package/utils/errorTranslate.json +0 -1336
@@ -8,7 +8,6 @@ import { get, clone } from '@shell/utils/object';
8
8
  import { removeObject } from '@shell/utils/array';
9
9
  import { Checkbox } from '@components/Form/Checkbox';
10
10
  import AsyncButton, { ASYNC_BUTTON_STATES } from '@shell/components/AsyncButton';
11
- import Select from '@shell/components/form/Select';
12
11
  import ActionDropdown from '@shell/components/ActionDropdown';
13
12
  import throttle from 'lodash/throttle';
14
13
  import debounce from 'lodash/debounce';
@@ -59,13 +58,13 @@ export default {
59
58
  THead,
60
59
  Checkbox,
61
60
  AsyncButton,
62
- Select,
63
61
  ActionDropdown,
64
62
  LabeledSelect,
65
63
  ButtonMultiAction,
66
64
  ActionMenu,
67
65
  ActionDropdownShell,
68
66
  },
67
+
69
68
  mixins: [
70
69
  filtering,
71
70
  sorting,
@@ -131,7 +130,7 @@ export default {
131
130
  },
132
131
  groupSort: {
133
132
  // Field to order groups by, defaults to groupBy
134
- type: Array,
133
+ type: String,
135
134
  default: null
136
135
  },
137
136
 
@@ -390,26 +389,7 @@ export default {
390
389
  hideManualRefreshButton: {
391
390
  type: Boolean,
392
391
  default: false
393
- },
394
- isBanner: {
395
- // Show isBanner input to filter rows
396
- type: Boolean,
397
- default: false
398
- },
399
- marginTopValue: {
400
- type: Number,
401
- default: 0
402
- },
403
- isFilterLabel: {
404
- type: Boolean,
405
- default: false
406
- },
407
- searchPlaceholder: {
408
- // search框内的输入提示
409
- type: String,
410
- default: ''
411
- },
412
-
392
+ }
413
393
  },
414
394
 
415
395
  data() {
@@ -424,21 +404,11 @@ export default {
424
404
 
425
405
  const isLoading = this.loading || false;
426
406
 
427
- let isCreatable = false;
428
- const lastPath = this.$route?.path.split('/').pop();
429
-
430
- if (lastPath.includes('.')) {
431
- isCreatable = this.$store.getters['type-map/optionsFor'](lastPath).isCreatable;
432
- } else if (lastPath === 'namespace') {
433
- isCreatable = this.$store.getters['type-map/optionsFor'](this.$route.name).isCreatable;
434
- }
435
-
436
407
  return {
437
408
  refreshButtonPhase: isLoading ? ASYNC_BUTTON_STATES.WAITING : ASYNC_BUTTON_STATES.ACTION,
438
409
  expanded: {},
439
410
  searchQuery,
440
411
  eventualSearchQuery,
441
- isCreatable,
442
412
  subMatches: null,
443
413
  actionOfInterest: null,
444
414
  loadingDelay: false,
@@ -446,28 +416,7 @@ export default {
446
416
  /**
447
417
  * The is the bool the DOM uses to show loading state. it's proxied from `loading` to avoid blipping the indicator (see usages)
448
418
  */
449
- isLoading,
450
- isMuchSelected: false,
451
- inputPerPage: '10',
452
- inputPage: '', // 输入的要跳至的页码
453
- perPageOptions: [
454
- {
455
- label: '10条/页',
456
- value: '10',
457
- },
458
- {
459
- label: '25条/页',
460
- value: '25',
461
- },
462
- {
463
- label: '50条/页',
464
- value: '50',
465
- },
466
- {
467
- label: '100条/页',
468
- value: '100',
469
- }
470
- ]
419
+ isLoading
471
420
  };
472
421
  },
473
422
 
@@ -482,13 +431,6 @@ export default {
482
431
  this._onScroll = this.onScroll.bind(this);
483
432
  $main?.addEventListener('scroll', this._onScroll);
484
433
 
485
- const tables = document.querySelectorAll('.sort-table-div');
486
-
487
- tables.forEach((table) => {
488
- this._onTableScroll = this.onTableScroll.bind(this, table);
489
- table.addEventListener('scroll', this._onTableScroll.bind(this, table));
490
- });
491
-
492
434
  this.debouncedPaginationChanged();
493
435
  },
494
436
 
@@ -503,12 +445,6 @@ export default {
503
445
  const $main = document.querySelector('main');
504
446
 
505
447
  $main?.removeEventListener('scroll', this._onScroll);
506
- // 移除所有表格容器的滚动事件监听器
507
- const tables = document.querySelectorAll('.sort-table-div');
508
-
509
- tables.forEach((table) => {
510
- table.removeEventListener('scroll', this._onTableScroll);
511
- });
512
448
  },
513
449
 
514
450
  watch: {
@@ -558,14 +494,6 @@ export default {
558
494
  this.watcherUpdateLiveAndDelayed(neu, old);
559
495
  },
560
496
 
561
- selectedRowsText(neu, old) {
562
- if (neu) {
563
- this.isMuchSelected = true;
564
- } else {
565
- this.isMuchSelected = false;
566
- }
567
- },
568
-
569
497
  // Ensure we update live and delayed columns on first load
570
498
  initalLoad: {
571
499
  handler(neu) {
@@ -621,22 +549,17 @@ export default {
621
549
  }
622
550
  };
623
551
 
552
+ onMounted(() => {
553
+ table.value.addEventListener('keyup', handleEnterKey);
554
+ });
624
555
 
625
556
  onBeforeUnmount(() => {
626
- table.value && table.value.removeEventListener('keyup', handleEnterKey);
557
+ table.value.removeEventListener('keyup', handleEnterKey);
627
558
  });
628
559
 
629
560
  const store = useStore();
630
561
  const { featureDropdownMenu } = useRuntimeFlag(store);
631
562
 
632
- onMounted(() => {
633
- table.value && table.value.addEventListener('keyup', handleEnterKey);
634
-
635
- console.log(featureDropdownMenu, ' featureDropdownMenu---------------------');
636
-
637
-
638
- });
639
-
640
563
  return {
641
564
  table,
642
565
  featureDropdownMenu,
@@ -865,115 +788,11 @@ export default {
865
788
  },
866
789
 
867
790
  methods: {
868
- // getTableList () {
869
- // const q = this.eventualSearchQuery;
870
-
871
- // this.searchQuery = q;
872
-
873
- // if (!this.hasAdvancedFiltering && this.useQueryParamsForSimpleFiltering) {
874
- // const route = {
875
- // name: this.$route.name,
876
- // params: { ...this.$route.params },
877
- // query: { ...this.$route.query, q }
878
- // };
879
-
880
- // if (!q && this.$route.query?.q) {
881
- // route.query = {};
882
- // }
883
-
884
- // this.$router.replace(route);
885
- // }
886
- // },
887
- onTableScroll(table, e) {
888
- // 记录最后滚动的距离
889
- let lastScrollTop = 0;
890
- let lastScrollLeft = 0;
891
-
892
- // 监听容器滚动
893
- table.addEventListener('scroll', (e) => {
894
- // 如果当前垂直滚动距离不等于上次
895
- if (e.target.scrollTop !== lastScrollTop) {
896
- // 更新最后距离
897
- lastScrollTop = e.target.scrollTop;
898
-
899
- // 移除左右阴影样式
900
- table.classList.remove('shadow-left');
901
- table.classList.remove('shadow-right');
902
-
903
- // 如果滚动到顶部,移除顶部阴影样式
904
- // if (lastScrollTop === 0) {
905
- // table.classList.remove('shadow-top');
906
- // }
907
- // // 否则添加顶部阴影样式
908
- // else {
909
- // table.classList.add('shadow-top');
910
- // }
911
-
912
- // 如果滚动到底部,移除底部阴影样式
913
- // if (lastScrollTop + table.clientHeight === table.scrollHeight) {
914
- // table.classList.remove('shadow-bottom');
915
- // }
916
- // // 否则添加底部阴影样式
917
- // else {
918
- // table.classList.add('shadow-bottom');
919
- // }
920
-
921
- // 阴影效果修正
922
- table.classList.remove('shadow-x');
923
- table.classList.add('shadow-y');
924
- }
925
-
926
- // 如果当前水平滚动距离不等于上次
927
- else if (e.target.scrollLeft !== lastScrollLeft) {
928
- // 更新最后距离
929
- lastScrollLeft = e.target.scrollLeft;
930
-
931
- // 移除上下阴影样式
932
- // table.classList.remove('shadow-top');
933
- // table.classList.remove('shadow-bottom');
934
-
935
- // 如果滚动到左边,移除左边阴影样式
936
- if (lastScrollLeft === 0) {
937
- table.classList.remove('shadow-left');
938
- }
939
- // 否则添加左边阴影样式
940
- else {
941
- table.classList.add('shadow-left');
942
- }
943
-
944
- // 如果滚动到右边,移除右边阴影样式
945
- if (lastScrollLeft + table.clientWidth === table.scrollWidth) {
946
- table.classList.remove('shadow-right');
947
- }
948
- // 否则添加右边阴影样式
949
- else {
950
- table.classList.add('shadow-right');
951
- }
952
-
953
- // 阴影效果修正
954
- table.classList.remove('shadow-y');
955
- table.classList.add('shadow-x');
956
- }
957
- });
958
- },
959
- changePerPage(value) {
960
- this.inputPerPage = value;
961
- this.setgetPerPage(value);
962
- },
963
- handleToPage() {
964
- this.setPage(parseInt(this.inputPage, 10));
965
- },
966
791
  refreshTableData() {
967
792
  this.$store.dispatch('resource-fetch/doManualRefresh');
968
793
  },
969
794
  get,
970
795
  dasherize,
971
- muchSelect(value) {
972
- console.log(value);
973
-
974
- this.isMuchSelected = !this.isMuchSelected;
975
- this.onToggleAll(value);
976
- },
977
796
 
978
797
  onScroll() {
979
798
  if (this.hasLiveColumns || this.hasDelayedColumns) {
@@ -1257,30 +1076,120 @@ export default {
1257
1076
  ref="container"
1258
1077
  :data-testid="componentTestid + '-list-container'"
1259
1078
  >
1260
- <!-- 主标题和过滤器区域 -->
1261
1079
  <div
1262
- :class="{'titled': $slots.title && $slots.title.length, 'mb-40': isFilterLabel}"
1263
- class="sortable-table-header mb-20"
1080
+ :class="{'titled': $slots.title && $slots.title.length}"
1081
+ class="sortable-table-header"
1264
1082
  >
1265
1083
  <slot name="title" />
1266
-
1267
- <!-- 顶部功能行区域 -->
1268
1084
  <div
1269
1085
  v-if="showHeaderRow"
1270
- class="fixed-header-table-actions"
1271
- :class="{button: !!$slots['header-button'], 'advanced-filtering': hasAdvancedFiltering, }"
1272
- style="display: flex;"
1086
+ class="fixed-header-actions"
1087
+ :class="{button: !!$slots['header-button'], 'advanced-filtering': hasAdvancedFiltering}"
1273
1088
  >
1089
+ <div
1090
+ :class="bulkActionsClass"
1091
+ class="bulk"
1092
+ >
1093
+ <slot name="header-left">
1094
+ <template v-if="tableActions">
1095
+ <button
1096
+ v-for="(act) in availableActions"
1097
+ :id="act.action"
1098
+ :key="act.action"
1099
+ v-clean-tooltip="actionTooltip"
1100
+ type="button"
1101
+ class="btn role-primary"
1102
+ :class="{[bulkActionClass]:true}"
1103
+ :disabled="!act.enabled"
1104
+ :data-testid="componentTestid + '-' + act.action"
1105
+ role="button"
1106
+ :aria-label="act.label"
1107
+ @click="applyTableAction(act, null, $event)"
1108
+ @keydown.enter.stop
1109
+ @mouseover="setBulkActionOfInterest(act)"
1110
+ @mouseleave="setBulkActionOfInterest(null)"
1111
+ >
1112
+ <i
1113
+ v-if="act.icon"
1114
+ :class="act.icon"
1115
+ />
1116
+ <span v-clean-html="act.label" />
1117
+ </button>
1118
+ <template v-if="featureDropdownMenu">
1119
+ <ActionDropdownShell
1120
+ :disabled="!selectedRows.length"
1121
+ :hidden-actions="hiddenActions"
1122
+ :action-tooltip="actionTooltip"
1123
+ @click="applyTableAction"
1124
+ @mouseover="setBulkActionOfInterest"
1125
+ @mouseleave="setBulkActionOfInterest"
1126
+ />
1127
+ </template>
1128
+ <template v-else>
1129
+ <ActionDropdown
1130
+ :class="bulkActionsDropdownClass"
1131
+ class="bulk-actions-dropdown"
1132
+ :disable-button="!selectedRows.length"
1133
+ size="sm"
1134
+ >
1135
+ <template #button-content>
1136
+ <button
1137
+ ref="actionDropDown"
1138
+ class="btn bg-primary mr-0"
1139
+ :disabled="!selectedRows.length"
1140
+ >
1141
+ <i class="icon icon-gear" />
1142
+ <span>{{ t('sortableTable.bulkActions.collapsed.label') }}</span>
1143
+ <i class="ml-10 icon icon-chevron-down" />
1144
+ </button>
1145
+ </template>
1146
+ <template #popover-content>
1147
+ <ul class="list-unstyled menu">
1148
+ <li
1149
+ v-for="(act, i) in hiddenActions"
1150
+ :key="i"
1151
+ v-close-popper
1152
+ v-clean-tooltip="{
1153
+ content: actionTooltip,
1154
+ placement: 'right'
1155
+ }"
1156
+ :class="{ disabled: !act.enabled }"
1157
+ @click="applyTableAction(act, null, $event)"
1158
+ @mouseover="setBulkActionOfInterest(act)"
1159
+ @mouseleave="setBulkActionOfInterest(null)"
1160
+ >
1161
+ <i
1162
+ v-if="act.icon"
1163
+ :class="act.icon"
1164
+ />
1165
+ <span v-clean-html="act.label" />
1166
+ </li>
1167
+ </ul>
1168
+ </template>
1169
+ </ActionDropdown>
1170
+ </template>
1171
+ <label
1172
+ v-if="selectedRowsText"
1173
+ :class="bulkActionAvailabilityClass"
1174
+ class="action-availability"
1175
+ >
1176
+ {{ selectedRowsText }}
1177
+ </label>
1178
+ </template>
1179
+ </slot>
1180
+ </div>
1181
+ <div
1182
+ v-if="!hasAdvancedFiltering && $slots['header-middle']"
1183
+ class="middle"
1184
+ >
1185
+ <slot name="header-middle" />
1186
+ </div>
1274
1187
 
1275
- <!-- 搜索栏、右插槽、刷新按钮、高级筛选等区域 -->
1276
1188
  <div
1277
1189
  v-if="search || hasAdvancedFiltering || isTooManyItemsToAutoUpdate || $slots['header-right']"
1278
1190
  class="search row"
1279
1191
  data-testid="search-box-filter-row"
1280
- style="max-height: 32px;"
1281
1192
  >
1282
-
1283
- <!-- 已应用的高级筛选 -->
1284
1193
  <ul
1285
1194
  v-if="hasAdvancedFiltering"
1286
1195
  class="advanced-filters-applied"
@@ -1297,9 +1206,15 @@ export default {
1297
1206
  <div class="bg" />
1298
1207
  </li>
1299
1208
  </ul>
1300
-
1209
+ <slot name="watch-controls" />
1301
1210
  <slot name="header-right" />
1302
-
1211
+ <AsyncButton
1212
+ v-if="!hideManualRefreshButton && isTooManyItemsToAutoUpdate"
1213
+ mode="manual-refresh"
1214
+ :size="manualRefreshButtonSize"
1215
+ :current-phase="refreshButtonPhase"
1216
+ @click="debouncedRefreshTableData"
1217
+ />
1303
1218
  <div
1304
1219
  v-if="hasAdvancedFiltering"
1305
1220
  ref="advanced-filter-group"
@@ -1355,8 +1270,6 @@ export default {
1355
1270
  </div>
1356
1271
  </div>
1357
1272
  </div>
1358
-
1359
- <!-- 搜索描述文本(隐藏) -->
1360
1273
  <p
1361
1274
  v-else-if="search"
1362
1275
  id="describe-filter-sortable-table"
@@ -1364,614 +1277,375 @@ export default {
1364
1277
  >
1365
1278
  {{ t('sortableTable.filteringDescription') }}
1366
1279
  </p>
1280
+ <input
1281
+ v-if="search"
1282
+ ref="searchQuery"
1283
+ v-model="eventualSearchQuery"
1284
+ type="search"
1285
+ class="input-sm search-box"
1286
+ :aria-label="t('sortableTable.searchLabel')"
1287
+ aria-describedby="describe-filter-sortable-table"
1288
+ :placeholder="t('sortableTable.search')"
1289
+ >
1367
1290
  <slot name="header-button" />
1368
-
1369
- <div style="display: flex;">
1370
-
1371
- <slot name="search-main-button" />
1372
-
1373
- <!-- 搜索输入框 -->
1374
- <input
1375
- v-if="search"
1376
- ref="searchQuery"
1377
- v-model="eventualSearchQuery"
1378
- type="search"
1379
- class="input-sm search-box"
1380
- :aria-label="t('sortableTable.searchLabel')"
1381
- aria-describedby="describe-filter-sortable-table"
1382
- :placeholder="t('sortableTable.search')+searchPlaceholder"
1383
- >
1384
- <!-- <button v-if="search" @click="getTableList(eventualSearchQuery)" calss="search-btn role-secondary">
1385
- 检索
1386
- </button> -->
1387
-
1388
- <!-- 手动刷新按钮 -->
1389
- <AsyncButton
1390
- v-if="isTooManyItemsToAutoUpdate"
1391
- mode="manual-refresh"
1392
- :size="manualRefreshButtonSize"
1393
- :current-phase="refreshButtonPhase"
1394
- @click="debouncedRefreshTableData"
1395
- />
1396
-
1397
- </div>
1398
-
1399
- </div>
1400
-
1401
- <!-- 中间区域插槽 -->
1402
- <div
1403
- v-if="!hasAdvancedFiltering && $slots['header-middle']"
1404
- class="middle"
1405
- style="margin-left: 10px;"
1406
- >
1407
- <slot name="header-middle" />
1408
1291
  </div>
1409
1292
  </div>
1410
1293
  </div>
1294
+ <table
1295
+ ref="table"
1296
+ class="sortable-table"
1297
+ :class="classObject"
1298
+ width="100%"
1299
+ role="table"
1300
+ >
1301
+ <THead
1302
+ v-if="showHeaders"
1303
+ :label-for="labelFor"
1304
+ :columns="columns"
1305
+ :group="group"
1306
+ :group-options="advGroupOptions"
1307
+ :has-advanced-filtering="hasAdvancedFiltering"
1308
+ :adv-filter-hide-labels-as-cols="advFilterHideLabelsAsCols"
1309
+ :table-actions="tableActions"
1310
+ :table-cols-options="columnOptions"
1311
+ :row-actions="rowActions"
1312
+ :sub-expand-column="subExpandColumn"
1313
+ :row-actions-width="rowActionsWidth"
1314
+ :how-much-selected="howMuchSelected"
1315
+ :sort-by="sortBy"
1316
+ :default-sort-by="_defaultSortBy"
1317
+ :descending="descending"
1318
+ :no-rows="noRows"
1319
+ :loading="isLoading && !loadingDelay"
1320
+ :no-results="noResults"
1321
+ @on-toggle-all="onToggleAll"
1322
+ @on-sort-change="changeSort"
1323
+ @col-visibility-change="changeColVisibility"
1324
+ @group-value-change="(val) => $emit('group-value-change', val)"
1325
+ @update-cols-options="updateColsOptions"
1326
+ />
1411
1327
 
1412
- <div v-if="$slots['banner']">
1413
- <slot name="banner"></slot>
1414
- </div>
1415
-
1416
- <div class="sort-table-div">
1417
- <table
1418
- ref="table"
1419
- class="sortable-table"
1420
- :class="classObject"
1421
- width="100%"
1422
- role="table"
1423
- >
1424
- <THead
1425
- v-if="showHeaders"
1426
- :label-for="labelFor"
1427
- :columns="columns"
1428
- :group="group"
1429
- :group-options="advGroupOptions"
1430
- :has-advanced-filtering="hasAdvancedFiltering"
1431
- :adv-filter-hide-labels-as-cols="advFilterHideLabelsAsCols"
1432
- :table-actions="tableActions"
1433
- :table-cols-options="columnOptions"
1434
- :row-actions="rowActions"
1435
- :sub-expand-column="subExpandColumn"
1436
- :row-actions-width="rowActionsWidth"
1437
- :how-much-selected="howMuchSelected"
1438
- :sort-by="sortBy"
1439
- :default-sort-by="_defaultSortBy"
1440
- :descending="descending"
1441
- :no-rows="noRows"
1442
- :loading="isLoading && !loadingDelay"
1443
- :no-results="noResults"
1444
- @on-toggle-all="onToggleAll"
1445
- @on-sort-change="changeSort"
1446
- @col-visibility-change="changeColVisibility"
1447
- @group-value-change="(val) => $emit('group-value-change', val)"
1448
- @update-cols-options="updateColsOptions"
1449
- />
1328
+ <!-- Don't display anything if we're loading and the delay has yet to pass -->
1329
+ <div v-if="isLoading && !loadingDelay" />
1450
1330
 
1451
- <!-- Don't display anything if we're loading and the delay has yet to pass -->
1452
- <div v-if="isLoading && !loadingDelay" />
1453
-
1454
- <tbody v-else-if="isLoading && !altLoading">
1455
- <slot name="loading">
1456
- <tr>
1457
- <td :colspan="fullColspan">
1458
- <div class="data-loading">
1459
- <i class="icon-spin icon icon-spinner" />
1460
- <t
1461
- k="generic.loading"
1462
- :raw="true"
1463
- />
1464
- </div>
1465
- </td>
1466
- </tr>
1467
- </slot>
1468
- </tbody>
1469
- <tbody v-else-if="noRows">
1470
- <slot name="no-rows">
1471
- <tr class="no-rows">
1472
- <td :colspan="fullColspan">
1331
+ <tbody v-else-if="isLoading && !altLoading">
1332
+ <slot name="loading">
1333
+ <tr>
1334
+ <td :colspan="fullColspan">
1335
+ <div class="data-loading">
1336
+ <i class="icon-spin icon icon-spinner" />
1473
1337
  <t
1474
- v-if="showNoRows"
1475
- :k="noRowsKey"
1338
+ k="generic.loading"
1339
+ :raw="true"
1476
1340
  />
1477
- </td>
1478
- </tr>
1479
- </slot>
1480
- </tbody>
1481
- <tbody v-else-if="noResults">
1482
- <slot name="no-results">
1483
- <tr class="no-results">
1484
- <td
1485
- :colspan="fullColspan"
1486
- class="text-center"
1341
+ </div>
1342
+ </td>
1343
+ </tr>
1344
+ </slot>
1345
+ </tbody>
1346
+ <tbody v-else-if="noRows">
1347
+ <slot name="no-rows">
1348
+ <tr class="no-rows">
1349
+ <td :colspan="fullColspan">
1350
+ <t
1351
+ v-if="showNoRows"
1352
+ :k="noRowsKey"
1353
+ />
1354
+ </td>
1355
+ </tr>
1356
+ </slot>
1357
+ </tbody>
1358
+ <tbody v-else-if="noResults">
1359
+ <slot name="no-results">
1360
+ <tr class="no-results">
1361
+ <td
1362
+ :colspan="fullColspan"
1363
+ class="text-center"
1364
+ >
1365
+ <t :k="noDataKey" />
1366
+ </td>
1367
+ </tr>
1368
+ </slot>
1369
+ </tbody>
1370
+ <tbody
1371
+ v-for="(groupedRows) in displayRows"
1372
+ v-else
1373
+ :key="groupedRows.key"
1374
+ tabindex="-1"
1375
+ :class="{ group: groupBy }"
1376
+ >
1377
+ <slot
1378
+ v-if="groupBy"
1379
+ name="group-row"
1380
+ :group="groupedRows"
1381
+ :fullColspan="fullColspan"
1382
+ >
1383
+ <tr class="group-row">
1384
+ <td :colspan="fullColspan">
1385
+ <slot
1386
+ name="group-by"
1387
+ :group="groupedRows.grp"
1487
1388
  >
1488
- <t :k="noDataKey" />
1489
- </td>
1490
- </tr>
1491
- </slot>
1492
- </tbody>
1493
- <tbody
1494
- v-for="(groupedRows) in displayRows"
1495
- v-else
1496
- :key="groupedRows.key"
1497
- tabindex="-1"
1498
- :class="{ group: groupBy }"
1389
+ <div
1390
+ v-trim-whitespace
1391
+ class="group-tab"
1392
+ >
1393
+ {{ groupedRows.ref }}
1394
+ </div>
1395
+ </slot>
1396
+ </td>
1397
+ </tr>
1398
+ </slot>
1399
+ <template
1400
+ v-for="(row, i) in groupedRows.rows"
1401
+ :key="i"
1499
1402
  >
1500
1403
  <slot
1501
- v-if="groupBy"
1502
- name="group-row"
1503
- :group="groupedRows"
1504
- :fullColspan="fullColspan"
1505
- >
1506
- <tr class="group-row">
1507
- <td :colspan="fullColspan">
1508
- <slot
1509
- name="group-by"
1510
- :group="groupedRows.grp"
1511
- >
1512
- <div
1513
- v-trim-whitespace
1514
- class="group-tab"
1515
- >
1516
- {{ groupedRows.ref }}
1517
- </div>
1518
- </slot>
1519
- </td>
1520
- </tr>
1521
- </slot>
1522
- <template
1523
- v-for="(row, i) in groupedRows.rows"
1524
- :key="i"
1404
+ name="main-row"
1405
+ :row="row.row"
1525
1406
  >
1526
1407
  <slot
1527
- name="main-row"
1528
- :row="row.row"
1408
+ :name="'main-row:' + (row.row.mainRowKey || i)"
1409
+ :full-colspan="fullColspan"
1529
1410
  >
1530
- <slot
1531
- :name="'main-row:' + (row.row.mainRowKey || i)"
1532
- :full-colspan="fullColspan"
1411
+ <!-- The data-cant-run-bulk-action-of-interest attribute is being used instead of :class because
1412
+ because our selection.js invokes toggleClass and :class clobbers what was added by toggleClass if
1413
+ the value of :class changes. -->
1414
+ <tr
1415
+ class="main-row"
1416
+ :data-testid="componentTestid + '-' + i + '-row'"
1417
+ :class="{ 'has-sub-row': row.showSubRow}"
1418
+ :data-node-id="row.key"
1419
+ :data-cant-run-bulk-action-of-interest="actionOfInterest && !row.canRunBulkActionOfInterest"
1533
1420
  >
1534
- <!-- The data-cant-run-bulk-action-of-interest attribute is being used instead of :class because
1535
- because our selection.js invokes toggleClass and :class clobbers what was added by toggleClass if
1536
- the value of :class changes. -->
1537
- <tr
1538
- class="main-row"
1539
- :data-testid="componentTestid + '-' + i + '-row'"
1540
- :class="{ 'has-sub-row': row.showSubRow}"
1541
- :data-node-id="row.key"
1542
- :data-cant-run-bulk-action-of-interest="actionOfInterest && !row.canRunBulkActionOfInterest"
1421
+ <td
1422
+ v-if="tableActions"
1423
+ class="row-check"
1424
+ align="middle"
1543
1425
  >
1544
- <td
1545
- v-if="tableActions"
1546
- class="row-check"
1547
- align="middle"
1548
- >
1549
- {{ row.mainRowKey }}
1550
- <Checkbox
1551
- class="selection-checkbox"
1552
- :data-node-id="row.key"
1553
- :data-testid="componentTestid + '-' + i + '-checkbox'"
1554
- :value="selectedRows.includes(row.row)"
1555
- :alternate-label="t('sortableTable.genericRowCheckbox', { item: row && row.row ? row.row.id : '' })"
1556
- />
1557
- </td>
1558
- <td
1559
- v-if="subExpandColumn"
1560
- class="row-expand"
1561
- align="middle"
1562
- >
1563
- <i
1564
- data-title="Toggle Expand"
1565
- :class="{
1566
- icon: true,
1567
- 'icon-chevron-right': !expanded[row.row[keyField]],
1568
- 'icon-chevron-down': !!expanded[row.row[keyField]]
1569
- }"
1570
- @click.stop="toggleExpand(row.row)"
1571
- />
1572
- </td>
1573
- <template
1574
- v-for="(col, j) in row.columns"
1575
- :key="j"
1426
+ {{ row.mainRowKey }}
1427
+ <Checkbox
1428
+ class="selection-checkbox"
1429
+ :data-node-id="row.key"
1430
+ :data-testid="componentTestid + '-' + i + '-checkbox'"
1431
+ :value="selectedRows.includes(row.row)"
1432
+ :alternate-label="t('sortableTable.genericRowCheckbox', { item: row && row.row ? row.row.id : '' })"
1433
+ />
1434
+ </td>
1435
+ <td
1436
+ v-if="subExpandColumn"
1437
+ class="row-expand"
1438
+ align="middle"
1439
+ >
1440
+ <i
1441
+ data-title="Toggle Expand"
1442
+ :class="{
1443
+ icon: true,
1444
+ 'icon-chevron-right': !expanded[row.row[keyField]],
1445
+ 'icon-chevron-down': !!expanded[row.row[keyField]]
1446
+ }"
1447
+ @click.stop="toggleExpand(row.row)"
1448
+ />
1449
+ </td>
1450
+ <template
1451
+ v-for="(col, j) in row.columns"
1452
+ :key="j"
1453
+ >
1454
+ <slot
1455
+ :name="'col:' + col.col.name"
1456
+ :row="row.row"
1457
+ :col="col.col"
1458
+ :dt="dt"
1459
+ :expanded="expanded"
1460
+ :rowKey="row.key"
1576
1461
  >
1577
- <slot
1578
- :name="'col:' + col.col.name"
1579
- :row="row.row"
1580
- :col="col.col"
1581
- :dt="dt"
1582
- :expanded="expanded"
1583
- :rowKey="row.key"
1462
+ <td
1463
+ v-show="!hasAdvancedFiltering || (hasAdvancedFiltering && col.col.isColVisible)"
1464
+ :key="col.col.name"
1465
+ :data-title="col.col.label"
1466
+ :data-testid="`sortable-cell-${ i }-${ j }`"
1467
+ :align="col.col.align || 'left'"
1468
+ :class="{['col-'+col.dasherize]: !!col.col.formatter, [col.col.breakpoint]: !!col.col.breakpoint, ['skip-select']: col.col.skipSelect}"
1469
+ :width="col.col.width"
1584
1470
  >
1585
- <td
1586
- v-show="!hasAdvancedFiltering || (hasAdvancedFiltering && col.col.isColVisible)"
1587
- :key="col.col.name"
1588
- :data-title="col.col.label"
1589
- :data-testid="`sortable-cell-${ i }-${ j }`"
1590
- :align="'left'"
1591
- :class="{['col-'+col.dasherize]: !!col.col.formatter, [col.col.breakpoint]: !!col.col.breakpoint, ['skip-select']: col.col.skipSelect}"
1592
- :width="col.col.width"
1471
+ <slot
1472
+ :name="'cell:' + col.col.name"
1473
+ :row="row.row"
1474
+ :col="col.col"
1475
+ :value="col.value"
1593
1476
  >
1594
- <slot
1595
- :name="'cell:' + col.col.name"
1477
+ <component
1478
+ :is="col.component"
1479
+ v-if="col.component && col.needRef"
1480
+ ref="column"
1481
+ :value="col.value"
1596
1482
  :row="row.row"
1597
1483
  :col="col.col"
1484
+ :get-custom-detail-link="getCustomDetailLink"
1485
+ v-bind="col.col.formatterOpts"
1486
+ :row-key="row.key"
1487
+ />
1488
+ <component
1489
+ :is="col.component"
1490
+ v-else-if="col.component"
1598
1491
  :value="col.value"
1599
- >
1600
- <component
1601
- :is="col.component"
1602
- v-if="col.component && col.needRef"
1603
- ref="column"
1604
- :value="col.value"
1605
- :row="row.row"
1606
- :col="col.col"
1607
- :get-custom-detail-link="getCustomDetailLink"
1608
- v-bind="col.col.formatterOpts"
1609
- :row-key="row.key"
1610
- />
1611
- <component
1612
- :is="col.component"
1613
- v-else-if="col.component"
1614
- :value="col.value"
1615
- :row="row.row"
1616
- :col="col.col"
1617
- v-bind="col.col.formatterOpts"
1618
- :row-key="row.key"
1619
- />
1620
- <component
1621
- :is="col.col.formatter"
1622
- v-else-if="col.col.formatter"
1623
- :value="col.value"
1624
- :row="row.row"
1625
- :col="col.col"
1626
- v-bind="col.col.formatterOpts"
1627
- :row-key="row.key"
1628
- />
1629
- <template v-else-if="col.value !== ''">
1630
- {{ col.formatted }}
1631
- </template>
1632
- <template v-else-if="col.col.dashIfEmpty">
1633
- <span class="text-muted">&mdash;</span>
1634
- </template>
1635
- </slot>
1636
- </td>
1637
- </slot>
1638
- </template>
1639
- <td
1640
- v-if="rowActions"
1641
- :align="'left'"
1642
- style="height:60px"
1643
- >
1644
- <div style="display: flex;align-items: center;">
1645
- <slot
1646
- name="row-actions"
1647
- :row="row.row"
1648
- :index="i"
1649
- >
1650
- </slot>
1651
- <template v-if="featureDropdownMenu">
1652
- <ActionMenu
1653
- :resource="row.row"
1654
- :data-testid="componentTestid + '-' + i + '-action-button'"
1655
- :button-aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
1492
+ :row="row.row"
1493
+ :col="col.col"
1494
+ v-bind="col.col.formatterOpts"
1495
+ :row-key="row.key"
1656
1496
  />
1657
- </template>
1658
- <template v-else>
1659
- <ButtonMultiAction
1660
- :id="`actionButton+${i}+${(row.row && row.row.name) ? row.row.name : ''}`"
1661
- :ref="`actionButton${i}`"
1662
- aria-haspopup="true"
1663
- aria-expanded="false"
1664
- :aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
1665
- :data-testid="componentTestid + '-' + i + '-action-button'"
1666
- :borderless="true"
1667
- @click="handleActionButtonClick(i, $event)"
1668
- @keyup.enter="handleActionButtonClick(i, $event)"
1669
- @keyup.space="handleActionButtonClick(i, $event)"
1497
+ <component
1498
+ :is="col.col.formatter"
1499
+ v-else-if="col.col.formatter"
1500
+ :value="col.value"
1501
+ :row="row.row"
1502
+ :col="col.col"
1503
+ v-bind="col.col.formatterOpts"
1504
+ :row-key="row.key"
1670
1505
  />
1671
- </template>
1672
- </div>
1673
- </td>
1674
- </tr>
1675
- </slot>
1676
- </slot>
1677
- <!-- <slot
1678
- v-if="row.showSubRow"
1679
- name="sub-row"
1680
- :full-colspan="fullColspan"
1681
- :row="row.row"
1682
- :sub-matches="subMatches"
1683
- :keyField="keyField"
1684
- :componentTestid="componentTestid"
1685
- :i="i"
1686
- :onRowMouseEnter="onRowMouseEnter"
1687
- :onRowMouseLeave="onRowMouseLeave"
1688
- >
1689
- <tr
1690
- v-if="row.row.stateDescription"
1691
- :key="row.row[keyField] + '-description'"
1692
- :data-testid="componentTestid + '-' + i + '-row-description'"
1693
- class="state-description sub-row"
1694
- @mouseenter="onRowMouseEnter"
1695
- @mouseleave="onRowMouseLeave"
1696
- >
1697
- <td
1698
- v-if="tableActions"
1699
- class="row-check"
1700
- align="middle"
1701
- />
1506
+ <template v-else-if="col.value !== ''">
1507
+ {{ col.formatted }}
1508
+ </template>
1509
+ <template v-else-if="col.col.dashIfEmpty">
1510
+ <span class="text-muted">&mdash;</span>
1511
+ </template>
1512
+ </slot>
1513
+ </td>
1514
+ </slot>
1515
+ </template>
1702
1516
  <td
1703
- :colspan="fullColspan - (tableActions ? 1: 0)"
1704
- :class="{ 'text-error' : row.row.stateObj.error }"
1517
+ v-if="rowActions"
1705
1518
  >
1706
- {{ row.row.stateDescription }}
1519
+ <slot
1520
+ name="row-actions"
1521
+ :row="row.row"
1522
+ :index="i"
1523
+ >
1524
+ <template v-if="featureDropdownMenu">
1525
+ <ActionMenu
1526
+ :resource="row.row"
1527
+ :data-testid="componentTestid + '-' + i + '-action-button'"
1528
+ :button-aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
1529
+ />
1530
+ </template>
1531
+ <template v-else>
1532
+ <ButtonMultiAction
1533
+ :id="`actionButton+${i}+${(row.row && row.row.name) ? row.row.name : ''}`"
1534
+ :ref="`actionButton${i}`"
1535
+ aria-haspopup="true"
1536
+ aria-expanded="false"
1537
+ :aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
1538
+ :data-testid="componentTestid + '-' + i + '-action-button'"
1539
+ :borderless="true"
1540
+ @click="handleActionButtonClick(i, $event)"
1541
+ @keyup.enter="handleActionButtonClick(i, $event)"
1542
+ @keyup.space="handleActionButtonClick(i, $event)"
1543
+ />
1544
+ </template>
1545
+ </slot>
1707
1546
  </td>
1708
1547
  </tr>
1709
- </slot> -->
1710
- </template>
1711
- </tbody>
1712
- </table>
1713
- </div>
1548
+ </slot>
1549
+ </slot>
1550
+ <slot
1551
+ v-if="row.showSubRow"
1552
+ name="sub-row"
1553
+ :full-colspan="fullColspan"
1554
+ :row="row.row"
1555
+ :sub-matches="subMatches"
1556
+ :keyField="keyField"
1557
+ :componentTestid="componentTestid"
1558
+ :i="i"
1559
+ :onRowMouseEnter="onRowMouseEnter"
1560
+ :onRowMouseLeave="onRowMouseLeave"
1561
+ >
1562
+ <tr
1563
+ v-if="row.row.stateDescription"
1564
+ :key="row.row[keyField] + '-description'"
1565
+ :data-testid="componentTestid + '-' + i + '-row-description'"
1566
+ class="state-description sub-row"
1567
+ @mouseenter="onRowMouseEnter"
1568
+ @mouseleave="onRowMouseLeave"
1569
+ >
1570
+ <td
1571
+ v-if="tableActions"
1572
+ class="row-check"
1573
+ align="middle"
1574
+ />
1575
+ <td
1576
+ :colspan="fullColspan - (tableActions ? 1: 0)"
1577
+ :class="{ 'text-error' : row.row.stateObj.error }"
1578
+ >
1579
+ {{ row.row.stateDescription }}
1580
+ </td>
1581
+ </tr>
1582
+ </slot>
1583
+ </template>
1584
+ </tbody>
1585
+ </table>
1714
1586
  <div
1715
- v-if="!noRows && !noResults"
1716
- :class="$route.path=== '/account'? 'chebox-padding':''"
1717
- style="display: flex;justify-content: flex-start;align-content: center;height: 62px;position: sticky;bottom: 0;background-color: var(--header-bg);padding: 15px 20px 0 30px;margin: 0 -20px 0 -20px;z-index:13"
1587
+ v-if="showPaging"
1588
+ class="paging"
1718
1589
  >
1719
- <div style="display: flex;justify-content: center;height: 32px;">
1720
- <Checkbox
1721
- v-if="tableActions&&availableActions.some(item => item.action != 'download')"
1722
- :value="isMuchSelected"
1723
- class="check"
1724
- data-testid="sortable-table_check_select_all"
1725
- :disabled="noRows || noResults"
1726
- style="display: flex;justify-content: center;align-content: center;"
1727
- @update:value = "muchSelect"
1728
- />
1729
- </div>
1730
- <div
1731
- :class="{'titled': $slots.title && $slots.title.length}"
1732
- class="sortable-table-header"
1733
- style="margin-left: 10px;min-width: 55%;"
1590
+ <button
1591
+ type="button"
1592
+ class="btn btn-sm role-multi-action"
1593
+ data-testid="pagination-first"
1594
+ :disabled="page == 1 || loading"
1595
+ role="button"
1596
+ :aria-label="t('sortableTable.ariaLabel.firstPageBtn')"
1597
+ @click="goToPage('first')"
1734
1598
  >
1735
- <slot name="title" />
1736
- <div
1737
- v-if="showHeaderRow"
1738
- class="fixed-footer-actions"
1739
- :class="{button: !!$slots['header-button'], 'advanced-filtering': hasAdvancedFiltering,}"
1740
- >
1741
- <div
1742
- :class="bulkActionsClass"
1743
- class="bulk"
1744
- >
1745
- <slot name="header-left">
1746
- <template v-if="tableActions">
1747
- <button
1748
- v-for="(act) in availableActions"
1749
- :id="act.action"
1750
- :key="act.action"
1751
- v-clean-tooltip="actionTooltip"
1752
- type="button"
1753
- class="btn role-primary"
1754
- :class="{[bulkActionClass]:true}"
1755
- :disabled="!act.enabled"
1756
- :data-testid="componentTestid + '-' + act.action"
1757
- role="button"
1758
- :aria-label="act.label"
1759
- @click="applyTableAction(act, null, $event)"
1760
- @keydown.enter.stop
1761
- @mouseover="setBulkActionOfInterest(act)"
1762
- @mouseleave="setBulkActionOfInterest(null)"
1763
- >
1764
- <!-- <i
1765
- v-if="act.icon"
1766
- :class="act.icon"
1767
- style="line-height: 12px;height: 12px;font-size: 12px;"
1768
- /> -->
1769
- <span v-clean-html="act.label" />
1770
- </button>
1771
- <template v-if="featureDropdownMenu">
1772
- <ActionDropdownShell
1773
- :disabled="!selectedRows.length"
1774
- :hidden-actions="hiddenActions"
1775
- :action-tooltip="actionTooltip"
1776
- @click="applyTableAction"
1777
- @mouseover="setBulkActionOfInterest"
1778
- @mouseleave="setBulkActionOfInterest"
1779
- />
1780
- </template>
1781
- <template v-else>
1782
- <ActionDropdown
1783
- :class="bulkActionsDropdownClass"
1784
- class="bulk-actions-dropdown"
1785
- :disable-button="!selectedRows.length"
1786
- size="sm"
1787
- >
1788
- <template #button-content>
1789
- <button
1790
- ref="actionDropDown"
1791
- class="btn bg-primary mr-0"
1792
- :disabled="!selectedRows.length"
1793
- >
1794
- <i class="icon icon-gear" />
1795
- <span>{{ t('sortableTable.bulkActions.collapsed.label') }}</span>
1796
- <i class="ml-10 icon icon-chevron-down" />
1797
- </button>
1798
- </template>
1799
- <template #popover-content>
1800
- <ul class="list-unstyled menu">
1801
- <li
1802
- v-for="(act, i) in hiddenActions"
1803
- :key="i"
1804
- v-close-popper
1805
- v-clean-tooltip="{
1806
- content: actionTooltip,
1807
- placement: 'right'
1808
- }"
1809
- :class="{ disabled: !act.enabled }"
1810
- @click="applyTableAction(act, null, $event)"
1811
- @mouseover="setBulkActionOfInterest(act)"
1812
- @mouseleave="setBulkActionOfInterest(null)"
1813
- >
1814
- <i
1815
- v-if="act.icon"
1816
- :class="act.icon"
1817
- />
1818
- <span v-clean-html="act.label" />
1819
- </li>
1820
- </ul>
1821
- </template>
1822
- </ActionDropdown>
1823
- </template>
1824
- <label
1825
- v-if="selectedRowsText"
1826
- :class="bulkActionAvailabilityClass"
1827
- class="action-availability"
1828
- >
1829
- {{ tableActions&&availableActions.some(item => item.action != 'download') ?selectedRowsText: '' }}
1830
- </label>
1831
-
1832
- </template>
1833
- </slot>
1834
- </div>
1835
- </div>
1836
- </div>
1837
-
1838
- <!-- 分页 -->
1839
- <div
1840
- v-if="showPaging"
1841
- class="paging"
1599
+ <i
1600
+ class="icon icon-chevron-beginning"
1601
+ :alt="t('sortableTable.alt.firstPageBtn')"
1602
+ />
1603
+ </button>
1604
+ <button
1605
+ type="button"
1606
+ class="btn btn-sm role-multi-action"
1607
+ data-testid="pagination-prev"
1608
+ :disabled="page == 1 || loading"
1609
+ role="button"
1610
+ :aria-label="t('sortableTable.ariaLabel.prevPageBtn')"
1611
+ @click="goToPage('prev')"
1842
1612
  >
1843
- <div style="height: 100%; align-content: center;">
1844
- {{ filteredRows.length }} 条
1845
- </div>
1846
-
1847
- <button
1848
- type="button"
1849
- class="btn btn-sm role-multi-action page-btn-normal"
1850
- :disabled="page == 1"
1851
- :style="{ color: page <= totalPages ? `var(--default-text) !important` : `var(--paimary)`,border: page <= totalPages ? `solid thin var(--border)` : `solid thin var(--paimary)`}"
1852
- @click="goToPage('prev')"
1853
- >
1854
- <!-- <i class="icon icon-chevron-left" /> -->
1855
- <
1856
- </button>
1857
- <button
1858
- type="button"
1859
- class="btn btn-sm role-multi-action page-btn-normal"
1860
- :style="{ color: (page == 1) ? `var(--primary)`:`var(--default-text) !important`,border: (page == 1) ? `solid thin var(--primary)` : `solid thin var(--border)`}"
1861
- @click="goToPage('first')"
1862
- >
1863
- <!-- <i class="icon icon-chevron-beginning" /> -->
1864
- {{ 1 }}
1865
- </button>
1866
- <template v-if="totalPages > 2">
1867
- <div style="display: flex;flex-direction: row;gap: 10px;">
1868
- <button
1869
- v-if="page - 2 > 1 && page <= totalPages "
1870
- type="button"
1871
- class="btn btn-sm role-multi-action page-btn-normal"
1872
- :style="{ color: `var(--default-text) !important`,border: `solid thin white`}"
1873
- >
1874
- ...
1875
- </button>
1876
- <button
1877
- v-if="page - 1 > 1 && page <= totalPages "
1878
- type="button"
1879
- class="btn btn-sm role-multi-action page-btn-normal"
1880
- :style="{ color: `var(--default-text) !important`,border: `solid thin var(--border)`}"
1881
- @click="setPage(page-1)"
1882
- >
1883
- {{ page-1 }}
1884
- </button>
1885
- <button
1886
- v-if="page > 1 && page < totalPages"
1887
- type="button"
1888
- class="btn btn-sm role-multi-action page-btn-normal"
1889
- :style="{ color: `var(--default-text)`,border: `solid thin var(--primary)`}"
1890
- @click="setPage(page)"
1891
- >
1892
- {{ page }}
1893
- </button>
1894
- <button
1895
- v-if="page + 1 < totalPages "
1896
- type="button"
1897
- class="btn btn-sm role-multi-action page-btn-normal"
1898
- :style="{ color: `var(--default-text) !important`,border: `solid thin var(--border)`}"
1899
- @click="setPage(page+1)"
1900
- >
1901
- {{ page+1 }}
1902
- </button>
1903
- <button
1904
- v-if="page +2 < totalPages "
1905
- type="button"
1906
- class="btn btn-sm role-multi-action page-btn-normal"
1907
- :style="{ color: `var(--default-text) !important`,border: `solid thin white`}"
1908
- >
1909
- ...
1910
- </button>
1911
- </div>
1912
- </template>
1913
- <!-- <button
1914
- type="button"
1915
- class="btn btn-sm role-multi-action"
1916
- style="padding: 0;max-width: 32px;background-color: white !important;"
1917
- >
1918
- {{ page }}
1919
- </button> -->
1920
- <button
1921
- v-if="totalPages > 1"
1922
- type="button"
1923
- class="btn btn-sm role-multi-action page-btn-normal"
1924
- :style="{ color: (page == totalPages) ? `var(--primary)`:`var(--default-text) !important`,border: (page == totalPages) ? `solid thin var(--primary)` : `solid thin var(--border)`}"
1925
- @click="goToPage('last')"
1926
- >
1927
- <!-- <i class="icon icon-chevron-end" /> -->
1928
- {{ totalPages }}
1929
- </button>
1930
- <button
1931
- type="button"
1932
- class="btn btn-sm role-multi-action page-btn-normal"
1933
- :disabled="page == totalPages"
1934
- :style="{ color: page <= totalPages ? `var(--default-text) !important` : `var(--paimary)`,border: page <= totalPages ? `solid thin var(--border)` : `solid thin var(--paimary)`}"
1935
- @click="goToPage('next')"
1936
- >
1937
- <!-- <i class="icon icon-chevron-right" /> -->
1938
- >
1939
- </button>
1940
-
1941
- <!-- 分页页码切换 -->
1942
- <Select
1943
- :mode="inputPerPage"
1944
- :searchable="false"
1945
- :clearable="false"
1946
- :options="perPageOptions"
1947
- v-model:value="inputPerPage"
1948
- class="pageSelect"
1949
- @update:value="changePerPage"
1613
+ <i
1614
+ class="icon icon-chevron-left"
1615
+ :alt="t('sortableTable.alt.prevPageBtn')"
1950
1616
  />
1951
-
1952
- <div style="height: 100%; align-content: center;">
1953
- 跳至
1954
- </div>
1955
- <input
1956
- v-model="inputPage"
1957
- type="number"
1958
- min="1"
1959
- step="1"
1960
- style="padding: 0px 10px;"
1961
- @keyup.enter="handleToPage"
1962
- >
1963
- <div style="height: 100%; align-content: center;">
1964
-
1965
- </div>
1966
- <!-- <button
1617
+ </button>
1618
+ <span>
1619
+ {{ pagingDisplay }}
1620
+ </span>
1621
+ <button
1967
1622
  type="button"
1968
1623
  class="btn btn-sm role-multi-action"
1969
- style="padding: 0;max-width: 80px;background-color: white !important;"
1970
- @click="setPage(inputPage)"
1624
+ data-testid="pagination-next"
1625
+ :disabled="page == totalPages || loading"
1626
+ role="button"
1627
+ :aria-label="t('sortableTable.ariaLabel.nextPageBtn')"
1628
+ @click="goToPage('next')"
1971
1629
  >
1972
-
1973
- </button> -->
1974
- </div>
1630
+ <i
1631
+ class="icon icon-chevron-right"
1632
+ :alt="t('sortableTable.alt.nextPageBtn')"
1633
+ />
1634
+ </button>
1635
+ <button
1636
+ type="button"
1637
+ class="btn btn-sm role-multi-action"
1638
+ data-testid="pagination-last"
1639
+ :disabled="page == totalPages || loading"
1640
+ role="button"
1641
+ :aria-label="t('sortableTable.ariaLabel.lastPageBtn')"
1642
+ @click="goToPage('last')"
1643
+ >
1644
+ <i
1645
+ class="icon icon-chevron-end"
1646
+ :alt="t('sortableTable.alt.lastPageBtn')"
1647
+ />
1648
+ </button>
1975
1649
  </div>
1976
1650
  <button
1977
1651
  v-if="search"
@@ -2155,12 +1829,9 @@ export default {
2155
1829
  }
2156
1830
 
2157
1831
  .search-box {
2158
- height: 32px;
2159
- margin-left: 0px;
2160
- /* min-width: 180px; */
2161
- min-width: 280px;
2162
- width: 280px !important;
2163
- border: 1px solid rgb(217, 217, 217);
1832
+ height: 40px;
1833
+ margin-left: 10px;
1834
+ min-width: 180px;
2164
1835
  }
2165
1836
  </style>
2166
1837
 
@@ -2186,8 +1857,7 @@ export default {
2186
1857
  border-collapse: collapse;
2187
1858
  min-width: 400px;
2188
1859
  border-radius: 5px 5px 0 0;
2189
- border-bottom: 1px solid var(--border);
2190
- /* outline: 1px solid var(--border); */
1860
+ outline: 1px solid var(--border);
2191
1861
  background: var(--sortable-table-bg);
2192
1862
  border-radius: 4px;
2193
1863
 
@@ -2234,7 +1904,7 @@ export default {
2234
1904
  }
2235
1905
 
2236
1906
  &:hover, &.sub-row-hovered {
2237
- /* background-color: var(--sortable-table-hover-bg); */
1907
+ background-color: var(--sortable-table-hover-bg);
2238
1908
  }
2239
1909
 
2240
1910
  &.state-description > td {
@@ -2249,7 +1919,7 @@ export default {
2249
1919
  }
2250
1920
 
2251
1921
  tr.row-selected {
2252
- /* background: var(--sortable-table-selected-bg); */
1922
+ background: var(--sortable-table-selected-bg);
2253
1923
  }
2254
1924
 
2255
1925
  .no-rows {
@@ -2264,15 +1934,12 @@ export default {
2264
1934
  background-color: var(--body-bg);
2265
1935
  }
2266
1936
  }
2267
- .no-results{
2268
- height: 60px;
2269
- }
2270
1937
 
2271
1938
  &.group {
2272
1939
  &:before {
2273
1940
  content: "";
2274
1941
  display: block;
2275
- height: 0px;
1942
+ height: 20px;
2276
1943
  background-color: transparent;
2277
1944
  }
2278
1945
  }
@@ -2370,12 +2037,12 @@ export default {
2370
2037
  align-items: center;
2371
2038
  }
2372
2039
  }
2373
- .fixed-footer-actions.button{
2040
+ .fixed-header-actions.button{
2374
2041
  grid-template-columns: [bulk] auto [middle] min-content [search] minmax(min-content, 350px);
2375
2042
  }
2376
2043
 
2377
- .fixed-footer-actions {
2378
- /* padding: 0 0 20px 0; */
2044
+ .fixed-header-actions {
2045
+ padding: 0 0 20px 0;
2379
2046
  width: 100%;
2380
2047
  z-index: z-index('fixedTableHeader');
2381
2048
  background: transparent;
@@ -2490,233 +2157,13 @@ export default {
2490
2157
  }
2491
2158
  }
2492
2159
 
2493
-
2494
- .fixed-header-table-actions {
2495
- width: 100%;
2496
- z-index: z-index('fixedTableHeader');
2497
- background: transparent;
2498
- display: flex;
2499
- justify-content: space-between;
2500
-
2501
-
2502
- .bulk {
2503
- $gap: 10px;
2504
-
2505
- & > BUTTON {
2506
- display: none; // Handled dynamically
2507
- }
2508
-
2509
- & > BUTTON:not(:last-of-type) {
2510
- margin-right: $gap;
2511
- }
2512
-
2513
- .action-availability {
2514
- display: none; // Handled dynamically
2515
- margin-left: $gap;
2516
- vertical-align: middle;
2517
- margin-top: 2px;
2518
- }
2519
-
2520
- .dropdown-button {
2521
- $disabled-color: var(--disabled-text);
2522
- $disabled-cursor: not-allowed;
2523
- li.disabled {
2524
- color: $disabled-color;
2525
- cursor: $disabled-cursor;
2526
-
2527
- &:hover {
2528
- color: $disabled-color;
2529
- background-color: unset;
2530
- cursor: $disabled-cursor;
2531
- }
2532
- }
2533
- }
2534
-
2535
- .bulk-action {
2536
- .icon {
2537
- vertical-align: -10%;
2538
- }
2539
- }
2540
- }
2541
-
2542
- .middle {
2543
- white-space: nowrap;
2544
-
2545
- .icon.icon-backup.animate {
2546
- animation-name: spin;
2547
- animation-duration: 1000ms;
2548
- animation-iteration-count: infinite;
2549
- animation-timing-function: linear;
2550
- }
2551
-
2552
- @keyframes spin {
2553
- from {
2554
- transform:rotate(0deg);
2555
- }
2556
- to {
2557
- transform:rotate(360deg);
2558
- }
2559
- }
2560
- }
2561
-
2562
- .search {
2563
- text-align: right;
2564
- justify-content: flex-end;
2565
- }
2566
-
2567
- .bulk-actions-dropdown {
2568
- display: none; // Handled dynamically
2569
-
2570
- .dropdown-button {
2571
- background-color: var(--primary);
2572
-
2573
- &:hover {
2574
- background-color: var(--primary-hover-bg);
2575
- color: var(--primary-hover-text);
2576
- }
2577
-
2578
- > *, .icon-chevron-down {
2579
- color: var(--primary-text);
2580
- }
2581
-
2582
- .button-divider {
2583
- border-color: var(--primary-text);
2584
- }
2585
-
2586
- &.disabled {
2587
- border-color: var(--disabled-bg);
2588
-
2589
- .icon-chevron-down {
2590
- color: var(--disabled-text) !important;
2591
- }
2592
-
2593
- .button-divider {
2594
- border-color: var(--disabled-text);
2595
- }
2596
- }
2597
- }
2598
- }
2599
- }
2600
-
2601
2160
  .paging {
2602
- //text-align: center;
2603
- display: flex;
2604
- justify-content: flex-end;
2605
- gap: 0 10px;
2161
+ margin-top: 10px;
2606
2162
  text-align: center;
2607
- max-height: 32px;
2608
- background-color: transparent;
2609
- flex: 1;
2163
+
2610
2164
  SPAN {
2611
2165
  display: inline-block;
2612
- //min-width: 200px;
2613
- min-width: auto;
2614
- }
2615
-
2616
- /* 针对Webkit浏览器(如Chrome, Safari) */
2617
- input[type="number"]::-webkit-inner-spin-button,
2618
- input[type="number"]::-webkit-outer-spin-button {
2619
- -webkit-appearance: none;
2620
- margin: 0;
2621
- }
2622
-
2623
- /* 针对Firefox */
2624
- input[type="number"] {
2625
- -moz-appearance: textfield;
2626
- // background-color: var(--disabled-bg) !important;
2627
- border: 1px var(--border) solid;
2628
- border-radius: 2px;
2629
- width: 50px;
2630
- height: 100%;
2166
+ min-width: 200px;
2631
2167
  }
2632
2168
  }
2633
-
2634
2169
  </style>
2635
- <style scoped lang="scss">
2636
- :deep() .role-link{
2637
- min-width: unset !important;
2638
- width: 38px;
2639
- min-height: 20px !important;
2640
- height: 20px !important;
2641
- line-height: 20px !important;
2642
- &:hover{
2643
- background-color: unset !important;
2644
- }
2645
- }
2646
- :deep() .checkbox-outer-container-extra{
2647
- margin-top: 0px !important;
2648
- }
2649
- .pageSelect {
2650
- overflow: hidden;
2651
- max-width: 100px;
2652
- }
2653
- .page-btn-normal {
2654
- padding: 0;
2655
- max-width: 32px;
2656
- background-color:white !important;
2657
- }
2658
-
2659
- .pageSelect{
2660
- &:deep() .vs__actions:after{
2661
- padding-top: 10px;
2662
- }
2663
- }
2664
- .sort-table-div{
2665
- width:100%;
2666
- white-space:nowrap;
2667
- overflow-x: auto;
2668
- }
2669
-
2670
- /* 滚动阴影左边 */
2671
- .shadow-left td:nth-child(2)::after{
2672
- content: "";
2673
- position: absolute;
2674
- top: 0;
2675
- width: 10px;
2676
- height: 100%;
2677
- right: -10px;
2678
- background: linear-gradient(to right, rgba(5, 5, 5, 0.06), transparent);
2679
- }
2680
- .shadow-left{
2681
- &:deep() th:nth-child(2)::after{
2682
- content: "";
2683
- position: absolute;
2684
- top: 0;
2685
- width: 10px;
2686
- height: 100%;
2687
- right: -10px;
2688
- background: linear-gradient(to right, rgba(5, 5, 5, 0.06), transparent);
2689
- }
2690
- }
2691
-
2692
- /* 滚动阴影右边 */
2693
- .shadow-right td:nth-last-child(1)::after{
2694
- content: "";
2695
- position: absolute;
2696
- top: 0;
2697
- width: 10px;
2698
- height: 100%;
2699
- left: -10px;
2700
- background: linear-gradient(to left, rgba(5, 5, 5, 0.06), transparent);
2701
- }
2702
-
2703
- .shadow-right{
2704
- &:deep() th:nth-last-child(1)::after {
2705
- content: "";
2706
- position: absolute;
2707
- top: 0;
2708
- width: 10px;
2709
- height: 100%;
2710
- left: -10px;
2711
- background: linear-gradient(to left, rgba(5, 5, 5, 0.06), transparent);
2712
- }
2713
- }
2714
-
2715
- /* 滚动阴影垂直修正 */
2716
- .shadow-y td:nth-child(-n + 2), /* 前两列 */
2717
- .shadow-y td:nth-last-child(1) /* 后一列 */
2718
- {
2719
- z-index: 3;
2720
- }
2721
- </style>
2722
-