dashboard-shell-shell 3.0.5-test.11 → 3.0.5-test.13

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 (33) hide show
  1. package/assets/icons/iconfont.css +4 -1
  2. package/assets/images/pl/dark/logo.png +0 -0
  3. package/assets/styles/global/_tooltip.scss +5 -1
  4. package/assets/translations/zh-hans.yaml +1 -1
  5. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +22 -19
  6. package/components/Drawer/ResourceDetailDrawer/index.vue +2 -2
  7. package/components/GlobalRoleBindings.vue +1 -58
  8. package/components/Resource/Detail/TitleBar/index.vue +26 -23
  9. package/components/ResourceDetail/Masthead/index.vue +1 -1
  10. package/components/ResourceDetail/Masthead/latest.vue +1 -1
  11. package/components/ResourceDetail/Masthead/legacy.vue +4 -4
  12. package/components/SortableTable/index.vue +1 -1
  13. package/components/auth/Principal.vue +7 -3
  14. package/components/auth/RoleDetailEdit.vue +1 -1
  15. package/components/form/ArrayList.vue +1 -1
  16. package/components/form/KeyValue.vue +15 -2
  17. package/components/form/Labels.vue +1 -1
  18. package/components/form/MatchExpressions.vue +2 -2
  19. package/components/form/NameNsDescription.vue +1 -1
  20. package/components/form/ResourceQuota/NamespaceRow.vue +1 -1
  21. package/components/form/ResourceQuota/Project.vue +1 -1
  22. package/components/form/ResourceQuota/ProjectRow.vue +1 -1
  23. package/components/nav/NamespaceFilter.vue +14 -19
  24. package/components/nav/TopLevelMenu.vue +98 -125
  25. package/config/router/navigation-guards/index.js +52 -3
  26. package/initialize/app-extended.js +7 -1
  27. package/package.json +1 -1
  28. package/pages/account/index.vue +89 -65
  29. package/pages/home.vue +3 -4
  30. package/pkg/vue.config.js +1 -1
  31. package/rancher-components/Banner/Banner.vue +4 -1
  32. package/utils/errorTranslate.json +82 -2
  33. package/vue.config.js +1 -1
@@ -52,10 +52,7 @@ export default {
52
52
  helper.update(args);
53
53
  }
54
54
 
55
- const topLevelPermissions = sessionStorage.getItem('TOPLEVELPERMISSIONS') || ''
56
-
57
55
  return {
58
- topLevelPermissions,
59
56
  shown: false,
60
57
  displayVersion,
61
58
  fullVersion,
@@ -523,7 +520,7 @@ export default {
523
520
  </div>
524
521
 
525
522
  <!-- 品牌Logo -->
526
- <div v-if="topLevelPermissions && topLevelPermissions === 'superadmin'" class="side-menu-logo">
523
+ <div class="side-menu-logo">
527
524
  <BrandImage
528
525
  data-testid="side-menu__brand-img"
529
526
  :alt="t('nav.alt.mainMenuRancherLogo')"
@@ -534,7 +531,7 @@ export default {
534
531
 
535
532
  <!-- ====================== 菜单内容区 ====================== -->
536
533
  <div class="body">
537
- <div v-if="topLevelPermissions && topLevelPermissions === 'superadmin'">
534
+ <div>
538
535
 
539
536
  <!-- 首页按钮 -->
540
537
  <div @click="hide()">
@@ -650,7 +647,7 @@ export default {
650
647
  </template>
651
648
 
652
649
  <!-- ====================== 集群列表 ====================== -->
653
- <template v-if="topLevelPermissions && topLevelPermissions === 'superadmin' && !!allClustersCount">
650
+ <template v-if="!!allClustersCount">
654
651
  <div
655
652
  ref="clusterList"
656
653
  class="clusters"
@@ -833,7 +830,7 @@ export default {
833
830
  </div>
834
831
  </div>
835
832
 
836
- <!-- 查看所有集群按钮 -->
833
+ <!-- See all clusters -->
837
834
  <router-link
838
835
  v-if="allClustersCount > maxClustersToShow"
839
836
  class="clusters-all"
@@ -853,128 +850,104 @@ export default {
853
850
  </template>
854
851
 
855
852
  <!-- ====================== 多集群应用区 ====================== -->
856
- <div :style="!(topLevelPermissions && topLevelPermissions === 'superadmin') ? { placeContent: 'flex-start', display: 'flex', flexDirection: 'column' } : {}" class="category">
853
+ <div class="category">
857
854
  <!-- 多集群应用 -->
858
855
  <template v-if="multiClusterApps.length">
859
- <template v-if="topLevelPermissions && topLevelPermissions === 'superadmin'">
860
- <div
861
- class="category-title"
856
+ <div
857
+ class="category-title"
858
+ >
859
+ <hr role="none">
860
+ <span>
861
+ {{ t('nav.categories.multiCluster') }}
862
+ </span>
863
+ </div>
864
+ <div
865
+ v-for="(a, i) in appBar.multiClusterApps"
866
+ :key="i"
867
+ @click="hide()"
868
+ >
869
+ <router-link
870
+ class="option"
871
+ :class="{'active-menu-link': a.isMenuActive }"
872
+ :to="a.to"
873
+ role="link"
874
+ :aria-label="`${t('nav.ariaLabel.multiClusterApps')} ${ a.label }`"
862
875
  >
863
- <hr role="none">
864
- <span>
865
- {{ t('nav.categories.multiCluster') }}
866
- </span>
867
- </div>
868
- <template v-for="(a, i) in appBar.multiClusterApps" :key="i">
869
- <div
870
- v-if="a.value === 'harvesterManager'"
871
- @click="hide()"
872
- >
873
- <router-link
874
- class="option"
875
- :class="{'active-menu-link': a.isMenuActive }"
876
- :to="a.to"
877
- role="link"
878
- :aria-label="`${t('nav.ariaLabel.multiClusterApps')} ${ a.label }`"
879
- >
880
- <IconOrSvg
881
- v-clean-tooltip="getTooltipConfig(a.label)"
882
- class="app-icon"
883
- :icon="a.icon"
884
- :src="a.svg"
885
- />
886
- <span class="option-link">{{ a.label }}</span>
887
- </router-link>
888
- </div>
889
- </template>
890
- </template>
891
- <template v-else>
892
- <template v-for="(a, i) in appBar.multiClusterApps" :key="i">
893
- <div
894
- v-if="a.value === 'harvesterManager'"
895
- @click="hide()"
896
- >
897
- <router-link
898
- class="option"
899
- :class="{'active-menu-link': a.isMenuActive }"
900
- :to="a.to"
901
- role="link"
902
- :aria-label="`${t('nav.ariaLabel.multiClusterApps')} ${ a.label }`"
903
- >
904
- <IconOrSvg
905
- v-clean-tooltip="getTooltipConfig(a.label)"
906
- class="app-icon"
907
- :icon="a.icon"
908
- :src="a.svg"
909
- />
910
- <span class="option-link">{{ a.label }}</span>
911
- </router-link>
912
- </div>
913
- </template>
914
- </template>
876
+ <IconOrSvg
877
+ v-clean-tooltip="getTooltipConfig(a.label)"
878
+ class="app-icon"
879
+ :icon="a.icon"
880
+ :src="a.svg"
881
+ />
882
+ <span class="option-link">{{ a.label }}</span>
883
+ </router-link>
884
+ </div>
915
885
  </template>
916
886
 
917
- <!-- 配置类应用 -->
918
- <div>
919
- <template v-if="configurationApps.length">
920
- <template v-if="topLevelPermissions && topLevelPermissions === 'superadmin'">
921
- <div
922
- class="category-title"
923
- >
924
- <hr role="none">
925
- <span>
926
- {{ t('nav.categories.configuration') }}
927
- </span>
928
- </div>
929
- <div
930
- v-for="(a, i) in appBar.configurationApps"
931
- :key="i"
932
- @click="hide()"
933
- >
934
- <router-link
935
- class="option"
936
- :class="{'active-menu-link': a.isMenuActive }"
937
- :to="a.to"
938
- role="link"
939
- :aria-label="`${t('nav.ariaLabel.configurationApps')} ${ a.label }`"
940
- >
941
- <IconOrSvg
942
- v-clean-tooltip="getTooltipConfig(a.label)"
943
- class="app-icon"
944
- :icon="a.icon"
945
- :src="a.svg"
946
- />
947
- <div>{{ a.label }}</div>
948
- </router-link>
949
- </div>
950
- </template>
951
- <template v-else>
952
- <template v-for="(a, i) in appBar.configurationApps" :key="i">
953
- <div
954
- v-if="a.value === 'settings' || a.value === 'auth'"
955
- @click="hide()"
956
- >
957
- <router-link
958
- class="option"
959
- :class="{'active-menu-link': a.isMenuActive }"
960
- :to="a.to"
961
- role="link"
962
- :aria-label="`${t('nav.ariaLabel.configurationApps')} ${ a.label }`"
963
- >
964
- <IconOrSvg
965
- v-clean-tooltip="getTooltipConfig(a.label)"
966
- class="app-icon"
967
- :icon="a.icon"
968
- :src="a.svg"
969
- />
970
- <div>{{ a.label }}</div>
971
- </router-link>
972
- </div>
973
- </template>
974
- </template>
975
- <div style="height: 40px;"></div>
976
- </template>
977
- </div>
887
+ <!-- Configuration apps menu -->
888
+ <template v-if="configurationApps.length">
889
+ <div
890
+ class="category-title"
891
+ >
892
+ <hr role="none">
893
+ <span>
894
+ {{ t('nav.categories.configuration') }}
895
+ </span>
896
+ </div>
897
+ <div
898
+ v-for="(a, i) in appBar.configurationApps"
899
+ :key="i"
900
+ @click="hide()"
901
+ >
902
+ <router-link
903
+ class="option"
904
+ :class="{'active-menu-link': a.isMenuActive }"
905
+ :to="a.to"
906
+ role="link"
907
+ :aria-label="`${t('nav.ariaLabel.configurationApps')} ${ a.label }`"
908
+ >
909
+ <IconOrSvg
910
+ v-clean-tooltip="getTooltipConfig(a.label)"
911
+ class="app-icon"
912
+ :icon="a.icon"
913
+ :src="a.svg"
914
+ />
915
+ <div>{{ a.label }}</div>
916
+ </router-link>
917
+ </div>
918
+ </template>
919
+ </div>
920
+ </div>
921
+
922
+ <!-- Footer -->
923
+ <div
924
+ class="footer"
925
+ >
926
+ <div
927
+ v-if="canEditSettings"
928
+ class="support"
929
+ @click="hide()"
930
+ >
931
+ <router-link
932
+ :to="{name: 'support'}"
933
+ role="link"
934
+ :aria-label="t('nav.ariaLabel.support')"
935
+ >
936
+ {{ t('nav.support', {hasSupport}) }}
937
+ </router-link>
938
+ </div>
939
+ <div
940
+ class="version"
941
+ :class="{'version-small': largeAboutText}"
942
+ @click="hide()"
943
+ >
944
+ <router-link
945
+ :to="{ name: 'about' }"
946
+ role="link"
947
+ :aria-label="t('nav.ariaLabel.about')"
948
+ >
949
+ {{ aboutText }}
950
+ </router-link>
978
951
  </div>
979
952
  </div>
980
953
  </div>
@@ -1644,7 +1617,7 @@ export default {
1644
1617
  }
1645
1618
 
1646
1619
  :deep() .v-popper__arrow-container {
1647
- display: none;
1620
+ display: none !important;
1648
1621
  }
1649
1622
 
1650
1623
  :deep() .v-popper:focus {
@@ -11,13 +11,62 @@ import { install as installPageTitle } from '@shell/config/router/navigation-gua
11
11
  import { install as installServerUpgradeGrowl } from '@shell/config/router/navigation-guards/server-upgrade-growl';
12
12
 
13
13
  /**
14
- * Install our router navigation guards. i.e. router.beforeEach(), router.afterEach()
14
+ * Install router navigation guards
15
+ * 1. 保留原有导航守卫顺序
16
+ * 2. 不在 beforeEach 改 params,只在 push/replace & afterEach 改 URL
15
17
  */
16
18
  export function installNavigationGuards(router, context) {
17
- // NOTE: the order of the installation matters.
18
- // Be intentional when adding, removing or modifying the guards that are installed.
19
+ // 最早执行:保证进入逻辑时 params 始终是 harvester
20
+ router.beforeEach((to, from, next) => {
21
+ let changed = false;
22
+ const params = { ...to.params };
23
+
24
+ if (params?.product?.includes('cloud')) {
25
+ params.product = params.product.replace(/cloud/g, 'harvester');
26
+ changed = true;
27
+ }
28
+
29
+ if (params?.cluster?.includes('cloud')) {
30
+ params.cluster = params.cluster.replace(/cloud/g, 'harvester');
31
+ changed = true;
32
+ }
33
+
34
+ if (params?.resource?.includes('cloud')) {
35
+ params.resource = params.resource.replace(/cloud/g, 'harvester');
36
+ changed = true;
37
+ }
38
+
39
+ if (changed) {
40
+ // 触发路由重定向
41
+ return next({ ...to, params });
42
+ }
43
+
44
+ next();
45
+ });
46
+
19
47
 
20
48
  const navigationGuardInstallers = [installLoadInitialSettings, installAttemptFirstLogin, installAuthentication, installProducts, installClusters, installRuntimeExtensionRoute, installI18N, installHandleInstallRedirect, installPageTitle, installRecordLastRoute, installServerUpgradeGrowl];
21
49
 
22
50
  navigationGuardInstallers.forEach((installer) => installer(router, context));
51
+
52
+ // 🔹 最后执行:只改地址栏,不改内部
53
+ router.afterEach((to) => {
54
+ const base = router.options.history?.base || ''; // 获取 router base,防止丢失
55
+ let cloudPath = to.fullPath.replace(/harvester/g, 'cloud');
56
+
57
+ // fullPath 可能已经不带 base,需要拼回去
58
+ if (!cloudPath.startsWith(base)) {
59
+ cloudPath = base + cloudPath;
60
+ }
61
+
62
+ // 获取当前地址栏完整 URL(path + search + hash)
63
+ const currentLocation = window.location.pathname + window.location.search + window.location.hash;
64
+
65
+ // 只有在地址栏实际不同的时候才替换,避免死循环
66
+ if (cloudPath !== currentLocation) {
67
+ console.info('[URL Replace] Updating address bar:', currentLocation, '→', cloudPath);
68
+ window.history.replaceState({}, '', cloudPath);
69
+ }
70
+ });
23
71
  }
72
+
@@ -55,7 +55,13 @@ async function extendApp(vueApp) {
55
55
  const next = (location) => appPartials.router.push(location);
56
56
  // Resolve route
57
57
 
58
- const path = getLocation(router.options.base, router.options.mode);
58
+ let path = getLocation(router.options.base, router.options.mode);
59
+
60
+ // 🔹 增加 rewrite,不改变原逻辑
61
+ if (path.includes('/cloud/')) {
62
+ path = path.replace(/cloud/g, 'harvester');
63
+ }
64
+
59
65
  const route = router.resolve(path);
60
66
 
61
67
  // Set context to app.context
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dashboard-shell-shell",
3
- "version": "3.0.5-test.11",
3
+ "version": "3.0.5-test.13",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -151,83 +151,102 @@ export default {
151
151
  <template>
152
152
  <Loading v-if="$fetchState.pending" />
153
153
  <div v-else>
154
- <BackLink :link="backLink" />
155
- <h1>
156
- <TabTitle breadcrumb="vendor-only">
157
- {{ t('accountAndKeys.title') }}
158
- </TabTitle>
159
- </h1>
160
-
161
- <h2 v-t="'accountAndKeys.account.title'" />
162
- <div class="account">
163
- <Principal
164
- :value="principal.id"
165
- :use-muted="false"
166
- :show-labels="true"
167
- />
168
- <div>
169
- <button
170
- v-if="canChangePassword"
171
- role="button"
172
- :aria-label="t('accountAndKeys.account.change')"
173
- type="button"
174
- class="btn role-primary"
175
- data-testid="account_change_password"
176
- @click="showChangePasswordDialog"
177
- >
178
- {{ t("accountAndKeys.account.change") }}
179
- </button>
154
+ <div style="display: flex;align-items: center;margin-bottom: 20px;">
155
+ <!-- <BackLink class="backLinkCls" :link="backLink" /> -->
156
+ <div style="font-size: 26px;">
157
+ <TabTitle breadcrumb="vendor-only">
158
+ {{ t('accountAndKeys.title') }}
159
+ </TabTitle>
160
+ </div>
161
+ </div>
162
+
163
+ <div class="account_card mb-20">
164
+ <h5 v-t="'accountAndKeys.account.title'" />
165
+ <div class="account">
166
+ <Principal
167
+ :userLogoSize="79"
168
+ :value="principal.id"
169
+ :use-muted="false"
170
+ :show-labels="true"
171
+ />
172
+ <div>
173
+ <button
174
+ v-if="canChangePassword"
175
+ role="button"
176
+ :aria-label="t('accountAndKeys.account.change')"
177
+ type="button"
178
+ class="btn role-primary"
179
+ data-testid="account_change_password"
180
+ @click="showChangePasswordDialog"
181
+ >
182
+ {{ t("accountAndKeys.account.change") }}
183
+ </button>
184
+ </div>
180
185
  </div>
181
186
  </div>
182
187
 
183
- <hr role="none">
184
- <div class="keys-header">
185
- <div>
186
- <h2 v-t="'accountAndKeys.apiKeys.title'" />
187
- <div class="api-url">
188
- <span>{{ t("accountAndKeys.apiKeys.apiEndpoint") }}</span>
189
- <CopyToClipboardText
190
- :aria-label="t('accountAndKeys.apiKeys.copyApiEnpoint')"
191
- :text="apiUrl"
192
- />
188
+ <div class="account_card mb-20">
189
+ <div class="keys-header">
190
+ <div>
191
+ <h5 v-t="'accountAndKeys.apiKeys.title'" />
192
+ <div class="api-url">
193
+ <span>{{ t("accountAndKeys.apiKeys.apiEndpoint") }}</span>
194
+ <CopyToClipboardText
195
+ :aria-label="t('accountAndKeys.apiKeys.copyApiEnpoint')"
196
+ :text="apiUrl"
197
+ />
198
+ </div>
193
199
  </div>
194
200
  </div>
195
- <button
201
+ <div
196
202
  v-if="apiKeySchema"
197
- role="button"
198
- :aria-label="t('accountAndKeys.apiKeys.add.label')"
199
- class="btn role-primary add mb-20"
200
- data-testid="account_create_api_keys"
201
- @click="addKey"
203
+ class="keys"
202
204
  >
203
- {{ t('accountAndKeys.apiKeys.add.label') }}
204
- </button>
205
- </div>
206
- <div
207
- v-if="apiKeySchema"
208
- class="keys"
209
- >
210
- <ResourceTable
211
- :schema="apiKeySchema"
212
- :rows="apiKeys"
213
- :headers="apiKeyheaders"
214
- key-field="id"
215
- data-testid="api_keys_list"
216
- :search="true"
217
- :row-actions="true"
218
- :table-actions="true"
219
- />
220
- </div>
221
- <div v-else>
222
- <Banner
223
- color="warning"
224
- :label="t('accountAndKeys.apiKeys.notAllowed')"
225
- />
205
+ <ResourceTable
206
+ :schema="apiKeySchema"
207
+ :rows="apiKeys"
208
+ :headers="apiKeyheaders"
209
+ key-field="id"
210
+ data-testid="api_keys_list"
211
+ :search="true"
212
+ :row-actions="true"
213
+ :table-actions="true"
214
+ >
215
+ <template #header-right>
216
+ <button
217
+ style="margin-right: 10px;"
218
+ v-if="apiKeySchema"
219
+ role="button"
220
+ :aria-label="t('accountAndKeys.apiKeys.add.label')"
221
+ class="btn role-primary add"
222
+ data-testid="account_create_api_keys"
223
+ @click="addKey"
224
+ >
225
+ {{ t('accountAndKeys.apiKeys.add.label') }}
226
+ </button>
227
+ </template>
228
+ </ResourceTable>
229
+ </div>
230
+ <div v-else>
231
+ <Banner
232
+ color="warning"
233
+ :label="t('accountAndKeys.apiKeys.notAllowed')"
234
+ />
235
+ </div>
226
236
  </div>
227
237
  </div>
228
238
  </template>
229
239
 
230
240
  <style lang='scss' scoped>
241
+ .account_card {
242
+ border: 1px solid #d7d7d7;
243
+ padding: 20px;
244
+ box-sizing: border-box;
245
+ }
246
+ :deep() .back-link {
247
+ font-size: 26px;
248
+ margin: 0 20px 0 0 !important;
249
+ }
231
250
  hr {
232
251
  margin: 20px 0;
233
252
  }
@@ -237,6 +256,10 @@ export default {
237
256
  justify-content: space-between
238
257
  }
239
258
 
259
+ .principal .avatar {
260
+ width: 287px !important;
261
+ }
262
+
240
263
  .keys-header {
241
264
  display: flex;
242
265
  div {
@@ -254,6 +277,7 @@ export default {
254
277
 
255
278
  .api-url {
256
279
  display: flex;
280
+ margin: 20px 0;
257
281
 
258
282
  > span {
259
283
  margin-right: 6px;
package/pages/home.vue CHANGED
@@ -486,7 +486,6 @@ export default defineComponent({
486
486
 
487
487
  <!-- 页签标题组件,不显示子页面标题和面包屑 -->
488
488
  <TabTitle
489
- v-if="topLevelPermissionsVis"
490
489
  :show-child="false"
491
490
  :breadcrumb="false"
492
491
  >
@@ -496,14 +495,14 @@ export default defineComponent({
496
495
  </TabTitle>
497
496
 
498
497
  <!-- 首页欢迎横幅 -->
499
- <BannerGraphic
498
+ <!-- <BannerGraphic
500
499
  v-if="topLevelPermissionsVis"
501
500
  :small="true"
502
501
  :title="t('landing.welcomeToRancher', {vendor})"
503
502
  :pref="HIDE_HOME_PAGE_CARDS"
504
503
  pref-key="welcomeBanner"
505
504
  data-testid="home-banner-graphic"
506
- />
505
+ /> -->
507
506
  <IndentedPanel class="mt-20 mb-20">
508
507
 
509
508
  <!-- 主面板布局 -->
@@ -548,7 +547,7 @@ export default defineComponent({
548
547
  v-if="canCreateCluster || !!provClusterSchema"
549
548
  #header-right
550
549
  >
551
- <div v-if="topLevelPermissionsVis" class="table-heading">
550
+ <div class="table-heading">
552
551
  <router-link
553
552
  v-if="!!provClusterSchema"
554
553
  :to="manageLocation"
package/pkg/vue.config.js CHANGED
@@ -73,7 +73,7 @@ module.exports = function(dir) {
73
73
  });
74
74
 
75
75
  // Auto-generate module to import the types (model, detail, edit etc)
76
- const autoImportPlugin = new VirtualModulesPlugin({ 'node_modules/dashboard-shell-shell/auto-import': generateTypeImport('@pkg', dir) });
76
+ const autoImportPlugin = new VirtualModulesPlugin({ 'node_modules/@rancher/auto-import': generateTypeImport('@pkg', dir) });
77
77
 
78
78
  config.plugins.unshift(dynamicImporterOverride);
79
79
  config.plugins.unshift(modelLoaderImporterOverride);
@@ -164,6 +164,7 @@ $icon-size: 24px;
164
164
  margin: 0px 0px 20px 0px;
165
165
  position: relative;
166
166
  width: 100%;
167
+ height: 32px;
167
168
  color: var(--body-text);
168
169
 
169
170
  &__icon {
@@ -201,13 +202,15 @@ $icon-size: 24px;
201
202
  }
202
203
 
203
204
  &__content {
204
- padding: 9px 10px;
205
+ padding: 0px 10px;
205
206
  transition: all 0.2s ease;
206
207
  line-height: 12px;
207
208
  width: 100%;
208
209
  border-left: solid $left-border-size transparent;
209
210
  display: flex;
210
211
  gap: 3px;
212
+ height: 32px;
213
+ align-items: center;
211
214
  word-wrap:break-word;
212
215
  word-break:break-all;
213
216