This is an automated email from the ASF dual-hosted git repository.

panyuepeng pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-streampark.git


The following commit(s) were added to refs/heads/dev by this push:
     new d9827e155 [Improve] Sync code from  2.1.3 (#3608)
d9827e155 is described below

commit d9827e1550fa231d8c9bcff58dbf22bdf0708469
Author: benjobs <[email protected]>
AuthorDate: Thu Mar 14 13:13:38 2024 +0800

    [Improve] Sync code from  2.1.3 (#3608)
    
    * [Improve] 2.1.3 ddl
    
    * [Improve] docker|email check improvements
    
    * [Improve] new icons
    
    * [Improve] new icons
    
    * [Improve] alert-message minor improvement
    
    * [Improve] scrollbar style improvements
    
    * [Improve] method name improvements
    
    * [Improve] job state style improvements
    
    * [Improve] job state improvements
    
    * [Improve] component import improvements
    
    * [Improve] check-style issue improvements
    
    * [Improve] delete SECURITY.md
    
    ---------
    
    Co-authored-by: benjobs <[email protected]>
---
 .../common/conf/InternalConfigHolder.scala         |   3 +
 .../src/main/assembly/script/data/mysql-data.sql   |   3 +-
 .../src/main/assembly/script/data/pgsql-data.sql   |   1 +
 .../main/assembly/script/upgrade/mysql/2.1.2.sql}  |  16 +-
 .../main/assembly/script/upgrade/mysql/2.1.3.sql   |  46 ++++++
 .../main/assembly/script/upgrade/pgsql/2.1.2.sql   |  19 +++
 .../main/assembly/script/upgrade/pgsql/2.1.3.sql   |  24 +++
 .../console/core/controller/SettingController.java |   4 +-
 .../console/core/runner/EnvInitializer.java        |   6 +-
 .../core/service/impl/SavePointServiceImpl.java    |   4 +-
 .../console/core/service/SavePointServiceTest.java |  15 +-
 .../src/api/setting/index.ts                       |  71 ++++++++-
 .../src/assets/icons/edit.svg                      |   2 +-
 .../src/assets/icons/maven.svg                     |   3 +-
 .../src/assets/icons/nginx.svg                     |   1 +
 .../src/assets/icons/save.svg                      |   1 +
 .../src/assets/icons/settings.svg                  |   3 +-
 .../src/assets/icons/settings2.svg                 |   1 +
 .../src/design/public.less                         |  43 +++---
 .../src/locales/lang/en/flink/app.ts               |  52 ++++++-
 .../src/locales/lang/en/setting/system.ts          |  67 ++++++++-
 .../src/locales/lang/en/sys.ts                     |   4 +-
 .../src/locales/lang/zh-CN/flink/app.ts            |  46 +++++-
 .../src/locales/lang/zh-CN/menu.ts                 |   2 +-
 .../src/locales/lang/zh-CN/setting/system.ts       |  67 ++++++++-
 .../src/views/flink/app/View.vue                   |  11 +-
 .../components/AppView/StartApplicationModal.vue   |  80 +++++-----
 .../src/views/flink/app/components/State.less      |   4 -
 .../src/views/flink/app/components/State.tsx       |  63 ++++----
 .../src/views/setting/{Alarm => alarm}/View.vue    |   0
 .../{Alarm => alarm}/components/AlertModal.vue     |   0
 .../{Alarm => alarm}/components/AlertTypeInfo.vue  |   0
 .../{Alarm => alarm}/components/DetailModal.vue    |   0
 .../{Alarm => alarm}/components/index.data.ts      |   0
 .../setting/{Alarm => alarm}/components/index.ts   |   0
 .../src/views/setting/system/SettingForm.vue       | 166 +++++++++++++++++++++
 .../setting/{System => system}/SettingList.vue     |  75 +++++++---
 .../src/views/setting/{System => system}/View.vue  |   0
 .../src/views/setting/system/config.ts             | 138 +++++++++++++++++
 .../setting/{YarnQueue => yarnqueue}/View.vue      |   0
 .../{YarnQueue => yarnqueue}/YarnQueueDrawer.vue   |   0
 .../setting/{YarnQueue => yarnqueue}/index.data.ts |   0
 .../useYarnQueueRender.tsx                         |   0
 43 files changed, 877 insertions(+), 164 deletions(-)

diff --git 
a/streampark-common/src/main/scala/org/apache/streampark/common/conf/InternalConfigHolder.scala
 
b/streampark-common/src/main/scala/org/apache/streampark/common/conf/InternalConfigHolder.scala
index d61ac2ee3..764031108 100644
--- 
a/streampark-common/src/main/scala/org/apache/streampark/common/conf/InternalConfigHolder.scala
+++ 
b/streampark-common/src/main/scala/org/apache/streampark/common/conf/InternalConfigHolder.scala
@@ -43,6 +43,9 @@ object InternalConfigHolder extends Logger {
   /** configuration key options storage (key -> ConfigOption) */
   private val confOptions = new ConcurrentHashMap[String, 
InternalOption](initialCapacity)
 
+  /** Initialize the ConfigHub. */
+  Seq(CommonConfig, K8sFlinkConfig)
+
   /** Register the ConfigOption */
   private[conf] def register(@Nonnull conf: InternalOption): Unit = {
     confOptions.put(conf.key, conf)
diff --git 
a/streampark-console/streampark-console-service/src/main/assembly/script/data/mysql-data.sql
 
b/streampark-console/streampark-console-service/src/main/assembly/script/data/mysql-data.sql
index 5ae06037d..b47810339 100644
--- 
a/streampark-console/streampark-console-service/src/main/assembly/script/data/mysql-data.sql
+++ 
b/streampark-console/streampark-console-service/src/main/assembly/script/data/mysql-data.sql
@@ -284,7 +284,8 @@ insert into `t_setting` values (6, 'alert.email.port', 
null, 'Alert Email Smtp P
 insert into `t_setting` values (7, 'alert.email.from', null, 'Alert Sender 
Email', 'Email to send alerts', 1);
 insert into `t_setting` values (8, 'alert.email.userName', null, 'Alert  Email 
User', 'Authentication username used to send alert emails', 1);
 insert into `t_setting` values (9, 'alert.email.password', null, 'Alert Email 
Password', 'Authentication password used to send alarm email', 1);
-insert into `t_setting` values (10, 'alert.email.ssl', 'false', 'Alert Email 
SSL', 'Whether to enable SSL in the mailbox that sends the alert', 2);insert 
into `t_setting` values (11, 'docker.register.address', null, 'Docker Register 
Address', 'Docker container service address', 1);
+insert into `t_setting` values (10, 'alert.email.ssl', 'false', 'Alert Email 
SSL', 'Whether to enable SSL in the mailbox that sends the alert', 2);
+insert into `t_setting` values (11, 'docker.register.address', null, 'Docker 
Register Address', 'Docker container service address', 1);
 insert into `t_setting` values (12, 'docker.register.user', null, 'Docker 
Register User', 'Docker container service authentication username', 1);
 insert into `t_setting` values (13, 'docker.register.password', null, 'Docker 
Register Password', 'Docker container service authentication password', 1);
 insert into `t_setting` values (14, 'docker.register.namespace', null, 'Docker 
namespace', 'Namespace for docker image used in docker building env and target 
image register', 1);
diff --git 
a/streampark-console/streampark-console-service/src/main/assembly/script/data/pgsql-data.sql
 
b/streampark-console/streampark-console-service/src/main/assembly/script/data/pgsql-data.sql
index 0e5a96462..d9105e592 100644
--- 
a/streampark-console/streampark-console-service/src/main/assembly/script/data/pgsql-data.sql
+++ 
b/streampark-console/streampark-console-service/src/main/assembly/script/data/pgsql-data.sql
@@ -255,6 +255,7 @@ insert into "public"."t_setting" values (12, 
'docker.register.user', null, 'Dock
 insert into "public"."t_setting" values (13, 'docker.register.password', null, 
'Docker Register Password', 'Docker container service authentication password', 
1);
 insert into "public"."t_setting" values (14, 'docker.register.namespace', 
null, 'Docker namespace', 'Namespace for docker image used in docker building 
env and target image register', 1);
 insert into "public"."t_setting" values (15, 'ingress.mode.default', null, 
'Ingress domain address', 'Automatically generate an nginx-based ingress by 
passing in a domain name', 1);
+
 -- ----------------------------
 -- Records of t_user
 -- ----------------------------
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/index.ts
 
b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.2.sql
similarity index 69%
copy from 
streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/index.ts
copy to 
streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.2.sql
index 72e4e953c..c0fafc641 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/index.ts
+++ 
b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.2.sql
@@ -6,7 +6,7 @@
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
  *
- *    https://www.apache.org/licenses/LICENSE-2.0
+ *    http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-export { default as DetailModal } from './DetailModal.vue';
-export { default as AlertModal } from './AlertModal.vue';
-export { default as AlertTypeInfo } from './AlertTypeInfo.vue';
+
+use streampark;
+
+set names utf8mb4;
+set foreign_key_checks = 0;
+
+alter table `t_flink_app`
+    add column `dependency` text collate utf8mb4_general_ci default null after 
`main_class`,
+    modify column `modify_time` datetime not null comment 'modify time';
+
+set foreign_key_checks = 1;
diff --git 
a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.3.sql
 
b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.3.sql
new file mode 100644
index 000000000..b942363db
--- /dev/null
+++ 
b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/mysql/2.1.3.sql
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use streampark;
+
+set names utf8mb4;
+set foreign_key_checks = 0;
+
+update `t_menu` set menu_name='Apache Flink',order_num=1 where menu_id = 
120000;
+update `t_menu` set order_num=3 where menu_id = 110000;
+update `t_menu` set order_num=2 where menu_id = 130000;
+delete from `t_menu` where menu_id=110300;
+
+alter table `t_flink_app`
+    modify column `args` longtext,
+    modify column `dynamic_properties` longtext,
+    modify column `k8s_pod_template` longtext,
+    modify column `k8s_jm_pod_template` longtext,
+    modify column `k8s_tm_pod_template` longtext,
+    modify column `options` longtext comment 'json form of parameter 
collection ',
+    modify column `modify_time` datetime default null comment 'modify time';
+
+alter table `t_flink_cluster`
+    modify column `options` longtext comment 'json form of parameter 
collection ',
+    modify column `dynamic_properties` longtext comment 'allows specifying 
multiple generic configuration options',
+    modify column `exception` longtext comment 'exception information';
+
+alter table `t_message` modify column `context` longtext;
+
+alter table `t_flink_project` drop column `git_credential`;
+
+set foreign_key_checks = 1;
diff --git 
a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.2.sql
 
b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.2.sql
new file mode 100644
index 000000000..a87816bf4
--- /dev/null
+++ 
b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.2.sql
@@ -0,0 +1,19 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*    http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+alter table "public"."t_flink_app"
+    add column "dependency" text collate "pg_catalog"."default";
diff --git 
a/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.3.sql
 
b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.3.sql
new file mode 100644
index 000000000..2f147d89a
--- /dev/null
+++ 
b/streampark-console/streampark-console-service/src/main/assembly/script/upgrade/pgsql/2.1.3.sql
@@ -0,0 +1,24 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*    http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+alter table "public"."t_flink_project"
+    drop column "git_credential";
+
+update "public"."t_menu" set menu_name='Apache Flink',order_num=1 where 
menu_id = 120000;
+update "public"."t_menu" set order_num=3 where menu_id = 110000;
+update "public"."t_menu" set order_num=2 where menu_id = 130000;
+delete from "public"."t_menu" where menu_id=110300;
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/SettingController.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/SettingController.java
index 6517e7e1e..27b357549 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/SettingController.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/SettingController.java
@@ -43,7 +43,7 @@ import java.util.List;
 @Slf4j
 @Validated
 @RestController
-@RequestMapping("flink/setting")
+@RequestMapping("setting")
 public class SettingController {
 
   @Autowired private SettingService settingService;
@@ -122,7 +122,7 @@ public class SettingController {
   }
 
   @Operation(summary = "Check hadoop status")
-  @PostMapping("checkHadoop")
+  @PostMapping("check/hadoop")
   public RestResponse checkHadoop() {
     try {
       HadoopUtils.hdfs().getStatus();
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/runner/EnvInitializer.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/runner/EnvInitializer.java
index 032dc9e0c..635c2a465 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/runner/EnvInitializer.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/runner/EnvInitializer.java
@@ -77,6 +77,9 @@ public class EnvInitializer implements ApplicationRunner {
   @SneakyThrows
   @Override
   public void run(ApplicationArguments args) throws Exception {
+
+    checkAppHome();
+
     // init InternalConfig
     initConfig();
 
@@ -101,6 +104,7 @@ public class EnvInitializer implements ApplicationRunner {
               Utils.requireNotNull(config);
               InternalConfigHolder.set(config, env.getProperty(key, 
config.classType()));
             });
+
     InternalConfigHolder.log();
 
     settingService.getMavenConfig().updateConfig();
@@ -118,8 +122,6 @@ public class EnvInitializer implements ApplicationRunner {
 
   public synchronized void storageInitialize(StorageType storageType) {
 
-    checkAppHome();
-
     if (initialized.contains(storageType)) {
       return;
     }
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/SavePointServiceImpl.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/SavePointServiceImpl.java
index 2a570ba6b..8616e5a21 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/SavePointServiceImpl.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/SavePointServiceImpl.java
@@ -146,7 +146,7 @@ public class SavePointServiceImpl extends 
ServiceImpl<SavePointMapper, SavePoint
     // task, see if Application conf is configured when the task is defined, 
if checkpoints are
     // configured
     // and enabled, read `state.savepoints.dir`
-    savepointPath = getSavepointFromAppCfgIfStreamParkOrSQLJob(application);
+    savepointPath = getSavepointFromConfig(application);
     if (StringUtils.isNotBlank(savepointPath)) {
       return savepointPath;
     }
@@ -340,7 +340,7 @@ public class SavePointServiceImpl extends 
ServiceImpl<SavePointMapper, SavePoint
    */
   @VisibleForTesting
   @Nullable
-  public String getSavepointFromAppCfgIfStreamParkOrSQLJob(Application 
application) {
+  public String getSavepointFromConfig(Application application) {
     if (!application.isStreamParkJob() && !application.isFlinkSqlJob()) {
       return null;
     }
diff --git 
a/streampark-console/streampark-console-service/src/test/java/org/apache/streampark/console/core/service/SavePointServiceTest.java
 
b/streampark-console/streampark-console-service/src/test/java/org/apache/streampark/console/core/service/SavePointServiceTest.java
index e56d76f6e..e2851c419 100644
--- 
a/streampark-console/streampark-console-service/src/test/java/org/apache/streampark/console/core/service/SavePointServiceTest.java
+++ 
b/streampark-console/streampark-console-service/src/test/java/org/apache/streampark/console/core/service/SavePointServiceTest.java
@@ -95,17 +95,17 @@ class SavePointServiceTest extends SpringUnitTestBase {
 
     // Test for non-(StreamPark job Or FlinkSQL job)
     app.setAppType(ApplicationType.APACHE_FLINK.getType());
-    
assertThat(savePointServiceImpl.getSavepointFromAppCfgIfStreamParkOrSQLJob(app)).isNull();
+    assertThat(savePointServiceImpl.getSavepointFromConfig(app)).isNull();
     app.setAppType(ApplicationType.STREAMPARK_FLINK.getType());
     app.setJobType(FlinkDevelopmentMode.CUSTOM_CODE.getMode());
-    
assertThat(savePointServiceImpl.getSavepointFromAppCfgIfStreamParkOrSQLJob(app)).isNull();
+    assertThat(savePointServiceImpl.getSavepointFromConfig(app)).isNull();
 
     // Test for (StreamPark job Or FlinkSQL job) without application config.
     app.setAppType(ApplicationType.STREAMPARK_FLINK.getType());
-    
assertThat(savePointServiceImpl.getSavepointFromAppCfgIfStreamParkOrSQLJob(app)).isNull();
+    assertThat(savePointServiceImpl.getSavepointFromConfig(app)).isNull();
     app.setAppType(ApplicationType.STREAMPARK_FLINK.getType());
     app.setJobType(FlinkDevelopmentMode.CUSTOM_CODE.getMode());
-    
assertThat(savePointServiceImpl.getSavepointFromAppCfgIfStreamParkOrSQLJob(app)).isNull();
+    assertThat(savePointServiceImpl.getSavepointFromConfig(app)).isNull();
 
     // Test for (StreamPark job Or FlinkSQL job) with application config just 
disabled checkpoint.
     ApplicationConfig appCfg = new ApplicationConfig();
@@ -114,7 +114,7 @@ class SavePointServiceTest extends SpringUnitTestBase {
     appCfg.setContent("state.savepoints.dir=hdfs:///test");
     appCfg.setFormat(ConfigFileTypeEnum.PROPERTIES.getValue());
     configService.save(appCfg);
-    
assertThat(savePointServiceImpl.getSavepointFromAppCfgIfStreamParkOrSQLJob(app)).isNull();
+    assertThat(savePointServiceImpl.getSavepointFromConfig(app)).isNull();
 
     // Test for (StreamPark job or FlinkSQL job) with application config and 
enabled checkpoint and
     // configured value.
@@ -122,7 +122,7 @@ class SavePointServiceTest extends SpringUnitTestBase {
     // Test for non-value for CHECKPOINTING_INTERVAL
     appCfg.setContent("");
     configService.updateById(appCfg);
-    
assertThat(savePointServiceImpl.getSavepointFromAppCfgIfStreamParkOrSQLJob(app)).isNull();
+    assertThat(savePointServiceImpl.getSavepointFromConfig(app)).isNull();
 
     // Test for configured CHECKPOINTING_INTERVAL
     appCfg.setContent(
@@ -135,8 +135,7 @@ class SavePointServiceTest extends SpringUnitTestBase {
     effective.setAppId(appId);
     effective.setTargetType(EffectiveTypeEnum.CONFIG.getType());
     effectiveService.save(effective);
-    
assertThat(savePointServiceImpl.getSavepointFromAppCfgIfStreamParkOrSQLJob(app))
-        .isEqualTo("hdfs:///test");
+    
assertThat(savePointServiceImpl.getSavepointFromConfig(app)).isEqualTo("hdfs:///test");
   }
 
   @Test
diff --git 
a/streampark-console/streampark-console-webapp/src/api/setting/index.ts 
b/streampark-console/streampark-console-webapp/src/api/setting/index.ts
index 305e1a0ed..c2f771c23 100644
--- a/streampark-console/streampark-console-webapp/src/api/setting/index.ts
+++ b/streampark-console/streampark-console-webapp/src/api/setting/index.ts
@@ -18,11 +18,16 @@ import { SystemSetting } from './types/setting.type';
 import { defHttp } from '/@/utils/http/axios';
 
 enum SETTING_APi {
-  GET = '/flink/setting/get',
-  ALL = '/flink/setting/all',
-  CHECK_HADOOP = '/flink/setting/checkHadoop',
-  SYNC = '/flink/setting/sync',
-  UPDATE = '/flink/setting/update',
+  GET = '/setting/get',
+  GET_DOCKER = '/setting/docker',
+  GET_EMAIL = '/setting/email',
+  ALL = '/setting/all',
+  UPDATE = '/setting/update',
+  CHECK_HADOOP = '/setting/check/hadoop',
+  CHECK_DOCKER = '/setting/check/docker',
+  UPDATE_DOCKER = '/setting/update/docker',
+  CHECK_EMAIL = '/setting/check/email',
+  UPDATE_ALERT = '/setting/update/email',
 }
 /**
  * Get system settings
@@ -58,3 +63,59 @@ export function fetchCheckHadoop(): Promise<boolean> {
     url: SETTING_APi.CHECK_HADOOP,
   });
 }
+
+/**
+ * get docker setting info
+ */
+export function fetchDockerConfig() {
+  return defHttp.post({ url: SETTING_APi.GET_DOCKER });
+}
+
+/**
+ * verify docker setting info
+ */
+export function fetchVerifyDocker(data: Recordable): Promise<boolean> {
+  return defHttp.post({
+    url: SETTING_APi.CHECK_DOCKER,
+    data,
+  });
+}
+
+/**
+ * verify docker setting info
+ */
+export function fetchVerifyEmail(data: Recordable): Promise<boolean> {
+  return defHttp.post({
+    url: SETTING_APi.CHECK_EMAIL,
+    data,
+  });
+}
+
+/**
+ * get alert setting info
+ */
+export function fetchEmailConfig() {
+  return defHttp.post({ url: SETTING_APi.GET_EMAIL });
+}
+
+/**
+ * Update docker setting
+ * @returns {Promise<Boolean>}
+ */
+export function fetchDockerUpdate(data: Recordable): Promise<boolean> {
+  return defHttp.post({
+    url: SETTING_APi.UPDATE_DOCKER,
+    data,
+  });
+}
+
+/**
+ * Update alert setting
+ * @returns {Promise<Boolean>}
+ */
+export function fetchEmailUpdate(data: Recordable): Promise<boolean> {
+  return defHttp.post({
+    url: SETTING_APi.UPDATE_ALERT,
+    data,
+  });
+}
diff --git 
a/streampark-console/streampark-console-webapp/src/assets/icons/edit.svg 
b/streampark-console/streampark-console-webapp/src/assets/icons/edit.svg
index 2a71bf093..d5d43830b 100644
--- a/streampark-console/streampark-console-webapp/src/assets/icons/edit.svg
+++ b/streampark-console/streampark-console-webapp/src/assets/icons/edit.svg
@@ -1 +1 @@
-<svg t="1620578438157" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="2776" width="200" height="200"><path 
d="M595.344218 147.059903L124.550908 617.923026a130.060986 130.060986 0 0 
0-33.486806 57.19984l-74.629734 265.288109-15.638036 55.850128a21.967717 
21.967717 0 0 0 5.585013 21.339403 22.223697 22.223697 0 0 0 21.479028 
5.585013l55.850129-15.638036c164.315731-46.309065 234.058579-65.85661 
265.451005-74.629734a128.804358 128.804358 0 0 0 56. [...]
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 
1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";><svg 
t="1709024206856" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="4211" 
xmlns:xlink="http://www.w3.org/1999/xlink"; width="200" height="200"><path 
d="M975.292814 48.762787c-65.054118-65.01705-170.864362-65.01705-235.918479 
0L71.003511 717.133611c-0.315077 0.315077-0.500416 0.70429-0.778425 
1.0379-0.389213 0. [...]
diff --git 
a/streampark-console/streampark-console-webapp/src/assets/icons/maven.svg 
b/streampark-console/streampark-console-webapp/src/assets/icons/maven.svg
index 4698a8643..f4ba2429f 100644
--- a/streampark-console/streampark-console-webapp/src/assets/icons/maven.svg
+++ b/streampark-console/streampark-console-webapp/src/assets/icons/maven.svg
@@ -1 +1,2 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 
1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";><svg 
t="1615703567163" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="5337" 
xmlns:xlink="http://www.w3.org/1999/xlink"; width="200" 
height="200"><defs><style type="text/css"></style></defs><path d="M105.344 
681.92l134.144-288.128 84.48-189.824c49.024 0.512 87.68 0.768 115.84 0.768 
23.232 0 58.88-0.256 107.136-0. [...]
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 
1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";><svg 
t="1615703567163" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="5337" 
xmlns:xlink="http://www.w3.org/1999/xlink"; width="200" 
height="200"><defs><style type="text/css"></style></defs><path d="M105.344 
681.92l134.144-288.128 84.48-189.824c49.024 0.512 87.68 0.768 115.84 0.768 
23.232 0 58.88-0.256 107.136-0. [...]
+
diff --git 
a/streampark-console/streampark-console-webapp/src/assets/icons/nginx.svg 
b/streampark-console/streampark-console-webapp/src/assets/icons/nginx.svg
new file mode 100644
index 000000000..edea410d8
--- /dev/null
+++ b/streampark-console/streampark-console-webapp/src/assets/icons/nginx.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 
1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";><svg 
t="1709024889720" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="2632" 
xmlns:xlink="http://www.w3.org/1999/xlink"; width="200" height="200"><path 
d="M77.483 512.683V275.797c-0.342-8.533 4.096-16.384 11.946-20.138L498.347 
19.456c7.509-4.437 15.701-5.461 22.869-1.024L931.84 255.659c7.168 4.096 11.264 
11.946 1 [...]
diff --git 
a/streampark-console/streampark-console-webapp/src/assets/icons/save.svg 
b/streampark-console/streampark-console-webapp/src/assets/icons/save.svg
new file mode 100644
index 000000000..e68d48a10
--- /dev/null
+++ b/streampark-console/streampark-console-webapp/src/assets/icons/save.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 
1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";><svg 
t="1709024733288" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="1610" 
xmlns:xlink="http://www.w3.org/1999/xlink"; width="200" height="200"><path 
d="M426.666667 128h-149.333334v234.453333c0 12.074667 9.450667 21.546667 
21.205334 21.546667h298.922666c11.626667 0 21.205333-9.6 
21.205334-21.546667V128h-64v149. [...]
diff --git 
a/streampark-console/streampark-console-webapp/src/assets/icons/settings.svg 
b/streampark-console/streampark-console-webapp/src/assets/icons/settings.svg
index e9c5f4b7e..aad0880e4 100644
--- a/streampark-console/streampark-console-webapp/src/assets/icons/settings.svg
+++ b/streampark-console/streampark-console-webapp/src/assets/icons/settings.svg
@@ -4,6 +4,5 @@
      xmlns="http://www.w3.org/2000/svg"; p-id="2563" 
xmlns:xlink="http://www.w3.org/1999/xlink";
      width="200" height="200">
   <path
-    d="M361.088 149.482667c0-59.093333 48.384-106.816 
107.626667-106.816h86.570666c59.349333 0 107.626667 47.637333 107.626667 
106.816v29.162666a368.213333 368.213333 0 0 1 67.050667 
37.098667l25.429333-14.464c51.328-29.226667 117.013333-11.882667 146.752 
38.869333l43.306667 73.898667c29.994667 51.2 12.16 116.693333-39.616 
146.133333l-22.528 12.821334a361.6 361.6 0 0 1 0 77.994666l22.528 
12.8c51.754667 29.44 69.610667 94.933333 39.594666 146.154667l-43.306666 
73.898667c-29.717333 50.7733 [...]
-    p-id="2564"></path>
+    d="M361.088 149.482667c0-59.093333 48.384-106.816 
107.626667-106.816h86.570666c59.349333 0 107.626667 47.637333 107.626667 
106.816v29.162666a368.213333 368.213333 0 0 1 67.050667 
37.098667l25.429333-14.464c51.328-29.226667 117.013333-11.882667 146.752 
38.869333l43.306667 73.898667c29.994667 51.2 12.16 116.693333-39.616 
146.133333l-22.528 12.821334a361.6 361.6 0 0 1 0 77.994666l22.528 
12.8c51.754667 29.44 69.610667 94.933333 39.594666 146.154667l-43.306666 
73.898667c-29.717333 50.7733 [...]
 </svg>
diff --git 
a/streampark-console/streampark-console-webapp/src/assets/icons/settings2.svg 
b/streampark-console/streampark-console-webapp/src/assets/icons/settings2.svg
new file mode 100644
index 000000000..762a6ac3b
--- /dev/null
+++ 
b/streampark-console/streampark-console-webapp/src/assets/icons/settings2.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 
1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";><svg 
t="1709025744880" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="5453" 
xmlns:xlink="http://www.w3.org/1999/xlink"; width="200" height="200"><path 
d="M972.497609 
405.74732l-98.044936-19.647386c-4.927846-14.175557-10.751664-27.935127-17.183463-41.246711l55.550264-83.325396c16.927471-25.375207
 13.567576-59.198 [...]
diff --git 
a/streampark-console/streampark-console-webapp/src/design/public.less 
b/streampark-console/streampark-console-webapp/src/design/public.less
index 05cecb1ae..547c88b53 100644
--- a/streampark-console/streampark-console-webapp/src/design/public.less
+++ b/streampark-console/streampark-console-webapp/src/design/public.less
@@ -3,33 +3,40 @@
   height: 100%;
 }
 
-// =================================
-// ==============scrollbar==========
-// =================================
-
 ::-webkit-scrollbar {
-  width: 7px;
-  height: 8px;
+  width: 8px;
 }
 
-// ::-webkit-scrollbar-track {
-//   background: transparent;
-// }
+::-webkit-scrollbar-corner {
+  background: @component-background;
+}
 
 ::-webkit-scrollbar-track {
-  background-color: rgb(0 0 0 / 5%);
+  border-radius: 0;
+  background-color: fade(@border-color-split, 20%);
 }
 
 ::-webkit-scrollbar-thumb {
-  // background: rgba(0, 0, 0, 0.6);
-  background-color: rgb(144 147 153 / 30%);
-  // background-color: rgba(144, 147, 153, 0.3);
-  border-radius: 2px;
-  box-shadow: inset 0 0 6px rgb(0 0 0 / 20%);
+  width: 8px;
+  height: 8px;
+  border-radius: 4px;
+  background: @border-color-split;
+}
+
+::-webkit-scrollbar:horizontal {
+  height: 8px;
+}
+
+::-webkit-scrollbar-thumb:window-inactive {
+  background: transparent;
+}
+
+::-webkit-scrollbar-track:window-inactive {
+  background: transparent;
 }
 
 ::-webkit-scrollbar-thumb:hover {
-  background-color: @border-color-dark;
+  background: darken(@border-color-split, 10%);
 }
 
 // =================================
@@ -50,7 +57,7 @@
   }
 }
 
-.tip-info {
-  margin-top: 10px;
+.extra .conf-switch {
   color: darkgrey;
+  margin-left: 8px;
 }
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/app.ts 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/app.ts
index e9b778af3..ff03d2723 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/app.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/app.ts
@@ -78,6 +78,38 @@ export default {
     releaseFail: 'release application failed,',
     releasing: 'Current Application is releasing',
   },
+  runState: {
+    added: 'ADDED',
+    initializing: 'INITIALIZING',
+    created: 'CREATED',
+    starting: 'STARTING',
+    restarting: 'RESTARTING',
+    running: 'RUNNING',
+    failing: 'FAILING',
+    failed: 'FAILED',
+    lost: 'LOST',
+    cancelling: 'CANCELLING',
+    canceled: 'CANCELED',
+    finished: 'FINISHED',
+    suspended: 'SUSPENDED',
+    reconciling: 'RECONCILING',
+    mapping: 'MAPPING',
+    silent: 'SILENT',
+    terminated: 'TERMINATED',
+  },
+  releaseState: {
+    failed: 'FAILED',
+    success: 'SUCCESS',
+    waiting: 'WAITING',
+    releasing: 'RELEASING',
+    pending: 'PENDING',
+  },
+  clusterState: {
+    created: 'CREATED',
+    started: 'STARTED',
+    canceled: 'SHUTDOWN',
+    lost: 'LOST',
+  },
   detail: {
     detailTitle: 'Application Info',
     flinkWebUi: 'Flink Web UI',
@@ -126,19 +158,27 @@ export default {
     },
   },
   view: {
-    buildTitle: 'Application releasing Progress',
+    buildTitle: 'Job releasing Progress',
     stepTitle: 'Steps Detail',
     errorLog: 'Error Log',
     errorSummary: 'Error Summary',
     errorStack: 'Error Stack',
-    logTitle: 'Start Log : Application Name [ {0} ]',
+    logTitle: 'Start Log : Job Name [ {0} ]',
     refreshTime: 'last refresh time',
     refresh: 'refresh',
-    start: 'Start Application',
-    stop: 'Stop application',
+    start: 'Start Job',
+    stop: 'Stop Job',
     savepoint: 'Trigger Savepoint',
-    recheck: 'the associated project has changed and this job need to be 
rechecked',
-    changed: 'the application has changed.',
+    fromSavepoint: 'From savepoint',
+    savepointTip: 'Restore the job from savepoint or latest checkpoint',
+    savepointInput: 'Specify the savepoint/checkpoint path',
+    savepointSwitch:
+      'Specify the savepoint/checkpoint path (Double-click to switch between 
"input" and "select")',
+    ignoreRestored: 'Ignore failed',
+    ignoreRestoredTip:
+      'ignore savepoint then cannot be restored, Same 
as:-allowNonRestoredState(-n)',
+    recheck: 'the associated project has changed and this job need to be 
release',
+    changed: 'the job has been updated',
   },
   pod: {
     choice: 'Choice',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/system.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/system.ts
index ca20ab348..065cdae1f 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/system.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/system.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 export default {
-  systemSetting: 'Environment Setting',
+  systemSetting: 'System Setting',
   systemSettingItems: {
     mavenSetting: {
       name: 'Maven Setting',
@@ -34,6 +34,69 @@ export default {
     },
   },
   update: {
-    success: 'setting updated successfully',
+    success: 'Setting updated successfully',
+    dockerNotStart: 'Please make sure your Docker service has been started. 🙂',
+  },
+  title: {
+    mavenSettings: 'Maven Settings File Path',
+    mavenRepository: 'Maven Central Repository',
+    mavenUser: 'Maven Central Repository Auth User',
+    mavenPassword: 'Maven Central Repository Auth Password',
+    docker: 'Docker Setting',
+    email: 'Alert Mailbox Setting',
+    ingress: 'Ingress domain address Setting',
+  },
+  desc: {
+    mavenSettings: 'Specify the full path to the local maven settings.xml',
+    mavenRepository: 'Maven central repository, Specify a repository address 
for maven.',
+    mavenUser: 'Maven central repository authentication username',
+    mavenPassword: 'Maven central repository authentication password',
+    docker: 'Docker container service settings',
+    email: 'Alert Mailbox settings used to sent alert email',
+    ingress: 'Automatically generate an nginx-based ingress by passing in a 
domain name',
+  },
+  docker: {
+    address: {
+      label: 'Docker Address',
+      desc: 'Docker container service address',
+    },
+    namespace: {
+      label: 'Docker namespace',
+      desc: 'Namespace for docker image used in docker building env and target 
image register',
+    },
+    userName: {
+      label: 'Docker Username',
+      desc: 'Docker container service authentication username',
+    },
+    password: {
+      label: 'Docker Password',
+      desc: 'Docker container service authentication password',
+    },
+  },
+  email: {
+    host: {
+      label: 'Smtp Host',
+      desc: 'Alert Mailbox Smtp Host',
+    },
+    port: {
+      label: 'Smtp Port',
+      desc: 'Smtp Port of the mailbox',
+    },
+    from: {
+      label: 'Email Address',
+      desc: 'Your email address used to send alert email',
+    },
+    userName: {
+      label: 'Email Username',
+      desc: 'Authentication username used to send email,generally: your email 
address',
+    },
+    password: {
+      label: 'Email Password',
+      desc: 'Authentication password used to send alert email',
+    },
+    ssl: {
+      label: 'SSL enable',
+      desc: 'Whether to enable SSL in the mailbox that sends the alert',
+    },
   },
 };
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/sys.ts 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/sys.ts
index 0db8bca22..40e48bede 100644
--- a/streampark-console/streampark-console-webapp/src/locales/lang/en/sys.ts
+++ b/streampark-console/streampark-console-webapp/src/locales/lang/en/sys.ts
@@ -106,8 +106,8 @@ export default {
     otherSignIn: 'Sign in with',
     ssoSignIn: 'Sign in with SSO',
 
-    ldapTip: 'Sign in with LDAP',
-    passwordTip: 'Sign in with password',
+    ldapTip: 'Sign in by LDAP',
+    passwordTip: 'Sign in by password',
 
     // notify
     loginSuccessTitle: 'Login successful',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/app.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/app.ts
index 17652ec3e..f571b49c1 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/app.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/app.ts
@@ -76,6 +76,31 @@ export default {
     releaseFail: '发布作业失败',
     releasing: '当前作业正在发布中',
   },
+  runState: {
+    added: '新增作业',
+    initializing: '初始化',
+    created: '创建中',
+    starting: '启动中',
+    restarting: '重启中',
+    running: '运行中',
+    failing: '失败中',
+    failed: '作业失败',
+    lost: '作业失联',
+    cancelling: '取消中',
+    canceled: '已取消',
+    finished: '已完成',
+    suspended: '已暂停',
+    reconciling: '调整中',
+    mapping: '映射中',
+    silent: '沉默',
+    terminated: '终止',
+  },
+  clusterState: {
+    created: '新增',
+    started: '运行中',
+    canceled: '停止',
+    lost: '失联',
+  },
   detail: {
     detailTitle: '作业详情',
     flinkWebUi: 'Flink Web UI',
@@ -123,7 +148,7 @@ export default {
     },
   },
   view: {
-    buildTitle: '应用程序启动进度',
+    buildTitle: '作业启动进度',
     stepTitle: '步骤详情',
     errorLog: '错误日志',
     errorSummary: '错误摘要',
@@ -131,11 +156,17 @@ export default {
     logTitle: '启动日志 : 应用名称 [ {0} ]',
     refreshTime: '上次刷新时间',
     refresh: '刷新',
-    start: '开启应用',
+    start: '启动作业',
     stop: '停止应用',
     savepoint: '触发 Savepoint',
-    recheck: '关联的项目已更改,需要重新检查此作业',
-    changed: '应用程序已更改。',
+    recheck: '关联的项目已更新,需要重新发布此作业',
+    changed: '作业已更新',
+    fromSavepoint: 'Savepoint 恢复',
+    savepointTip: '作业从 savepoint 或 checkpoint 恢复状态',
+    savepointInput: '指定 savepoint/checkpoint 路径',
+    savepointSwitch: '指定 savepoint/checkpoint 路径 (双击切换"下拉框选择"或"手动输入")',
+    ignoreRestored: '忽略失败',
+    ignoreRestoredTip: '当状态恢复失败时跳过错误,作业继续运行, 同参数:-allowNonRestoredState(-n)',
   },
   pod: {
     choice: '选择',
@@ -200,6 +231,13 @@ export default {
     silent: '静默',
     terminated: '终止',
   },
+  releaseState: {
+    failed: '发布失败',
+    success: '发布成功',
+    waiting: '待发布',
+    releasing: '发布中',
+    pending: '待回滚',
+  },
   addAppTips: {
     developmentModePlaceholder: '请选择开发模式',
     developmentModeIsRequiredMessage: '开发模式必填',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/menu.ts 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/menu.ts
index 9ccc42746..45bd9960c 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/menu.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/menu.ts
@@ -19,7 +19,7 @@ export default {
     menu: '系统管理',
     user: '用户管理',
     role: '角色管理',
-    token: 'Token 管理',
+    token: '秘钥管理',
     team: '团队管理',
     member: '成员管理',
   },
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/system.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/system.ts
index 03c138c8e..b1e5232aa 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/system.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/system.ts
@@ -18,10 +18,10 @@ export default {
   systemSetting: '系统设置',
   systemSettingItems: {
     mavenSetting: {
-      name: 'Maven配置',
+      name: 'Maven 配置',
     },
     dockerSetting: {
-      name: 'Docker环境配置',
+      name: 'Docker 环境配置',
     },
     emailSetting: {
       name: '邮箱配置',
@@ -35,5 +35,68 @@ export default {
   },
   update: {
     success: '设置更新成功!',
+    dockerNotStart: '依赖你本地的Docker服务,请确保本地Docker已启动. 🙂',
+  },
+  title: {
+    mavenSettings: 'Maven Settings 文件路径',
+    mavenRepository: 'Maven 仓库',
+    mavenUser: 'Maven 仓库访问用户',
+    mavenPassword: 'Maven 仓库访问密码',
+    docker: 'Docker 容器服务设置',
+    email: '告警邮箱设置',
+    ingress: 'Ingress 域名设置',
+  },
+  desc: {
+    mavenSettings: '指定本地 maven 的 settings.xml 文件完成路径',
+    mavenRepository: '设置 maven 的仓库地址,可以是 maven 私服的地址或者阿里云等 maven 私服地址',
+    mavenUser: '访问 maven 私服所需的认证用户名(如需要)',
+    mavenPassword: '访问 maven 私服所需的认证密码(如需要)',
+    docker: '设置 docker 容器服务的注册信息',
+    email: '设置用于发送告警的 email 发送者信息',
+    ingress: 'ingress 设置,通过传入域名自动生成基于 nginx 的 ingress',
+  },
+  docker: {
+    address: {
+      label: 'Docker 地址',
+      desc: 'Docker 容器的服务地址',
+    },
+    namespace: {
+      label: 'Docker 命名空间',
+      desc: 'Docker 构建环境和目标镜像注册使用的命名空间',
+    },
+    userName: {
+      label: 'Docker 用户名',
+      desc: 'Docker 容器服务认证用户名',
+    },
+    password: {
+      label: 'Docker 密码',
+      desc: 'Docker 容器服务认证密码',
+    },
+  },
+  email: {
+    host: {
+      label: '发送邮件服务器',
+      desc: '发送告警邮件的服务器 Smtp 主机',
+    },
+    port: {
+      label: '发送邮件Smtp端口',
+      desc: '告警邮箱 Smtp端口',
+    },
+    userName: {
+      label: '邮箱用户名',
+      desc: '发送告警邮件的用户名,一般是你的邮箱地址',
+    },
+    password: {
+      label: '邮箱密码',
+      desc: '用于发送告警邮件的认证密码',
+    },
+    from: {
+      label: '邮箱地址',
+      desc: '用于发送告警的邮箱',
+    },
+    ssl: {
+      label: '开启 SSL',
+      desc: '是否在发送告警邮箱中启用 SSL',
+    },
   },
 };
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue 
b/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue
index 9b4a4e8e8..66ebdfd16 100644
--- a/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue
+++ b/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue
@@ -25,7 +25,7 @@
   import { useI18n } from '/@/hooks/web/useI18n';
   import { AppStateEnum, JobTypeEnum, OptionStateEnum, ReleaseStateEnum } from 
'/@/enums/flinkEnum';
   import { useTimeoutFn } from '@vueuse/core';
-  import { Tooltip, Badge, Divider, Tag, Popover } from 'ant-design-vue';
+  import { Tooltip, Badge, Tag, Popover } from 'ant-design-vue';
   import { fetchAppRecord } from '/@/api/flink/app';
   import { useTable } from '/@/components/Table';
   import { PageWrapper } from '/@/components/Page';
@@ -207,7 +207,7 @@
       app['optionState'] === OptionStateEnum.SAVEPOINTING
     ) {
       // yarn-per-job|yarn-session|yarn-application
-      handleView(app, unref(yarn));
+      await handleView(app, unref(yarn));
     }
   }
 
@@ -336,13 +336,6 @@
             :title="releaseTitleMap[record.release] || ''"
             :data="record"
           />
-          <Divider type="vertical" style="margin: 0 4px" 
v-if="record.buildStatus" />
-          <State
-            option="build"
-            :maxTitle="titleLenRef.maxBuild"
-            :click="openBuildProgressDetailDrawer.bind(null, record)"
-            :data="record"
-          />
         </template>
         <template v-if="column.dataIndex === 'operation'">
           <TableAction v-bind="getTableActions(record, currentTablePage)" />
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/components/AppView/StartApplicationModal.vue
 
b/streampark-console/streampark-console-webapp/src/views/flink/app/components/AppView/StartApplicationModal.vue
index 98a0f0188..a78699829 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/app/components/AppView/StartApplicationModal.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/app/components/AppView/StartApplicationModal.vue
@@ -24,7 +24,7 @@
   });
 </script>
 <script setup lang="ts" name="StartApplicationModal">
-  import { h } from 'vue';
+  import { h, ref } from 'vue';
   import { Select, Input, Tag } from 'ant-design-vue';
   import { BasicForm, useForm } from '/@/components/Form';
   import { SvgIcon, Icon } from '/@/components/Icon';
@@ -32,16 +32,15 @@
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useRouter } from 'vue-router';
   import { fetchCheckStart, fetchForcedStop, fetchStart } from 
'/@/api/flink/app';
-
-  import { AppExistsEnum, RestoreModeEnum } from '/@/enums/flinkEnum';
-  import { fetchFlinkEnv } from '/@/api/flink/flinkEnv';
-  import { renderFlinkAppRestoreMode } from 
'/@/views/flink/app/hooks/useFlinkRender';
+  import { AppExistsEnum } from '/@/enums/flinkEnum';
 
   const SelectOption = Select.Option;
 
   const { t } = useI18n();
   const { Swal } = useMessage();
   const router = useRouter();
+  const selectInput = ref<boolean>(false);
+  const selectValue = ref<string>(null);
 
   const emits = defineEmits(['register', 'updateOption']);
   const receiveData = reactive<Recordable>({});
@@ -56,61 +55,50 @@
     }
   });
 
+  function handleSavePointTip(list) {
+    if (list != null && list.length > 0) {
+      return t('flink.app.view.savepointSwitch');
+    }
+    return t('flink.app.view.savepointInput');
+  }
+
   const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
     name: 'startApplicationModal',
     labelWidth: 120,
     schemas: [
       {
         field: 'startSavePointed',
-        label: 'from savepoint',
+        label: t('flink.app.view.fromSavepoint'),
         component: 'Switch',
         componentProps: {
           checkedChildren: 'ON',
           unCheckedChildren: 'OFF',
         },
         defaultValue: true,
-        afterItem: () =>
-          h(
-            'span',
-            { class: 'tip-info' },
-            'restore the application from savepoint or latest checkpoint',
-          ),
+        afterItem: () => h('span', { class: 'conf-switch' }, 
t('flink.app.view.savepointTip')),
       },
       {
         field: 'startSavePoint',
-        label: 'savepoint',
+        label: 'Savepoint',
         component:
           receiveData.historySavePoint && receiveData.historySavePoint.length 
> 0
             ? 'Select'
             : 'Input',
         afterItem: () =>
-          h(
-            'span',
-            { class: 'tip-info' },
-            'restore the application from savepoint or latest checkpoint',
-          ),
+          h('span', { class: 'conf-switch' }, 
handleSavePointTip(receiveData.historySavePoint)),
         slot: 'savepoint',
         ifShow: ({ values }) => values.startSavePointed,
         required: true,
       },
-      {
-        field: 'restoreMode',
-        label: 'restore mode',
-        component: 'Select',
-        defaultValue: RestoreModeEnum.NO_CLAIM,
-        render: (renderCallbackParams) => 
renderFlinkAppRestoreMode(renderCallbackParams),
-        ifShow: ({ values }) => values.startSavePointed && checkFlinkVersion(),
-      },
       {
         field: 'allowNonRestoredState',
-        label: 'ignore restored',
+        label: t('flink.app.view.ignoreRestored'),
         component: 'Switch',
         componentProps: {
           checkedChildren: 'ON',
           unCheckedChildren: 'OFF',
         },
-        afterItem: () =>
-          h('span', { class: 'tip-info' }, 'ignore savepoint then cannot be 
restored'),
+        afterItem: () => h('span', { class: 'conf-switch' }, 
t('flink.app.view.ignoreRestoredTip')),
         defaultValue: false,
         ifShow: ({ values }) => values.startSavePointed,
       },
@@ -135,16 +123,20 @@
     await handleDoSubmit();
   }
 
+  async function handleReset() {
+    selectInput.value = false;
+    selectValue.value = null;
+  }
+
   /* submit */
   async function handleDoSubmit() {
     try {
       const formValue = (await validate()) as Recordable;
       const savePointed = formValue.startSavePointed;
       const savePointPath = savePointed ? formValue['startSavePoint'] : null;
-      const restoreMode = savePointed ? formValue['restoreMode'] : null;
+      handleReset();
       const { data } = await fetchStart({
         id: receiveData.application.id,
-        restoreMode,
         savePointed,
         savePoint: savePointPath,
         allowNonRestored: formValue.allowNonRestoredState || false,
@@ -152,7 +144,7 @@
       if (data.data) {
         Swal.fire({
           icon: 'success',
-          title: 'The current job is starting',
+          title: t('flink.app.operation.starting'),
           showConfirmButton: false,
           timer: 2000,
         });
@@ -190,10 +182,14 @@
     }
   }
 
-  async function checkFlinkVersion() {
-    const versionId = receiveData.application.versionId;
-    const flinkVersion = await fetchFlinkEnv(versionId);
-    return parseInt(flinkVersion.versionOfMiddle) >= 15;
+  function handleSavepoint(model, field, input) {
+    selectInput.value = input;
+    if (input) {
+      selectValue.value = model[field];
+      model[field] = null;
+    } else {
+      model[field] = selectValue.value;
+    }
   }
 </script>
 <template>
@@ -201,6 +197,7 @@
     @register="registerModal"
     :minHeight="100"
     @ok="handleSubmit"
+    @cancel="handleReset"
     :okText="t('common.apply')"
     :cancelText="t('common.cancelText')"
   >
@@ -211,8 +208,12 @@
 
     <BasicForm @register="registerForm" class="!pt-40px">
       <template #savepoint="{ model, field }">
-        <template v-if="receiveData.historySavePoint && 
receiveData.historySavePoint.length > 0">
-          <Select allow-clear v-model:value="model[field]">
+        <template
+          v-if="
+            !selectInput && receiveData.historySavePoint && 
receiveData.historySavePoint.length > 0
+          "
+        >
+          <Select v-model:value="model[field]" 
@dblclick="handleSavepoint(model, field, true)">
             <SelectOption v-for="(k, i) in receiveData.historySavePoint" 
:key="i" :value="k.path">
               <span style="color: darkgrey">
                 <Icon icon="ant-design:clock-circle-outlined" />
@@ -232,8 +233,9 @@
         </template>
         <Input
           v-else
+          @dblclick="handleSavepoint(model, field, false)"
           type="text"
-          placeholder="Please enter savepoint manually"
+          :placeholder="$t('flink.app.view.savepointInput')"
           v-model:value="model[field]"
         />
       </template>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/components/State.less
 
b/streampark-console/streampark-console-webapp/src/views/flink/app/components/State.less
index 74c6995d3..895a612a0 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/app/components/State.less
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/app/components/State.less
@@ -47,10 +47,6 @@
   animation: reconciling-color 800ms ease-out infinite alternate;
 }
 
-.status-processing-probing {
-  animation: probing-color 800ms ease-out infinite alternate;
-}
-
 @keyframes deploying-color {
   0% {
     border-color: #1abbdc;
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/components/State.tsx
 
b/streampark-console/streampark-console-webapp/src/views/flink/app/components/State.tsx
index d9b1e26fd..564b32d01 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/app/components/State.tsx
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/app/components/State.tsx
@@ -19,63 +19,60 @@ import { computed, defineComponent, toRefs, unref } from 
'vue';
 import { Tag, Tooltip } from 'ant-design-vue';
 import './State.less';
 import { AppStateEnum, ReleaseStateEnum, OptionStateEnum } from 
'/@/enums/flinkEnum';
+import { useI18n } from '/@/hooks/web/useI18n';
+const { t } = useI18n();
 
 /*  state map*/
 export const stateMap = {
-  [AppStateEnum.ADDED]: { color: '#2f54eb', title: 'ADDED' },
+  [AppStateEnum.ADDED]: { color: '#2f54eb', title: 
t('flink.app.runState.added') },
   [AppStateEnum.INITIALIZING]: {
     color: '#738df8',
-    title: 'INITIALIZING',
+    title: t('flink.app.runState.initializing'),
     class: 'status-processing-initializing',
   },
-  [AppStateEnum.CREATED]: { color: '#2f54eb', title: 'CREATED' },
+  [AppStateEnum.CREATED]: { color: '#2f54eb', title: 
t('flink.app.runState.created') },
   [AppStateEnum.STARTING]: {
     color: '#1AB58E',
-    title: 'STARTING',
+    title: t('flink.app.runState.starting'),
     class: 'status-processing-starting',
   },
   [AppStateEnum.RESTARTING]: {
     color: '#13c2c2',
-    title: 'RESTARTING',
+    title: t('flink.app.runState.restarting'),
     class: 'status-processing-restarting',
   },
   [AppStateEnum.RUNNING]: {
     color: '#52c41a',
-    title: 'RUNNING',
+    title: t('flink.app.runState.running'),
     class: 'status-processing-running',
   },
   [AppStateEnum.FAILING]: {
     color: '#fa541c',
-    title: 'FAILING',
+    title: t('flink.app.runState.failing'),
     class: 'status-processing-failing',
   },
-  [AppStateEnum.FAILED]: { color: '#f5222d', title: 'FAILED' },
-  [AppStateEnum.CANCELLING]: { color: '#faad14', title: 'CANCELLING' },
-  [AppStateEnum.CANCELED]: { color: '#fa8c16', title: 'CANCELED' },
-  [AppStateEnum.FINISHED]: { color: '#1677ff', title: 'FINISHED' },
-  [AppStateEnum.SUSPENDED]: { color: '#722ed1', title: 'SUSPENDED' },
+  [AppStateEnum.FAILED]: { color: '#f5222d', title: 
t('flink.app.runState.failed') },
+  [AppStateEnum.CANCELLING]: { color: '#faad14', title: 
t('flink.app.runState.cancelling') },
+  [AppStateEnum.CANCELED]: { color: '#fa8c16', title: 
t('flink.app.runState.canceled') },
+  [AppStateEnum.FINISHED]: { color: '#1890ff', title: 
t('flink.app.runState.finished') },
+  [AppStateEnum.SUSPENDED]: { color: '#722ed1', title: 
t('flink.app.runState.suspended') },
   [AppStateEnum.RECONCILING]: {
     color: '#eb2f96',
-    title: 'RECONCILING',
+    title: t('flink.app.runState.reconciling'),
     class: 'status-processing-reconciling',
   },
-  [AppStateEnum.LOST]: { color: '#000000', title: 'LOST' },
+  [AppStateEnum.LOST]: { color: '#333333', title: t('flink.app.runState.lost') 
},
   [AppStateEnum.MAPPING]: {
     color: '#13c2c2',
-    title: 'MAPPING',
+    title: t('flink.app.runState.mapping'),
     class: 'status-processing-restarting',
   },
   [AppStateEnum.SILENT]: {
     color: '#738df8',
-    title: 'SILENT',
+    title: t('flink.app.runState.silent'),
     class: 'status-processing-initializing',
   },
-  [AppStateEnum.TERMINATED]: { color: '#8E50FF', title: 'TERMINATED' },
-  [AppStateEnum.PROBING]: {
-    color: '#2febc9',
-    title: 'PROBING',
-    class: 'status-processing-probing',
-  },
+  [AppStateEnum.TERMINATED]: { color: '#8E50FF', title: 
t('flink.app.runState.terminated') },
 };
 /*  option state map*/
 export const optionStateMap = {
@@ -103,16 +100,19 @@ export const optionStateMap = {
 
 /* release state map*/
 export const releaseStateMap = {
-  [ReleaseStateEnum.FAILED]: { color: '#f5222d', title: 'FAILED' },
-  [ReleaseStateEnum.DONE]: { color: '#52c41a', title: 'DONE' },
-  [ReleaseStateEnum.NEED_RELEASE]: { color: '#fa8c16', title: 'WAITING' },
+  [ReleaseStateEnum.FAILED]: { color: '#f5222d', title: 
t('flink.app.releaseState.failed') },
+  [ReleaseStateEnum.DONE]: { color: '#52c41a', title: 
t('flink.app.releaseState.success') },
+  [ReleaseStateEnum.NEED_RELEASE]: { color: '#fa8c16', title: 
t('flink.app.releaseState.waiting') },
   [ReleaseStateEnum.RELEASING]: {
     color: '#52c41a',
-    title: 'RELEASING',
+    title: t('flink.app.releaseState.releasing'),
     class: 'status-processing-deploying',
   },
-  [ReleaseStateEnum.NEED_RESTART]: { color: '#fa8c16', title: 'PENDING' },
-  [ReleaseStateEnum.NEED_ROLLBACK]: { color: '#fa8c16', title: 'WAITING' },
+  [ReleaseStateEnum.NEED_RESTART]: { color: '#fa8c16', title: 
t('flink.app.releaseState.pending') },
+  [ReleaseStateEnum.NEED_ROLLBACK]: {
+    color: '#fa8c16',
+    title: t('flink.app.releaseState.waiting'),
+  },
 };
 
 /* build state map*/
@@ -127,6 +127,7 @@ export const buildStatusMap = {
   3: { color: '#2ECC71', title: 'SUCCESS' },
   4: { color: '#E74C3C', title: 'FAILURE' },
 };
+
 const overviewMap = {
   running: { color: '#52c41a', title: 'RUNNING' },
   canceled: { color: '#fa8c16', title: 'CANCELED' },
@@ -152,14 +153,15 @@ export default defineComponent({
   },
   setup(props) {
     const { data, option } = toRefs(props);
+
     const tagWidth = computed(() => {
       if (props.maxTitle === undefined) return 0;
       // create a dom to calculate the width of the tag
       const dom = document.createElement('span');
       dom.style.display = 'inline-block';
       dom.style.fontSize = '10px';
-      dom.style.padding = '0 3px';
-      dom.style.borderRadius = '1px';
+      dom.style.padding = '0 2px';
+      dom.style.borderRadius = '2px';
       dom.textContent = props.maxTitle;
       document.body.appendChild(dom);
       const width = dom.clientWidth + 2;
@@ -177,6 +179,7 @@ export default defineComponent({
         </Tag>
       );
     };
+
     const getStyle = computed(() => {
       if (tagWidth.value > 0) {
         return { width: `${tagWidth.value}px`, textAlign: 'center' };
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/View.vue 
b/streampark-console/streampark-console-webapp/src/views/setting/alarm/View.vue
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/Alarm/View.vue
rename to 
streampark-console/streampark-console-webapp/src/views/setting/alarm/View.vue
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/AlertModal.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/alarm/components/AlertModal.vue
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/AlertModal.vue
rename to 
streampark-console/streampark-console-webapp/src/views/setting/alarm/components/AlertModal.vue
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/AlertTypeInfo.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/alarm/components/AlertTypeInfo.vue
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/AlertTypeInfo.vue
rename to 
streampark-console/streampark-console-webapp/src/views/setting/alarm/components/AlertTypeInfo.vue
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/DetailModal.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/alarm/components/DetailModal.vue
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/DetailModal.vue
rename to 
streampark-console/streampark-console-webapp/src/views/setting/alarm/components/DetailModal.vue
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/index.data.ts
 
b/streampark-console/streampark-console-webapp/src/views/setting/alarm/components/index.data.ts
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/index.data.ts
rename to 
streampark-console/streampark-console-webapp/src/views/setting/alarm/components/index.data.ts
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/index.ts
 
b/streampark-console/streampark-console-webapp/src/views/setting/alarm/components/index.ts
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/Alarm/components/index.ts
rename to 
streampark-console/streampark-console-webapp/src/views/setting/alarm/components/index.ts
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/system/SettingForm.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/system/SettingForm.vue
new file mode 100644
index 000000000..72a040e22
--- /dev/null
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/system/SettingForm.vue
@@ -0,0 +1,166 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      https://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<script setup lang="ts">
+  import { computed, ref } from 'vue';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import {
+    fetchEmailConfig,
+    fetchEmailUpdate,
+    fetchDockerConfig,
+    fetchDockerUpdate,
+    fetchVerifyDocker,
+    fetchVerifyEmail,
+  } from '/@/api/setting';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { isNullOrUnDef } from '/@/utils/is';
+  import { settingFormSchema } from './config';
+  import { SvgIcon } from '/@/components/Icon';
+  import Swal from 'sweetalert2';
+
+  const emit = defineEmits(['success', 'register']);
+  const { t } = useI18n();
+  defineOptions({ name: 'DockerSetting' });
+
+  const settingConfig = ref<Recordable>({});
+  const type = ref('docker');
+  const title = computed(() => {
+    if (type.value == 'docker') return 
t('setting.system.systemSettingItems.dockerSetting.name');
+    if (type.value == 'email') return 
t('setting.system.systemSettingItems.emailSetting.name');
+    return '';
+  });
+  const [registerModal, { closeModal, changeLoading }] = useModalInner(async 
(data) => {
+    try {
+      changeLoading(true);
+      await resetFields();
+      type.value = data.type;
+
+      if (data.type === 'docker') {
+        settingConfig.value = await fetchDockerConfig();
+      } else if (data.type === 'email') {
+        settingConfig.value = await fetchEmailConfig();
+      }
+
+      await setFieldsValue(
+        Object.keys(settingConfig.value).reduce((pre, cur) => {
+          if (!isNullOrUnDef(settingConfig.value[cur])) pre[cur] = 
settingConfig.value[cur];
+          return pre;
+        }, {}),
+      );
+    } catch (error) {
+      console.error(error);
+    } finally {
+      changeLoading(false);
+    }
+  });
+  const [registerForm, { validate, setFieldsValue, resetFields }] = useForm({
+    colon: true,
+    labelWidth: 140,
+    name: 'SettingForm',
+    labelCol: { span: 8 },
+    wrapperCol: { span: 14 },
+    baseColProps: { span: 24 },
+    showActionButtonGroup: false,
+  });
+  const formSchemas = computed((): FormSchema[] => {
+    if (Reflect.has(settingFormSchema, type.value)) {
+      return settingFormSchema[type.value];
+    }
+    return Object.keys(settingConfig.value).map((key) => {
+      return {
+        field: key,
+        label: key,
+        component: 'Input',
+      };
+    });
+  });
+  async function handleOk() {
+    try {
+      const formData = await validate();
+      if (type.value === 'docker') {
+        const resp = await fetchVerifyDocker(formData);
+        switch (resp.status) {
+          case 200:
+            await fetchDockerUpdate(formData);
+            break;
+          case 400:
+            Swal.fire({
+              icon: 'error',
+              title: t('setting.system.update.dockerNotStart'),
+              showConfirmButton: true,
+              timer: 3500,
+            });
+            return;
+          case 500:
+            Swal.fire({
+              icon: 'error',
+              title: resp.msg,
+              showConfirmButton: true,
+              timer: 3500,
+            });
+            return;
+          default:
+            break;
+        }
+      }
+      if (type.value === 'email') {
+        const resp = await fetchVerifyEmail(formData);
+        if (resp.status === 200) {
+          await fetchEmailUpdate(formData);
+        } else {
+          Swal.fire({
+            icon: 'error',
+            title: resp.msg,
+            showConfirmButton: true,
+            timer: 3500,
+          });
+          return;
+        }
+      }
+      Swal.fire({
+        icon: 'success',
+        title: t('setting.system.update.success'),
+        showConfirmButton: false,
+        timer: 2000,
+      });
+      closeModal();
+      emit('success');
+    } catch (error) {
+      console.error(error);
+    }
+  }
+  async function afterClose() {
+    settingConfig.value = [];
+  }
+</script>
+
+<template>
+  <BasicModal
+    @register="registerModal"
+    :width="650"
+    @ok="handleOk"
+    :after-close="afterClose"
+    centered
+  >
+    <template #title>
+      <SvgIcon v-if="type === 'docker'" name="docker" size="20" 
class="ml-10px" />
+      <SvgIcon v-if="type === 'email'" name="mail" size="18" class="ml-10px" />
+      {{ title }}
+    </template>
+    <BasicForm @register="registerForm" :schemas="formSchemas" 
style="margin-top: 30px" />
+  </BasicModal>
+</template>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/System/SettingList.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/system/SettingList.vue
similarity index 64%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/System/SettingList.vue
rename to 
streampark-console/streampark-console-webapp/src/views/setting/system/SettingList.vue
index f510b09fb..1d67ccf7c 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/setting/System/SettingList.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/system/SettingList.vue
@@ -16,6 +16,7 @@
 -->
 <script lang="ts">
   import { defineComponent } from 'vue';
+  import { useModal } from '/@/components/Modal';
   export default defineComponent({
     name: 'MavenSetting',
   });
@@ -27,29 +28,43 @@
   import { fetchSystemSettingUpdate } from '/@/api/setting';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useI18n } from '/@/hooks/web/useI18n';
+  import SettingForm from './SettingForm.vue';
+
+  const { t } = useI18n();
 
   const AvatarMap = {
-    'streampark.maven.settings': 'settings',
+    'streampark.maven.settings': 'settings2',
     'streampark.maven.central.repository': 'maven',
     'streampark.maven.auth.user': 'user',
     'streampark.maven.auth.password': 'mvnpass',
     'docker.register.address': 'docker',
-    'docker.register.namespace': 'namespace',
-    'docker.register.user': 'auth',
-    'docker.register.password': 'password',
-    'alert.email.host': 'host',
-    'alert.email.port': 'port',
     'alert.email.from': 'mail',
-    'alert.email.userName': 'user',
-    'alert.email.password': 'keys',
-    'alert.email.ssl': 'ssl',
-    'ingress.mode.default': 'settings',
+    'ingress.mode.default': 'nginx',
+  };
+
+  const settingTitles = {
+    'streampark.maven.settings': t('setting.system.title.mavenSettings'),
+    'streampark.maven.central.repository': 
t('setting.system.title.mavenRepository'),
+    'streampark.maven.auth.user': t('setting.system.title.mavenUser'),
+    'streampark.maven.auth.password': t('setting.system.title.mavenPassword'),
+    'docker.register.address': t('setting.system.title.docker'),
+    'alert.email.from': t('setting.system.title.email'),
+    'ingress.mode.default': t('setting.system.title.ingress'),
+  };
+
+  const settingDesc = {
+    'streampark.maven.settings': t('setting.system.desc.mavenSettings'),
+    'streampark.maven.central.repository': 
t('setting.system.desc.mavenRepository'),
+    'streampark.maven.auth.user': t('setting.system.desc.mavenUser'),
+    'streampark.maven.auth.password': t('setting.system.desc.mavenPassword'),
+    'docker.register.address': t('setting.system.desc.docker'),
+    'alert.email.from': t('setting.system.desc.email'),
+    'ingress.mode.default': t('setting.system.desc.ingress'),
   };
 
   const ListItem = List.Item;
   const ListItemMeta = ListItem.Meta;
 
-  const { t } = useI18n();
   const emits = defineEmits(['updateValue', 'reload']);
   defineProps({
     data: {
@@ -63,15 +78,26 @@
   });
 
   const { createMessage } = useMessage();
+  const [registerModal, { openModal }] = useModal();
   function handleSwitch(record: SystemSetting) {
     emits('updateValue', record);
   }
   /* edit input */
   function handleEdit(record: SystemSetting) {
-    if (!record.editable) {
-      record.submitting = true;
+    if (record.settingKey.startsWith('docker.register')) {
+      openModal(true, {
+        type: 'docker',
+      });
+    } else if (record.settingKey.startsWith('alert.email')) {
+      openModal(true, {
+        type: 'email',
+      });
+    } else {
+      if (!record.editable) {
+        record.submitting = true;
+      }
+      record.editable = !record.editable;
     }
-    record.editable = !record.editable;
   }
   /* edit commit */
   async function handleSubmit(record: SystemSetting) {
@@ -88,9 +114,15 @@
 
 <template>
   <List>
-    <template v-for="item in data" :key="item.settingKey">
-      <ListItem>
-        <ListItemMeta :title="item.settingName" 
:description="item.description" style="width: 50%">
+    <template v-for="item in data">
+      <ListItem v-if="AvatarMap[item.settingKey]" :key="item.settingKey">
+        <ListItemMeta style="width: 50%">
+          <template #title>
+            {{ settingTitles[item.settingKey] }}
+          </template>
+          <template #description>
+            {{ settingDesc[item.settingKey] }}
+          </template>
           <template #avatar>
             <div class="avatar">
               <SvgIcon :name="AvatarMap[item.settingKey]" />
@@ -127,14 +159,19 @@
         <template #actions>
           <div v-if="item.type === 1" v-auth="'setting:update'">
             <a v-if="!item.submitting" @click="handleEdit(item)">
-              {{ t('common.edit') }}
+              <a-button type="primary" shape="circle">
+                <SvgIcon name="edit" />
+              </a-button>
             </a>
             <a v-else @click="handleSubmit(item)">
-              {{ t('common.submitText') }}
+              <a-button type="primary" shape="circle">
+                <SvgIcon name="save" />
+              </a-button>
             </a>
           </div>
         </template>
       </ListItem>
     </template>
   </List>
+  <SettingForm @register="registerModal" @success="emits('reload')" />
 </template>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/System/View.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/system/View.vue
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/System/View.vue
rename to 
streampark-console/streampark-console-webapp/src/views/setting/system/View.vue
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/system/config.ts
 
b/streampark-console/streampark-console-webapp/src/views/setting/system/config.ts
new file mode 100644
index 000000000..f8aa94850
--- /dev/null
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/system/config.ts
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { FormSchema } from '/@/components/Form';
+import { useI18n } from '/@/hooks/web/useI18n';
+
+export type SettingType = 'docker' | 'email';
+
+type SettingForm = {
+  [P in SettingType]: FormSchema[];
+};
+
+const { t } = useI18n();
+export const settingFormSchema: SettingForm = {
+  docker: [
+    {
+      field: 'address',
+      label: t('setting.system.docker.address.label'),
+      helpMessage: t('setting.system.docker.address.desc'),
+      component: 'Input',
+      componentProps: {
+        placeholder: t('setting.system.docker.address.label'),
+      },
+      required: true,
+    },
+    {
+      field: 'namespace',
+      label: t('setting.system.docker.namespace.label'),
+      helpMessage: t('setting.system.docker.namespace.desc'),
+      component: 'Input',
+      componentProps: {
+        placeholder: t('setting.system.docker.namespace.label'),
+      },
+      required: true,
+    },
+    {
+      field: 'userName',
+      label: t('setting.system.docker.userName.label'),
+      helpMessage: t('setting.system.docker.userName.desc'),
+      component: 'Input',
+      componentProps: {
+        placeholder: t('setting.system.docker.userName.label'),
+      },
+      required: true,
+    },
+    {
+      field: 'password',
+      label: t('setting.system.docker.password.label'),
+      helpMessage: t('setting.system.docker.password.desc'),
+      component: 'InputPassword',
+      componentProps: {
+        autocomplete: 'new-password',
+        placeholder: t('setting.system.docker.password.label'),
+      },
+      required: true,
+    },
+  ],
+  email: [
+    {
+      field: 'host',
+      label: t('setting.system.email.host.label'),
+      helpMessage: t('setting.system.email.host.desc'),
+      component: 'Input',
+      componentProps: {
+        placeholder: t('setting.system.email.host.label'),
+      },
+      required: true,
+    },
+    {
+      field: 'port',
+      label: t('setting.system.email.port.label'),
+      helpMessage: t('setting.system.email.port.desc'),
+      component: 'InputNumber',
+      componentProps: {
+        class: '!w-full',
+        controls: false,
+        min: 0,
+        max: 65535,
+        placeholder: t('setting.system.email.port.label'),
+      },
+      required: true,
+    },
+    {
+      field: 'from',
+      label: t('setting.system.email.from.label'),
+      helpMessage: t('setting.system.email.from.desc'),
+      component: 'Input',
+      componentProps: {
+        placeholder: t('setting.system.email.from.label'),
+      },
+      required: true,
+    },
+    {
+      field: 'userName',
+      label: t('setting.system.email.userName.label'),
+      helpMessage: t('setting.system.email.userName.desc'),
+      component: 'Input',
+      componentProps: {
+        placeholder: t('setting.system.email.userName.label'),
+      },
+      required: true,
+    },
+    {
+      field: 'password',
+      label: t('setting.system.email.password.label'),
+      helpMessage: t('setting.system.email.password.label'),
+      component: 'InputPassword',
+      componentProps: {
+        autocomplete: 'new-password',
+        placeholder: t('setting.system.email.password.label'),
+      },
+      required: true,
+    },
+    {
+      field: 'ssl',
+      label: t('setting.system.email.ssl.label'),
+      helpMessage: t('setting.system.email.ssl.label'),
+      component: 'Switch',
+      componentProps: {
+        checkedChildren: 'ON',
+        unCheckedChildren: 'OFF',
+      },
+    },
+  ],
+};
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/View.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/yarnqueue/View.vue
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/View.vue
rename to 
streampark-console/streampark-console-webapp/src/views/setting/yarnqueue/View.vue
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/YarnQueueDrawer.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/yarnqueue/YarnQueueDrawer.vue
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/YarnQueueDrawer.vue
rename to 
streampark-console/streampark-console-webapp/src/views/setting/yarnqueue/YarnQueueDrawer.vue
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/index.data.ts
 
b/streampark-console/streampark-console-webapp/src/views/setting/yarnqueue/index.data.ts
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/index.data.ts
rename to 
streampark-console/streampark-console-webapp/src/views/setting/yarnqueue/index.data.ts
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/useYarnQueueRender.tsx
 
b/streampark-console/streampark-console-webapp/src/views/setting/yarnqueue/useYarnQueueRender.tsx
similarity index 100%
rename from 
streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/useYarnQueueRender.tsx
rename to 
streampark-console/streampark-console-webapp/src/views/setting/yarnqueue/useYarnQueueRender.tsx

Reply via email to