dashboard-shell-shell 1.0.1000000113 → 1.0.1000000117

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 (125) 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 -25
  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 +146 -84
  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 +480 -370
  35. package/components/Tabbed/index.vue +4 -5
  36. package/components/auth/Principal.vue +3 -2
  37. package/components/auth/RoleDetailEdit.vue +59 -6
  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 +31 -7
  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/router/navigation-guards/index.js +3 -3
  66. package/config/settings.ts +28 -0
  67. package/edit/management.cattle.io.user.vue +17 -4
  68. package/edit/networking.k8s.io.ingress/RulePath.vue +1 -1
  69. package/edit/token.vue +1 -1
  70. package/list/harvesterhci.io.management.cluster.vue +17 -0
  71. package/list/management.cattle.io.setting.vue +22 -13
  72. package/list/management.cattle.io.user.vue +25 -14
  73. package/list/provisioning.cattle.io.cluster.vue +6 -7
  74. package/mixins/brand.js +17 -0
  75. package/package.json +1 -1
  76. package/pages/auth/login.vue +84 -29
  77. package/pages/c/_cluster/auth/roles/index.vue +61 -14
  78. package/pages/c/_cluster/settings/banners.vue +174 -101
  79. package/pages/c/_cluster/settings/brand.vue +348 -301
  80. package/pages/c/_cluster/settings/performance.vue +61 -38
  81. package/pages/home.vue +70 -21
  82. package/pages/prefs.vue +25 -23
  83. package/pkg/tsconfig.json +9 -9
  84. package/pkg/vue.config.js +1 -1
  85. package/promptRemove/mixin/roleDeletionCheck.js +2 -2
  86. package/scripts/clean +0 -0
  87. package/scripts/extension/bundle +0 -0
  88. package/scripts/extension/helm/scripts/package +0 -0
  89. package/scripts/extension/helm/scripts/patch +0 -0
  90. package/scripts/extension/helm/scripts/version +0 -0
  91. package/scripts/extension/helmpatch +0 -0
  92. package/scripts/extension/parse-tag-name +0 -0
  93. package/scripts/extension/publish +0 -0
  94. package/scripts/publish-shell.sh +86 -60
  95. package/scripts/serve-pkgs +0 -0
  96. package/scripts/sync-shell-deps +0 -0
  97. package/scripts/typegen.sh +44 -28
  98. package/store/i18n.js +5 -5
  99. package/store/prefs.js +17 -5
  100. package/store/type-map.js +2 -1
  101. package/types/shell/index.d.ts +1 -1
  102. package/utils/error.js +4 -0
  103. package/utils/router.js +4 -4
  104. package/vue.config.js +1 -6
  105. package/components/rancherResourceDetail/Masthead.vue +0 -769
  106. package/components/rancherResourceDetail/__tests__/Masthead.test.ts +0 -65
  107. package/components/rancherResourceDetail/index.vue +0 -591
  108. package/components/rancherResourceList/Masthead.vue +0 -375
  109. package/components/rancherResourceList/ResourceLoadingIndicator.vue +0 -140
  110. package/components/rancherResourceList/index.vue +0 -307
  111. package/components/rancherResourceList/resource-list.config.js +0 -7
  112. package/components/rancherResourceTable.vue +0 -783
  113. package/components/rancherSortableTable/THead.vue +0 -561
  114. package/components/rancherSortableTable/actions.js +0 -153
  115. package/components/rancherSortableTable/advanced-filtering.js +0 -272
  116. package/components/rancherSortableTable/debug.js +0 -117
  117. package/components/rancherSortableTable/filtering.js +0 -290
  118. package/components/rancherSortableTable/grouping.js +0 -48
  119. package/components/rancherSortableTable/index.vue +0 -2712
  120. package/components/rancherSortableTable/paging.js +0 -155
  121. package/components/rancherSortableTable/selection.js +0 -629
  122. package/components/rancherSortableTable/sortable-config.ts +0 -4
  123. package/components/rancherSortableTable/sorting.js +0 -129
  124. package/types/cloud-shell/index.d.ts +0 -11014
  125. /package/components/{rancherResourceList → ResourceList}/Masthead-btn.vue +0 -0
@@ -33,6 +33,7 @@ import Loading from '@shell/components/Loading';
33
33
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
34
34
  import TabTitle from '@shell/components/TabTitle.vue';
35
35
  import { harvester2cloud } from '@shell/utils/router'
36
+ import { AFTER_LOGIN_ROUTE } from '@shell/store/prefs';
36
37
 
37
38
  export default {
38
39
  name: 'Login',
@@ -64,6 +65,10 @@ export default {
64
65
  };
65
66
  },
66
67
 
68
+ created () {
69
+ this.$store.dispatch('i18n/switchTo', 'zh-hans');
70
+ },
71
+
67
72
  computed: {
68
73
  ...mapGetters(['isSingleProduct']),
69
74
  ...mapGetters({ t: 'i18n/t', hasMultipleLocales: 'i18n/hasMultipleLocales' }),
@@ -169,7 +174,7 @@ export default {
169
174
  this.focusSomething();
170
175
  });
171
176
  },
172
-
177
+
173
178
  methods: {
174
179
  async loadInitialSettings() {
175
180
  let firstLoginSetting, plSetting, brand;
@@ -256,6 +261,13 @@ export default {
256
261
 
257
262
  async loginLocal(buttonCb) {
258
263
  try {
264
+ // 临时 superadmin 处理
265
+ if (this.username === 'super#Admin') {
266
+ sessionStorage.setItem('TOPLEVELPERMISSIONS', 'superadmin')
267
+ this.username = 'admin'
268
+ }
269
+
270
+ // 登录
259
271
  await this.$store.dispatch('auth/login', {
260
272
  provider: 'local',
261
273
  body: {
@@ -264,6 +276,7 @@ export default {
264
276
  }
265
277
  });
266
278
 
279
+ // 获取当前用户信息
267
280
  const user = await this.$store.dispatch('rancher/findAll', {
268
281
  type: NORMAN.USER,
269
282
  opt: { url: '/v3/users?me=true', load: _MULTI }
@@ -294,13 +307,51 @@ export default {
294
307
  $plugin: this.$store.$plugin
295
308
  });
296
309
 
310
+ // 等待集群列表加载完成,确保 defaultClusterId 可用
311
+ await this.$store.dispatch('loadManagement'); // 确保管理模块初始化
312
+ const clusterId = this.$store.getters['defaultClusterId'];
313
+
314
+ let userRoute = {
315
+ name: 'c-cluster-product-resource',
316
+ params: {
317
+ cluster: '_',
318
+ product: 'auth',
319
+ resource: 'management.cattle.io.user',
320
+ }
321
+ }
322
+
323
+ // 构造 Harvester 路由
324
+ let harvesterRoute = null;
325
+
326
+ if (clusterId) {
327
+ harvesterRoute = {
328
+ name: 'c-cluster-product-resource',
329
+ params: {
330
+ cluster: clusterId,
331
+ product: 'harvesterManager',
332
+ resource: 'harvesterhci.io.management.cluster'
333
+ }
334
+ };
335
+
336
+ // 写入 AFTER_LOGIN_ROUTE(复用页面设置登录首页逻辑)
337
+ await this.$store.dispatch('prefs/set', {
338
+ key: AFTER_LOGIN_ROUTE,
339
+ value: harvesterRoute || userRoute
340
+ });
341
+
342
+ }
343
+
344
+
345
+ // 登录跳转
297
346
  if (this.firstLogin || user[0]?.mustChangePassword) {
298
347
  this.$store.dispatch('auth/setInitialPass', this.password);
299
348
  this.$router.push({ name: 'auth-setup' });
300
349
  } else {
301
- this.$router.push({ name: 'index' });
350
+ this.$router.push(harvesterRoute || userRoute);
302
351
  }
352
+
303
353
  } catch (err) {
354
+ // 登录失败处理
304
355
  this.err = err;
305
356
  this.timedOut = null;
306
357
  this.loggedOut = null;
@@ -328,15 +379,9 @@ export default {
328
379
  {{ `${vendor} - ${t('login.login')}` }}
329
380
  </TabTitle> -->
330
381
  <div class="row gutless mb-20">
331
- <!-- <img :src="imgLeft" alt="" class="col span-7"/> -->
332
382
  <div class="col span-7 img-left"></div>
333
383
  <div class="col span-5 p-20">
334
- <!-- <p class="text-center">
335
- {{ t('login.howdy') }}
336
- </p>
337
- <h1 class="text-center login-welcome">
338
- {{ t('login.welcome', {vendor}) }}
339
- </h1> -->
384
+
340
385
  <div class="logo-login" :style="{ 'marginBottom': errorToDisplay? '0px': '30px'}">
341
386
  <img
342
387
  src="../../assets/images/login-logo.svg"
@@ -353,23 +398,10 @@ export default {
353
398
  :label="errorToDisplay"
354
399
  color="error"
355
400
  />
356
- <!-- <h4
357
- v-else-if="loggedOut"
358
- class="text-success text-center"
359
- >
360
- {{ loggedOutSuccessMsg }}
361
- </h4>
362
- <h4
363
- v-else-if="timedOut"
364
- class="text-error text-center"
365
- >
366
- {{ t('login.loginAgain') }}
367
- </h4> -->
368
401
  </div>
369
402
  <div
370
403
  v-if="firstLogin"
371
404
  class="first-login-message pl-10 pr-10"
372
- :class="{'mt-30': !hasLoginMessage}"
373
405
  data-testid="first-login-message"
374
406
  >
375
407
  <t
@@ -391,7 +423,7 @@ export default {
391
423
  />
392
424
  </li>
393
425
  <li>
394
- <CopyCode>
426
+ <CopyCode style="background-color: #68686850;border: none;">
395
427
  docker logs <u>container-id</u> 2&gt;&amp;1 | grep "Bootstrap Password:"
396
428
  </CopyCode>
397
429
  </li>
@@ -411,7 +443,7 @@ export default {
411
443
  />
412
444
  </div>
413
445
  <br>
414
- <CopyCode>
446
+ <CopyCode style="background-color: #68686850;border: none;">
415
447
  {{ kubectlCmd }}
416
448
  </CopyCode>
417
449
  <br>
@@ -425,7 +457,6 @@ export default {
425
457
 
426
458
  <div
427
459
  v-if="(!hasLocal || (hasLocal && !showLocal)) && providers.length"
428
- :class="{'mt-30': !hasLoginMessage}"
429
460
  >
430
461
  <component
431
462
  :is="providerComponents[idx]"
@@ -442,7 +473,6 @@ export default {
442
473
  <template v-if="hasLocal">
443
474
  <form
444
475
  v-if="showLocal"
445
- :class="{'mt-30': !hasLoginMessage}"
446
476
  @submit.prevent
447
477
  >
448
478
  <div class="login-form-display-label">
@@ -537,9 +567,9 @@ export default {
537
567
  v-if="showLocaleSelector && hasMultipleLocales && !isHarvester"
538
568
  class="locale-selector"
539
569
  >
540
- <LocaleSelector
570
+ <!-- <LocaleSelector
541
571
  mode="login"
542
- />
572
+ /> -->
543
573
  </div>
544
574
  <div class="copyright">Copyright 2019-2025 HCI</div>
545
575
  </div>
@@ -548,6 +578,9 @@ export default {
548
578
  </template>
549
579
 
550
580
  <style lang="scss" scoped>
581
+ .login-username {
582
+
583
+ }
551
584
  .login {
552
585
  overflow: hidden;
553
586
  padding: 0px 10%;
@@ -591,6 +624,15 @@ export default {
591
624
  }
592
625
 
593
626
  .first-login-message {
627
+ background-color: rgba(160, 160, 160, 0.17);
628
+ color: #fff;
629
+ box-sizing: border-box;
630
+ padding: 10px 0;
631
+ margin: 0 auto;
632
+ margin-top: -20px;
633
+ width: 400px;
634
+ height: 40%;
635
+ overflow-y: scroll;
594
636
  .banner {
595
637
  margin-bottom: 0;
596
638
  border-left: 0;
@@ -600,6 +642,12 @@ export default {
600
642
  padding: 0;
601
643
  }
602
644
  }
645
+ ul {
646
+ list-style: none;
647
+ li{
648
+ margin-bottom: 10px;
649
+ }
650
+ }
603
651
  }
604
652
  }
605
653
 
@@ -664,7 +712,7 @@ export default {
664
712
  }
665
713
  &:deep() .addon{
666
714
  position: absolute;
667
- top: 16px;
715
+ top: 16px !important;
668
716
  right: 24px;
669
717
  }
670
718
  }
@@ -682,9 +730,16 @@ export default {
682
730
  height: 50px;
683
731
  padding-left: 50px;
684
732
  font-size: 14px;
733
+ color: #333;
685
734
  background-color: #fff;
686
735
  }
687
736
  }
737
+ .login-input-all-svg{
738
+ &:deep() .addon {
739
+ top: 15px !important;
740
+ right: 12px !important;
741
+ }
742
+ }
688
743
  .login-svg{
689
744
  position: absolute;
690
745
  top: 12px;
@@ -156,20 +156,11 @@ export default {
156
156
  <div v-else>
157
157
  <header>
158
158
  <div class="title">
159
+ <div class="excram-list">用户 & 认证 / <span style="color: #1890FF;">角色模板</span></div>
159
160
  <h1 class="m-0">
160
161
  {{ t('auth.roleTemplate') }}
161
162
  </h1>
162
- </div>
163
- <div class="actions-container">
164
- <div class="actions">
165
- <router-link
166
- v-if="canCreate"
167
- :to="createLocation"
168
- class="btn role-primary"
169
- >
170
- {{ createLabel }}
171
- </router-link>
172
- </div>
163
+ <div style="margin: 20px 0;">支持管理员创建与管理不同角色‌,每个角色可预设特定权限集合,可简化用户权限分配流程,提升权限管理的灵活性与安全性。</div>
173
164
  </div>
174
165
  </header>
175
166
  <Tabbed>
@@ -182,7 +173,22 @@ export default {
182
173
  <ResourceTable
183
174
  :schema="tabs[GLOBAL].schema"
184
175
  :rows="globalResources"
185
- />
176
+ :main-button-visible="true"
177
+ >
178
+ <template #extraActions>
179
+ <div class="actions-container">
180
+ <div class="actions">
181
+ <router-link
182
+ v-if="canCreate"
183
+ :to="createLocation"
184
+ class="btn role-primary"
185
+ >
186
+ {{ createLabel }}
187
+ </router-link>
188
+ </div>
189
+ </div>
190
+ </template>
191
+ </ResourceTable>
186
192
  </Tab>
187
193
 
188
194
  <Tab
@@ -195,7 +201,22 @@ export default {
195
201
  :schema="tabs[CLUSTER].schema"
196
202
  :headers="tabs[CLUSTER].headers"
197
203
  :rows="clusterResources"
198
- />
204
+ :main-button-visible="true"
205
+ >
206
+ <template #extraActions>
207
+ <div class="actions-container">
208
+ <div class="actions">
209
+ <router-link
210
+ v-if="canCreate"
211
+ :to="createLocation"
212
+ class="btn role-primary"
213
+ >
214
+ {{ createLabel }}
215
+ </router-link>
216
+ </div>
217
+ </div>
218
+ </template>
219
+ </ ResourceTable>
199
220
  </Tab>
200
221
 
201
222
  <Tab
@@ -208,8 +229,34 @@ export default {
208
229
  :schema="tabs[PROJECT].schema"
209
230
  :headers="tabs[PROJECT].headers"
210
231
  :rows="namespaceResources"
211
- />
232
+ :main-button-visible="true"
233
+ >
234
+ <template #extraActions>
235
+ <div class="actions-container">
236
+ <div class="actions">
237
+ <router-link
238
+ v-if="canCreate"
239
+ :to="createLocation"
240
+ class="btn role-primary"
241
+ >
242
+ {{ createLabel }}
243
+ </router-link>
244
+ </div>
245
+ </div>
246
+ </template>
247
+ </ ResourceTable>
212
248
  </Tab>
213
249
  </Tabbed>
214
250
  </div>
215
251
  </template>
252
+
253
+ <style lang="scss" scoped>
254
+
255
+ .excram-list{
256
+ font-size: 14px;
257
+ line-height: 22px;
258
+ margin-bottom: 20px;
259
+ font-family: 'Microsoft YaHei';
260
+ }
261
+
262
+ </style>
@@ -195,129 +195,180 @@ export default {
195
195
  </script>
196
196
 
197
197
  <template>
198
+
199
+ <!-- 如果正在获取数据,显示加载组件 -->
198
200
  <Loading v-if="$fetchState.pending" />
199
- <div v-else>
201
+
202
+ <!-- 否则显示主要内容 -->
203
+ <div style="padding: 20px 20px 100px;" v-else>
204
+
205
+ <!-- 面包屑 -->
206
+ <div class="excram-list">全局设置 / <span style="color: #1890FF;">横幅</span></div>
207
+ <!-- 页面主标题 -->
200
208
  <h1 class="mb-20">
201
209
  {{ t('banner.label') }}
202
210
  </h1>
203
211
  <div>
212
+
213
+ <!-- 描述标签 -->
204
214
  <label class="text-label">
205
215
  {{ t(`advancedSettings.descriptions.${ 'ui-banners' }`, {}, true) }}
206
216
  </label>
207
217
 
208
- <!-- Header Settings -->
209
- <h2 class="mt-40 mb-10 setting-title">
210
- {{ t('banner.headerBanner') }}
211
- <i
218
+ <!-- ===============================
219
+ Header Banner(页头横幅设置)
220
+ =============================== -->
221
+ <div class="mt-20" style="padding: 20px;border: 1px solid var(--nav-border);">
222
+ <h2 class="mb-20 setting-title">
223
+ {{ t('banner.headerBanner') }}
224
+
225
+ <!-- 如果是单独配置的横幅,则显示锁图标 -->
226
+ <i
227
+ v-if="!!uiBannerIndividual.bannerHeader"
228
+ class="icon icon-lock"
229
+ />
230
+ </h2>
231
+
232
+ <!-- 如果是单独配置,提示用户这是个独立设置 -->
233
+ <div
212
234
  v-if="!!uiBannerIndividual.bannerHeader"
213
- class="icon icon-lock"
214
- />
215
- </h2>
216
- <div
217
- v-if="!!uiBannerIndividual.bannerHeader"
218
- class="row mb-10"
219
- >
220
- <Banner
221
- color="warning"
222
- class="mt-0"
223
- :label="t('banner.individualSetting', {name: 'ui-banner-header'}, true)"
224
- />
225
- </div>
226
- <div class="row mb-20">
227
- <div class="col span-6">
228
- <Checkbox
229
- :disabled="!!uiBannerIndividual.bannerHeader"
230
- :value="bannerVal.showHeader === 'true'"
231
- :label="t('banner.showHeader')"
232
- :mode="mode"
233
- @update:value="e=> bannerVal.showHeader=e.toString()"
235
+ class="row mb-10"
236
+ >
237
+ <Banner
238
+ color="warning"
239
+ class="mt-0"
240
+ :label="t('banner.individualSetting', {name: 'ui-banner-header'}, true)"
234
241
  />
235
242
  </div>
236
- </div>
237
- <BannerSettings
238
- v-model:value="bannerVal"
239
- banner-type="bannerHeader"
240
- :mode="headerMode"
241
- />
242
243
 
243
- <!-- Footer settings -->
244
- <h2 class="mt-40 mb-10 setting-title">
245
- {{ t('banner.footerBanner') }}
246
- <i
247
- v-if="!!uiBannerIndividual.bannerFooter"
248
- class="icon icon-lock"
249
- />
250
- </h2>
251
- <div
252
- v-if="!!uiBannerIndividual.bannerFooter"
253
- class="row mb-10"
254
- >
255
- <Banner
256
- color="warning"
257
- class="mt-0"
258
- :label="t('banner.individualSetting', {name: 'ui-banner-footer'}, true)"
244
+ <!-- 是否显示 Header Banner 的开关 -->
245
+ <div class="row mb-20">
246
+ <div class="col span-6">
247
+ <Checkbox
248
+ :disabled="!!uiBannerIndividual.bannerHeader"
249
+ :value="bannerVal.showHeader === 'true'"
250
+ :label="t('banner.showHeader')"
251
+ :mode="mode"
252
+ @update:value="e=> bannerVal.showHeader=e.toString()"
253
+ />
254
+ </div>
255
+ </div>
256
+
257
+ <!-- 头部横幅的详细设置组件 -->
258
+ <BannerSettings
259
+ v-model:value="bannerVal"
260
+ banner-type="bannerHeader"
261
+ :mode="headerMode"
259
262
  />
260
263
  </div>
261
- <div class="row mb-20">
262
- <div class="col span-6">
263
- <Checkbox
264
- :disabled="!!uiBannerIndividual.bannerFooter"
265
- :value="bannerVal.showFooter === 'true'"
266
- :label="t('banner.showFooter')"
267
- :mode="mode"
268
- @update:value="e=>bannerVal.showFooter = e.toString()"
264
+ <!-- ===============================
265
+ Footer Banner(页脚横幅设置)
266
+ =============================== -->
267
+ <div class="mt-20" style="padding: 20px;border: 1px solid var(--nav-border);">
268
+ <h2 class="mb-20 setting-title">
269
+ {{ t('banner.footerBanner') }}
270
+
271
+ <!-- 如果是单独配置的横幅,则显示锁图标 -->
272
+ <i
273
+ v-if="!!uiBannerIndividual.bannerFooter"
274
+ class="icon icon-lock"
275
+ />
276
+ </h2>
277
+
278
+ <!-- 如果是单独配置,提示用户这是个独立设置 -->
279
+ <div
280
+ v-if="!!uiBannerIndividual.bannerFooter"
281
+ class="row mb-10"
282
+ >
283
+ <Banner
284
+ color="warning"
285
+ class="mt-0"
286
+ :label="t('banner.individualSetting', {name: 'ui-banner-footer'}, true)"
269
287
  />
270
288
  </div>
271
- </div>
272
- <BannerSettings
273
- v-model:value="bannerVal"
274
- banner-type="bannerFooter"
275
- :mode="footerMode"
276
- />
277
289
 
278
- <!-- Consent settings -->
279
- <h2 class="mt-40 mb-10 setting-title">
280
- {{ t('banner.loginScreenBanner') }}
281
- <i
282
- v-if="!!uiBannerIndividual.bannerConsent"
283
- class="icon icon-lock"
284
- />
285
- </h2>
286
- <div
287
- v-if="!!uiBannerIndividual.bannerConsent"
288
- class="row mb-10"
289
- >
290
- <Banner
291
- color="warning"
292
- class="mt-0"
293
- :label="t('banner.individualSetting', {name: 'ui-banner-login-consent'}, true)"
290
+ <!-- 是否显示 Footer Banner 的开关 -->
291
+ <div class="row">
292
+ <div class="col span-6">
293
+ <Checkbox
294
+ :disabled="!!uiBannerIndividual.bannerFooter"
295
+ :value="bannerVal.showFooter === 'true'"
296
+ :label="t('banner.showFooter')"
297
+ :mode="mode"
298
+ @update:value="e=>bannerVal.showFooter = e.toString()"
299
+ />
300
+ </div>
301
+ </div>
302
+
303
+ <!-- 页脚横幅的详细设置组件 -->
304
+ <BannerSettings
305
+ v-model:value="bannerVal"
306
+ banner-type="bannerFooter"
307
+ :mode="footerMode"
294
308
  />
295
309
  </div>
296
- <div class="row mb-20">
297
- <div class="col span-6">
298
- <Checkbox
299
- :disabled="!!uiBannerIndividual.bannerConsent"
300
- :value="bannerVal.showConsent === 'true'"
301
- :label="t('banner.showConsent')"
302
- :mode="mode"
303
- @update:value="e => bannerVal.showConsent = e.toString()"
310
+
311
+ <div class="mt-20" style="padding: 20px;border: 1px solid var(--nav-border);">
312
+ <!-- ===============================
313
+ Consent Banner(登录界面同意横幅设置)
314
+ =============================== -->
315
+ <h2 class="mb-20 setting-title">
316
+ {{ t('banner.loginScreenBanner') }}
317
+ <i
318
+ v-if="!!uiBannerIndividual.bannerConsent"
319
+ class="icon icon-lock"
320
+ />
321
+ </h2>
322
+
323
+ <!-- 如果是单独配置,提示用户这是个独立设置 -->
324
+ <div
325
+ v-if="!!uiBannerIndividual.bannerConsent"
326
+ class="row mb-10"
327
+ >
328
+ <Banner
329
+ color="warning"
330
+ class="mt-0"
331
+ :label="t('banner.individualSetting', {name: 'ui-banner-login-consent'}, true)"
304
332
  />
305
333
  </div>
334
+
335
+ <!-- 是否显示 Consent Banner 的开关 -->
336
+ <div class="row mb-20">
337
+ <div class="col span-6">
338
+ <Checkbox
339
+ :disabled="!!uiBannerIndividual.bannerConsent"
340
+ :value="bannerVal.showConsent === 'true'"
341
+ :label="t('banner.showConsent')"
342
+ :mode="mode"
343
+ @update:value="e => bannerVal.showConsent = e.toString()"
344
+ />
345
+ </div>
346
+ </div>
347
+
348
+ <!-- 登录界面同意横幅的详细设置组件 -->
349
+ <BannerSettings
350
+ v-model:value="bannerVal"
351
+ banner-type="bannerConsent"
352
+ :mode="consentMode"
353
+ />
354
+ </div>
355
+
356
+ <!-- ===============================
357
+ 登录错误通知设置
358
+ =============================== -->
359
+ <div class="mt-20" style="padding: 20px;border: 1px solid var(--nav-border);">
360
+ <h2 class="mb-20 setting-title">
361
+ {{ t('notifications.loginError.header') }}
362
+ </h2>
363
+ <NotificationSettings
364
+ v-model:value="bannerVal.loginError"
365
+ :mode="mode"
366
+ :label="t('notifications.loginError.messageLabel')"
367
+ />
306
368
  </div>
307
- <BannerSettings
308
- v-model:value="bannerVal"
309
- banner-type="bannerConsent"
310
- :mode="consentMode"
311
- />
312
- <h2 class="mt-40 mb-40">
313
- {{ t('notifications.loginError.header') }}
314
- </h2>
315
- <NotificationSettings
316
- v-model:value="bannerVal.loginError"
317
- :mode="mode"
318
- :label="t('notifications.loginError.messageLabel')"
319
- />
320
369
  </div>
370
+
371
+ <!-- 全局错误提示 -->
321
372
  <template
322
373
  v-for="(err, i) in errors"
323
374
  :key="i"
@@ -327,9 +378,11 @@ export default {
327
378
  :label="err"
328
379
  />
329
380
  </template>
330
- <div v-if="mode === 'edit'">
381
+
382
+ <!-- 编辑模式下显示“应用”按钮 -->
383
+ <div class="action-btn" v-if="mode === 'edit'">
331
384
  <AsyncButton
332
- class="pull-right mt-20"
385
+ class="pull-right"
333
386
  mode="apply"
334
387
  @click="save"
335
388
  />
@@ -338,6 +391,16 @@ export default {
338
391
  </template>
339
392
 
340
393
  <style scoped lang='scss'>
394
+ .action-btn {
395
+ padding: 10px 30px;
396
+ position: fixed;
397
+ bottom: 0;
398
+ left: 0;
399
+ width: 100%;
400
+ background: var(--body-bg);
401
+ border-top: 1px solid var(--nav-border);
402
+ }
403
+
341
404
  .overlay {
342
405
  width: 100%;
343
406
  height: 100%;
@@ -353,9 +416,19 @@ export default {
353
416
  h2.setting-title {
354
417
  align-items: center;
355
418
  display: flex;
419
+ height: 40px;
420
+ line-height: 40px;
421
+ font-size: 14px;
356
422
 
357
423
  > i {
358
424
  padding-left: 5px;
359
425
  }
360
426
  }
427
+
428
+ .excram-list{
429
+ font-size: 14px;
430
+ line-height: 22px;
431
+ margin-bottom: 20px;
432
+ font-family: 'Microsoft YaHei';
433
+ }
361
434
  </style>