dashboard-shell-shell 1.0.10000011122 → 3.0.2-test.20250913

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 (121) hide show
  1. package/assets/images/action.svg +6 -0
  2. package/assets/images/pl/logo.png +0 -0
  3. package/assets/styles/base/_functions.scss +0 -0
  4. package/assets/styles/base/_mixins.scss +1 -1
  5. package/assets/styles/global/_button.scss +17 -10
  6. package/assets/styles/global/_form.scss +2 -2
  7. package/assets/styles/global/_labeled-input.scss +6 -2
  8. package/assets/styles/global/_select.scss +6 -7
  9. package/assets/styles/global/_table.scss +3 -2
  10. package/assets/styles/global/_tooltip.scss +8 -1
  11. package/assets/styles/themes/_dark.scss +2 -0
  12. package/assets/styles/themes/_light.scss +5 -2
  13. package/assets/styles/vendor/vue-select.scss +2 -1
  14. package/assets/translations/en-us.yaml +1 -3
  15. package/assets/translations/zh-hans.yaml +51 -28
  16. package/components/ActionDropdown.vue +1 -0
  17. package/components/ActionMenuShell.vue +6 -3
  18. package/components/BrandImage.vue +22 -0
  19. package/components/ClusterIconMenu.vue +1 -1
  20. package/components/CodeMirror.vue +1 -0
  21. package/components/CruResource.vue +1 -1
  22. package/components/CruResourceFooter.vue +1 -1
  23. package/components/ExplorerProjectsNamespaces.vue +4 -24
  24. package/components/GlobalRoleBindings.vue +112 -48
  25. package/components/IndentedPanel.vue +4 -10
  26. package/components/PromptRemove.vue +3 -3
  27. package/components/ResourceDetail/Masthead.vue +190 -242
  28. package/components/ResourceDetail/index.vue +20 -5
  29. package/components/ResourceList/Masthead.vue +145 -83
  30. package/components/ResourceList/ResourceLoadingIndicator.vue +5 -2
  31. package/components/ResourceTable.vue +76 -1
  32. package/components/SideNav.vue +66 -29
  33. package/components/SortableTable/THead.vue +6 -0
  34. package/components/SortableTable/index.vue +481 -388
  35. package/components/Tabbed/index.vue +4 -5
  36. package/components/auth/Principal.vue +3 -2
  37. package/components/auth/RoleDetailEdit.vue +58 -5
  38. package/components/auth/SelectPrincipal.vue +1 -0
  39. package/components/form/BannerSettings.vue +18 -16
  40. package/components/form/ChangePassword.vue +4 -4
  41. package/components/form/ColorInput.vue +32 -8
  42. package/components/form/Footer.vue +1 -1
  43. package/components/form/InputWithSelect.vue +2 -0
  44. package/components/form/KeyValue.vue +27 -3
  45. package/components/form/LabeledSelect.vue +178 -178
  46. package/components/form/Members/ClusterPermissionsEditor.vue +1 -2
  47. package/components/form/Members/MembershipEditor.vue +1 -1
  48. package/components/form/NameNsDescription.vue +24 -11
  49. package/components/form/Password.vue +6 -2
  50. package/components/form/ResourceQuota/Namespace.vue +1 -1
  51. package/components/form/ResourceQuota/NamespaceRow.vue +13 -10
  52. package/components/form/ResourceQuota/ProjectRow.vue +0 -1
  53. package/components/form/Select.vue +2 -2
  54. package/components/nav/Favorite.vue +5 -1
  55. package/components/nav/Group.vue +69 -23
  56. package/components/nav/Header.vue +82 -17
  57. package/components/nav/HeaderPageActionMenu.vue +1 -0
  58. package/components/nav/NamespaceFilter.vue +0 -3
  59. package/components/nav/TopLevelMenu.vue +182 -119
  60. package/components/nav/Type.vue +48 -11
  61. package/composables/useClickOutside.ts +1 -1
  62. package/config/product/auth.js +16 -7
  63. package/config/product/explorer.js +1 -1
  64. package/config/product/settings.js +17 -8
  65. package/config/settings.ts +28 -0
  66. package/edit/management.cattle.io.user.vue +17 -4
  67. package/edit/networking.k8s.io.ingress/RulePath.vue +1 -1
  68. package/edit/token.vue +1 -1
  69. package/list/harvesterhci.io.management.cluster.vue +17 -0
  70. package/list/management.cattle.io.setting.vue +22 -13
  71. package/list/management.cattle.io.user.vue +25 -14
  72. package/list/provisioning.cattle.io.cluster.vue +6 -7
  73. package/mixins/brand.js +17 -0
  74. package/package.json +1 -1
  75. package/pages/auth/login.vue +49 -28
  76. package/pages/c/_cluster/auth/roles/index.vue +61 -14
  77. package/pages/c/_cluster/settings/banners.vue +174 -101
  78. package/pages/c/_cluster/settings/brand.vue +348 -301
  79. package/pages/c/_cluster/settings/performance.vue +61 -38
  80. package/pages/home.vue +70 -21
  81. package/pages/prefs.vue +25 -23
  82. package/pkg/tsconfig.json +9 -9
  83. package/pkg/vue.config.js +1 -1
  84. package/promptRemove/mixin/roleDeletionCheck.js +2 -2
  85. package/scripts/clean +0 -0
  86. package/scripts/extension/bundle +0 -0
  87. package/scripts/extension/helm/scripts/package +0 -0
  88. package/scripts/extension/helm/scripts/patch +0 -0
  89. package/scripts/extension/helm/scripts/version +0 -0
  90. package/scripts/extension/helmpatch +0 -0
  91. package/scripts/extension/parse-tag-name +0 -0
  92. package/scripts/extension/publish +0 -0
  93. package/scripts/publish-shell.sh +86 -60
  94. package/scripts/serve-pkgs +0 -0
  95. package/scripts/sync-shell-deps +0 -0
  96. package/scripts/typegen.sh +44 -28
  97. package/store/i18n.js +5 -5
  98. package/store/prefs.js +17 -5
  99. package/utils/error.js +4 -0
  100. package/vue.config.js +1 -6
  101. package/components/rancherResourceDetail/Masthead.vue +0 -769
  102. package/components/rancherResourceDetail/__tests__/Masthead.test.ts +0 -65
  103. package/components/rancherResourceDetail/index.vue +0 -591
  104. package/components/rancherResourceList/Masthead.vue +0 -375
  105. package/components/rancherResourceList/ResourceLoadingIndicator.vue +0 -140
  106. package/components/rancherResourceList/index.vue +0 -307
  107. package/components/rancherResourceList/resource-list.config.js +0 -7
  108. package/components/rancherResourceTable.vue +0 -783
  109. package/components/rancherSortableTable/THead.vue +0 -561
  110. package/components/rancherSortableTable/actions.js +0 -153
  111. package/components/rancherSortableTable/advanced-filtering.js +0 -272
  112. package/components/rancherSortableTable/debug.js +0 -117
  113. package/components/rancherSortableTable/filtering.js +0 -290
  114. package/components/rancherSortableTable/grouping.js +0 -48
  115. package/components/rancherSortableTable/index.vue +0 -2712
  116. package/components/rancherSortableTable/paging.js +0 -155
  117. package/components/rancherSortableTable/selection.js +0 -629
  118. package/components/rancherSortableTable/sortable-config.ts +0 -4
  119. package/components/rancherSortableTable/sorting.js +0 -129
  120. package/types/cloud-shell/index.d.ts +0 -11014
  121. /package/components/{rancherResourceList → ResourceList}/Masthead-btn.vue +0 -0
@@ -368,13 +368,12 @@ export default {
368
368
  padding: 0;
369
369
 
370
370
  &.horizontal {
371
- border: solid thin var(--border);
372
- border-bottom: 0;
371
+ border-bottom: solid 1px var(--border);
373
372
  display: flex;
374
373
  flex-direction: row;
375
374
 
376
375
  + .tab-container {
377
- border: solid thin var(--border);
376
+ // border: solid thin var(--border);
378
377
  }
379
378
 
380
379
  .tab.active {
@@ -443,7 +442,7 @@ export default {
443
442
  }
444
443
 
445
444
  .tab-container {
446
- padding: 20px;
445
+ padding: 20px 0;
447
446
 
448
447
  &.no-content {
449
448
  padding: 0 0 3px 0;
@@ -499,7 +498,7 @@ margin: 0px -20px;
499
498
  // }
500
499
  & .tab {
501
500
  /* width: 100%; */
502
- min-width: 120px;
501
+ min-width: 130px !important;
503
502
  height: 36px;
504
503
  /* border-top: solid 5px transparent; */
505
504
  display: flex;
@@ -154,7 +154,7 @@ export default {
154
154
  </template>
155
155
 
156
156
  <style lang="scss" scoped>
157
- $size: 40px;
157
+ $size: 20px;
158
158
 
159
159
  .principal {
160
160
  display: grid;
@@ -162,7 +162,7 @@ export default {
162
162
  "avatar name"
163
163
  "avatar description";
164
164
  grid-template-columns: $size auto;
165
- grid-template-rows: auto math.div($size, 2);
165
+ // grid-template-rows: auto math.div($size, 2);
166
166
  column-gap: 10px;
167
167
 
168
168
  th {
@@ -189,6 +189,7 @@ export default {
189
189
  .avatar {
190
190
  grid-area: avatar;
191
191
  text-align: center;
192
+
192
193
 
193
194
  DIV.empty {
194
195
  border: 1px solid var(--border);
@@ -331,7 +331,7 @@ export default {
331
331
  return this.value.listLocation;
332
332
  },
333
333
  ruleClass() {
334
- return `col ${ this.isNamespaced ? 'span-4' : 'span-3' }`;
334
+ return `ruleCls col ${ this.isNamespaced ? 'span-3' : 'span-3' }`;
335
335
  },
336
336
  // Detail View
337
337
  rules() {
@@ -536,7 +536,11 @@ export default {
536
536
  </script>
537
537
 
538
538
  <template>
539
+
540
+ <!-- 加载状态下显示 Loading 组件 -->
539
541
  <Loading v-if="$fetchState.pending" />
542
+
543
+ <!-- 主表单容器,基于 CruResource 封装 -->
540
544
  <CruResource
541
545
  v-else
542
546
  class="receiver"
@@ -550,7 +554,10 @@ export default {
550
554
  @finish="save"
551
555
  @cancel="cancel"
552
556
  >
557
+ <!-- ========== 详情模式显示规则表 ========== -->
553
558
  <template v-if="isDetail">
559
+
560
+ <!-- 当前规则列表 -->
554
561
  <SortableTable
555
562
  key-field="index"
556
563
  :rows="rules"
@@ -559,6 +566,8 @@ export default {
559
566
  :row-actions="false"
560
567
  :search="false"
561
568
  />
569
+
570
+ <!-- 继承的规则列表 -->
562
571
  <div
563
572
  v-for="(inherited, index) of inheritedRules"
564
573
  :key="index"
@@ -580,7 +589,11 @@ export default {
580
589
  />
581
590
  </div>
582
591
  </template>
592
+
593
+ <!-- ========== 表单编辑/创建模式 ========== -->
583
594
  <template v-else>
595
+
596
+ <!-- 名称 & 命名空间 & 描述输入 -->
584
597
  <NameNsDescription
585
598
  :value="value"
586
599
  :namespaced="isNamespaced"
@@ -591,11 +604,15 @@ export default {
591
604
  :rules="{ name: fvGetAndReportPathRules('displayName') }"
592
605
  @update:value="$emit('input', $event)"
593
606
  />
607
+
608
+ <!-- Rancher 相关的默认选项设置 -->
594
609
  <div
595
610
  v-if="isRancherType"
596
611
  class="row"
597
612
  >
598
613
  <div class="col span-6">
614
+
615
+ <!-- 默认角色选项 -->
599
616
  <RadioGroup
600
617
  v-model:value="defaultValue"
601
618
  name="storageSource"
@@ -606,6 +623,8 @@ export default {
606
623
  :mode="mode"
607
624
  />
608
625
  </div>
626
+
627
+ <!-- 锁定角色模板选项 -->
609
628
  <div
610
629
  v-if="isRancherRoleTemplate"
611
630
  class="col span-6"
@@ -622,17 +641,25 @@ export default {
622
641
  </div>
623
642
  </div>
624
643
  <div class="spacer" />
644
+
645
+ <!-- ========== 选项卡容器 ========== -->
625
646
  <Tabbed :side-tabs="true">
647
+
648
+ <!-- ---------- 授权资源 Tab ---------- -->
626
649
  <Tab
627
650
  name="grant-resources"
628
651
  :label="t('rbac.roletemplate.tabs.grantResources.label')"
629
652
  :weight="1"
630
653
  >
654
+
655
+ <!-- 校验错误展示 -->
631
656
  <Error
632
657
  :value="value.rules"
633
658
  :rules="fvGetAndReportPathRules('rules')"
634
659
  as-banner
635
660
  />
661
+
662
+ <!-- 规则编辑列表 -->
636
663
  <ArrayList
637
664
  v-model:value="value.rules"
638
665
  label="Resources"
@@ -642,9 +669,11 @@ export default {
642
669
  :default-add-value="defaultRule"
643
670
  :initial-empty-row="true"
644
671
  :show-header="true"
645
- add-label="Add Resource"
672
+ add-label="添加资源"
646
673
  :mode="mode"
647
674
  >
675
+
676
+ <!-- 列表头部 -->
648
677
  <template #column-headers>
649
678
  <div class="column-headers row">
650
679
  <div :class="ruleClass">
@@ -676,8 +705,12 @@ export default {
676
705
  </div>
677
706
  </div>
678
707
  </template>
708
+
709
+ <!-- 列内容渲染 -->
679
710
  <template #columns="props">
680
711
  <div class="columns row mr-20">
712
+
713
+ <!-- 动作(verbs)选择 -->
681
714
  <div :class="ruleClass">
682
715
  <!-- Select verbs -->
683
716
  <Select
@@ -694,8 +727,11 @@ export default {
694
727
  @update:value="updateSelectValue(props.row.value, 'verbs', $event)"
695
728
  />
696
729
  </div>
730
+
731
+ <!-- 资源(resources)选择 -->
697
732
  <div :class="ruleClass">
698
733
  <Select
734
+ style="width: auto;"
699
735
  :value="getRule('resources', props.row.value)"
700
736
  :disabled="isBuiltin"
701
737
  :options="resourceOptions"
@@ -709,6 +745,8 @@ export default {
709
745
  @createdListItem="setRule('resources', props.row.value, $event)"
710
746
  />
711
747
  </div>
748
+
749
+ <!-- API Groups 输入 -->
712
750
  <div :class="ruleClass">
713
751
  <LabeledInput
714
752
  :value="getRule('apiGroups', props.row.value)"
@@ -718,6 +756,8 @@ export default {
718
756
  @input="setRule('apiGroups', props.row.value, $event.target.value)"
719
757
  />
720
758
  </div>
759
+
760
+ <!-- 非命名空间 URL 输入(仅非命名空间模式显示) -->
721
761
  <div
722
762
  v-if="!isNamespaced"
723
763
  :class="ruleClass"
@@ -734,6 +774,8 @@ export default {
734
774
  </template>
735
775
  </ArrayList>
736
776
  </Tab>
777
+
778
+ <!-- ---------- 继承角色模板 Tab(仅 Rancher 角色模板可用) ---------- -->
737
779
  <Tab
738
780
  v-if="isRancherRoleTemplate"
739
781
  name="inherit-from"
@@ -746,7 +788,7 @@ export default {
746
788
  :remove-allowed="!isBuiltin"
747
789
  :add-allowed="!isBuiltin"
748
790
  label="Resources"
749
- add-label="Add Resource"
791
+ add-label="添加资源"
750
792
  :mode="mode"
751
793
  >
752
794
  <template #columns="props">
@@ -777,13 +819,16 @@ export default {
777
819
  </template>
778
820
 
779
821
  <style lang="scss" scoped>
822
+ .ruleCls {
823
+ margin-right: 10px;
824
+ }
780
825
  .required {
781
826
  color: var(--error);
782
827
  }
783
828
 
784
829
  :deep() {
785
830
  .column-headers {
786
- margin-right: 75px;
831
+ margin-right: 95px;
787
832
  margin-bottom: 5px;
788
833
  }
789
834
 
@@ -793,7 +838,6 @@ export default {
793
838
  .remove {
794
839
  display: flex;
795
840
  flex-direction: column;
796
- justify-content: center;
797
841
  align-items: flex-end;
798
842
  }
799
843
  }
@@ -805,4 +849,13 @@ export default {
805
849
  }
806
850
  }
807
851
  }
852
+
853
+ .columns {
854
+ :deep() {
855
+ .unlabeled-select {
856
+ width: auto;
857
+ }
858
+ }
859
+
860
+ }
808
861
  </style>
@@ -212,6 +212,7 @@ export default {
212
212
  <Principal
213
213
  :value="option.label"
214
214
  :use-muted="false"
215
+ style="padding-top: 6px;"
215
216
  />
216
217
  </template>
217
218
 
@@ -104,10 +104,10 @@ export default ({
104
104
  </script>
105
105
 
106
106
  <template>
107
- <div class="row mb-20">
107
+ <div class="row">
108
108
  <div class="col span-12">
109
109
  <div class="row">
110
- <div class="col span-6">
110
+ <div class="col span-5">
111
111
  <LabeledInput
112
112
  v-model:value="value[bannerType].text"
113
113
  :disabled="isUiDisabled"
@@ -154,18 +154,20 @@ export default ({
154
154
  <h3>
155
155
  {{ t('banner.bannerDecoration.label') }}
156
156
  </h3>
157
- <div
158
- v-for="(o, i) in textDecorationOptions"
159
- :key="i"
160
- >
161
- <Checkbox
162
- v-model:value="value[bannerType][o.style]"
163
- name="bannerDecoration"
164
- :data-testid="`banner_decoration_checkbox${bannerType}${o.label}`"
165
- class="banner-decoration-checkbox"
166
- :mode="mode"
167
- :label="o.label"
168
- />
157
+ <div style="display: flex;">
158
+ <div
159
+ v-for="(o, i) in textDecorationOptions"
160
+ :key="i"
161
+ >
162
+ <Checkbox
163
+ v-model:value="value[bannerType][o.style]"
164
+ name="bannerDecoration"
165
+ :data-testid="`banner_decoration_checkbox${bannerType}${o.label}`"
166
+ class="banner-decoration-checkbox"
167
+ :mode="mode"
168
+ :label="o.label"
169
+ />
170
+ </div>
169
171
  </div>
170
172
  </div>
171
173
  <div class="col span-2">
@@ -179,7 +181,7 @@ export default ({
179
181
  </div>
180
182
  </div>
181
183
  <div class="row mt-10">
182
- <div class="col span-6">
184
+ <div class="col span-5">
183
185
  <ColorInput
184
186
  v-model:value="value[bannerType].color"
185
187
  :default-value="themeVars.bannerTextColor"
@@ -187,7 +189,7 @@ export default ({
187
189
  :mode="mode"
188
190
  />
189
191
  </div>
190
- <div class="col span-6">
192
+ <div class="col span-7">
191
193
  <ColorInput
192
194
  v-model:value="value[bannerType].background"
193
195
  :default-value="themeVars.bannerBgColor"
@@ -353,7 +353,7 @@ export default {
353
353
  v-if="isRandomGenerated"
354
354
  :class="{'row': isCreateEdit}"
355
355
  >
356
- <div :class="{'col': isCreateEdit, 'span-8': isCreateEdit}">
356
+ <div :class="{'col': isCreateEdit, 'span-6': isCreateEdit}">
357
357
  <Password
358
358
  v-model:value="passwordGen"
359
359
  class="mt-10"
@@ -368,7 +368,7 @@ export default {
368
368
  class="userGen"
369
369
  :class="{'row': isCreateEdit}"
370
370
  >
371
- <div :class="{'col': isCreateEdit, 'span-4': isCreateEdit}">
371
+ <div :class="{'col': isCreateEdit, 'span-6': isCreateEdit}">
372
372
  <Password
373
373
  v-model:value="passwordNew"
374
374
  data-testid="account__new_password"
@@ -378,7 +378,7 @@ export default {
378
378
  :ignore-password-managers="!isChange"
379
379
  />
380
380
  </div>
381
- <div :class="{'col': isCreateEdit, 'span-4': isCreateEdit}">
381
+ <div :class="{'col': isCreateEdit, 'span-6': isCreateEdit}">
382
382
  <Password
383
383
  v-model:value="passwordConfirm"
384
384
  data-testid="account__confirm_password"
@@ -403,7 +403,7 @@ export default {
403
403
  class="text-error"
404
404
  :class="{'row': isCreateEdit}"
405
405
  >
406
- <div :class="{'col': isCreateEdit, 'span-8': isCreateEdit}">
406
+ <div :class="{'col': isCreateEdit, 'span-6': isCreateEdit}">
407
407
  <Banner
408
408
  v-for="(err, i) in errorMessages"
409
409
  :key="i"
@@ -93,8 +93,7 @@ export default {
93
93
 
94
94
  <template>
95
95
  <div
96
- class="color-input"
97
- :class="{[mode]:mode, disabled: isDisabled}"
96
+ class="color-input-box"
98
97
  :data-testid="componentTestid + '-color-input'"
99
98
  :tabindex="isDisabled ? -1 : 0"
100
99
  @keydown.space.prevent
@@ -107,6 +106,7 @@ export default {
107
106
  />{{ label }}</label>
108
107
  <div
109
108
  :data-testid="componentTestid + '-color-input_preview-container'"
109
+ :class="{[mode]:mode, disabled: isDisabled}"
110
110
  class="preview-container"
111
111
  @click.stop="$refs.input.click($event)"
112
112
  >
@@ -115,27 +115,30 @@ export default {
115
115
  class="color-display"
116
116
  >
117
117
  <input
118
+ style="margin-bottom: 6px;"
118
119
  ref="input"
119
120
  :aria-disabled="isDisabled ? 'true' : 'false'"
120
121
  :aria-label="t('generic.colorPicker')"
121
122
  type="color"
123
+ class="color-input"
122
124
  :disabled="isDisabled"
123
125
  tabindex="-1"
124
126
  :value="inputValue"
125
127
  @input="$emit('update:value', $event.target.value)"
126
128
  >
127
129
  </span>
128
- <span class="text-muted color-value">{{ inputValue }}</span>
130
+ <span class="color-value">{{ inputValue }}</span>
129
131
  </div>
130
132
  </div>
131
133
  </template>
132
134
 
133
135
  <style lang='scss' scoped>
134
- .color-input {
135
- border: 1px solid var(--border);
136
- border-radius: var(--border-radius);
137
- padding: 10px;
138
-
136
+ .color-input-box {
137
+ display: flex;
138
+ .text-label {
139
+ display: flex;
140
+ align-items: center;
141
+ }
139
142
  &:focus-visible {
140
143
  @include focus-outline;
141
144
  }
@@ -172,6 +175,26 @@ export default {
172
175
 
173
176
  .color-value {
174
177
  margin-left: 4px;
178
+ color: var(--input-label);
179
+ }
180
+
181
+ &.disabled, &.disabled .selected, &[disabled], &[disabled]:hover {
182
+ color: var(--input-disabled-text);
183
+ outline-width: 0;
184
+ border-color: var(--input-disabled-border);
185
+ cursor: not-allowed;
186
+
187
+ label, span, div, input {
188
+ cursor: not-allowed !important;
189
+ }
190
+
191
+ .color-value {
192
+ color: var(--muted);
193
+ }
194
+
195
+ &::placeholder {
196
+ color: var(--input-disabled-placeholder);
197
+ }
175
198
  }
176
199
  }
177
200
 
@@ -197,4 +220,5 @@ export default {
197
220
  }
198
221
  }
199
222
  }
223
+
200
224
  </style>
@@ -109,7 +109,7 @@ export default defineComponent({
109
109
  /* grid-area: right;
110
110
  text-align: right; */
111
111
  display: flex;
112
-
112
+ justify-content: flex-end;
113
113
  .btn, button {
114
114
  /* margin: 0 0 0 $column-gutter; */
115
115
  margin: 0 10px 0 0px;
@@ -217,6 +217,7 @@ export default {
217
217
 
218
218
  &.labeled-select {
219
219
  .selected {
220
+ background-color: var(--input-bg);
220
221
  color: var(--input-text);
221
222
  text-align: center;
222
223
  margin-right: 1em;
@@ -303,6 +304,7 @@ export default {
303
304
  // position: relative;
304
305
 
305
306
  .vs__selected {
307
+ background-color: var(--input-bg);
306
308
  color: var(--input-text);
307
309
  }
308
310
 
@@ -401,9 +401,17 @@ export default {
401
401
  obj.binary = false;
402
402
  obj.canEncode = this.handleBase64;
403
403
  obj.supported = true;
404
- // 上传镜像问题
405
- const s = this.rows.filter(item => item.key !== obj.key);
406
- this.rows = s
404
+
405
+ // 上传镜像问题
406
+ const s = this.rows.filter(item => {
407
+ // 如果 item.key 或 obj.key 为空,直接保留
408
+ if (!item.key || !obj.key) {
409
+ return true;
410
+ }
411
+ // 两者都有值时才比较
412
+ return item.key !== obj.key;
413
+ });
414
+ this.rows = s;
407
415
 
408
416
  this.rows.push(obj);
409
417
  this.queueUpdate();
@@ -587,6 +595,7 @@ export default {
587
595
  </script>
588
596
  <template>
589
597
  <div class="key-value">
598
+ <!-- 标题部分:如果传了 title 或者插槽 title 才显示 -->
590
599
  <div
591
600
  v-if="title || $slots.title"
592
601
  class="clearfix"
@@ -594,6 +603,7 @@ export default {
594
603
  <slot name="title">
595
604
  <h3>
596
605
  {{ title }}
606
+ <!-- 标题右侧提示信息 -->
597
607
  <i
598
608
  v-if="titleProtip"
599
609
  v-clean-tooltip="titleProtip"
@@ -602,6 +612,8 @@ export default {
602
612
  </h3>
603
613
  </slot>
604
614
  </div>
615
+
616
+ <!-- 整个 Key-Value 表格容器 -->
605
617
  <div
606
618
  class="kv-container"
607
619
  role="grid"
@@ -609,6 +621,8 @@ export default {
609
621
  :aria-colcount="extraColumns.length + 2"
610
622
  :style="containerStyle"
611
623
  >
624
+
625
+ <!-- 表头:有数据或者处于 view 模式才显示 -->
612
626
  <template v-if="rows.length || isView">
613
627
  <div class="rowgroup">
614
628
  <div class="row">
@@ -647,6 +661,8 @@ export default {
647
661
  </div>
648
662
  </div>
649
663
  </template>
664
+
665
+ <!-- 如果是 view 模式但没有数据,显示占位符 -->
650
666
  <template v-if="!rows.length && isView">
651
667
  <div class="rowgroup">
652
668
  <div class="row">
@@ -665,6 +681,8 @@ export default {
665
681
  </div>
666
682
  </div>
667
683
  </template>
684
+
685
+ <!-- 遍历 rows 渲染每一行 -->
668
686
  <template
669
687
  v-for="(row,i) in rows"
670
688
  v-else
@@ -842,6 +860,8 @@ export default {
842
860
  </div>
843
861
  </template>
844
862
  </div>
863
+
864
+ <!-- 底部新增/从文件导入 -->
845
865
  <div
846
866
  v-if="(addAllowed || readAllowed) && !isView"
847
867
  class="footer mt-10"
@@ -850,6 +870,8 @@ export default {
850
870
  name="add"
851
871
  :add="add"
852
872
  >
873
+
874
+ <!-- 添加按钮 -->
853
875
  <button
854
876
  v-if="addAllowed"
855
877
  type="button"
@@ -865,6 +887,8 @@ export default {
865
887
  :class="loading ? ['icon-lg', 'icon-spinner','icon-spin']: [addIcon]"
866
888
  /> {{ _addLabel }}
867
889
  </button>
890
+
891
+ <!-- 从文件读取 -->
868
892
  <FileSelector
869
893
  v-if="readAllowed"
870
894
  :disabled="isView"