dashboard-shell-shell 3.0.5-test.1 → 3.0.5-test.10

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 (50) hide show
  1. package/assets/styles/base/_variables.scss +3 -3
  2. package/assets/styles/global/_button.scss +7 -7
  3. package/assets/styles/global/_tooltip.scss +4 -4
  4. package/assets/styles/themes/_light.scss +3 -1
  5. package/assets/translations/zh-hans.yaml +76 -0
  6. package/components/ActionDropdown.vue +1 -1
  7. package/components/CopyToClipboard.vue +15 -0
  8. package/components/Drawer/Chrome.vue +2 -2
  9. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +22 -22
  10. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +1 -1
  11. package/components/Drawer/ResourceDetailDrawer/index.vue +2 -1
  12. package/components/ExplorerMembers.vue +18 -3
  13. package/components/PodSecurityAdmission.vue +1 -1
  14. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +1 -3
  15. package/components/Resource/Detail/Metadata/KeyValue.vue +8 -4
  16. package/components/Resource/Detail/Metadata/index.vue +3 -1
  17. package/components/Resource/Detail/TitleBar/Title.vue +4 -3
  18. package/components/Resource/Detail/TitleBar/Top.vue +2 -0
  19. package/components/Resource/Detail/TitleBar/index.vue +109 -24
  20. package/components/ResourceDetail/Masthead/legacy.vue +235 -164
  21. package/components/ResourceDetail/legacy.vue +29 -13
  22. package/components/ResourceList/Masthead.vue +22 -14
  23. package/components/SortableTable/index.vue +2 -2
  24. package/components/Tabbed/Tab.vue +1 -1
  25. package/components/Tabbed/index.vue +51 -32
  26. package/components/auth/Principal.vue +35 -11
  27. package/components/breadcrumb/index.vue +316 -0
  28. package/components/form/LabeledSelect.vue +3 -2
  29. package/components/form/NameNsDescription.vue +5 -5
  30. package/components/form/Taints.vue +2 -1
  31. package/components/form/WorkloadPorts.vue +143 -123
  32. package/components/nav/Header.vue +3 -4
  33. package/components/nav/NamespaceFilter.vue +1 -2
  34. package/edit/workload/index.vue +3 -3
  35. package/package.json +1 -1
  36. package/pages/account/index.vue +25 -79
  37. package/pages/c/_cluster/auth/roles/index.vue +38 -5
  38. package/pkg/tsconfig.json +9 -9
  39. package/pkg/vue.config.js +2 -2
  40. package/plugins/dashboard-store/resource-class.js +28 -27
  41. package/rancher-components/BadgeState/BadgeState.vue +33 -52
  42. package/rancher-components/Banner/Banner.vue +2 -2
  43. package/rancher-components/RcDropdown/RcDropdownMenu.vue +8 -7
  44. package/scripts/publish-shell.sh +1 -1
  45. package/store/i18n.js +3 -0
  46. package/store/type-map.js +1 -1
  47. package/types/shell/index.d.ts +4 -30
  48. package/utils/error.js +3 -1
  49. package/utils/errorTranslate.json +15 -0
  50. package/vue.config.js +5 -5
@@ -13,6 +13,8 @@ import { _CONFIG, _GRAPH, AS } from '@shell/config/query-params';
13
13
  import ButtonGroup from '@shell/components/ButtonGroup';
14
14
  import { ExtensionPoint, PanelLocation } from '@shell/core/types';
15
15
  import ExtensionPanel from '@shell/components/ExtensionPanel.vue';
16
+ import breadcrumb from '@shell/components/breadcrumb/index.vue'
17
+ import { useDefaultConfigTabProps } from '@shell/components/Drawer/ResourceDetailDrawer/composables';
16
18
 
17
19
  export interface Badge {
18
20
  color: 'bg-success' | 'bg-error' | 'bg-warning' | 'bg-info';
@@ -36,6 +38,8 @@ export interface TitleBarProps {
36
38
  showViewOptions?: boolean;
37
39
 
38
40
  onShowConfiguration?: (returnFocusSelector: string) => void;
41
+ value?: any;
42
+ parentRouteOverride?: string;
39
43
  }
40
44
 
41
45
  const showConfigurationIcon = require(`@shell/assets/images/icons/document.svg`);
@@ -43,7 +47,7 @@ const showConfigurationIcon = require(`@shell/assets/images/icons/document.svg`)
43
47
 
44
48
  <script setup lang="ts">
45
49
  const {
46
- resource, resourceTypeLabel, resourceTo, resourceName, description, badge, showViewOptions, onShowConfiguration,
50
+ parentRouteOverride, value, resource, resourceTypeLabel, resourceTo, resourceName, description, badge, showViewOptions, onShowConfiguration,
47
51
  } = defineProps<TitleBarProps>();
48
52
 
49
53
  const store = useStore();
@@ -54,6 +58,12 @@ const emit = defineEmits(['show-configuration']);
54
58
  const showConfigurationDataTestId = 'show-configuration-cta';
55
59
  const showConfigurationReturnFocusSelector = computed(() => `[data-testid="${ showConfigurationDataTestId }"]`);
56
60
 
61
+
62
+ const configTabProps = useDefaultConfigTabProps(resource);
63
+
64
+ console.log(configTabProps, ' configTabProps---------------------------------1');
65
+
66
+
57
67
  const currentView = ref(router?.currentRoute?.value?.query?.as || _CONFIG);
58
68
  const viewOptions = computed(() => {
59
69
  if (!showViewOptions) {
@@ -72,6 +82,42 @@ const viewOptions = computed(() => {
72
82
  ];
73
83
  });
74
84
 
85
+ const Svg = require('~shell/assets/images/API.svg')
86
+
87
+ const parent = computed(() =>{
88
+ const displayName = value?.parentNameOverride || store.getters['type-map/labelFor'](schema);
89
+ const product = store.getters['currentProduct'].name;
90
+
91
+ const defaultLocation = {
92
+ name: 'c-cluster-product-resource',
93
+ params: {
94
+ resource: resource,
95
+ product,
96
+ }
97
+ };
98
+
99
+ const location = value?.parentLocationOverride || defaultLocation;
100
+
101
+ if (parentRouteOverride) {
102
+ location.name = parentRouteOverride;
103
+ }
104
+
105
+ const out = {
106
+ location
107
+ };
108
+
109
+ return out;
110
+ })
111
+
112
+ const menuIcon = computed(() => {
113
+ const product = store.getters['productId'];
114
+
115
+ const resources = parent?.location?.params?.resource || ''
116
+
117
+ return store.getters['type-map/groupsForVirTypes'](product, resources);
118
+ })
119
+
120
+
75
121
  watch(
76
122
  () => currentView.value,
77
123
  () => {
@@ -82,33 +128,58 @@ watch(
82
128
 
83
129
  <template>
84
130
  <div class="title-bar">
131
+ <breadcrumb :resource="resource"/>
85
132
  <Top>
86
133
  <Title class="title">
134
+ <span v-if="menuIcon && parentRouteOverride === 'account' && resource=== 'token'" class="detailIcon-span">
135
+ <img
136
+ v-if="parentRouteOverride === 'account' && resource=== 'token'"
137
+ :src="Svg"
138
+ style="margin-top: 4px; margin-left: 5px;"
139
+ >
140
+ <i
141
+ v-else
142
+ :class="'icon-'+ menuIcon + ' detailIcon'"
143
+ />
144
+ </span>
145
+ <div class="resourceTypeLabelCls">
146
+ {{ resourceTypeLabel }}名称:
147
+ </div>
148
+ <span class="resource-name masthead-resource-title">
149
+ {{ resourceName }}
150
+ </span>
151
+ <BadgeState
152
+ v-if="badge"
153
+ class="badge-state"
154
+ :color="badge.color"
155
+ :label="badge.label"
156
+ />
157
+ <span
158
+ class="valid"
159
+ >|</span>
87
160
  <TabTitle :show-child="false">
88
- {{ resourceTypeLabel }}
161
+ 返回
89
162
  </TabTitle>
90
163
  <router-link
91
164
  v-if="resourceTo"
92
165
  :to="resourceTo"
93
166
  class="resource-link"
94
167
  >
95
- {{ resourceTypeLabel }}:
168
+ 返回
96
169
  </router-link>
97
170
  <span
98
171
  v-else
99
172
  class="resource-text"
100
173
  >
101
- {{ resourceTypeLabel }}:
102
- </span>
103
- <span class="resource-name masthead-resource-title">
104
- {{ resourceName }}
174
+ 返回
105
175
  </span>
106
- <BadgeState
107
- v-if="badge"
108
- class="badge-state"
109
- :color="badge.color"
110
- :label="badge.label"
111
- />
176
+ <div
177
+ v-if="description"
178
+ style="font-size: 12px;margin-left: 16px;"
179
+ class="bottom description text-deemphasized"
180
+ >
181
+ ( {{ description }} )
182
+ </div>
112
183
  </Title>
113
184
  <div class="actions">
114
185
  <!-- Please don't expand this pattern, this was a quick fix to resolve a conflict between the new masthead and fleet. -->
@@ -134,6 +205,7 @@ watch(
134
205
  </RcButton>
135
206
  <ActionMenu
136
207
  v-if="actionMenuResource"
208
+ :showIcon="true"
137
209
  button-role="multiAction"
138
210
  :resource="actionMenuResource"
139
211
  data-testid="masthead-action-menu"
@@ -141,12 +213,6 @@ watch(
141
213
  />
142
214
  </div>
143
215
  </Top>
144
- <div
145
- v-if="description"
146
- class="bottom description text-deemphasized"
147
- >
148
- {{ description }}
149
- </div>
150
216
  <ExtensionPanel
151
217
  :resource="resource"
152
218
  :type="ExtensionPoint.PANEL"
@@ -156,12 +222,27 @@ watch(
156
222
  </template>
157
223
 
158
224
  <style lang="scss" scoped>
225
+ .detailIcon-span{
226
+ width: 24px;
227
+ height: 24px;
228
+ display: inline-block;
229
+ position: relative;
230
+ background: var(--primary);
231
+ margin-right: 10px;
232
+ }
233
+ .resourceTypeLabelCls {
234
+ font-weight: 700;
235
+ }
236
+ .valid{
237
+ color: #d7d7d7;
238
+ margin: 0px 10px;
239
+ }
159
240
  .title-bar {
160
241
  min-width: 740px;
161
242
 
162
243
  .badge-state {
163
244
  font-size: 16px;
164
- margin-left: 12px;
245
+ margin-left: 5px;
165
246
  position: relative;
166
247
  }
167
248
 
@@ -170,15 +251,19 @@ watch(
170
251
  }
171
252
 
172
253
  &:deep() button[data-testid="masthead-action-menu"] {
173
- border-radius: 4px;
174
- width: 35px;
175
- height: 40px;
176
- margin-left: 16px;
254
+ border-radius: 2px;
255
+ width: 32px;
256
+ height: 32px;
257
+ margin-left: 10px;
177
258
 
178
259
  display: inline-flex;
179
260
  flex-direction: row;
180
261
  justify-content: center;
181
262
  align-items: center;
263
+
264
+ a{
265
+ color: #fff;
266
+ }
182
267
  }
183
268
 
184
269
  .description {
@@ -254,7 +254,7 @@ export default {
254
254
  },
255
255
 
256
256
  parent() {
257
- let displayName = this.value?.parentNameOverride || this.$store.getters['type-map/labelFor'](this.schema);
257
+ const displayName = this.value?.parentNameOverride || this.$store.getters['type-map/labelFor'](this.schema);
258
258
  const product = this.$store.getters['currentProduct'].name;
259
259
 
260
260
  const defaultLocation = {
@@ -491,192 +491,263 @@ export default {
491
491
  </script>
492
492
 
493
493
  <template>
494
-
495
- <!-- 顶部区域的容器 -->
496
494
  <div class="masthead">
497
495
  <div class="title">
498
-
499
- <!-- 面包屑导航(创建 API 密钥时不显示) -->
500
- <!-- <div
496
+ <!-- 创建api密钥不需要面包屑 -->
497
+ <div
501
498
  v-if="!(parentRouteOverride === 'account' && resource=== 'token')"
502
499
  class="excram-list"
503
500
  >
504
-
505
- 遍历 demoDisplay 生成面包屑路径
506
501
  <span
507
502
  v-for="(item,index) in demoDisplay"
508
503
  :key="index"
509
504
  >
510
- <span v-if="item">{{ item }}</span>
511
- <span v-if="item">/</span>
505
+ <span>{{ item }}</span>
506
+ <span>/</span>
512
507
  </span>
513
-
514
- 最后一个面包屑显示当前操作(查看/编辑/创建)+ 父资源名称
515
508
  <span class="excram-last-name">
516
509
  {{ (realMode === 'view'? '查看': realMode === 'edit' ? '编辑':'创建') + parent.displayName }}
517
510
  </span>
518
- </div> -->
519
-
520
- <header>
521
- <div class="title">
522
- <div class="primaryheader">
523
- <span class="primary-title">
524
-
525
- <!-- 图标区 -->
526
- <span v-if="menuIcon && !(parentRouteOverride === 'account' && resource=== 'token')" class="detailIcon-span">
527
- <!-- 如果是账户 API token,则显示图片,否则显示 icon -->
528
- <img
529
- v-if="parentRouteOverride === 'account' && resource=== 'token'"
530
- :src="Svg"
531
- style="margin-top: 4px; margin-left: 5px;"
532
- >
533
- <i
534
- v-else
535
- :class="'icon-'+ menuIcon + ' detailIcon'"
536
- />
537
- </span>
538
-
539
- <!-- 资源标题(创建时只显示“创建+名称”,否则显示“名称:”) -->
540
- <span class="detailIcon-span-title">{{ realMode=== 'create'? '创建': '' }}{{ parent.displayName }}{{ realMode=== 'create'? '': '名称:' }}</span>
541
-
542
- <!-- 如果不是创建模式,显示操作描述 -->
543
- <span v-if="realMode !== 'create'">
544
-
545
- <!-- 如果有覆盖方法,优先显示覆盖内容 -->
546
- <span v-if="value.detailPageHeaderActionOverride && value.detailPageHeaderActionOverride(realMode)">{{ value.detailPageHeaderActionOverride(realMode) }}</span>
547
-
548
- <!-- 否则用 t 组件国际化显示 -->
549
- <t
550
- v-else
551
- :k="'resourceDetail.header.' + realMode"
552
- :subtype="resourceSubtype"
553
- :name="displayName"
554
- :escapehtml="false"
555
- />
556
- </span>
557
-
558
- <!-- 状态点(DotState 组件) -->
559
- <DotState
560
- v-if="!isCreate && parent.showState"
561
- class="masthead-state"
562
- :value="value"
563
- />
564
-
565
- <!-- Istio 注入状态 -->
566
- <span
567
- v-if="!isCreate && value.injectionEnabled"
568
- class="masthead-istio"
511
+ </div>
512
+ <header>
513
+ <div class="title">
514
+ <div class="primaryheader">
515
+ <span class="primary-title">
516
+ <!-- <nuxt-link
517
+ v-if="location"
518
+ :to="location"
519
+ >
520
+ {{ parent.displayName }}:
521
+ </nuxt-link> -->
522
+ <span class="detailIcon-span">
523
+ <img
524
+ v-if="parentRouteOverride === 'account' && resource=== 'token'"
525
+ :src="Svg"
526
+ style="margin-top: 4px; margin-left: 5px;"
569
527
  >
570
- <i
571
- v-clean-tooltip="t('projectNamespaces.isIstioInjectionEnabled')"
572
- class="icon icon-sm icon-istio"
573
- />
574
- </span>
528
+ <i
529
+ v-else
530
+ :class="'icon-'+ menuIcon + ' detailIcon'"
531
+ />
575
532
  </span>
576
-
577
- <!-- 如果有返回路径,显示分隔符和“返回”链接 -->
533
+ <span class="detailIcon-span-title">{{ realMode=== 'create'? '创建': '' }}{{ parent.displayName }}{{ realMode=== 'create'? '': '名称:' }}</span>
534
+ <span v-if="realMode !== 'create'">
535
+ <span v-if="value.detailPageHeaderActionOverride && value.detailPageHeaderActionOverride(realMode)">{{ value.detailPageHeaderActionOverride(realMode) }}</span>
536
+ <t
537
+ v-else
538
+ :k="'resourceDetail.header.' + realMode"
539
+ :subtype="resourceSubtype"
540
+ :name="displayName"
541
+ :escapehtml="false"
542
+ />
543
+ </span>
544
+ <DotState
545
+ v-if="!isCreate && parent.showState"
546
+ class="masthead-state"
547
+ :value="value"
548
+ />
578
549
  <span
579
- v-if="location"
580
- class="valid"
581
- >|</span>
582
- <router-link
583
- v-if="location"
584
- :to="location"
550
+ v-if="!isCreate && value.injectionEnabled"
551
+ class="masthead-istio"
585
552
  >
586
- 返回
587
- </router-link>
588
- </div>
553
+ <i
554
+ v-clean-tooltip="t('projectNamespaces.isIstioInjectionEnabled')"
555
+ class="icon icon-sm icon-istio"
556
+ />
557
+ </span>
558
+ </span>
559
+ <span
560
+ v-if="location"
561
+ class="valid"
562
+ >|</span>
563
+ <router-link
564
+ v-if="location"
565
+ :to="location"
566
+ >
567
+ 返回
568
+ </router-link>
569
+ <!-- <h1>
570
+ <TabTitle
571
+ v-if="isCreate"
572
+ :showChild="false"
573
+ >
574
+ {{ parent.displayName }}
575
+ </TabTitle>
576
+ <TabTitle
577
+ v-else
578
+ :showChild="false"
579
+ >
580
+ {{ displayName }}
581
+ </TabTitle>
582
+ <router-link
583
+ v-if="location"
584
+ :to="location"
585
+ role="link"
586
+ class="masthead-resource-list-link"
587
+ :aria-label="parent.displayName"
588
+ >
589
+ {{ parent.displayName }}:
590
+ </router-link>
591
+ <span v-else>{{ parent.displayName }}:</span>
592
+ <span v-if="value?.detailPageHeaderActionOverride && value?.detailPageHeaderActionOverride(realMode)">{{ value?.detailPageHeaderActionOverride(realMode) }}</span>
593
+ <t
594
+ v-else
595
+ class="masthead-resource-title"
596
+ :k="'resourceDetail.header.' + realMode"
597
+ :subtype="resourceSubtype"
598
+ :name="displayName"
599
+ :escapehtml="false"
600
+ />
601
+ <BadgeState
602
+ v-if="!isCreate && parent.showState"
603
+ class="masthead-state"
604
+ :value="value"
605
+ />
606
+ <span
607
+ v-if="!isCreate && value.injectionEnabled"
608
+ class="masthead-istio"
609
+ >
610
+ <i
611
+ v-clean-tooltip="t('projectNamespaces.isIstioInjectionEnabled')"
612
+ class="icon icon-sm icon-istio"
613
+ />
614
+ </span>
615
+ <a
616
+ v-if="dev && !!resourceExternalLink"
617
+ v-clean-tooltip="t(resourceExternalLink.tipsKey || 'generic.resourceExternalLinkTips')"
618
+ class="resource-external"
619
+ rel="nofollow noopener noreferrer"
620
+ target="_blank"
621
+ :href="resourceExternalLink.url"
622
+ >
623
+ <i class="icon icon-external-link" />
624
+ </a>
625
+ </h1> -->
589
626
  </div>
590
-
591
- <!-- 右侧操作按钮区域(支持通过 slot 覆盖) -->
592
- <slot name="right">
593
- <div class="actions-container align-start" style="padding-right: 15px;">
594
- <div class="actions">
595
-
596
- <!-- 详情模式下的主操作按钮 -->
627
+ <!-- <div
628
+ v-if="!isCreate"
629
+ class="subheader"
630
+ >
631
+ <span v-if="isNamespace && project">{{ t("resourceDetail.masthead.project") }}: <router-link :to="project.detailLocation">{{ project.nameDisplay }}</router-link></span>
632
+ <span v-else-if="isWorkspace">{{ t("resourceDetail.masthead.workspace") }}: <router-link :to="workspaceLocation">{{ namespace }}</router-link></span>
633
+ <span v-else-if="namespace && !hasMultipleNamespaces">
634
+ {{ t("resourceDetail.masthead.namespace") }}:
635
+ <router-link
636
+ v-if="!hideNamespaceLocation"
637
+ :to="namespaceLocation"
638
+ data-testid="masthead-subheader-namespace"
639
+ >
640
+ {{ namespace }}
641
+ </router-link>
642
+ <span v-else>
643
+ {{ namespace }}
644
+ </span>
645
+ </span>
646
+ <span v-if="parent.showAge">
647
+ {{ t("resourceDetail.masthead.age") }}:
648
+ <LiveDate
649
+ class="live-date"
650
+ :value="value.creationTimestamp"
651
+ />
652
+ </span>
653
+ <span
654
+ v-if="value.showCreatedBy"
655
+ data-testid="masthead-subheader-createdBy"
656
+ >
657
+ {{ t("resourceDetail.masthead.createdBy") }}:
658
+ <router-link
659
+ v-if="value.createdBy.location"
660
+ :to="value.createdBy.location"
661
+ data-testid="masthead-subheader-createdBy-link"
662
+ >
663
+ {{ value.createdBy.displayName }}
664
+ </router-link>
665
+ <span
666
+ v-else
667
+ data-testid="masthead-subheader-createdBy_plain-text"
668
+ >
669
+ {{ value.createdBy.displayName }}
670
+ </span>
671
+ </span>
672
+ <span v-if="value.showPodRestarts">{{ t("resourceDetail.masthead.restartCount") }}:<span class="live-data"> {{ value.restartCount }}</span></span>
673
+ </div> -->
674
+ </div>
675
+ <slot name="right">
676
+ <div class="actions-container align-start">
677
+ <div class="actions">
678
+ <button
679
+ v-if="detailsAction && currentView === DETAIL_VIEW && isView"
680
+ type="button"
681
+ class="btn role-primary actions mr-10"
682
+ :disabled="!detailsAction.enabled"
683
+ @click="invokeDetailsAction"
684
+ >
685
+ {{ detailsAction.label }}
686
+ </button>
687
+ <ButtonGroup
688
+ v-if="showSensitiveToggle"
689
+ :value="!!hideSensitiveData"
690
+ icon-size="lg"
691
+ :options="sensitiveOptions"
692
+ class="mr-10"
693
+ @update:value="toggleSensitiveData"
694
+ />
695
+
696
+ <ButtonGroup
697
+ v-if="viewOptions && isView"
698
+ v-model:value="currentView"
699
+ :options="viewOptions"
700
+ class="mr-10"
701
+ />
702
+
703
+ <template v-if="featureDropdownMenu">
704
+ <ActionMenu
705
+ v-if="isView"
706
+ button-role="multiAction"
707
+ button-size="compact"
708
+ :resource="value"
709
+ data-testid="masthead-action-menu"
710
+ />
711
+ </template>
712
+ <template v-else>
597
713
  <button
598
- v-if="detailsAction && currentView === DETAIL_VIEW && isView && isManuallyHide"
714
+ v-if="isView"
715
+ ref="actions"
716
+ data-testid="masthead-action-menu"
717
+ aria-haspopup="true"
599
718
  type="button"
600
- class="btn role-primary actions mr-10"
601
- :disabled="!detailsAction.enabled"
602
- @click="invokeDetailsAction"
719
+ class="btn role-multi-action actions"
720
+ @click="showActions"
603
721
  >
604
- {{ detailsAction.label }}
722
+ <i class="icon icon-actions" />
605
723
  </button>
606
-
607
- <!-- 敏感信息显示/隐藏切换 -->
608
- <ButtonGroup
609
- v-if="showSensitiveToggle"
610
- :value="!!hideSensitiveData"
611
- icon-size="lg"
612
- :options="sensitiveOptions"
613
- class="mr-10"
614
- @update:value="toggleSensitiveData"
615
- />
616
-
617
- <!-- 视图切换按钮 -->
618
- <ButtonGroup
619
- v-if="viewOptions && isView"
620
- v-model:value="currentView"
621
- :options="viewOptions"
622
- class="mr-10"
623
- />
624
-
625
- <!-- 功能菜单:优先使用 ActionMenu 组件 -->
626
- <template v-if="featureDropdownMenu">
627
- <ActionMenu
628
- v-if="isView"
629
- button-role="multiAction"
630
- button-size="compact"
631
- :resource="value"
632
- data-testid="masthead-action-menu"
633
- />
634
- </template>
635
-
636
- <!-- 如果没有 featureDropdownMenu,则使用普通的多操作按钮 -->
637
- <template v-else>
638
- <button
639
- v-if="isView"
640
- ref="actions"
641
- data-testid="masthead-action-menu"
642
- aria-haspopup="true"
643
- type="button"
644
- class="btn role-multi-action actions"
645
- @click="showActions"
646
- >
647
- <i class="icon icon-actions" />
648
- </button>
649
- </template>
650
- </div>
724
+ </template>
651
725
  </div>
652
- </slot>
653
- </header>
654
-
655
- <!-- 扩展区域 -->
656
- <ExtensionPanel
657
- :resource="value"
658
- :type="extensionType"
659
- :location="extensionLocation"
660
- />
661
-
662
- <!-- 顶部状态 Banner -->
663
- <Banner
664
- v-if="banner && isView && !parent.hideBanner"
665
- class="state-banner mb-10"
666
- :color="banner.color"
667
- :label="banner.message == 'Waiting for API to be available' ? '等待 API 可用' : banner.message"
668
- />
669
-
670
- <!-- 管理警告 Banner -->
671
- <Banner
672
- v-if="managedWarning.show"
673
- color="warning"
674
- class="mb-20"
675
- :label="t('resourceDetail.masthead.managedWarning', managedWarning)"
676
- />
677
-
678
- <!-- 内容插槽 -->
679
- <slot />
726
+ </div>
727
+ </slot>
728
+ </header>
729
+
730
+ <!-- Extension area -->
731
+ <ExtensionPanel
732
+ :resource="value"
733
+ :type="extensionType"
734
+ :location="extensionLocation"
735
+ />
736
+
737
+ <Banner
738
+ v-if="banner && isView && !parent.hideBanner"
739
+ class="state-banner mb-10"
740
+ :color="banner.color"
741
+ :label="banner.message"
742
+ />
743
+ <Banner
744
+ v-if="managedWarning.show"
745
+ color="warning"
746
+ class="mb-20"
747
+ :label="t('resourceDetail.masthead.managedWarning', managedWarning)"
748
+ />
749
+
750
+ <slot />
680
751
  </div>
681
752
  </div>
682
753
  </template>