dashboard-shell-shell 3.0.5-test.25 → 3.0.5-test.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/styles/all.scss +18 -0
- package/assets/styles/fonts/_icons.scss +3 -2
- package/assets/translations/zh-hans.yaml +14 -6
- package/components/PodSecurityAdmission.vue +1 -1
- package/components/ResourceDetail/Masthead/legacy.vue +3 -2
- package/components/ResourceList/Masthead.vue +4 -1
- package/components/SideNav.vue +20 -20
- package/components/SortableTable/THead.vue +21 -1
- package/components/SortableTable/index.vue +12 -2
- package/components/breadcrumb/index.vue +2 -7
- package/components/form/Conditions.vue +15 -1
- package/components/form/Select.vue +1 -1
- package/components/formatter/WorkloadHealthScale.vue +4 -3
- package/components/nav/Group.vue +6 -0
- package/components/nav/Header.vue +7 -4
- package/components/nav/Type.vue +12 -0
- package/config/menuRouteMap.js +10 -0
- package/config/product/explorer.js +1 -0
- package/config/router/navigation-guards/index.js +14 -5
- package/edit/configmap.vue +4 -0
- package/edit/workload/index.vue +2 -2
- package/models/provisioning.cattle.io.cluster.js +19 -18
- package/package.json +1 -1
- package/utils/errorTranslate.json +14 -0
package/assets/styles/all.scss
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/* Chrome, Edge, Safari */
|
|
2
|
+
input[type="password"]::-webkit-textfield-decoration-container,
|
|
3
|
+
input[type="password"]::-webkit-inner-spin-button,
|
|
4
|
+
input[type="password"]::-webkit-clear-button {
|
|
5
|
+
display: none !important;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Edge / IE */
|
|
9
|
+
input[type="password"]::-ms-reveal {
|
|
10
|
+
display: none !important;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Firefox */
|
|
14
|
+
input[type="password"]::-moz-reveal {
|
|
15
|
+
display: none !important;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
1
19
|
.info-tab-title{
|
|
2
20
|
font-size: 14px;
|
|
3
21
|
line-height: 19px;
|
|
@@ -596,6 +596,7 @@ assignTo:
|
|
|
596
596
|
|
|
597
597
|
stateLabel:
|
|
598
598
|
Pending migration: 待迁移
|
|
599
|
+
Imagepullbackoff: 镜像拉取失败
|
|
599
600
|
VM error: 错误
|
|
600
601
|
Locked: 锁定
|
|
601
602
|
Bound: 已绑定
|
|
@@ -5532,7 +5533,12 @@ tableHeaders:
|
|
|
5532
5533
|
missingPL: 缺少 PL
|
|
5533
5534
|
memory: 内存
|
|
5534
5535
|
monitored: 已监控
|
|
5536
|
+
request: 资源请求量
|
|
5537
|
+
limit: 资源限制
|
|
5535
5538
|
name: 名称
|
|
5539
|
+
maxunavailable: 最大不可用数
|
|
5540
|
+
minAvailable: 最小可用数
|
|
5541
|
+
alloweddisruptions: 允许中断数
|
|
5536
5542
|
nameDisplay: 显示名称
|
|
5537
5543
|
nameUnlinked: 名称
|
|
5538
5544
|
namespace: 资源组
|
|
@@ -6520,10 +6526,6 @@ typeDescription:
|
|
|
6520
6526
|
projectsnamespaces: 项目/资源组是用于在集群内实现资源隔离和多租户管理的基本单元。通过项目可以对一组命名空间及相关资源进行统一的权限、配额和策略管理。
|
|
6521
6527
|
members: 集群和项目成员管理页面用于配置用户和用户组对资源的访问权限。通过角色绑定(RBAC)来控制成员在集群或项目范围内的操作权限,实现安全的协同管理。
|
|
6522
6528
|
event: 事件记录了 Kubernetes 资源(如 Pod、节点等)的状态变化、错误信息和生命周期事件。通过查看事件可以快速诊断应用部署和运行中的问题。
|
|
6523
|
-
apps.deployment: Deployment 提供了对 Pod 和 ReplicaSet 的声明式更新管理。您可以定义应用的期望状态(如副本数、镜像版本),Deployment 控制器将以受控的方式逐步完成部署和滚动更新。
|
|
6524
|
-
batch.job: Job 用于创建一个或多个 Pod 并确保指定数量的 Pod 成功终止。适用于运行一次性任务或批处理作业,任务完成后 Pod 不会自动重启。
|
|
6525
|
-
apps.statefulset: StatefulSet 用于管理有状态应用的工作负载 API 对象。它为每个 Pod 提供稳定的网络标识符和持久化存储,确保 Pod 的部署、扩展、更新和删除都有序进行。
|
|
6526
|
-
pod: Pod 是 Kubernetes 中最小的可部署计算单元,包含一个或多个共享存储和网络资源的容器。Pod 代表了集群中运行的单个应用实例,是工作负载执行的实际载体。
|
|
6527
6529
|
autoscaling.horizontalpodautoscaler: HorizontalPodAutoscaler (HPA) 用于根据观察到的 CPU 利用率或其他自定义指标自动调整工作负载的 Pod 副本数量,实现应用的水平自动扩缩容。
|
|
6528
6530
|
networking.k8s.io.ingress: Ingress 用于管理对集群内服务的外部访问,提供 HTTP 和 HTTPS 路由规则。通过 Ingress 可以配置负载均衡、SSL 终止和基于名称的虚拟主机等能力。
|
|
6529
6531
|
service: Service 用于将一组 Pod 暴露为网络服务,提供稳定的 IP 地址和 DNS 名称,并实现负载均衡。Service 确保您的应用程序在网络中可被发现和可靠访问。
|
|
@@ -6537,6 +6539,12 @@ typeDescription:
|
|
|
6537
6539
|
limitrange: 限制范围用于在命名空间内限制资源的使用量,包括 Pod 的计算资源(CPU、内存)请求和限制、存储卷大小以及可创建的资源对象数量,防止资源过度消耗。
|
|
6538
6540
|
resourcequota: 资源配额用于限制命名空间可以使用的总体资源总量,包括计算资源、存储资源以及可创建的对象数量。它确保命名空间内的资源使用不会超过分配的配额限制。
|
|
6539
6541
|
policy.poddisruptionbudget: Pod 中断预算用于限制自愿中断期间同时终止的 Pod 副本数量,确保应用的高可用性。它可以防止在节点维护或集群缩容时导致的应用服务中断。
|
|
6542
|
+
apps.daemonset: DaemonSet 在每个符合条件的节点上仅运行一个 Pod。当新节点添加到集群时,DaemonSet 会自动部署新节点。推荐用于全系统或可垂直扩展,且每个节点永远不需要超过一个 pod 的工作负载。
|
|
6543
|
+
apps.deployment: Deployment 运行分布在符合条件的节点中的可扩展数量的 Pod 副本。变更会逐步推出,并可回滚到之前的版本。推荐用于无状态和水平可扩展的工作负载。
|
|
6544
|
+
apps.statefulset: StatefulSet 管理有状态的应用,并保证创建的 Pod 的顺序和唯一性。推荐用于具有持久化存储或严格身份、法定人数或升级顺序要求的工作负载。
|
|
6545
|
+
batch.cronjob: CronJob 创建 Job,然后按照重复调度来运行 Pod。该调度以标准的 Unix cron 格式表示,并使用 Kubernetes Control Plane 的时区(通常是 UTC)。
|
|
6546
|
+
batch.job: Job 创建一个或多个 Pod。 Job 通过运行 Pod 直到其成功退出,以可靠执行一次性任务。失败的 Pod 会自动被替换,直到达到指定的完成运行次数。Job 还可以并行运行多个 Pod,或作为批处理工作队列。
|
|
6547
|
+
pod: Pod 是你可以在 Kubernetes 中创建和管理的最小可部署计算单元。Pod 是一个或多个容器,具有共享的存储和网络资源以及运行容器的规范。
|
|
6540
6548
|
typeLabel:
|
|
6541
6549
|
management.cattle.io.project: |-
|
|
6542
6550
|
{count, plural,
|
|
@@ -6786,8 +6794,8 @@ typeLabel:
|
|
|
6786
6794
|
}
|
|
6787
6795
|
autoscaling.horizontalpodautoscaler: |-
|
|
6788
6796
|
{count, plural,
|
|
6789
|
-
one {
|
|
6790
|
-
other {
|
|
6797
|
+
one { HPA }
|
|
6798
|
+
other { HPA }
|
|
6791
6799
|
}
|
|
6792
6800
|
networking.k8s.io.ingress: |-
|
|
6793
6801
|
{count, plural,
|
|
@@ -418,7 +418,7 @@ export default {
|
|
|
418
418
|
demoDisplay() {
|
|
419
419
|
const product = this.$store.getters['productId'];
|
|
420
420
|
|
|
421
|
-
const resources = this.location?.params?.resource || ''
|
|
421
|
+
const resources = this.location?.params?.resource || this.$route.params?.resource || ''
|
|
422
422
|
|
|
423
423
|
const productId = this.$store.getters['type-map/groupForBasicType'](this.$store.getters['productId'], resources);
|
|
424
424
|
|
|
@@ -441,7 +441,8 @@ export default {
|
|
|
441
441
|
menuIcon() {
|
|
442
442
|
const product = this.$store.getters['productId'];
|
|
443
443
|
|
|
444
|
-
const resources = this.location?.params?.resource || ''
|
|
444
|
+
const resources = this.location?.params?.resource || this.$route.params?.resource || ''
|
|
445
|
+
|
|
445
446
|
|
|
446
447
|
return this.$store.getters['type-map/groupsForVirTypes'](product, resources) || 'default menuIcon';
|
|
447
448
|
},
|
|
@@ -157,9 +157,12 @@ export default {
|
|
|
157
157
|
|
|
158
158
|
const product = this.$store.getters['productId'];
|
|
159
159
|
const productId = this.$store.getters['type-map/groupForBasicType'](this.$store.getters['productId'], this._createLocation?.params?.resource);
|
|
160
|
-
console.log(
|
|
160
|
+
console.log(product, 'product')
|
|
161
|
+
console.log(productId, 'productId')
|
|
161
162
|
console.log(this._createLocation?.params?.resource, 'this._createLocation?.params?.resource')
|
|
163
|
+
|
|
162
164
|
let parts = productId?.split('::');
|
|
165
|
+
console.log(parts, 'parts11111')
|
|
163
166
|
const newString = 'root';
|
|
164
167
|
|
|
165
168
|
if (!parts) {
|
package/components/SideNav.vue
CHANGED
|
@@ -244,31 +244,31 @@ export default {
|
|
|
244
244
|
|
|
245
245
|
replaceWith(this.groups, ...sortBy(out, ['weight:desc', 'label']));
|
|
246
246
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
|
|
248
|
+
if (sessionStorage.getItem('TOPLEVELPERMISSIONS') !== 'superadmin') {
|
|
249
|
+
// 递归过滤函数(根据 label)
|
|
250
|
+
this.groups = this.filterMenus(this.groups);
|
|
251
|
+
}
|
|
251
252
|
|
|
252
253
|
|
|
253
254
|
this.gettingGroups = false;
|
|
254
255
|
},
|
|
255
256
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
// },
|
|
257
|
+
filterMenus(menus) {
|
|
258
|
+
return menus.filter(item => item.name !== 'inUse' && item.name !== 'apps') // 过滤掉顶层 inUse 和 apps
|
|
259
|
+
// .map(item => {
|
|
260
|
+
// let newItem = { ...item };
|
|
261
|
+
// if (newItem.children) {
|
|
262
|
+
// // 过滤掉 children 里的 "资源大盘"
|
|
263
|
+
// newItem.children = newItem.children.filter(
|
|
264
|
+
// child => child.label !== '资源大盘'
|
|
265
|
+
// );
|
|
266
|
+
// // 递归处理剩下的 children
|
|
267
|
+
// newItem.children = filterMenus(newItem.children);
|
|
268
|
+
// }
|
|
269
|
+
// return newItem;
|
|
270
|
+
// });
|
|
271
|
+
},
|
|
272
272
|
|
|
273
273
|
getProductsGroups(out, loadProducts, namespaceMode, productMap) {
|
|
274
274
|
const clusterId = this.$store.getters['clusterId'];
|
|
@@ -208,9 +208,29 @@ export default {
|
|
|
208
208
|
return null;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
// 创建英文到中文的映射
|
|
212
|
+
const tooltipMap = {
|
|
213
|
+
'The minimum number of pods that must be available': '必须可用的 Pod 最小数量',
|
|
214
|
+
'The maximum number of pods that may be unavailable': '允许不可用的 Pod 最大数量',
|
|
215
|
+
'Calculated number of pods that may be disrupted at this time': '当前允许中断的 Pod 数量',
|
|
216
|
+
'Request represents a minimum amount of cpu/memory that a container may consume': 'Request 表示容器必须保证的最小 CPU/内存资源量',
|
|
217
|
+
'Limits control the maximum amount of cpu/memory that a container may use independent of contention on the node': 'Limits 控制容器可使用的 CPU/内存最大资源量,该限制与节点上的资源竞争情况无关',
|
|
218
|
+
|
|
219
|
+
};
|
|
220
|
+
|
|
211
221
|
const exists = this.$store.getters['i18n/exists'];
|
|
212
222
|
|
|
213
|
-
|
|
223
|
+
// 如果 tooltip 是语言包键名
|
|
224
|
+
if (exists(col.tooltip)) {
|
|
225
|
+
return this.t(col.tooltip);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 如果 tooltip 是英文文本,使用映射
|
|
229
|
+
if (tooltipMap[col.tooltip]) {
|
|
230
|
+
return tooltipMap[col.tooltip];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return col.tooltip; // 回退到原始文本
|
|
214
234
|
},
|
|
215
235
|
}
|
|
216
236
|
|
|
@@ -1074,12 +1074,22 @@ export default {
|
|
|
1074
1074
|
if ( col.labelKey ) {
|
|
1075
1075
|
return this.t(col.labelKey, undefined, true);
|
|
1076
1076
|
} else if ( col.label ) {
|
|
1077
|
-
|
|
1077
|
+
// 判断 label 是否为中文
|
|
1078
|
+
if (this.isChinese(col.label)) {
|
|
1079
|
+
return col.label;
|
|
1080
|
+
} else {
|
|
1081
|
+
return this.t(`tableHeaders.${col.label.replace(/\s+/g, '').toLowerCase()}`, undefined, true);
|
|
1082
|
+
}
|
|
1078
1083
|
}
|
|
1079
1084
|
|
|
1080
1085
|
return ucFirst(col.name);
|
|
1081
1086
|
},
|
|
1082
1087
|
|
|
1088
|
+
// 判断字符串是否包含中文
|
|
1089
|
+
isChinese(str) {
|
|
1090
|
+
return /[\u4e00-\u9fa5]/.test(str);
|
|
1091
|
+
},
|
|
1092
|
+
|
|
1083
1093
|
valueFor(row, col, isLabel) {
|
|
1084
1094
|
if (typeof col.value === 'function') {
|
|
1085
1095
|
return col.value(row);
|
|
@@ -2669,7 +2679,7 @@ export default {
|
|
|
2669
2679
|
.sort-table-div{
|
|
2670
2680
|
width:100%;
|
|
2671
2681
|
white-space:nowrap;
|
|
2672
|
-
overflow-x: auto;
|
|
2682
|
+
// overflow-x: auto;
|
|
2673
2683
|
}
|
|
2674
2684
|
|
|
2675
2685
|
/* 滚动阴影左边 */
|
|
@@ -44,13 +44,8 @@ export default {
|
|
|
44
44
|
demoDisplay() {
|
|
45
45
|
|
|
46
46
|
const resources = this.$route.params?.resource || ''
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const productId = this.$store.getters['type-map/groupForBasicType'](this.$store.getters['productId'], resources);
|
|
50
|
-
|
|
51
|
-
if (productId === undefined) {
|
|
52
|
-
return '';
|
|
53
|
-
}
|
|
47
|
+
|
|
48
|
+
let productId = this.$store.getters['type-map/groupForBasicType'](this.$store.getters['productId'], resources);
|
|
54
49
|
const parts = productId.split('::');
|
|
55
50
|
const newString = 'root';
|
|
56
51
|
|
|
@@ -68,6 +68,20 @@ export default {
|
|
|
68
68
|
};
|
|
69
69
|
});
|
|
70
70
|
},
|
|
71
|
+
},
|
|
72
|
+
methods: {
|
|
73
|
+
hans(row) {
|
|
74
|
+
const conditionMap = {
|
|
75
|
+
'DiskPressure': '磁盘压力',
|
|
76
|
+
'MemoryPressure': '内存压力',
|
|
77
|
+
'PIDPressure': '进程数压力',
|
|
78
|
+
'Ready': '就绪状态',
|
|
79
|
+
'EtcdIsVoter': 'Etcd投票成员',
|
|
80
|
+
'NetworkUnavailable': '网络不可用',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return conditionMap[row.condition] || row.condition;
|
|
84
|
+
}
|
|
71
85
|
}
|
|
72
86
|
};
|
|
73
87
|
</script>
|
|
@@ -83,7 +97,7 @@ export default {
|
|
|
83
97
|
:search="false"
|
|
84
98
|
>
|
|
85
99
|
<template #cell:condition="{row}">
|
|
86
|
-
<span :class="{'text-error': row.error}">{{ row
|
|
100
|
+
<span :class="{'text-error': row.error}">{{ hans(row) }}</span>
|
|
87
101
|
</template>
|
|
88
102
|
|
|
89
103
|
<template #cell:status="{row}">
|
|
@@ -204,6 +204,7 @@ export default {
|
|
|
204
204
|
<div
|
|
205
205
|
:id="id"
|
|
206
206
|
class="hs-popover__content"
|
|
207
|
+
style="width: auto;padding-right: 10px;"
|
|
207
208
|
:class="{expanded, [id]:true}"
|
|
208
209
|
>
|
|
209
210
|
<div>
|
|
@@ -212,14 +213,14 @@ export default {
|
|
|
212
213
|
:key="i"
|
|
213
214
|
class="counts"
|
|
214
215
|
>
|
|
215
|
-
<span class="counts-label">{{ obj.label }}</span>
|
|
216
|
-
<span>{{ obj.value }}</span>
|
|
216
|
+
<span class="counts-label" style="white-space: normal;">{{ obj.label }}</span>
|
|
217
|
+
<span style="white-space: normal;">{{ obj.value }}</span>
|
|
217
218
|
</div>
|
|
218
219
|
<div
|
|
219
220
|
v-if="canScale"
|
|
220
221
|
class="text-center scale"
|
|
221
222
|
>
|
|
222
|
-
<span>{{ t('tableHeaders.scale') }} </span>
|
|
223
|
+
<span style="white-space: nowrap;margin-right: 5px;">{{ t('tableHeaders.scale') }} </span>
|
|
223
224
|
<PlusMinus
|
|
224
225
|
:value="row.spec.replicas"
|
|
225
226
|
:disabled="disabled"
|
package/components/nav/Group.vue
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import Type from '@shell/components/nav/Type';
|
|
3
|
+
import { menuRouteMap } from '@shell/config/menuRouteMap';
|
|
4
|
+
|
|
3
5
|
export default {
|
|
4
6
|
name: 'Group',
|
|
5
7
|
|
|
@@ -178,6 +180,7 @@ export default {
|
|
|
178
180
|
|
|
179
181
|
let parentPath = '';
|
|
180
182
|
const cluster = this.$route.params?.cluster;
|
|
183
|
+
const resource = this.$route?.params?.resource || ''
|
|
181
184
|
|
|
182
185
|
// Where we use nested route configuration, consider the parent route when trying to identify the nav location
|
|
183
186
|
if (this.$route.matched.length > 1) {
|
|
@@ -201,6 +204,9 @@ export default {
|
|
|
201
204
|
return true;
|
|
202
205
|
} else if (parentPath && itemFullPath === parentPath) {
|
|
203
206
|
return true;
|
|
207
|
+
// === 新增映射逻辑 ===
|
|
208
|
+
} else if (menuRouteMap[item.name] && menuRouteMap[item.name].includes(resource)) {
|
|
209
|
+
return true;
|
|
204
210
|
}
|
|
205
211
|
}
|
|
206
212
|
}
|
|
@@ -670,7 +670,8 @@ export default {
|
|
|
670
670
|
</template>
|
|
671
671
|
|
|
672
672
|
<!-- 资源搜索按钮 -->
|
|
673
|
-
|
|
673
|
+
<!-- 2025/9/30 隐藏 -->
|
|
674
|
+
<!-- <button
|
|
674
675
|
v-if="showSearch"
|
|
675
676
|
id="header-btn-search"
|
|
676
677
|
v-clean-tooltip="t('nav.resourceSearch.toolTip', {key: searchShortcut})"
|
|
@@ -685,7 +686,7 @@ export default {
|
|
|
685
686
|
@click="openSearch()"
|
|
686
687
|
>
|
|
687
688
|
<i class="icon icon-search icon-lg" />
|
|
688
|
-
</button>
|
|
689
|
+
</button> -->
|
|
689
690
|
|
|
690
691
|
<!-- 搜索弹窗 -->
|
|
691
692
|
<app-modal
|
|
@@ -706,9 +707,10 @@ export default {
|
|
|
706
707
|
v-if="extensionHeaderActions.length"
|
|
707
708
|
class="header-buttons"
|
|
708
709
|
>
|
|
710
|
+
<template v-for="action, i in extensionHeaderActions" :key="`${action.label}${i}`">
|
|
711
|
+
<!-- kubectl-explain.action | 2025/9/30隐藏 -->
|
|
709
712
|
<button
|
|
710
|
-
v-
|
|
711
|
-
:key="`${action.label}${i}`"
|
|
713
|
+
v-if="action.labelKey !== 'kubectl-explain.action'"
|
|
712
714
|
v-clean-tooltip="handleExtensionTooltip(action)"
|
|
713
715
|
v-shortkey="action.shortcutKey"
|
|
714
716
|
:disabled="action.enabled ? !action.enabled(ctx) : false"
|
|
@@ -728,6 +730,7 @@ export default {
|
|
|
728
730
|
color="header"
|
|
729
731
|
/>
|
|
730
732
|
</button>
|
|
733
|
+
</template>
|
|
731
734
|
</div>
|
|
732
735
|
|
|
733
736
|
<!-- ===== 用户菜单(右上角头像 + 下拉) ===== -->
|
package/components/nav/Type.vue
CHANGED
|
@@ -3,6 +3,7 @@ import Favorite from '@shell/components/nav/Favorite';
|
|
|
3
3
|
import { TYPE_MODES } from '@shell/store/type-map';
|
|
4
4
|
|
|
5
5
|
import TabTitle from '@shell/components/TabTitle';
|
|
6
|
+
import { menuRouteMap } from '@shell/config/menuRouteMap';
|
|
6
7
|
|
|
7
8
|
const showFavoritesFor = [TYPE_MODES.FAVORITE, TYPE_MODES.USED];
|
|
8
9
|
|
|
@@ -61,6 +62,17 @@ export default {
|
|
|
61
62
|
const pageFullPath = this.$route.fullPath?.toLowerCase().split('#')[0]; // Ignore the shebang when comparing routes
|
|
62
63
|
const routeMetaNav = this.$route.meta?.nav;
|
|
63
64
|
|
|
65
|
+
const resource = this.$route?.params?.resource || ''
|
|
66
|
+
|
|
67
|
+
console.log(this.type, ' this.type----------------------------');
|
|
68
|
+
console.log(this.$route.params, ' this.$route?.params----------------------------');
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// 菜单选中动态映射逻辑
|
|
72
|
+
if (menuRouteMap[this.type.name] && menuRouteMap[this.type.name].includes(resource)) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
64
76
|
// If the route explicitly declares the nav path that should be highlighted, then use that
|
|
65
77
|
if (routeMetaNav) {
|
|
66
78
|
const cluster = this.$route.params?.cluster;
|
|
@@ -51,22 +51,31 @@ export function installNavigationGuards(router, context) {
|
|
|
51
51
|
|
|
52
52
|
// 🔹 最后执行:只改地址栏,不改内部
|
|
53
53
|
router.afterEach((to) => {
|
|
54
|
-
const base = router.options.history?.base || '';
|
|
55
|
-
|
|
54
|
+
const base = router.options.history?.base || '';
|
|
55
|
+
const pathParts = to.path.split('/');
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
const newPathParts = pathParts.map((part) => {
|
|
58
|
+
// 只替换 cluster/product/resource 对应的 segment
|
|
59
|
+
for (const key of ['cluster', 'product', 'resource']) {
|
|
60
|
+
if (to.params[key] && to.params[key] === part) {
|
|
61
|
+
return part.replace(/harvester/g, 'cloud');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return part;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
let cloudPath = newPathParts.join('/');
|
|
58
68
|
if (!cloudPath.startsWith(base)) {
|
|
59
69
|
cloudPath = base + cloudPath;
|
|
60
70
|
}
|
|
61
71
|
|
|
62
|
-
// 获取当前地址栏完整 URL(path + search + hash)
|
|
63
72
|
const currentLocation = window.location.pathname + window.location.search + window.location.hash;
|
|
64
73
|
|
|
65
|
-
// 只有在地址栏实际不同的时候才替换,避免死循环
|
|
66
74
|
if (cloudPath !== currentLocation) {
|
|
67
75
|
console.info('[URL Replace] Updating address bar:', currentLocation, '→', cloudPath);
|
|
68
76
|
window.history.replaceState({}, '', cloudPath);
|
|
69
77
|
}
|
|
70
78
|
});
|
|
79
|
+
|
|
71
80
|
}
|
|
72
81
|
|
package/edit/configmap.vue
CHANGED
package/edit/workload/index.vue
CHANGED
|
@@ -660,11 +660,11 @@ export default {
|
|
|
660
660
|
.deployment-tabs {
|
|
661
661
|
> .tabs.horizontal {
|
|
662
662
|
border-bottom: 1px solid var(--border);
|
|
663
|
-
margin-bottom: 20px;
|
|
664
663
|
}
|
|
665
664
|
|
|
666
665
|
> .tab-container{
|
|
667
|
-
|
|
666
|
+
padding: 20px 20px 0;
|
|
667
|
+
// border: none !important;
|
|
668
668
|
}
|
|
669
669
|
}
|
|
670
670
|
.padded {
|
|
@@ -115,25 +115,26 @@ export default class ProvCluster extends SteveModel {
|
|
|
115
115
|
const actions = [
|
|
116
116
|
// Note: Actions are not supported in the Steve API, so we check
|
|
117
117
|
// available actions for RKE1 clusters, but not RKE2 clusters.
|
|
118
|
+
// {
|
|
119
|
+
// action: 'openShell',
|
|
120
|
+
// label: this.$rootGetters['i18n/t']('nav.shell'),
|
|
121
|
+
// icon: 'icon icon-terminal',
|
|
122
|
+
// enabled: !!this.mgmt?.links.shell && ready,
|
|
123
|
+
// }, {
|
|
124
|
+
// action: 'downloadKubeConfig',
|
|
125
|
+
// bulkAction: 'downloadKubeConfigBulk',
|
|
126
|
+
// label: this.$rootGetters['i18n/t']('nav.kubeconfig.download'),
|
|
127
|
+
// icon: 'icon icon-download',
|
|
128
|
+
// bulkable: true,
|
|
129
|
+
// enabled: this.mgmt?.hasAction('generateKubeconfig'),
|
|
130
|
+
// }, {
|
|
131
|
+
// action: 'copyKubeConfig',
|
|
132
|
+
// label: this.t('cluster.copyConfig'),
|
|
133
|
+
// bulkable: false,
|
|
134
|
+
// enabled: this.mgmt?.hasAction('generateKubeconfig'),
|
|
135
|
+
// icon: 'icon icon-copy',
|
|
136
|
+
// },
|
|
118
137
|
{
|
|
119
|
-
action: 'openShell',
|
|
120
|
-
label: this.$rootGetters['i18n/t']('nav.shell'),
|
|
121
|
-
icon: 'icon icon-terminal',
|
|
122
|
-
enabled: !!this.mgmt?.links.shell && ready,
|
|
123
|
-
}, {
|
|
124
|
-
action: 'downloadKubeConfig',
|
|
125
|
-
bulkAction: 'downloadKubeConfigBulk',
|
|
126
|
-
label: this.$rootGetters['i18n/t']('nav.kubeconfig.download'),
|
|
127
|
-
icon: 'icon icon-download',
|
|
128
|
-
bulkable: true,
|
|
129
|
-
enabled: this.mgmt?.hasAction('generateKubeconfig'),
|
|
130
|
-
}, {
|
|
131
|
-
action: 'copyKubeConfig',
|
|
132
|
-
label: this.t('cluster.copyConfig'),
|
|
133
|
-
bulkable: false,
|
|
134
|
-
enabled: this.mgmt?.hasAction('generateKubeconfig'),
|
|
135
|
-
icon: 'icon icon-copy',
|
|
136
|
-
}, {
|
|
137
138
|
action: 'snapshotAction',
|
|
138
139
|
label: this.$rootGetters['i18n/t']('nav.takeSnapshot'),
|
|
139
140
|
icon: 'icon icon-snapshot',
|
package/package.json
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"translations": [
|
|
3
|
+
{
|
|
4
|
+
"pattern": "(\\S+) \"(\\S+)\" is forbidden: user \"(\\S+)\" cannot update resource \"(\\S+)\" in api group \"(\\S+)\" at the cluster scope",
|
|
5
|
+
"replacement": "$1 \"$2\" 被禁止:用户 \"$3\" 无权在集群范围内更新 API 组 \"$5\" 中的资源 \"$4\"",
|
|
6
|
+
"flags": "gi"
|
|
7
|
+
},
|
|
3
8
|
{
|
|
4
9
|
"pattern": "virtualmachine\\.kubevirt\\.io \"([^\"]+)\" is invalid:\\[metadata\\.labels: invalid value: \"([^\"]+)\": name part must consist of alphanumeric characters, '-', '_' or '\\.', and must start and end with an alphanumeric character\\]",
|
|
5
10
|
"replacement": "虚拟机 \"$1\" 标签值 \"$2\" 无效:只能包含字母、数字、'-','_'或'.',且以字母/数字开头和结尾",
|
|
@@ -83,6 +88,11 @@
|
|
|
83
88
|
{
|
|
84
89
|
"pattern": "failed to list objects with param:[\\s\\S]*?authorizationheadermalformed[\\s\\S]*?the region is wrong; expecting '([^']+)'[\\s\\S]*",
|
|
85
90
|
"replacement": "参数错误,无法连接到服务器",
|
|
91
|
+
"flags": "gi"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"pattern": "has timed out progressing.",
|
|
95
|
+
"replacement": "状态更新超时。",
|
|
86
96
|
"flags": "gi"
|
|
87
97
|
},
|
|
88
98
|
{
|
|
@@ -1658,6 +1668,10 @@
|
|
|
1658
1668
|
"pattern": "namespaces",
|
|
1659
1669
|
"replacement": "资源组"
|
|
1660
1670
|
},
|
|
1671
|
+
{
|
|
1672
|
+
"pattern": "replicaset",
|
|
1673
|
+
"replacement": "副本集"
|
|
1674
|
+
},
|
|
1661
1675
|
{
|
|
1662
1676
|
"pattern": "%",
|
|
1663
1677
|
"replacement": ""
|