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

benjobs 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 2dc4adab8 [Improve]: sync flink cluster & home & project page (#4079)
2dc4adab8 is described below

commit 2dc4adab8fb674eead9521e7c1ef8907f08387db
Author: Kriszu <[email protected]>
AuthorDate: Mon Sep 23 00:20:19 2024 +0800

    [Improve]: sync flink cluster & home & project page (#4079)
    
    * [Improve]: sync flink cluster & home & project page
    
    * [style]: lint code style
    
    ---------
    
    Co-authored-by: benjobs <[email protected]>
---
 .github/workflows/e2e.yml                          |  22 --
 .../core/controller/FlinkClusterController.java    |   8 +
 .../core/controller/FlinkEnvController.java        |   7 +
 .../streampark/console/core/entity/Project.java    |   3 +
 .../console/core/mapper/FlinkClusterMapper.java    |   5 +
 .../console/core/mapper/FlinkEnvMapper.java        |   5 +
 .../console/core/service/FlinkClusterService.java  |   4 +
 .../console/core/service/FlinkEnvService.java      |   4 +
 .../core/service/impl/FlinkClusterServiceImpl.java |  10 +
 .../core/service/impl/FlinkEnvServiceImpl.java     |  10 +
 .../resources/mapper/core/FlinkClusterMapper.xml   |  14 +
 .../main/resources/mapper/core/FlinkEnvMapper.xml  |  14 +
 .../src/api/flink/flinkCluster.ts                  |  12 +
 .../src/api/flink/flinkEnv.ts                      |  12 +
 .../src/api/spark/home.ts                          |  53 +++-
 .../src/components/Table/src/BasicTable.vue        |   5 +-
 .../src/components/Table/src/hooks/useTableForm.ts |   2 +-
 .../src/components/Table/src/types/table.ts        |   2 +-
 .../src/design/index.less                          |   9 +-
 .../src/enums/flinkEnum.ts                         |   2 +-
 .../src/locales/lang/en/flink/variable.ts          |   2 +
 .../src/locales/lang/en/setting/flinkCluster.ts    |   1 +
 .../src/locales/lang/en/setting/flinkHome.ts       |   2 +
 .../src/locales/lang/en/spark/home.ts              |   5 +
 .../src/locales/lang/zh-CN/flink/variable.ts       |   2 +
 .../src/locales/lang/zh-CN/setting/flinkCluster.ts |   1 +
 .../src/locales/lang/zh-CN/setting/flinkHome.ts    |   2 +
 .../src/locales/lang/zh-CN/spark/home.ts           |   5 +
 .../src/views/flink/app/EditFlink.vue              |   2 +-
 .../src/views/flink/app/EditStreamPark.vue         |   2 +-
 .../src/views/flink/app/data/index.ts              |   2 +-
 .../flink/app/hooks/useCreateAndEditSchema.ts      |   4 +-
 .../src/views/flink/app/utils/index.ts             |   2 +-
 .../src/views/flink/cluster/Add.vue                |  16 +-
 .../src/views/flink/cluster/Edit.vue               |  18 +-
 .../src/views/flink/cluster/View.vue               | 337 ++++++++++-----------
 .../src/views/flink/cluster/useClusterSetting.ts   |   8 +-
 .../src/views/flink/home/View.vue                  | 239 +++++++--------
 .../src/views/flink/home/components/Drawer.vue     |   4 +-
 .../src/views/flink/home/components/Modal.vue      |  98 +++---
 .../src/views/resource/project/View.vue            | 334 ++++++++++++++------
 .../src/views/setting/extlink/View.vue             |   2 +-
 .../src/views/spark/app/create.vue                 |   2 +-
 .../src/views/spark/app/edit.vue                   |   3 +-
 .../{flink => spark}/home/components/Drawer.vue    |  55 ++--
 .../src/views/spark/home/components/Modal.vue      |  17 +-
 .../src/views/spark/home/index.vue                 | 225 +++++++-------
 47 files changed, 920 insertions(+), 673 deletions(-)

diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index d16a7b966..96c841118 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -130,28 +130,6 @@ jobs:
             class: org.apache.streampark.e2e.cases.TokenManagementTest
           - name: UploadManagementTest
             class: org.apache.streampark.e2e.cases.UploadManagementTest
-          - name: ProjectsManagementTest
-            class: org.apache.streampark.e2e.cases.ProjectsManagementTest
-          - name: VariableManagementTest
-            class: org.apache.streampark.e2e.cases.VariableManagementTest
-          - name: Flink118OnRemoteClusterDeployTest
-            class: 
org.apache.streampark.e2e.cases.Flink118OnRemoteClusterDeployTest
-          - name: Flink117OnRemoteClusterDeployTest
-            class: 
org.apache.streampark.e2e.cases.Flink117OnRemoteClusterDeployTest
-          - name: Flink116OnRemoteClusterDeployTest
-            class: 
org.apache.streampark.e2e.cases.Flink116OnRemoteClusterDeployTest
-          - name: Flink116OnYarnClusterDeployTest
-            class: 
org.apache.streampark.e2e.cases.Flink116OnYarnClusterDeployTest
-          - name: Flink117OnYarnClusterDeployTest
-            class: 
org.apache.streampark.e2e.cases.Flink117OnYarnClusterDeployTest
-          - name: Flink118OnYarnClusterDeployTest
-            class: 
org.apache.streampark.e2e.cases.Flink118OnYarnClusterDeployTest
-          - name: FlinkSQL116OnYarnTest
-            class: org.apache.streampark.e2e.cases.FlinkSQL116OnYarnTest
-          - name: FlinkSQL117OnYarnTest
-            class: org.apache.streampark.e2e.cases.FlinkSQL117OnYarnTest
-          - name: FlinkSQL118OnYarnTest
-            class: org.apache.streampark.e2e.cases.FlinkSQL118OnYarnTest
     env:
       RECORDING_PATH: /tmp/recording-${{ matrix.case.name }}
     steps:
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/FlinkClusterController.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/FlinkClusterController.java
index bcd77d142..d08417708 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/FlinkClusterController.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/FlinkClusterController.java
@@ -18,6 +18,7 @@
 package org.apache.streampark.console.core.controller;
 
 import org.apache.streampark.common.enums.ClusterState;
+import org.apache.streampark.console.base.domain.RestRequest;
 import org.apache.streampark.console.base.domain.RestResponse;
 import org.apache.streampark.console.base.exception.InternalException;
 import org.apache.streampark.console.core.bean.ResponseResult;
@@ -27,6 +28,7 @@ import org.apache.streampark.console.core.util.ServiceHelper;
 
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -45,6 +47,12 @@ public class FlinkClusterController {
     @Autowired
     private FlinkClusterService flinkClusterService;
 
+    @PostMapping("page")
+    public RestResponse findPage(FlinkCluster flinkCluster, RestRequest 
restRequest) {
+        IPage<FlinkCluster> flinkClusters = 
flinkClusterService.findPage(flinkCluster, restRequest);
+        return RestResponse.success(flinkClusters);
+    }
+
     @PostMapping("alive")
     public RestResponse listAvailableCluster() {
         List<FlinkCluster> flinkClusters = 
flinkClusterService.listAvailableCluster();
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/FlinkEnvController.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/FlinkEnvController.java
index 78b8e54d6..56e984c9e 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/FlinkEnvController.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/controller/FlinkEnvController.java
@@ -17,11 +17,13 @@
 
 package org.apache.streampark.console.core.controller;
 
+import org.apache.streampark.console.base.domain.RestRequest;
 import org.apache.streampark.console.base.domain.RestResponse;
 import org.apache.streampark.console.core.entity.FlinkEnv;
 import org.apache.streampark.console.core.enums.FlinkEnvCheckEnum;
 import org.apache.streampark.console.core.service.FlinkEnvService;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -40,6 +42,11 @@ public class FlinkEnvController {
     @Autowired
     private FlinkEnvService flinkEnvService;
 
+    @PostMapping("page")
+    public RestResponse findPage(FlinkEnv flinkEnv, RestRequest restRequest) {
+        IPage<FlinkEnv> envs = flinkEnvService.findPage(flinkEnv, restRequest);
+        return RestResponse.success(envs);
+    }
     @PostMapping("list")
     public RestResponse list() {
         List<FlinkEnv> flinkEnvList = flinkEnvService.list();
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/entity/Project.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/entity/Project.java
index 98005c673..8d354de6a 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/entity/Project.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/entity/Project.java
@@ -66,6 +66,9 @@ public class Project extends BaseEntity {
 
     private String url;
 
+    /** git branch or tag */
+    private String refs;
+
     /** git branch */
     private String branches;
 
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/FlinkClusterMapper.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/FlinkClusterMapper.java
index 0a6efbe73..b8758b2c0 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/FlinkClusterMapper.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/FlinkClusterMapper.java
@@ -22,10 +22,15 @@ import 
org.apache.streampark.console.core.entity.FlinkCluster;
 import org.apache.ibatis.annotations.Param;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
 public interface FlinkClusterMapper extends BaseMapper<FlinkCluster> {
 
     boolean existsByClusterId(@Param("clusterId") String clusterId, 
@Param("id") Long id);
 
     boolean existsByClusterName(@Param("clusterName") String clusterName, 
@Param("id") Long id);
+
+    IPage<FlinkCluster> findPage(
+                                 Page<FlinkCluster> page, @Param("cluster") 
FlinkCluster flinkCluster);
 }
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/FlinkEnvMapper.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/FlinkEnvMapper.java
index a86fc3db2..c1315404d 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/FlinkEnvMapper.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/mapper/FlinkEnvMapper.java
@@ -18,14 +18,19 @@
 package org.apache.streampark.console.core.mapper;
 
 import org.apache.streampark.console.core.entity.FlinkEnv;
+import org.apache.streampark.console.core.entity.Project;
 
 import org.apache.ibatis.annotations.Param;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
 public interface FlinkEnvMapper extends BaseMapper<FlinkEnv> {
 
     FlinkEnv selectByAppId(@Param("appId") Long appId);
 
     void setDefault(@Param("id") Long id);
+
+    IPage<FlinkEnv> findPage(Page<Project> page, @Param("flinkEnv") FlinkEnv 
flinkEnv);
 }
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/FlinkClusterService.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/FlinkClusterService.java
index b8ae5baa4..055cdd412 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/FlinkClusterService.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/FlinkClusterService.java
@@ -19,9 +19,11 @@ package org.apache.streampark.console.core.service;
 
 import org.apache.streampark.common.enums.ClusterState;
 import org.apache.streampark.common.enums.FlinkExecutionMode;
+import org.apache.streampark.console.base.domain.RestRequest;
 import org.apache.streampark.console.core.bean.ResponseResult;
 import org.apache.streampark.console.core.entity.FlinkCluster;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 
 import java.util.Collection;
@@ -130,4 +132,6 @@ public interface FlinkClusterService extends 
IService<FlinkCluster> {
      * @param state flink cluster state
      */
     void updateClusterState(Long id, ClusterState state);
+
+    IPage<FlinkCluster> findPage(FlinkCluster flinkCluster, RestRequest 
restRequest);
 }
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/FlinkEnvService.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/FlinkEnvService.java
index d715a344e..23abd0eb8 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/FlinkEnvService.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/FlinkEnvService.java
@@ -17,9 +17,11 @@
 
 package org.apache.streampark.console.core.service;
 
+import org.apache.streampark.console.base.domain.RestRequest;
 import org.apache.streampark.console.core.entity.FlinkEnv;
 import org.apache.streampark.console.core.enums.FlinkEnvCheckEnum;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 
 import java.io.IOException;
@@ -103,4 +105,6 @@ public interface FlinkEnvService extends IService<FlinkEnv> 
{
      * @param id The ID to check for validity.
      */
     void validity(Long id);
+
+    IPage<FlinkEnv> findPage(FlinkEnv flinkEnv, RestRequest restRequest);
 }
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/FlinkClusterServiceImpl.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/FlinkClusterServiceImpl.java
index 00408144e..e2820d661 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/FlinkClusterServiceImpl.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/FlinkClusterServiceImpl.java
@@ -20,8 +20,10 @@ package org.apache.streampark.console.core.service.impl;
 import org.apache.streampark.common.enums.ClusterState;
 import org.apache.streampark.common.enums.FlinkExecutionMode;
 import org.apache.streampark.common.util.YarnUtils;
+import org.apache.streampark.console.base.domain.RestRequest;
 import org.apache.streampark.console.base.exception.ApiAlertException;
 import org.apache.streampark.console.base.exception.ApiDetailException;
+import org.apache.streampark.console.base.mybatis.pager.MybatisPager;
 import org.apache.streampark.console.core.bean.ResponseResult;
 import org.apache.streampark.console.core.entity.FlinkCluster;
 import org.apache.streampark.console.core.mapper.FlinkClusterMapper;
@@ -42,6 +44,8 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.annotations.VisibleForTesting;
 import lombok.extern.slf4j.Slf4j;
@@ -328,6 +332,12 @@ public class FlinkClusterServiceImpl extends 
ServiceImpl<FlinkClusterMapper, Fli
         update(updateWrapper);
     }
 
+    @Override
+    public IPage<FlinkCluster> findPage(FlinkCluster flinkCluster, RestRequest 
restRequest) {
+        Page<FlinkCluster> page = MybatisPager.getPage(restRequest);
+        return this.baseMapper.findPage(page, flinkCluster);
+    }
+
     @Override
     public void remove(Long id) {
         FlinkCluster flinkCluster = getById(id);
diff --git 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/FlinkEnvServiceImpl.java
 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/FlinkEnvServiceImpl.java
index b8ffbafea..a58a918d0 100644
--- 
a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/FlinkEnvServiceImpl.java
+++ 
b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/core/service/impl/FlinkEnvServiceImpl.java
@@ -17,8 +17,11 @@
 
 package org.apache.streampark.console.core.service.impl;
 
+import org.apache.streampark.console.base.domain.RestRequest;
 import org.apache.streampark.console.base.exception.ApiAlertException;
+import org.apache.streampark.console.base.mybatis.pager.MybatisPager;
 import org.apache.streampark.console.core.entity.FlinkEnv;
+import org.apache.streampark.console.core.entity.Project;
 import org.apache.streampark.console.core.enums.FlinkEnvCheckEnum;
 import org.apache.streampark.console.core.mapper.FlinkEnvMapper;
 import org.apache.streampark.console.core.service.FlinkClusterService;
@@ -26,6 +29,8 @@ import 
org.apache.streampark.console.core.service.FlinkEnvService;
 import 
org.apache.streampark.console.core.service.application.ApplicationInfoService;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -158,6 +163,11 @@ public class FlinkEnvServiceImpl extends 
ServiceImpl<FlinkEnvMapper, FlinkEnv>
         FlinkEnv flinkEnv = getById(id);
         checkOrElseAlert(flinkEnv);
     }
+    @Override
+    public IPage<FlinkEnv> findPage(FlinkEnv flinkEnv, RestRequest 
restRequest) {
+        Page<Project> page = MybatisPager.getPage(restRequest);
+        return this.baseMapper.findPage(page, flinkEnv);
+    }
 
     private void checkOrElseAlert(FlinkEnv flinkEnv) {
 
diff --git 
a/streampark-console/streampark-console-service/src/main/resources/mapper/core/FlinkClusterMapper.xml
 
b/streampark-console/streampark-console-service/src/main/resources/mapper/core/FlinkClusterMapper.xml
index 769fe521c..6a9ae790d 100644
--- 
a/streampark-console/streampark-console-service/src/main/resources/mapper/core/FlinkClusterMapper.xml
+++ 
b/streampark-console/streampark-console-service/src/main/resources/mapper/core/FlinkClusterMapper.xml
@@ -45,6 +45,20 @@
         <result column="alert_id" jdbcType="BIGINT" property="alertId"/>
     </resultMap>
 
+    <select id="findPage" 
resultType="org.apache.streampark.console.core.entity.FlinkCluster">
+        select * from t_flink_cluster
+        <where>
+            <if test="cluster.clusterName != null and cluster.clusterName != 
''">
+                <if test="_databaseId == 'mysql'">
+                    and cluster_name like concat('%', 
#{cluster.clusterName},'%')
+                </if>
+                <if test="_databaseId == 'pgsql'">
+                    and cluster_name like '%' || #{cluster.clusterName} || '%'
+                </if>
+            </if>
+        </where>
+    </select>
+
     <select id="existsByClusterId" resultType="java.lang.Boolean" 
parameterType="java.lang.String">
         select
          CASE
diff --git 
a/streampark-console/streampark-console-service/src/main/resources/mapper/core/FlinkEnvMapper.xml
 
b/streampark-console/streampark-console-service/src/main/resources/mapper/core/FlinkEnvMapper.xml
index ccdcbf808..ee0b18fdb 100644
--- 
a/streampark-console/streampark-console-service/src/main/resources/mapper/core/FlinkEnvMapper.xml
+++ 
b/streampark-console/streampark-console-service/src/main/resources/mapper/core/FlinkEnvMapper.xml
@@ -29,6 +29,20 @@
         <result column="create_time" jdbcType="DATE" property="createTime"/>
     </resultMap>
 
+    <select id="findPage" 
resultType="org.apache.streampark.console.core.entity.FlinkEnv">
+        select * from t_flink_env
+        <where>
+            <if test="flinkEnv.flinkName != null and flinkEnv.flinkName != ''">
+                <if test="_databaseId == 'mysql'">
+                    and flink_name like concat('%', #{flinkEnv.flinkName},'%')
+                </if>
+                <if test="_databaseId == 'pgsql'">
+                    and flink_name like '%' || #{flinkEnv.flinkName} || '%'
+                </if>
+            </if>
+        </where>
+    </select>
+
     <select id="selectByAppId" 
resultType="org.apache.streampark.console.core.entity.FlinkEnv" 
parameterType="java.lang.Long">
         select v.*
         from t_flink_env v
diff --git 
a/streampark-console/streampark-console-webapp/src/api/flink/flinkCluster.ts 
b/streampark-console/streampark-console-webapp/src/api/flink/flinkCluster.ts
index 72a62977f..0141bf526 100644
--- a/streampark-console/streampark-console-webapp/src/api/flink/flinkCluster.ts
+++ b/streampark-console/streampark-console-webapp/src/api/flink/flinkCluster.ts
@@ -18,8 +18,10 @@ import { AxiosResponse } from 'axios';
 import { FlinkCluster } from './flinkCluster.type';
 import { Result } from '/#/axios';
 import { defHttp } from '/@/utils/http/axios';
+import type { BasicTableParams } from '../model/baseModel';
 
 enum FLINK_API {
+  PAGE = '/flink/cluster/page',
   LIST = '/flink/cluster/list',
   REMOTE_URL = '/flink/cluster/remote_url',
   CREATE = '/flink/cluster/create',
@@ -30,6 +32,16 @@ enum FLINK_API {
   SHUTDOWN = '/flink/cluster/shutdown',
   DELETE = '/flink/cluster/delete',
 }
+/**
+ * flink cluster
+ * @returns Promise<FlinkEnv[]>
+ */
+export function fetchFlinkClusterPage(data: BasicTableParams) {
+  return defHttp.post<FlinkCluster[]>({
+    url: FLINK_API.PAGE,
+    data,
+  });
+}
 /**
  * flink cluster
  * @returns Promise<FlinkEnv[]>
diff --git 
a/streampark-console/streampark-console-webapp/src/api/flink/flinkEnv.ts 
b/streampark-console/streampark-console-webapp/src/api/flink/flinkEnv.ts
index acefec874..6012d9e32 100644
--- a/streampark-console/streampark-console-webapp/src/api/flink/flinkEnv.ts
+++ b/streampark-console/streampark-console-webapp/src/api/flink/flinkEnv.ts
@@ -18,8 +18,10 @@ import { AxiosResponse } from 'axios';
 import { FlinkCreate, FlinkEnv } from './flinkEnv.type';
 import { Result } from '/#/axios';
 import { defHttp } from '/@/utils/http/axios';
+import type { BasicTableParams } from '../model/baseModel';
 
 enum FLINK_API {
+  PAGE = '/flink/env/page',
   LIST = '/flink/env/list',
   CREATE = '/flink/env/create',
   DELETE = '/flink/env/delete',
@@ -30,6 +32,16 @@ enum FLINK_API {
   DEFAULT = '/flink/env/default',
   VALIDITY = '/flink/env/validity',
 }
+/**
+ * flink environment data
+ * @returns Promise<FlinkEnv[]>
+ */
+export function fetchFlinkEnvPage(data: BasicTableParams) {
+  return defHttp.post<FlinkEnv[]>({
+    url: FLINK_API.PAGE,
+    data,
+  });
+}
 /**
  * flink environment data
  * @returns Promise<FlinkEnv[]>
diff --git a/streampark-console/streampark-console-webapp/src/api/spark/home.ts 
b/streampark-console/streampark-console-webapp/src/api/spark/home.ts
index 5ff145196..565d12bd2 100644
--- a/streampark-console/streampark-console-webapp/src/api/spark/home.ts
+++ b/streampark-console/streampark-console-webapp/src/api/spark/home.ts
@@ -18,21 +18,21 @@
 import type { SparkCreate, SparkEnv } from './home.type';
 import { defHttp } from '/@/utils/http/axios';
 
-enum FLINK_API {
-  LIST = '/spark/env/list',
-  CHECK = '/spark/env/check',
-  CREATE = '/spark/env/create',
-  UPDATE = '/spark/env/update',
-  DELETE = '/spark/env/delete',
-  DEFAULT = '/spark/env/default',
-}
+const apiPrefix = '/spark/env';
 /**
  * spark environment data
  * @returns Promise<SparkEnv[]>
  */
 export function fetchSparkEnvList() {
   return defHttp.post<SparkEnv[]>({
-    url: FLINK_API.LIST,
+    url: `${apiPrefix}/list`,
+  });
+}
+
+export function fetchSparkEnv(id: string) {
+  return defHttp.post<SparkEnv>({
+    url: `${apiPrefix}/get`,
+    data: { id: id },
   });
 }
 
@@ -42,7 +42,7 @@ export function fetchSparkEnvList() {
  */
 export function fetchSetDefault(id: string) {
   return defHttp.post({
-    url: FLINK_API.DEFAULT,
+    url: `${apiPrefix}/default`,
     data: { id },
   });
 }
@@ -53,7 +53,7 @@ export function fetchSetDefault(id: string) {
  */
 export function fetchSparkEnvRemove(id: string) {
   return defHttp.post({
-    url: FLINK_API.DELETE,
+    url: `${apiPrefix}/delete`,
     data: { id },
   });
 }
@@ -67,7 +67,10 @@ export function fetchSparkEnvCheck(data: {
   sparkName: string;
   sparkHome: string;
 }) {
-  return defHttp.post({ url: FLINK_API.CHECK, data });
+  return defHttp.post({
+    url: `${apiPrefix}/check`,
+    data,
+  });
 }
 
 /**
@@ -75,7 +78,13 @@ export function fetchSparkEnvCheck(data: {
  *
  */
 export function fetchSparkEnvCreate(data: SparkCreate) {
-  return defHttp.post({ url: FLINK_API.CREATE, data }, { isTransformResponse: 
false });
+  return defHttp.post(
+    {
+      url: `${apiPrefix}/create`,
+      data,
+    },
+    { isTransformResponse: false },
+  );
 }
 
 /**
@@ -83,5 +92,21 @@ export function fetchSparkEnvCreate(data: SparkCreate) {
  * @param data
  */
 export function fetchSparkEnvUpdate(data: SparkCreate) {
-  return defHttp.post({ url: FLINK_API.UPDATE, data }, { isTransformResponse: 
false });
+  return defHttp.post(
+    {
+      url: `${apiPrefix}/update`,
+      data,
+    },
+    { isTransformResponse: false },
+  );
+}
+
+/**
+ * Configure synchronization
+ */
+export function fetchSparkSync(id: string): Promise<boolean> {
+  return defHttp.post<boolean>({
+    url: `${apiPrefix}/sync`,
+    data: { id },
+  });
 }
diff --git 
a/streampark-console/streampark-console-webapp/src/components/Table/src/BasicTable.vue
 
b/streampark-console/streampark-console-webapp/src/components/Table/src/BasicTable.vue
index cfded6e12..153831208 100644
--- 
a/streampark-console/streampark-console-webapp/src/components/Table/src/BasicTable.vue
+++ 
b/streampark-console/streampark-console-webapp/src/components/Table/src/BasicTable.vue
@@ -16,7 +16,7 @@
       </template>
     </BasicForm>
 
-    <div ref="tableContainerRef" class="relative">
+    <div ref="tableContainerRef" class="relative flex-1 table-box">
       <Table
         ref="tableElRef"
         v-bind="getBindValues"
@@ -409,6 +409,9 @@
       margin-right: 0;
     }
 
+    .table-box {
+      background-color: @component-background;
+    }
     .ant-table-wrapper {
       padding: 10px;
       background-color: @component-background;
diff --git 
a/streampark-console/streampark-console-webapp/src/components/Table/src/hooks/useTableForm.ts
 
b/streampark-console/streampark-console-webapp/src/components/Table/src/hooks/useTableForm.ts
index bc09c7f6d..ea5b75eb5 100644
--- 
a/streampark-console/streampark-console-webapp/src/components/Table/src/hooks/useTableForm.ts
+++ 
b/streampark-console/streampark-console-webapp/src/components/Table/src/hooks/useTableForm.ts
@@ -7,7 +7,7 @@ import { isFunction } from '/@/utils/is';
 export function useTableForm(
   propsRef: ComputedRef<BasicTableProps>,
   slots: Slots,
-  fetch: (opt?: FetchParams | undefined) => Promise<void>,
+  fetch: (opt?: FetchParams | undefined) => Promise<Recordable<any>[] | 
undefined>,
   getLoading: ComputedRef<boolean | undefined>,
 ) {
   const getFormProps = computed((): Partial<FormProps> => {
diff --git 
a/streampark-console/streampark-console-webapp/src/components/Table/src/types/table.ts
 
b/streampark-console/streampark-console-webapp/src/components/Table/src/types/table.ts
index d9c8e5eb1..5a59aad1a 100644
--- 
a/streampark-console/streampark-console-webapp/src/components/Table/src/types/table.ts
+++ 
b/streampark-console/streampark-console-webapp/src/components/Table/src/types/table.ts
@@ -84,7 +84,7 @@ export interface GetColumnsParams {
 export type SizeType = 'default' | 'middle' | 'small' | 'large';
 
 export interface TableActionType {
-  reload: (opt?: FetchParams) => Promise<void>;
+  reload: (opt?: FetchParams) => Promise<Recordable<any>[] | undefined>;
   getSelectRows: <T = Recordable>() => T[];
   clearSelectedRowKeys: () => void;
   expandAll: () => void;
diff --git a/streampark-console/streampark-console-webapp/src/design/index.less 
b/streampark-console/streampark-console-webapp/src/design/index.less
index 6cfb2301b..33a2daafc 100644
--- a/streampark-console/streampark-console-webapp/src/design/index.less
+++ b/streampark-console/streampark-console-webapp/src/design/index.less
@@ -105,7 +105,7 @@ span {
 .ant-card,
 .ant-alert,
 .bold-tag,
-.bold-tag > .ant-tag,
+.bold-tag>.ant-tag,
 .ant-tabs-tab,
 .ant-btn-group,
 textarea.ant-input,
@@ -116,14 +116,17 @@ textarea.ant-input,
   border-radius: 1px !important;
 }
 
-.pop-tip {
+.pop-tip,
+.tip-info {
   display: inline-block;
   margin-top: 5px;
   color: darkgrey;
 }
 
 [data-theme='dark'] {
-  .pop-tip {
+
+  .pop-tip,
+  .tip-info {
     color: #666;
   }
 }
diff --git 
a/streampark-console/streampark-console-webapp/src/enums/flinkEnum.ts 
b/streampark-console/streampark-console-webapp/src/enums/flinkEnum.ts
index e758aacac..4bc831a60 100644
--- a/streampark-console/streampark-console-webapp/src/enums/flinkEnum.ts
+++ b/streampark-console/streampark-console-webapp/src/enums/flinkEnum.ts
@@ -13,7 +13,7 @@ export enum BuildStateEnum {
 /* ExecutionMode  */
 export enum ExecModeEnum {
   /** remote (standalone) */
-  REMOTE = 1,
+  STANDALONE = 1,
   /** yarn per-job (deprecated, please use yarn-application mode) */
   YARN_PER_JOB = 2,
   /** yarn session */
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/variable.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/variable.ts
index a71ab219f..f00403480 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/variable.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/flink/variable.ts
@@ -23,6 +23,8 @@ export default {
   add: 'Add',
   success: ' successful',
   fail: ' failed',
+  searchByCode: 'Search by var code',
+  searchByDesc: 'Search by description',
   table: {
     title: 'Variable List',
     variableCode: 'Variable Code',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/flinkCluster.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/flinkCluster.ts
index d61fc9a05..0f054b559 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/flinkCluster.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/flinkCluster.ts
@@ -21,6 +21,7 @@ export default {
   start: 'Start Cluster',
   edit: 'Edit Cluster',
   delete: 'Are you sure delete this cluster ?',
+  searchByName: 'Search by cluster name',
   form: {
     clusterName: 'Cluster Name',
     address: 'Cluster URL',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/flinkHome.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/flinkHome.ts
index 9055e03b2..1e827f98e 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/flinkHome.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/setting/flinkHome.ts
@@ -26,6 +26,8 @@ export default {
   flinkHomePlaceholder: 'Please enter flink home',
   description: 'description',
   descriptionPlaceholder: 'Please enter description',
+  flinkVersion: 'Flink Version',
+  searchByName: 'Search by flink name',
   operateMessage: {
     flinkNameTips: 'The flink name, e.g: flink-1.12',
     flinkNameIsRepeated: 'Flink name already exists',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/spark/home.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/spark/home.ts
index 878e55e6f..209702450 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/en/spark/home.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/en/spark/home.ts
@@ -16,6 +16,11 @@
  */
 export default {
   title: 'Spark Home',
+  sparkVersion: 'Spark Version',
+  searchByName: 'Search by Spark Name',
+  conf: 'Spark Conf',
+  sync: 'Sync Conf',
+  edit: 'Edit Spark Env',
   tips: {
     remove: 'The current spark home has been successfully deleted.',
     setDefault: 'Successfully set the default spark home.',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/variable.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/variable.ts
index b40cc90b9..0f6e61182 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/variable.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/flink/variable.ts
@@ -23,6 +23,8 @@ export default {
   add: '添加',
   success: '成功',
   fail: '失败',
+  searchByCode: '根据变量 code 搜索',
+  searchByDesc: '根据变量描述搜索',
   table: {
     title: '变量列表',
     variableCode: '变量Code',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/flinkCluster.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/flinkCluster.ts
index 9cfdb9b84..d27416e19 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/flinkCluster.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/flinkCluster.ts
@@ -21,6 +21,7 @@ export default {
   start: '开启集群',
   edit: '编辑集群',
   delete: '确定要删除此集群 ?',
+  searchByName: '根据 Flink 集群名称搜索',
   form: {
     clusterName: '集群名称',
     address: '集群URL',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/flinkHome.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/flinkHome.ts
index c69f59925..3e1ce0223 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/flinkHome.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/setting/flinkHome.ts
@@ -26,6 +26,8 @@ export default {
   flinkHomePlaceholder: '请输入Flink安装路径',
   description: '描述',
   descriptionPlaceholder: 'Flink描述',
+  flinkVersion: 'Flink 版本',
+  searchByName: '根据 Flink 名称搜索',
   operateMessage: {
     flinkNameTips: 'Flink别名,举例: Flink-1.12',
     flinkNameIsRepeated: 'Flink名称已存在',
diff --git 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/spark/home.ts
 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/spark/home.ts
index 1f18dc9c3..c046f9722 100644
--- 
a/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/spark/home.ts
+++ 
b/streampark-console/streampark-console-webapp/src/locales/lang/zh-CN/spark/home.ts
@@ -16,6 +16,11 @@
  */
 export default {
   title: 'Spark Home',
+  sparkVersion: 'Spark 版本',
+  searchByName: '按Spark名称搜索',
+  conf: 'Spark Conf',
+  sync: '同步 Conf',
+  edit: '编辑 Spark 环境',
   tips: {
     remove: '当前的 spark home 已被成功删除。',
     setDefault: '成功设置默认spark home',
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/EditFlink.vue
 
b/streampark-console/streampark-console-webapp/src/views/flink/app/EditFlink.vue
index 3c803f59c..8bbf90134 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/app/EditFlink.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/app/EditFlink.vue
@@ -112,7 +112,7 @@
         Object.assign(defaultParams, { executionMode: app.executionMode });
       }
       switch (app.executionMode) {
-        case ExecModeEnum.REMOTE:
+        case ExecModeEnum.STANDALONE:
           defaultParams['remoteClusterId'] = app.flinkClusterId;
           break;
         case ExecModeEnum.YARN_SESSION:
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/EditStreamPark.vue
 
b/streampark-console/streampark-console-webapp/src/views/flink/app/EditStreamPark.vue
index 5bbca4e39..d142c3f8a 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/app/EditStreamPark.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/app/EditStreamPark.vue
@@ -126,7 +126,7 @@
         ...resetParams,
       };
       switch (app.executionMode) {
-        case ExecModeEnum.REMOTE:
+        case ExecModeEnum.STANDALONE:
           defaultParams['remoteClusterId'] = app.flinkClusterId;
           break;
         case ExecModeEnum.YARN_SESSION:
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/data/index.ts
 
b/streampark-console/streampark-console-webapp/src/views/flink/app/data/index.ts
index fa1eed09c..c90a49017 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/app/data/index.ts
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/app/data/index.ts
@@ -61,7 +61,7 @@ export const k8sRestExposedType = [
 ];
 
 export const executionModes = [
-  { label: 'remote', value: ExecModeEnum.REMOTE, disabled: false },
+  { label: 'remote', value: ExecModeEnum.STANDALONE, disabled: false },
   { label: 'yarn application', value: ExecModeEnum.YARN_APPLICATION, disabled: 
false },
   { label: 'yarn session', value: ExecModeEnum.YARN_SESSION, disabled: false },
   { label: 'kubernetes session', value: ExecModeEnum.KUBERNETES_SESSION, 
disabled: false },
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useCreateAndEditSchema.ts
 
b/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useCreateAndEditSchema.ts
index de0fa14cd..c464b76aa 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useCreateAndEditSchema.ts
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useCreateAndEditSchema.ts
@@ -204,8 +204,8 @@ export const useCreateAndEditSchema = (
         field: 'remoteClusterId',
         label: t('flink.app.flinkCluster'),
         component: 'Select',
-        render: (param) => 
renderFlinkCluster(getExecutionCluster(ExecModeEnum.REMOTE), param),
-        ifShow: ({ values }) => values.executionMode == ExecModeEnum.REMOTE,
+        render: (param) => 
renderFlinkCluster(getExecutionCluster(ExecModeEnum.STANDALONE), param),
+        ifShow: ({ values }) => values.executionMode == 
ExecModeEnum.STANDALONE,
         rules: [
           { required: true, message: 
t('flink.app.addAppTips.flinkClusterIsRequiredMessage') },
         ],
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/utils/index.ts
 
b/streampark-console/streampark-console-webapp/src/views/flink/app/utils/index.ts
index 51c16b533..070483871 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/app/utils/index.ts
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/app/utils/index.ts
@@ -249,7 +249,7 @@ function getFlinkClusterId(values: Recordable) {
   if (values.executionMode == ExecModeEnum.YARN_SESSION) {
     return values.yarnSessionClusterId;
   }
-  if (values.executionMode == ExecModeEnum.REMOTE) {
+  if (values.executionMode == ExecModeEnum.STANDALONE) {
     return values.remoteClusterId;
   }
   if (values.executionMode == ExecModeEnum.KUBERNETES_SESSION) {
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/cluster/Add.vue 
b/streampark-console/streampark-console-webapp/src/views/flink/cluster/Add.vue
index c3224a03c..fec925d15 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/cluster/Add.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/cluster/Add.vue
@@ -14,13 +14,6 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<script lang="ts">
-  import { defineComponent } from 'vue';
-
-  export default defineComponent({
-    name: 'AddCluster',
-  });
-</script>
 <script setup lang="ts" name="AddCluster">
   import { PageWrapper } from '/@/components/Page';
   import { BasicForm, useForm } from '/@/components/Form';
@@ -82,8 +75,13 @@
   }
 </script>
 <template>
-  <PageWrapper content-background content-class="py-30px">
-    <BasicForm @register="registerForm" @submit="handleSubmitCluster" 
:schemas="getClusterSchema">
+  <PageWrapper content-full-height content-background>
+    <BasicForm
+      @register="registerForm"
+      @submit="handleSubmitCluster"
+      :schemas="getClusterSchema"
+      class="!my-30px"
+    >
       <template #formFooter>
         <div class="flex items-center w-full justify-center">
           <a-button @click="go('/flink/cluster')">
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/cluster/Edit.vue 
b/streampark-console/streampark-console-webapp/src/views/flink/cluster/Edit.vue
index 7dd04fe12..0d1fe1a2c 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/cluster/Edit.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/cluster/Edit.vue
@@ -14,14 +14,9 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<script lang="ts">
-  import { defineComponent, unref, ref } from 'vue';
-  import { useGo } from '/@/hooks/web/usePage';
-  export default defineComponent({
-    name: 'EditCluster',
-  });
-</script>
 <script setup lang="ts" name="EditCluster">
+  import { unref, ref } from 'vue';
+  import { useGo } from '/@/hooks/web/usePage';
   import { PageWrapper } from '/@/components/Page';
   import { BasicForm, useForm } from '/@/components/Form';
   import { useMessage } from '/@/hooks/web/useMessage';
@@ -132,8 +127,13 @@
   });
 </script>
 <template>
-  <PageWrapper content-background content-class="py-30px">
-    <BasicForm @register="registerForm" @submit="handleSubmitCluster" 
:schemas="getClusterSchema">
+  <PageWrapper content-background content-full-height>
+    <BasicForm
+      @register="registerForm"
+      @submit="handleSubmitCluster"
+      :schemas="getClusterSchema"
+      class="!my-30px"
+    >
       <template #formFooter>
         <div class="flex items-center w-full justify-center">
           <a-button @click="go('/flink/cluster')">
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/cluster/View.vue 
b/streampark-console/streampark-console-webapp/src/views/flink/cluster/View.vue
index 75bb327e0..d4a32fb1f 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/cluster/View.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/cluster/View.vue
@@ -14,50 +14,86 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<script lang="ts">
-  import { defineComponent } from 'vue';
-  import { useTimeoutFn } from '@vueuse/core';
-  import { onUnmounted } from 'vue';
-
-  export default defineComponent({
-    name: 'FlinkClusterSetting',
-  });
-</script>
 <script lang="ts" setup name="FlinkClusterSetting">
-  import { onMounted, ref } from 'vue';
+  import { nextTick, onUnmounted } from 'vue';
+  import { useTimeoutFn } from '@vueuse/core';
   import { SvgIcon } from '/@/components/Icon';
-  import { List, Popconfirm, Tooltip, Card, Tag } from 'ant-design-vue';
+  import { Col, Tag } from 'ant-design-vue';
   import { ClusterStateEnum, ExecModeEnum } from '/@/enums/flinkEnum';
-  import {
-    PauseCircleOutlined,
-    EyeOutlined,
-    PlusOutlined,
-    PlayCircleOutlined,
-    EditOutlined,
-    DeleteOutlined,
-  } from '@ant-design/icons-vue';
+  import { PlusOutlined } from '@ant-design/icons-vue';
   import { useMessage } from '/@/hooks/web/useMessage';
   import {
     fetchClusterRemove,
     fetchClusterShutdown,
     fetchClusterStart,
-    fetchFlinkCluster,
+    fetchFlinkClusterPage,
   } from '/@/api/flink/flinkCluster';
   import { FlinkCluster } from '/@/api/flink/flinkCluster.type';
   import { useGo } from '/@/hooks/web/usePage';
   import { useI18n } from '/@/hooks/web/useI18n';
   import { PageWrapper } from '/@/components/Page';
-  import { BasicTitle } from '/@/components/Basic';
-  import State from './State';
-
-  const ListItem = List.Item;
-  const ListItemMeta = ListItem.Meta;
+  import { BasicTable, TableAction, useTable } from '/@/components/Table';
+  defineOptions({
+    name: 'FlinkClusterSetting',
+  });
+  const executionModeMap = {
+    [ExecModeEnum.STANDALONE]: {
+      color: '#2db7f5',
+      text: 'standalone',
+    },
+    [ExecModeEnum.YARN_SESSION]: {
+      color: '#87d068',
+      text: 'yarn session',
+    },
+    [ExecModeEnum.KUBERNETES_SESSION]: {
+      color: '#108ee9',
+      text: 'k8s session',
+    },
+  };
 
   const go = useGo();
   const { t } = useI18n();
   const { Swal, createMessage } = useMessage();
-  const clusters = ref<FlinkCluster[]>([]);
-  const loading = ref(false);
+  const [registerTable, { reload, getLoading }] = useTable({
+    api: fetchFlinkClusterPage,
+    columns: [
+      { dataIndex: 'clusterName', title: 
t('setting.flinkCluster.form.clusterName') },
+      { dataIndex: 'executionMode', title: 
t('setting.flinkCluster.form.executionMode') },
+      { dataIndex: 'address', title: t('setting.flinkCluster.form.address') },
+      { dataIndex: 'description', title: t('setting.flinkHome.description') },
+    ],
+    formConfig: {
+      schemas: [
+        {
+          field: 'clusterName',
+          label: '',
+          component: 'Input',
+          componentProps: {
+            placeholder: t('setting.flinkCluster.searchByName'),
+            allowClear: true,
+          },
+          colProps: { span: 6 },
+        },
+      ],
+      rowProps: {
+        gutter: 14,
+      },
+      submitOnChange: true,
+      showActionButtonGroup: false,
+    },
+    rowKey: 'id',
+    pagination: true,
+    useSearchForm: true,
+    showTableSetting: false,
+    showIndexColumn: false,
+    canResize: false,
+    actionColumn: {
+      width: 200,
+      title: t('component.table.operation'),
+      dataIndex: 'action',
+    },
+  });
+
   function handleIsStart(item) {
     return item.clusterState === ClusterStateEnum.RUNNING;
   }
@@ -89,7 +125,7 @@
   /* delete */
   async function handleDelete(item: FlinkCluster) {
     await fetchClusterRemove(item.id);
-    await getFlinkCluster();
+    handlePageDataReload(true);
     createMessage.success('The current cluster is remove');
   }
   /* shutdown */
@@ -105,172 +141,115 @@
     }
   }
 
-  async function getFlinkCluster() {
-    try {
-      loading.value = true;
-      const clusterList = await fetchFlinkCluster();
-      clusters.value = clusterList;
-    } catch (error) {
-      console.error(error);
-    } finally {
-      loading.value = false;
-    }
+  function handlePageDataReload(polling = false) {
+    nextTick(() => {
+      reload({ polling });
+    });
   }
+
   const { start, stop } = useTimeoutFn(() => {
     // Prevent another request from being initiated while the previous request 
is pending
-    if (!loading.value) {
-      getFlinkCluster();
+    if (!getLoading()) {
+      handlePageDataReload(true);
     }
     start();
   }, 1000 * 3);
 
-  onMounted(() => {
-    getFlinkCluster();
-  });
   onUnmounted(() => {
     stop();
   });
 </script>
 <template>
-  <PageWrapper contentFullHeight>
-    <Card :bordered="false">
-      <BasicTitle>{{ t('setting.flinkCluster.title') }}</BasicTitle>
-      <div v-auth="'project:create'">
-        <a-button
-          type="dashed"
-          style="width: 100%; margin-top: 20px"
-          @click="go('/flink/add_cluster')"
-        >
-          <PlusOutlined />
-          {{ t('common.add') }}
-        </a-button>
-      </div>
-      <List>
-        <ListItem v-for="(item, index) in clusters" 
:key="'cluster'.concat(String(index))">
-          <ListItemMeta
-            :title="item.clusterName"
-            style="width: 20%"
-            :description="item.description"
-          >
-            <template #avatar>
-              <SvgIcon class="avatar p-15px" name="flink" size="60" />
-            </template>
-          </ListItemMeta>
-          <div class="list-content" style="width: 10%">
-            <div class="list-content-item">
-              <span>{{ t('setting.flinkCluster.form.executionMode') }}</span>
-              <p style="margin-top: 10px">
-                <Tag v-if="item.executionMode === ExecModeEnum.REMOTE" 
color="#2db7f5">REMOTE</Tag>
-                <Tag v-if="item.executionMode === ExecModeEnum.YARN_SESSION" 
color="#87d068"
-                  >YARN SESSION</Tag
-                >
-                <Tag v-if="item.executionMode === 
ExecModeEnum.KUBERNETES_SESSION" color="#108ee9"
-                  >KUBERNETES SESSION</Tag
-                >
-              </p>
-            </div>
-          </div>
-          <div
-            class="list-content"
-            style="width: 40%"
-            v-if="
-              item.executionMode === ExecModeEnum.REMOTE ||
-              item.executionMode === ExecModeEnum.YARN_SESSION
-            "
+  <PageWrapper contentFullHeight fixed-height content-class="flex flex-col">
+    <BasicTable @register="registerTable" class="flex flex-col">
+      <template #form-formFooter>
+        <Col :span="5" :offset="13" class="text-right">
+          <a-button type="primary" @click="() => go('/flink/add_cluster')">
+            <PlusOutlined />
+            {{ t('common.add') }}
+          </a-button>
+        </Col>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.dataIndex === 'clusterName'">
+          <svg-icon class="avatar" name="flink" :size="20" />
+          {{ record.clusterName }}
+        </template>
+        <template v-if="column.dataIndex === 'executionMode'">
+          <Tag
+            v-if="executionModeMap[record.executionMode]"
+            :color="executionModeMap[record.executionMode]?.color"
           >
-            <div class="list-content-item">
-              <span>{{ t('setting.flinkCluster.form.address') }}</span>
-              <p style="margin-top: 10px">
-                <a :href="`/proxy/cluster/${item.id}/`" target="_blank">
-                  {{ item.address }}
-                </a>
-              </p>
-            </div>
-          </div>
-          <div
-            class="list-content"
-            style="width: 10%"
+            {{ executionModeMap[record.executionMode]?.text }}
+          </Tag>
+        </template>
+        <template v-if="column.dataIndex === 'address'">
+          <a
+            :href="`/proxy/cluster/${record.id}/`"
+            target="_blank"
             v-if="
-              item.executionMode === ExecModeEnum.REMOTE ||
-              item.executionMode === ExecModeEnum.YARN_SESSION
+              record.executionMode === ExecModeEnum.STANDALONE ||
+              record.executionMode === ExecModeEnum.YARN_SESSION
             "
           >
-            <div class="list-content-item">
-              <span>{{ t('setting.flinkCluster.form.runState') }}</span>
-              <p style="margin-top: 10px">
-                <State :data="{ clusterState: item.clusterState }" />
-              </p>
-            </div>
-          </div>
-          <template #actions>
-            <Tooltip :title="t('setting.flinkCluster.edit')">
-              <a-button
-                v-auth="'cluster:update'"
-                :disabled="handleIsStart(item)"
-                @click="handleEditCluster(item)"
-                shape="circle"
-                size="large"
-                class="control-button"
-              >
-                <EditOutlined />
-              </a-button>
-            </Tooltip>
-            <template v-if="handleIsStart(item)">
-              <Tooltip :title="t('setting.flinkCluster.stop')">
-                <a-button
-                  :disabled="item.executionMode === ExecModeEnum.REMOTE"
-                  v-auth="'cluster:create'"
-                  @click="handleShutdownCluster(item)"
-                  shape="circle"
-                  size="large"
-                  style="margin-left: 3px"
-                  class="control-button"
-                >
-                  <PauseCircleOutlined />
-                </a-button>
-              </Tooltip>
-            </template>
-            <template v-else>
-              <Tooltip :title="t('setting.flinkCluster.start')">
-                <a-button
-                  :disabled="item.executionMode === ExecModeEnum.REMOTE"
-                  v-auth="'cluster:create'"
-                  @click="handleDeployCluster(item)"
-                  shape="circle"
-                  size="large"
-                  class="control-button"
-                >
-                  <PlayCircleOutlined />
-                </a-button>
-              </Tooltip>
-            </template>
-            <Tooltip :title="t('setting.flinkCluster.detail')">
-              <a-button
-                :disabled="!handleIsStart(item)"
-                v-auth="'app:detail'"
-                shape="circle"
-                :href="`/proxy/cluster/${item.id}/`"
-                target="_blank"
-                size="large"
-                class="control-button"
-              >
-                <EyeOutlined />
-              </a-button>
-            </Tooltip>
-
-            <Popconfirm
-              :title="t('setting.flinkCluster.delete')"
-              :cancel-text="t('common.no')"
-              :ok-text="t('common.yes')"
-              @confirm="handleDelete(item)"
-            >
-              <a-button type="danger" shape="circle" size="large" 
class="control-button">
-                <DeleteOutlined />
-              </a-button>
-            </Popconfirm>
-          </template>
-        </ListItem>
-      </List>
-    </Card>
+            {{ record.address }}
+          </a>
+          <span v-else> - </span>
+        </template>
+        <template v-if="column.dataIndex === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:note-edit-line',
+                auth: 'cluster:update',
+                tooltip: t('setting.flinkCluster.edit'),
+                disabled: handleIsStart(record),
+                onClick: handleEditCluster.bind(null, record),
+              },
+              {
+                icon: 'ant-design:pause-circle-outlined',
+                auth: 'cluster:create',
+                ifShow: handleIsStart(record),
+                disabled: record.executionMode === ExecModeEnum.STANDALONE,
+                tooltip: t('setting.flinkCluster.stop'),
+                onClick: handleShutdownCluster.bind(null, record),
+              },
+              {
+                icon: 'ant-design:play-circle-outlined',
+                auth: 'cluster:create',
+                ifShow: !handleIsStart(record),
+                disabled: record.executionMode === ExecModeEnum.STANDALONE,
+                tooltip: t('setting.flinkCluster.start'),
+                onClick: handleDeployCluster.bind(null, record),
+              },
+              {
+                icon: 'ant-design:eye-outlined',
+                auth: 'app:detail',
+                disabled: !handleIsStart(record),
+                tooltip: t('setting.flinkCluster.detail'),
+                href: `/proxy/cluster/${record.id}/`,
+                target: '_blank',
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                tooltip: t('common.delText'),
+                popConfirm: {
+                  title: t('setting.flinkCluster.delete'),
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
   </PageWrapper>
 </template>
+<style lang="less" scoped>
+  .cluster-card-list {
+    background-color: @component-background;
+    height: 100%;
+  }
+</style>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/cluster/useClusterSetting.ts
 
b/streampark-console/streampark-console-webapp/src/views/flink/cluster/useClusterSetting.ts
index 7d6090f21..9d420d795 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/cluster/useClusterSetting.ts
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/cluster/useClusterSetting.ts
@@ -121,7 +121,7 @@ export const useClusterSetting = () => {
           options: [
             {
               label: 'remote',
-              value: ExecModeEnum.REMOTE,
+              value: ExecModeEnum.STANDALONE,
             },
             { label: 'yarn session', value: ExecModeEnum.YARN_SESSION },
             { label: 'kubernetes session', value: 
ExecModeEnum.KUBERNETES_SESSION },
@@ -149,7 +149,7 @@ export const useClusterSetting = () => {
         componentProps: {
           placeholder: t('setting.flinkCluster.placeholder.addressRemoteMode'),
         },
-        ifShow: ({ values }) => values.executionMode == ExecModeEnum.REMOTE,
+        ifShow: ({ values }) => values.executionMode == 
ExecModeEnum.STANDALONE,
         rules: [{ required: true, message: 
t('setting.flinkCluster.required.address') }],
       },
       {
@@ -170,7 +170,7 @@ export const useClusterSetting = () => {
         },
         ifShow: ({ values }) =>
           values.executionMode == ExecModeEnum.YARN_SESSION ||
-          values.executionMode == ExecModeEnum.REMOTE,
+          values.executionMode == ExecModeEnum.STANDALONE,
       },
       {
         field: 'clusterId',
@@ -352,7 +352,7 @@ export const useClusterSetting = () => {
     };
 
     switch (values.executionMode) {
-      case ExecModeEnum.REMOTE:
+      case ExecModeEnum.STANDALONE:
         Object.assign(params, {
           address: values.address,
         });
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/home/View.vue 
b/streampark-console/streampark-console-webapp/src/views/flink/home/View.vue
index 1e4306c12..4fe3d14c6 100644
--- a/streampark-console/streampark-console-webapp/src/views/flink/home/View.vue
+++ b/streampark-console/streampark-console-webapp/src/views/flink/home/View.vue
@@ -14,49 +14,72 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<script lang="ts">
-  import { defineComponent } from 'vue';
-  import { useI18n } from '/@/hooks/web/useI18n';
-  export default defineComponent({
-    name: 'FlinkEnvSetting',
-  });
-</script>
 <script lang="ts" setup name="FlinkEnvSetting">
-  import { onMounted, ref } from 'vue';
+  import { ref } from 'vue';
+  import { useI18n } from '/@/hooks/web/useI18n';
   import { useModal } from '/@/components/Modal';
   import { SvgIcon } from '/@/components/Icon';
-  import { List, Switch, Card, Popconfirm, Tooltip } from 'ant-design-vue';
-  import {
-    CheckOutlined,
-    CloseOutlined,
-    DeleteOutlined,
-    EyeOutlined,
-    EditOutlined,
-    PlusOutlined,
-  } from '@ant-design/icons-vue';
+  import { Col, Switch } from 'ant-design-vue';
+  import { CheckOutlined, CloseOutlined, PlusOutlined } from 
'@ant-design/icons-vue';
   import { FlinkEnvModal, FlinkEnvDrawer } from './components';
   import {
     fetchValidity,
     fetchDefaultSet,
-    fetchListFlinkEnv,
     fetchFlinkEnvRemove,
     fetchFlinkInfo,
+    fetchFlinkEnvPage,
   } from '/@/api/flink/flinkEnv';
   import { FlinkEnv } from '/@/api/flink/flinkEnv.type';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useDrawer } from '/@/components/Drawer';
   import { PageWrapper } from '/@/components/Page';
-  import { BasicTitle } from '/@/components/Basic';
-
-  const ListItem = List.Item;
-  const ListItemMeta = ListItem.Meta;
+  import { BasicTable, TableAction, useTable } from '/@/components/Table';
 
   const { t } = useI18n();
   const versionId = ref<string | null>(null);
   const { Swal, createMessage } = useMessage();
-  const flinks = ref<FlinkEnv[]>([]);
   const [registerModal, { openModal: openFlinkModal }] = useModal();
   const [registerFlinkDraw, { openDrawer: openEnvDrawer }] = useDrawer();
+  const [registerTable, { reload, getDataSource }] = useTable({
+    api: fetchFlinkEnvPage,
+    columns: [
+      { dataIndex: 'flinkName', title: t('setting.flinkHome.flinkName') },
+      { dataIndex: 'flinkHome', title: t('setting.flinkHome.flinkHome') },
+      { dataIndex: 'version', title: t('setting.flinkHome.flinkVersion') },
+      { dataIndex: 'default', title: 'Default' },
+      { dataIndex: 'description', title: t('setting.flinkHome.description') },
+    ],
+    formConfig: {
+      schemas: [
+        {
+          field: 'flinkName',
+          label: '',
+          component: 'Input',
+          componentProps: {
+            placeholder: t('setting.flinkHome.searchByName'),
+            allowClear: true,
+          },
+          colProps: { span: 6 },
+        },
+      ],
+      rowProps: {
+        gutter: 14,
+      },
+      submitOnChange: true,
+      showActionButtonGroup: false,
+    },
+    rowKey: 'id',
+    pagination: true,
+    useSearchForm: true,
+    showTableSetting: false,
+    showIndexColumn: false,
+    canResize: false,
+    actionColumn: {
+      width: 200,
+      title: t('component.table.operation'),
+      dataIndex: 'action',
+    },
+  });
   /* Edit button */
   async function handleEditFlink(item: FlinkEnv) {
     const resp = await fetchValidity(item.id);
@@ -81,7 +104,7 @@
   async function handleDelete(item: FlinkEnv) {
     const resp = await fetchFlinkEnvRemove(item.id);
     if (resp.data.code == 200) {
-      await getFlinkSetting();
+      await reload();
       createMessage.success('The current flink home is removed.');
     }
   }
@@ -96,113 +119,79 @@
         showConfirmButton: false,
         timer: 2000,
       });
-      getFlinkSetting();
+      reload();
     }
   }
-
-  /* Get flink environment data */
-  async function getFlinkSetting() {
-    flinks.value = await fetchListFlinkEnv();
-  }
-
-  onMounted(() => {
-    getFlinkSetting();
-  });
 </script>
 <template>
-  <PageWrapper contentFullHeight>
-    <Card :bordered="false">
-      <BasicTitle>{{ t('setting.flinkHome.title') }}</BasicTitle>
-      <div v-auth="'project:create'">
-        <a-button
-          id="e2e-env-add-btn"
-          type="dashed"
-          style="width: 100%; margin-top: 20px"
-          @click="openFlinkModal(true, {})"
-        >
-          <PlusOutlined />
-          {{ t('common.add') }}
-        </a-button>
-      </div>
-      <List>
-        <ListItem v-for="(item, index) in flinks" :key="index">
-          <ListItemMeta style="width: 60%" :title="item.flinkName" 
:description="item.description">
-            <template #avatar>
-              <SvgIcon class="avatar p-15px" name="flink" size="60" />
-            </template>
-          </ListItemMeta>
-
-          <div class="list-content flex" style="width: 40%">
-            <div class="list-content-item" style="width: 60%">
-              <span>Flink Home</span>
-              <p style="margin-top: 10px">
-                {{ item.flinkHome }}
-              </p>
-            </div>
-            <div class="list-content-item">
-              <span>Default</span>
-              <p style="margin-top: 10px">
-                <Switch
-                  :disabled="item.isDefault"
-                  @click="handleSetDefault(item)"
-                  v-model:checked="item.isDefault"
-                >
-                  <template #checkedChildren>
-                    <CheckOutlined />
-                  </template>
-                  <template #unCheckedChildren>
-                    <CloseOutlined />
-                  </template>
-                </Switch>
-              </p>
-            </div>
+  <PageWrapper contentFullHeight fixed-height content-class="flex flex-col">
+    <BasicTable @register="registerTable" class="flex flex-col">
+      <template #form-formFooter>
+        <Col :span="5" :offset="13" class="text-right">
+          <div v-auth="'project:create'">
+            <a-button id="e2e-env-add-btn" type="primary" 
@click="openFlinkModal(true, {})">
+              <PlusOutlined />
+              {{ t('common.add') }}
+            </a-button>
           </div>
+        </Col>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.dataIndex === 'flinkName'">
+          <svg-icon class="avatar" name="flink" :size="20" />
+          {{ record.flinkName }}
+        </template>
+        <template v-if="column.dataIndex === 'default'">
+          <Switch
+            :disabled="record.isDefault"
+            @click="handleSetDefault(record)"
+            v-model:checked="record.isDefault"
+          >
+            <template #checkedChildren>
+              <CheckOutlined />
+            </template>
+            <template #unCheckedChildren>
+              <CloseOutlined />
+            </template>
+          </Switch>
+        </template>
+        <template v-if="column.dataIndex === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:note-edit-line',
+                tooltip: t('setting.flinkHome.edit'),
+                onClick: handleEditFlink.bind(null, record),
+              },
+              {
+                icon: 'ant-design:eye-outlined',
+                tooltip: t('setting.flinkHome.conf'),
+                onClick: handleFlinkConf.bind(null, record),
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                tooltip: t('common.delText'),
+                disabled: record.isDefault && getDataSource()?.length > 1,
+                popConfirm: {
+                  title: t('setting.flinkHome.delete'),
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
 
-          <template #actions>
-            <Tooltip :title="t('setting.flinkHome.edit')">
-              <a-button
-                @click="handleEditFlink(item)"
-                shape="circle"
-                size="large"
-                class="control-button"
-              >
-                <EditOutlined />
-              </a-button>
-            </Tooltip>
-            <Tooltip :title="t('setting.flinkHome.conf')">
-              <a-button
-                shape="circle"
-                @click="handleFlinkConf(item)"
-                target="_blank"
-                size="large"
-                class="control-button"
-              >
-                <EyeOutlined />
-              </a-button>
-            </Tooltip>
-            <Popconfirm
-              :title="t('setting.flinkHome.delete')"
-              :cancel-text="t('common.no')"
-              :ok-text="t('common.yes')"
-              @confirm="handleDelete(item)"
-            >
-              <a-button
-                :disabled="item.isDefault && flinks.length > 1"
-                type="danger"
-                shape="circle"
-                size="large"
-                class="control-button"
-              >
-                <DeleteOutlined />
-              </a-button>
-            </Popconfirm>
-          </template>
-        </ListItem>
-      </List>
-    </Card>
-
-    <FlinkEnvModal @register="registerModal" @reload="getFlinkSetting" />
+    <FlinkEnvModal @register="registerModal" @reload="reload" />
     <FlinkEnvDrawer @register="registerFlinkDraw" width="60%" />
   </PageWrapper>
 </template>
-<style lang="less"></style>
+<style lang="less" scoped>
+  .home-card-list {
+    background-color: @component-background;
+    height: 100%;
+  }
+</style>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/home/components/Drawer.vue
 
b/streampark-console/streampark-console-webapp/src/views/flink/home/components/Drawer.vue
index 8ad4ade92..7e90c64af 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/home/components/Drawer.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/home/components/Drawer.vue
@@ -96,11 +96,11 @@
 </script>
 <template>
   <BasicDrawer title="Flink Conf" @register="registerDrawer" width="40%" 
placement="right">
-    <div style="padding-bottom: 15px">
+    <div class="py-15px pl-10px">
       {{ t('setting.flinkHome.title') }}: &nbsp;&nbsp; {{ flinkInfo.flinkHome 
}}
     </div>
     <div>
-      {{ t('setting.flinkHome.sync') }} :
+      <div class="pl-10px">{{ t('setting.flinkHome.sync') }} :</div>
       <div class="py-15px">
         <div ref="conf" style="height: 120px"></div>
         <a-button
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/home/components/Modal.vue
 
b/streampark-console/streampark-console-webapp/src/views/flink/home/components/Modal.vue
index 71361c441..60124f283 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/home/components/Modal.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/home/components/Modal.vue
@@ -14,16 +14,9 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<script lang="ts">
-  import { defineComponent } from 'vue';
-  import { useI18n } from '/@/hooks/web/useI18n';
-
-  export default defineComponent({
-    name: 'FlinkModal',
-  });
-</script>
 <script lang="ts" setup name="FlinkModal">
   import { h, ref } from 'vue';
+  import { useI18n } from '/@/hooks/web/useI18n';
   import { BasicForm, useForm } from '/@/components/Form';
   import { SvgIcon } from '/@/components/Icon';
   import { BasicModal, useModalInner } from '/@/components/Modal';
@@ -35,12 +28,10 @@
   const { t } = useI18n();
   const { Swal } = useMessage();
   const [registerForm, { setFieldsValue, validate, resetFields }] = useForm({
-    labelWidth: 120,
     colon: true,
     showActionButtonGroup: false,
-    labelCol: { lg: 7, sm: 7 },
-    wrapperCol: { lg: 16, sm: 4 },
-    baseColProps: { span: 24 },
+    layout: 'vertical',
+    baseColProps: { span: 22, offset: 1 },
     schemas: [
       {
         field: 'flinkName',
@@ -77,6 +68,7 @@
         componentProps: {
           placeholder: t('setting.flinkHome.descriptionPlaceholder'),
           allowClear: true,
+          rows: 3,
         },
       },
     ],
@@ -91,47 +83,38 @@
 
   /* form submit */
   async function handleSubmit() {
-    changeOkLoading(true);
-    let formValue;
     try {
-      formValue = await validate();
-    } catch (error) {
-      console.warn('validate error:', error);
-      return;
-    } finally {
-      changeOkLoading(false);
-    }
-    // Detection environment
-    const { data: resp } = await fetchCheckEnv({
-      id: versionId.value,
-      flinkName: formValue.flinkName,
-      flinkHome: formValue.flinkHome,
-    });
-    const checkResp = parseInt(resp.data);
-    if (checkResp !== FlinkEnvCheckEnum.OK) {
-      switch (checkResp) {
-        case FlinkEnvCheckEnum.INVALID_PATH:
-          Swal.fire(
-            'Failed',
-            t('setting.flinkHome.operateMessage.flinkHomePathIsInvalid'),
-            'error',
-          );
-          break;
-        case FlinkEnvCheckEnum.NAME_REPEATED:
-          Swal.fire('Failed', 
t('setting.flinkHome.operateMessage.flinkNameIsRepeated'), 'error');
-          break;
-        case FlinkEnvCheckEnum.FLINK_DIST_NOT_FOUND:
-          Swal.fire('Failed', 
t('setting.flinkHome.operateMessage.flinkDistNotFound'), 'error');
-          break;
-        case FlinkEnvCheckEnum.FLINK_DIST_REPEATED:
-          Swal.fire('Failed', 
t('setting.flinkHome.operateMessage.flinkDistIsRepeated'), 'error');
-          break;
+      changeOkLoading(true);
+      const formValue = await validate();
+      // Detection environment
+      const { data: resp } = await fetchCheckEnv({
+        id: versionId.value,
+        flinkName: formValue.flinkName,
+        flinkHome: formValue.flinkHome,
+      });
+      const checkResp = parseInt(resp.data);
+      if (checkResp !== FlinkEnvCheckEnum.OK) {
+        switch (checkResp) {
+          case FlinkEnvCheckEnum.INVALID_PATH:
+            Swal.fire(
+              'Failed',
+              t('setting.flinkHome.operateMessage.flinkHomePathIsInvalid'),
+              'error',
+            );
+            break;
+          case FlinkEnvCheckEnum.NAME_REPEATED:
+            Swal.fire('Failed', 
t('setting.flinkHome.operateMessage.flinkNameIsRepeated'), 'error');
+            break;
+          case FlinkEnvCheckEnum.FLINK_DIST_NOT_FOUND:
+            Swal.fire('Failed', 
t('setting.flinkHome.operateMessage.flinkDistNotFound'), 'error');
+            break;
+          case FlinkEnvCheckEnum.FLINK_DIST_REPEATED:
+            Swal.fire('Failed', 
t('setting.flinkHome.operateMessage.flinkDistIsRepeated'), 'error');
+            break;
+        }
+        changeOkLoading(false);
+        return;
       }
-      changeOkLoading(false);
-      return;
-    }
-
-    try {
       let message: string;
       let success = false;
       // create
@@ -172,16 +155,25 @@
       } else {
         Swal.fire('Failed', message.replaceAll(/\[StreamPark]/g, ''), 'error');
       }
+    } catch (error) {
+      console.warn('validate error:', error);
+      return;
     } finally {
       changeOkLoading(false);
     }
   }
 </script>
 <template>
-  <BasicModal @register="registerModalInner" v-bind="$attrs" 
@ok="handleSubmit">
+  <BasicModal
+    :width="600"
+    centered
+    @register="registerModalInner"
+    v-bind="$attrs"
+    @ok="handleSubmit"
+  >
     <template #title>
       <SvgIcon name="flink" />
-      {{ t('common.add') }}
+      {{ versionId ? t('common.edit') : t('common.add') }}
     </template>
     <BasicForm @register="registerForm" />
   </BasicModal>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/resource/project/View.vue
 
b/streampark-console/streampark-console-webapp/src/views/resource/project/View.vue
index 7501adad7..950e4a892 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/resource/project/View.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/resource/project/View.vue
@@ -15,74 +15,154 @@
   limitations under the License.
 -->
 <template>
-  <PageWrapper contentFullHeight contentBackground contentClass="px-20px">
+  <PageWrapper
+    contentFullHeight
+    fixed-height
+    content-background
+    contentClass="px-10px"
+    class="sp-project"
+  >
     <a-card class="header" :bordered="false">
-      <template #extra>
-        <a-radio-group v-model:value="queryParams.buildState">
-          <a-radio-button
-            v-for="item in buttonList"
-            @click="handleQuery(item.key)"
-            :value="item.key"
-            :key="item.key"
-            >{{ item.label }}</a-radio-button
-          >
-        </a-radio-group>
-        <a-input-search
-          v-model:value="searchValue"
-          @search="handleSearch"
-          :placeholder="t('flink.project.searchPlaceholder')"
-          class="search-input"
-        />
+      <template #title>
+        <div class="flex items-center justify-between">
+          <div>
+            <a-radio-group v-model:value="queryParams.buildState">
+              <a-radio-button
+                v-for="item in buttonList"
+                @click="handleQuery(item.key)"
+                :value="item.key"
+                :key="item.key"
+                >{{ item.label }}</a-radio-button
+              >
+            </a-radio-group>
+            <a-input
+              v-model:value="queryParams.name"
+              allow-clear
+              @search="() => reload()"
+              :placeholder="t('flink.project.searchPlaceholder')"
+              class="search-input"
+            />
+          </div>
+          <div class="operate bg-white" v-auth="'project:create'">
+            <a-button id="e2e-project-create-btn" type="primary" 
class="w-full" @click="onAdd">
+              <Icon icon="ant-design:plus-outlined" />
+              {{ t('common.add') }}
+            </a-button>
+          </div>
+        </div>
       </template>
     </a-card>
-    <div class="operate pt-20px bg-white" v-auth="'project:create'">
-      <a-button id="e2e-project-create-btn" type="dashed" style="width: 100%" 
@click="onAdd">
-        <Icon icon="ant-design:plus-outlined" />
-        {{ t('common.add') }}
-      </a-button>
-    </div>
-    <a-card :bordered="false">
-      <a-spin :spinning="loading">
-        <a-list>
-          <ListItem
-            :key="item.id"
-            v-for="item in projectDataSource"
-            :item="item"
-            @view-log="handleViewLog"
-            @success="handleListItemSuccess"
+    <BasicTable @register="registerTable">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.dataIndex === 'branches'">
+          <div v-if="record.refs">
+            <a-tag
+              v-if="record.refs?.startsWith('refs/tags/') > 0"
+              color="green"
+              style="border-radius: 4px"
+            >
+              {{ record.refs?.replace('refs/tags/', '') }}
+            </a-tag>
+            <a-tag v-else color="blue" style="border-radius: 4px">
+              {{ record.refs?.replace('refs/heads/', '') }}
+            </a-tag>
+          </div>
+          <span v-else>-</span>
+        </template>
+        <template v-if="column.dataIndex === 'type'">
+          <a-badge
+            class="build-badge"
+            v-if="record.buildState == BuildStateEnum.NEED_REBUILD"
+            count="NEW"
+            title="this project has changed, need rebuild"
+          >
+            <svg-icon class="avatar" :name="projectMap[record.type]" 
:size="20" />
+          </a-badge>
+          <svg-icon v-else class="avatar" :name="projectMap[record.type]" 
:size="20" />
+          {{ projectMap[record.type].toUpperCase() }}
+        </template>
+        <template v-if="column.dataIndex === 'buildState'">
+          <a-badge
+            status="success"
+            title="installing"
+            class="mr-10px"
+            v-if="record.buildState == BuildStateEnum.BUILDING"
           />
-        </a-list>
-        <div class="text-center mt-10px">
-          <a-pagination
-            class="w-full"
-            showLessItems
-            hideOnSinglePage
-            :pageSize="pageInfo.pageSize"
-            :total="pageInfo.total"
-            @change="handlePageChange"
+          <a-tag
+            class="bold-tag"
+            :color="buildStateMap[record.buildState]?.color || '#f5222d'"
+            :class="buildStateMap[record.buildState]?.className"
+          >
+            {{ buildStateMap[record.buildState]?.label || 
t('flink.project.projectStatus.failed') }}
+          </a-tag>
+        </template>
+        <template v-if="column.dataIndex === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'ant-design:code-outlined',
+                auth: 'project:build',
+                tooltip: t('flink.project.operationTips.seeBuildLog'),
+                onClick: handleViewLog.bind(null, record),
+              },
+              {
+                icon: 'ant-design:thunderbolt-outlined',
+                auth: 'project:build',
+                ifShow: record.buildState !== BuildStateEnum.BUILDING,
+                popConfirm: {
+                  title: t('flink.project.operationTips.buildProjectMessage'),
+                  placement: 'left',
+                  confirm: handleBuild.bind(null, record),
+                },
+              },
+              {
+                icon: 'clarity:note-edit-line',
+                ifShow: record.buildState !== BuildStateEnum.BUILDING,
+                auth: 'project:update',
+                tooltip: t('common.edit'),
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                tooltip: t('common.delText'),
+                ifShow: record.buildState !== BuildStateEnum.BUILDING,
+                auth: 'project:delete',
+                popConfirm: {
+                  title: t('flink.project.operationTips.deleteProjectMessage'),
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
           />
-        </div>
-      </a-spin>
-    </a-card>
+        </template>
+      </template>
+    </BasicTable>
     <LogModal @register="registerLogModal" />
   </PageWrapper>
 </template>
 <script lang="ts">
-  import { defineComponent, onUnmounted, reactive, ref, unref, watch } from 
'vue';
+  import { defineComponent, nextTick, onUnmounted, reactive, ref, watch } from 
'vue';
 
   import { PageWrapper } from '/@/components/Page';
-  import { statusList } from './project.data';
-  import { RadioGroup, Radio, Input, Card, List, Spin, Pagination } from 
'ant-design-vue';
-  import { getList } from '/@/api/resource/project';
+  import { buildStateMap, statusList } from './project.data';
+  import { RadioGroup, Radio, Card, Tag, Badge, Input } from 'ant-design-vue';
+  import { buildProject, deleteProject, getList } from 
'/@/api/resource/project';
   import { ProjectRecord } from '/@/api/resource/project/model/projectModel';
-  import ListItem from './components/ListItem.vue';
-  import Icon from '/@/components/Icon/src/Icon.vue';
   import { useGo } from '/@/hooks/web/usePage';
   import { useTimeoutFn } from '@vueuse/core';
   import { useI18n } from '/@/hooks/web/useI18n';
   import { useModal } from '/@/components/Modal';
   import LogModal from './components/LogModal.vue';
   import { useUserStoreWithOut } from '/@/store/modules/user';
+  import { BasicTable, TableAction, useTable } from '/@/components/Table';
+  import { BuildStateEnum } from '/@/enums/flinkEnum';
+  import { buildUUID } from '/@/utils/uuid';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { useRouter } from 'vue-router';
+  import { ProjectTypeEnum } from '/@/enums/projectEnum';
+  import Icon, { SvgIcon } from '/@/components/Icon';
 
   export default defineComponent({
     name: 'ProjectView',
@@ -90,19 +170,22 @@
       PageWrapper,
       ARadioGroup: RadioGroup,
       ARadioButton: Radio.Button,
-      AInputSearch: Input.Search,
-      APagination: Pagination,
       ACard: Card,
-      AList: List,
-      ListItem,
-      ASpin: Spin,
+      ATag: Tag,
+      ABadge: Badge,
+      AInput: Input,
       Icon,
+      SvgIcon,
       LogModal,
+      TableAction,
+      BasicTable,
     },
     setup() {
       const go = useGo();
       const userStore = useUserStoreWithOut();
       const { t } = useI18n();
+      const router = useRouter();
+      const { Swal, createMessage } = useMessage();
       const [registerLogModal, { openModal: openLogModal }] = useModal();
       const buttonList = reactive(statusList);
       const loading = ref(false);
@@ -119,46 +202,104 @@
       });
 
       let projectDataSource = ref<Array<ProjectRecord>>([]);
+      const projectMap = {
+        [ProjectTypeEnum.FLINK]: 'flink',
+        [ProjectTypeEnum.SPARK]: 'spark',
+      };
 
       function onAdd() {
         go(`/project/add`);
       }
 
-      function handleSearch(value: string) {
-        queryParams.name = value;
-        pageInfo.currentPage = 1;
-        queryParams.name = searchValue.value;
-        queryData();
-      }
-
-      function queryData(showLoading = true) {
-        if (showLoading) loading.value = true;
-        getList({
+      async function getRequestList(params: Recordable) {
+        return getList({
           ...queryParams,
-          pageNum: pageInfo.currentPage,
-          pageSize: pageInfo.pageSize,
-        }).then((res) => {
-          loading.value = false;
-          pageInfo.total = Number(res.total);
-          projectDataSource.value = res.records;
+          pageNum: params.pageNum,
+          pageSize: params.pageSize,
         });
       }
+      const [registerTable, { reload, getLoading, setPagination }] = useTable({
+        api: getRequestList,
+        columns: [
+          { dataIndex: 'name', title: t('flink.project.form.projectName') },
+          { dataIndex: 'type', title: t('flink.project.form.projectType') },
+          { dataIndex: 'branches', title: t('flink.project.form.branches') },
+          { dataIndex: 'lastBuild', title: t('flink.project.form.lastBuild') },
+          { dataIndex: 'buildState', title: t('flink.project.form.buildState') 
},
+        ],
+        rowKey: 'id',
+        useSearchForm: false,
+        striped: false,
+        canResize: false,
+        bordered: false,
+        showIndexColumn: false,
+        actionColumn: {
+          width: 200,
+          title: t('component.table.operation'),
+          dataIndex: 'action',
+        },
+      });
+
+      async function handleBuild(record: ProjectRecord) {
+        try {
+          await buildProject({
+            id: record.id,
+            socketId: buildUUID(),
+          });
+          Swal.fire({
+            icon: 'success',
+            title: t('flink.project.operationTips.projectIsbuildingMessage'),
+            showConfirmButton: false,
+            timer: 2000,
+          });
+        } catch (e) {
+          
createMessage.error(t('flink.project.operationTips.projectIsbuildFailedMessage'));
+        }
+      }
+      const handleEdit = function (record: ProjectRecord) {
+        router.push({ path: '/flink/project/edit', query: { id: record.id } });
+      };
+      async function handleDelete(record: ProjectRecord) {
+        try {
+          const res = await deleteProject({ id: record.id });
+          if (res.data) {
+            Swal.fire({
+              icon: 'success',
+              title: 
t('flink.project.operationTips.deleteProjectSuccessMessage'),
+              showConfirmButton: false,
+              timer: 2000,
+            });
+            reload();
+          } else {
+            Swal.fire(
+              'Failed',
+              
t('flink.project.operationTips.deleteProjectFailedDetailMessage'),
+              'error',
+            );
+          }
+        } catch (e) {
+          
createMessage.error(t('flink.project.operationTips.deleteProjectFailedMessage'));
+        }
+      }
 
       const handleQuery = function (val: string | undefined) {
-        pageInfo.currentPage = 1;
+        setPagination({ current: 1 });
         queryParams.buildState = val!;
-        queryParams.name = searchValue.value;
-        queryData();
+        reload();
       };
 
-      const { start, stop } = useTimeoutFn(
-        () => {
-          if (!unref(loading)) queryData(false);
-          start();
-        },
-        2000,
-        { immediate: false },
-      );
+      function handlePageDataReload(polling = false) {
+        nextTick(() => {
+          reload({ polling });
+        });
+      }
+
+      const { start, stop } = useTimeoutFn(() => {
+        if (!getLoading()) {
+          handlePageDataReload(true);
+        }
+        start();
+      }, 2000);
       /* View log */
       function handleViewLog(value: Recordable) {
         openLogModal(true, { project: value });
@@ -167,26 +308,23 @@
       watch(
         () => userStore.getTeamId,
         (val) => {
-          if (val) queryData();
+          if (val) {
+            setPagination({ current: 1 });
+            reload();
+          }
         },
       );
-      queryData();
-      start();
 
       onUnmounted(() => {
         stop();
       });
-      function handlePageChange(val: number) {
-        pageInfo.currentPage = val;
-        queryParams.name = searchValue.value;
-        queryData();
-      }
-      function handleListItemSuccess() {
-        pageInfo.currentPage = 1;
-        queryData();
-      }
+
       return {
         t,
+        BuildStateEnum,
+        buildStateMap,
+        registerTable,
+        reload,
         searchValue,
         pageInfo,
         buildState,
@@ -196,12 +334,12 @@
         projectDataSource,
         loading,
         onAdd,
-        handleSearch,
         registerLogModal,
         handleViewLog,
-        queryData,
-        handlePageChange,
-        handleListItemSuccess,
+        handleBuild,
+        handleEdit,
+        handleDelete,
+        projectMap,
       };
     },
   });
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/extlink/View.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/extlink/View.vue
index cae28f22d..48ab91bee 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/setting/extlink/View.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/extlink/View.vue
@@ -54,7 +54,7 @@
       await fetchExternalLinkDelete(id);
       Swal.fire({
         icon: 'success',
-        title: 'Delete Alert Config successful!',
+        title: 'Delete successful!',
         showConfirmButton: false,
         timer: 2000,
       });
diff --git 
a/streampark-console/streampark-console-webapp/src/views/spark/app/create.vue 
b/streampark-console/streampark-console-webapp/src/views/spark/app/create.vue
index 69304f8bb..81ba38d34 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/spark/app/create.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/spark/app/create.vue
@@ -69,7 +69,7 @@
       tags: values.tags,
       yarnQueue: values.yarnQueue,
       resourceFrom: ResourceFromEnum.UPLOAD,
-      config: null,
+      config: values.config,
       appProperties: values.appProperties,
       appArgs: values.args,
       hadoopUser: values.hadoopUser,
diff --git 
a/streampark-console/streampark-console-webapp/src/views/spark/app/edit.vue 
b/streampark-console/streampark-console-webapp/src/views/spark/app/edit.vue
index f42a457d5..7dfffe044 100644
--- a/streampark-console/streampark-console-webapp/src/views/spark/app/edit.vue
+++ b/streampark-console/streampark-console-webapp/src/views/spark/app/edit.vue
@@ -84,7 +84,7 @@
       tags: values.tags,
       yarnQueue: values.yarnQueue,
       resourceFrom: ResourceFromEnum.UPLOAD,
-      config: null,
+      config: values.config,
       appProperties: values.appProperties,
       appArgs: values.args,
       hadoopUser: values.hadoopUser,
@@ -116,7 +116,6 @@
   /* Submit to create */
   async function handleAppSubmit(formValue: Recordable) {
     let config = formValue.configOverride;
-    console.log(config, formValue);
     if (config != null && config !== undefined && config.trim() != '') {
       config = encryptByBase64(config);
     } else {
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/home/components/Drawer.vue
 
b/streampark-console/streampark-console-webapp/src/views/spark/home/components/Drawer.vue
similarity index 68%
copy from 
streampark-console/streampark-console-webapp/src/views/flink/home/components/Drawer.vue
copy to 
streampark-console/streampark-console-webapp/src/views/spark/home/components/Drawer.vue
index 8ad4ade92..59c67927b 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/home/components/Drawer.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/spark/home/components/Drawer.vue
@@ -14,33 +14,34 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<script lang="ts">
-  import { defineComponent } from 'vue';
-
-  export default defineComponent({
-    name: 'FlinkEnvDraw',
-  });
-</script>
-<script lang="ts" setup name="FlinkEnvDraw">
+<script lang="ts" setup>
   import { ref, reactive } from 'vue';
   import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
   import { SyncOutlined } from '@ant-design/icons-vue';
   import { useMonaco } from '/@/hooks/web/useMonaco';
-  import { FlinkEnv } from '/@/api/flink/flinkEnv.type';
-  import { fetchFlinkInfo, fetchFlinkSync } from '/@/api/flink/flinkEnv';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useI18n } from '/@/hooks/web/useI18n';
+  import { SparkEnv } from '/@/api/spark/home.type';
+  import { fetchSparkEnv, fetchSparkSync } from '/@/api/spark/home';
 
   const { t } = useI18n();
-  const flinkInfo = reactive<Recordable>({});
+  const sparkInfo = reactive({} as SparkEnv);
   const conf = ref();
   const syncLoading = ref(false);
   const { Swal } = useMessage();
-  const [registerDrawer] = useDrawerInner((data: FlinkEnv) => {
-    Object.assign(flinkInfo, data);
-    setContent(flinkInfo.flinkConf);
-    const height = document.documentElement.offsetHeight || 
document.body.offsetHeight;
-    conf.value.style.height = height - 210 + 'px';
+  const [registerDrawer, { changeLoading }] = useDrawerInner(async (data: 
SparkEnv) => {
+    try {
+      changeLoading(true);
+      const res = await fetchSparkEnv(data.id);
+      Object.assign(sparkInfo, res);
+      setContent(res.sparkConf || '');
+      const height = document.documentElement.offsetHeight || 
document.body.offsetHeight;
+      conf.value.style.height = height - 210 + 'px';
+    } catch (error) {
+      console.error(error);
+    } finally {
+      changeLoading(false);
+    }
   });
 
   const { setContent } = useMonaco(conf, {
@@ -77,13 +78,13 @@
   async function handleSync() {
     try {
       syncLoading.value = true;
-      await fetchFlinkSync(flinkInfo.id);
-      const flinkResult = await fetchFlinkInfo(flinkInfo.id);
-      Object.assign(flinkInfo, flinkResult);
-      setContent(flinkInfo.flinkConf);
+      await fetchSparkSync(sparkInfo.id);
+      const res = await fetchSparkEnv(sparkInfo.id);
+      Object.assign(sparkInfo, res);
+      setContent(res.sparkConf);
       Swal.fire({
         icon: 'success',
-        title: flinkResult.flinkName.concat(' conf sync successful!'),
+        title: res.sparkName.concat(' conf sync successful!'),
         showConfirmButton: false,
         timer: 2000,
       });
@@ -95,22 +96,22 @@
   }
 </script>
 <template>
-  <BasicDrawer title="Flink Conf" @register="registerDrawer" width="40%" 
placement="right">
-    <div style="padding-bottom: 15px">
-      {{ t('setting.flinkHome.title') }}: &nbsp;&nbsp; {{ flinkInfo.flinkHome 
}}
+  <BasicDrawer title="Spark Conf" @register="registerDrawer" width="70%" 
placement="right">
+    <div class="py-15px pl-10px">
+      {{ t('spark.home.title') }}: &nbsp;&nbsp; {{ sparkInfo.sparkHome }}
     </div>
     <div>
-      {{ t('setting.flinkHome.sync') }} :
+      <div class="pl-10px">{{ t('spark.home.sync') }} :</div>
       <div class="py-15px">
         <div ref="conf" style="height: 120px"></div>
         <a-button
           type="primary"
-          class="float-right mt-10px mr-130px"
+          class="float-right mt-10px mr-10px"
           @click="handleSync"
           :loading="syncLoading"
         >
           <SyncOutlined />
-          {{ t('setting.flinkHome.sync') }}
+          {{ t('spark.home.sync') }}
         </a-button>
       </div>
     </div>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/spark/home/components/Modal.vue
 
b/streampark-console/streampark-console-webapp/src/views/spark/home/components/Modal.vue
index cd58b268d..b6ce18e84 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/spark/home/components/Modal.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/spark/home/components/Modal.vue
@@ -33,12 +33,10 @@
   const { t } = useI18n();
   const { Swal } = useMessage();
   const [registerForm, { setFieldsValue, validate, resetFields }] = useForm({
-    labelWidth: 120,
     colon: true,
     showActionButtonGroup: false,
-    labelCol: { lg: 7, sm: 7 },
-    wrapperCol: { lg: 16, sm: 4 },
-    baseColProps: { span: 24 },
+    layout: 'vertical',
+    baseColProps: { span: 22, offset: 1 },
     schemas: [
       {
         field: 'sparkName',
@@ -69,6 +67,7 @@
         componentProps: {
           placeholder: t('spark.home.placeholder.description'),
           allowClear: true,
+          rows: 3,
         },
       },
     ],
@@ -157,10 +156,16 @@
   }
 </script>
 <template>
-  <BasicModal @register="registerModalInner" v-bind="$attrs" 
@ok="handleSubmit">
+  <BasicModal
+    @register="registerModalInner"
+    :width="600"
+    centered
+    v-bind="$attrs"
+    @ok="handleSubmit"
+  >
     <template #title>
       <SvgIcon name="spark" />
-      {{ t('common.add') }}
+      {{ versionId ? t('common.edit') : t('common.') }}
     </template>
     <BasicForm @register="registerForm" />
   </BasicModal>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/spark/home/index.vue 
b/streampark-console/streampark-console-webapp/src/views/spark/home/index.vue
index 94b7fe88a..52ac0e5c5 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/spark/home/index.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/spark/home/index.vue
@@ -16,24 +16,19 @@
 -->
 <script lang="ts" setup>
   import { useI18n } from '/@/hooks/web/useI18n';
-  import { onMounted, ref } from 'vue';
+  import { ref } from 'vue';
   import { useModal } from '/@/components/Modal';
   import { SvgIcon } from '/@/components/Icon';
-  import { List, Switch, Card, Popconfirm, Tooltip } from 'ant-design-vue';
-  import {
-    CheckOutlined,
-    CloseOutlined,
-    DeleteOutlined,
-    EditOutlined,
-    PlusOutlined,
-  } from '@ant-design/icons-vue';
+  import { Col, Switch } from 'ant-design-vue';
+  import { CheckOutlined, CloseOutlined, PlusOutlined } from 
'@ant-design/icons-vue';
   import { SparkEnvModal } from './components';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { PageWrapper } from '/@/components/Page';
-  import { BasicTitle } from '/@/components/Basic';
   import { fetchSetDefault, fetchSparkEnvList, fetchSparkEnvRemove } from 
'/@/api/spark/home';
   import { SparkEnv } from '/@/api/spark/home.type';
-
+  import { BasicTable, TableAction, useTable } from '/@/components/Table';
+  // import { useDrawer } from '/@/components/Drawer';
+  // import SparkEnvDrawer from './components/Drawer.vue';
   defineOptions({
     name: 'SparkEnvSetting',
   });
@@ -41,12 +36,53 @@
   const { t } = useI18n();
   const versionId = ref<string | null>(null);
   const { Swal, createMessage } = useMessage();
-  const sparkEnvs = ref<SparkEnv[]>([]);
-  const [registerModal, { openModal: openFlinkModal }] = useModal();
+  const [registerModal, { openModal: openSparkModal }] = useModal();
+  // const [registerSparkDraw, { openDrawer: openEnvDrawer }] = useDrawer();
+  const [registerTable, { reload, getDataSource }] = useTable({
+    api: fetchSparkEnvList,
+    columns: [
+      { dataIndex: 'sparkName', title: t('spark.home.form.sparkName') },
+      { dataIndex: 'sparkHome', title: t('spark.home.form.sparkHome') },
+      { dataIndex: 'version', title: t('spark.home.sparkVersion') },
+      { dataIndex: 'default', title: 'Default' },
+      { dataIndex: 'description', title: t('spark.home.form.description') },
+    ],
+    formConfig: {
+      schemas: [
+        {
+          field: 'sparkName',
+          label: '',
+          component: 'Input',
+          componentProps: {
+            placeholder: t('spark.home.searchByName'),
+            allowClear: true,
+          },
+          colProps: { span: 6 },
+        },
+      ],
+      rowProps: {
+        gutter: 14,
+      },
+      submitOnChange: true,
+      showActionButtonGroup: false,
+    },
+    rowKey: 'id',
+    pagination: true,
+    useSearchForm: true,
+    showTableSetting: false,
+    showIndexColumn: false,
+    canResize: false,
+    actionColumn: {
+      width: 200,
+      title: t('component.table.operation'),
+      dataIndex: 'action',
+    },
+  });
+
   /* Edit button */
   async function handleEditSpark(item: SparkEnv) {
     versionId.value = item.id;
-    openFlinkModal(true, {
+    openSparkModal(true, {
       versionId: item.id,
       sparkName: item.sparkName,
       sparkHome: item.sparkHome,
@@ -56,11 +92,20 @@
 
   /* delete spark home */
   async function handleDelete(item: SparkEnv) {
-    await fetchSparkEnvRemove(item.id);
-    await getSparkEnv();
-    createMessage.success(t('spark.home.tips.remove'));
+    try {
+      await fetchSparkEnvRemove(item.id);
+      reload();
+      createMessage.success(t('spark.home.tips.remove'));
+    } catch (error) {
+      console.error(error);
+    }
   }
 
+  /* View configuration */
+  // async function handleSparkConf(item: SparkEnv) {
+  //   openEnvDrawer(true, item);
+  // }
+
   /* set as default environment */
   async function handleSetDefault(item: SparkEnv) {
     if (item.isDefault) {
@@ -71,104 +116,68 @@
         showConfirmButton: false,
         timer: 2000,
       });
-      getSparkEnv();
+      reload();
     }
   }
-
-  /* Get spark environment data */
-  async function getSparkEnv() {
-    sparkEnvs.value = await fetchSparkEnvList();
-  }
-
-  onMounted(() => {
-    getSparkEnv();
-  });
 </script>
 <template>
-  <PageWrapper contentFullHeight>
-    <Card :bordered="false">
-      <BasicTitle>{{ t('spark.home.title') }}</BasicTitle>
-      <div>
-        <a-button
-          type="dashed"
-          style="width: 100%; margin-top: 20px"
-          @click="openFlinkModal(true, {})"
-        >
-          <PlusOutlined />
-          {{ t('common.add') }}
-        </a-button>
-      </div>
-      <List>
-        <List.Item v-for="(item, index) in sparkEnvs" :key="index">
-          <List.Item.Meta
-            style="width: 60%"
-            :title="item.sparkName"
-            :description="item.description"
+  <PageWrapper contentFullHeight fixed-height content-class="flex flex-col">
+    <BasicTable @register="registerTable" class="flex flex-col">
+      <template #form-formFooter>
+        <Col :span="5" :offset="13" class="text-right">
+          <a-button type="primary" @click="openSparkModal(true, {})">
+            <PlusOutlined />
+            {{ t('common.add') }}
+          </a-button>
+        </Col>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.dataIndex === 'flinkName'">
+          <svg-icon class="avatar" name="flink" :size="20" />
+          {{ record.flinkName }}
+        </template>
+        <template v-if="column.dataIndex === 'default'">
+          <Switch
+            :disabled="record.isDefault"
+            @click="handleSetDefault(record)"
+            v-model:checked="record.isDefault"
           >
-            <template #avatar>
-              <SvgIcon class="avatar p-15px" name="spark" size="60" />
+            <template #checkedChildren>
+              <CheckOutlined />
             </template>
-          </List.Item.Meta>
-
-          <div class="list-content flex" style="width: 40%">
-            <div class="list-content-item" style="width: 60%">
-              <span>{{ t('spark.home.title') }}</span>
-              <p style="margin-top: 10px">
-                {{ item.sparkHome }}
-              </p>
-            </div>
-            <div class="list-content-item">
-              <span>Default</span>
-              <p style="margin-top: 10px">
-                <Switch
-                  :disabled="item.isDefault"
-                  @click="handleSetDefault(item)"
-                  v-model:checked="item.isDefault"
-                >
-                  <template #checkedChildren>
-                    <CheckOutlined />
-                  </template>
-                  <template #unCheckedChildren>
-                    <CloseOutlined />
-                  </template>
-                </Switch>
-              </p>
-            </div>
-          </div>
-
-          <template #actions>
-            <Tooltip :title="t('common.edit')">
-              <a-button
-                @click="handleEditSpark(item)"
-                shape="circle"
-                size="large"
-                class="control-button"
-              >
-                <EditOutlined />
-              </a-button>
-            </Tooltip>
-            <Popconfirm
-              :title="t('common.delText')"
-              :cancel-text="t('common.no')"
-              :ok-text="t('common.yes')"
-              @confirm="handleDelete(item)"
-            >
-              <a-button
-                :disabled="item.isDefault && sparkEnvs.length > 1"
-                type="danger"
-                shape="circle"
-                size="large"
-                class="control-button"
-              >
-                <DeleteOutlined />
-              </a-button>
-            </Popconfirm>
-          </template>
-        </List.Item>
-      </List>
-    </Card>
+            <template #unCheckedChildren>
+              <CloseOutlined />
+            </template>
+          </Switch>
+        </template>
+        <template v-if="column.dataIndex === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:note-edit-line',
+                auth: 'project:build',
+                tooltip: t('spark.home.edit'),
+                onClick: handleEditSpark.bind(null, record),
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                tooltip: t('common.delText'),
+                disabled: record.isDefault && getDataSource()?.length > 1,
+                popConfirm: {
+                  title: t('common.delText'),
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
 
-    <SparkEnvModal @register="registerModal" @reload="getSparkEnv" />
+    <SparkEnvModal @register="registerModal" @reload="reload" />
+    <!-- <SparkEnvDrawer @register="registerSparkDraw" /> -->
   </PageWrapper>
 </template>
 <style lang="less"></style>

Reply via email to