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

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


The following commit(s) were added to refs/heads/dev-2.1.5 by this push:
     new b3e43d222 [Improve]: Adjusting the project page layout to a table 
(#4014)
b3e43d222 is described below

commit b3e43d22252a012e0ffc42d312eda4f0ed40d2c0
Author: Kriszu <[email protected]>
AuthorDate: Sun Sep 1 10:54:58 2024 +0800

    [Improve]: Adjusting the project page layout to a table (#4014)
---
 .../src/components/Page/src/PageWrapper.vue        |   5 +-
 .../components/SimpleMenu/src/SimpleSubMenu.vue    |  24 +-
 .../src/components/Table/src/BasicTable.vue        |   7 +-
 .../src/hooks/web/useContentHeight.ts              |  27 +-
 .../src/layouts/default/footer/index.vue           |  12 +-
 .../src/layouts/default/menu/index.vue             |   2 +-
 .../src/layouts/default/sider/LayoutSider.vue      |  31 +-
 .../src/views/flink/app/View.vue                   |  16 +-
 .../views/flink/app/hooks/useAppTableColumns.ts    |  13 +-
 .../src/views/flink/project/View.vue               | 315 ++++++++++++++-------
 .../src/views/flink/variable/View.vue              |  17 +-
 .../src/views/setting/Alarm/index.vue              | 249 ++++++++--------
 .../src/views/setting/ExternalLink/index.vue       |  38 ++-
 .../src/views/setting/FlinkCluster/index.vue       |  42 +--
 .../src/views/setting/FlinkHome/index.vue          |  41 +--
 .../src/views/setting/System/index.vue             |  12 +-
 .../src/views/setting/YarnQueue/index.vue          |   9 +-
 .../src/views/system/member/Member.vue             |  18 +-
 .../src/views/system/menu/Menu.vue                 |   9 +-
 .../src/views/system/role/Role.vue                 |   9 +-
 .../src/views/system/team/Team.vue                 |   9 +-
 .../src/views/system/token/Token.vue               |   4 +-
 .../src/views/system/user/User.vue                 |   9 +-
 23 files changed, 530 insertions(+), 388 deletions(-)

diff --git 
a/streampark-console/streampark-console-webapp/src/components/Page/src/PageWrapper.vue
 
b/streampark-console/streampark-console-webapp/src/components/Page/src/PageWrapper.vue
index 721f1c1ce..6f6993291 100644
--- 
a/streampark-console/streampark-console-webapp/src/components/Page/src/PageWrapper.vue
+++ 
b/streampark-console/streampark-console-webapp/src/components/Page/src/PageWrapper.vue
@@ -167,9 +167,10 @@
 
   .@{prefix-cls} {
     position: relative;
-
+    padding: 16px;
+    padding-bottom: 0;
     .@{prefix-cls}-content {
-      margin: 16px;
+      // margin: 16px;
       border-radius: @border-radius-base;
     }
 
diff --git 
a/streampark-console/streampark-console-webapp/src/components/SimpleMenu/src/SimpleSubMenu.vue
 
b/streampark-console/streampark-console-webapp/src/components/SimpleMenu/src/SimpleSubMenu.vue
index 726a4bd35..ebafe4c0e 100644
--- 
a/streampark-console/streampark-console-webapp/src/components/SimpleMenu/src/SimpleSubMenu.vue
+++ 
b/streampark-console/streampark-console-webapp/src/components/SimpleMenu/src/SimpleSubMenu.vue
@@ -22,18 +22,20 @@
     :collapsedShowTitle="collapsedShowTitle"
   >
     <template #title>
-      <span class="menu-down-svg">
-        <SvgIcon v-if="item.path === '/system'" name="management" size="25" />
-        <SvgIcon v-if="item.path === '/flink'" name="flink3" size="25" />
-        <SvgIcon v-if="item.path === '/setting'" name="settings" size="25" />
-      </span>
-      <div v-if="collapsedShowTitle && getIsCollapseParent" class="mt-2 
collapse-title">
-        {{ getI18nName }}
+      <div class="flex items-center">
+        <span class="menu-down-svg flex items-center">
+          <SvgIcon v-if="item.path === '/system'" name="management" :size="20" 
/>
+          <SvgIcon v-if="item.path === '/flink'" name="flink3" :size="20" />
+          <SvgIcon v-if="item.path === '/setting'" name="settings" :size="20" 
/>
+        </span>
+        <div v-if="collapsedShowTitle && getIsCollapseParent" class="mt-2 
collapse-title">
+          {{ getI18nName }}
+        </div>
+        <span v-show="getShowSubTitle" :class="['ml-2', 
`${prefixCls}-sub-title`]">
+          {{ getI18nName }}
+        </span>
+        <SimpleMenuTag :item="item" :collapseParent="!!collapse && !!parent" />
       </div>
-      <span v-show="getShowSubTitle" :class="['ml-2', 
`${prefixCls}-sub-title`]">
-        {{ getI18nName }}
-      </span>
-      <SimpleMenuTag :item="item" :collapseParent="!!collapse && !!parent" />
     </template>
     <template v-for="childrenItem in item.children || []" 
:key="childrenItem.path">
       <SimpleSubMenu v-bind="$props" :item="childrenItem" :parent="false" />
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 0a78ada66..482fed44d 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"
@@ -397,7 +397,7 @@
     }
 
     &-form-container {
-      padding: 16px;
+      // padding: 16px;
 
       .ant-form {
         padding: 12px 10px 6px;
@@ -412,6 +412,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/hooks/web/useContentHeight.ts
 
b/streampark-console/streampark-console-webapp/src/hooks/web/useContentHeight.ts
index a3411788c..1e0523886 100644
--- 
a/streampark-console/streampark-console-webapp/src/hooks/web/useContentHeight.ts
+++ 
b/streampark-console/streampark-console-webapp/src/hooks/web/useContentHeight.ts
@@ -1,6 +1,7 @@
-import { ComputedRef, isRef, nextTick, Ref, ref, unref, watch } from 'vue';
-import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
-import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
+import type { ComputedRef, Ref } from 'vue';
+import { isRef, nextTick, ref, unref, watch } from 'vue';
+import { onMountedOrActivated } from '../core/onMountedOrActivated';
+import { useWindowSizeFn } from '../event/useWindowSizeFn';
 import { useLayoutHeight } from 
'/@/layouts/default/content/useContentViewHeight';
 import { getViewportOffset } from '/@/utils/domUtils';
 import { isNumber, isString } from '/@/utils/is';
@@ -30,7 +31,7 @@ export function useContentHeight(
   flag: ComputedRef<Boolean>,
   anchorRef: Ref,
   subtractHeightRefs: Ref[],
-  substractSpaceRefs: Ref[],
+  subtractSpaceRefs: Ref[],
   upwardSpace: Ref<Upward> | ComputedRef<Upward> | Upward = 0,
   offsetHeightRef: Ref<number> = ref(0),
 ) {
@@ -55,7 +56,7 @@ export function useContentHeight(
     direction: 'all' | 'top' | 'bottom' = 'all',
   ): number {
     function numberPx(px: string) {
-      return Number(px.replace(/[^\d]/g, ''));
+      return Number(px.replace(/\D/g, ''));
     }
     let subtractHeight = 0;
     const ZERO_PX = '0px';
@@ -101,16 +102,16 @@ export function useContentHeight(
     }
     const { bottomIncludeBody } = getViewportOffset(anchorEl);
 
-    // substract elements height
-    let substractHeight = 0;
+    // subtract elements height
+    let subtractHeight = 0;
     subtractHeightRefs.forEach((item) => {
-      substractHeight += getEl(unref(item))?.offsetHeight ?? 0;
+      subtractHeight += getEl(unref(item))?.offsetHeight ?? 0;
     });
 
     // subtract margins / paddings
-    let substractSpaceHeight = calcSubtractSpace(anchorEl) ?? 0;
-    substractSpaceRefs.forEach((item) => {
-      substractSpaceHeight += calcSubtractSpace(getEl(unref(item)));
+    let subtractSpaceHeight = calcSubtractSpace(anchorEl) ?? 0;
+    subtractSpaceRefs.forEach((item) => {
+      subtractSpaceHeight += calcSubtractSpace(getEl(unref(item)));
     });
 
     // upwardSpace
@@ -145,8 +146,8 @@ export function useContentHeight(
       bottomIncludeBody -
       unref(layoutFooterHeightRef) -
       unref(offsetHeightRef) -
-      substractHeight -
-      substractSpaceHeight -
+      subtractHeight -
+      subtractSpaceHeight -
       upwardSpaceHeight;
 
     // compensation height
diff --git 
a/streampark-console/streampark-console-webapp/src/layouts/default/footer/index.vue
 
b/streampark-console/streampark-console-webapp/src/layouts/default/footer/index.vue
index 5887e01db..45ba848e0 100644
--- 
a/streampark-console/streampark-console-webapp/src/layouts/default/footer/index.vue
+++ 
b/streampark-console/streampark-console-webapp/src/layouts/default/footer/index.vue
@@ -16,13 +16,6 @@
 -->
 <template>
   <Footer :class="prefixCls" v-if="getShowLayoutFooter" ref="footerRef">
-    <div :class="`${prefixCls}__links`">
-      <a @click="openWindow(SITE_URL)">{{ t('layout.footer.website') }}</a>
-
-      <GithubFilled :class="`${prefixCls}__github`" />
-
-      <a @click="openWindow(DOC_URL)">{{ t('layout.footer.onlineDocument') 
}}</a>
-    </div>
     <div :class="`${prefixCls}__copyright`" 
@click="openWindow('https://incubator.apache.org/')">
       Copyright &copy;{{ new Date().getFullYear() }} The Apache Software 
Foundation. Apache
       StreamPark, StreamPark, and its feather logo are trademarks of The 
Apache Software Foundation
@@ -34,8 +27,6 @@
   import { computed, defineComponent, unref, ref } from 'vue';
   import { Layout } from 'ant-design-vue';
 
-  import { GithubFilled } from '@ant-design/icons-vue';
-
   import { DOC_URL, GITHUB_URL, SITE_URL } from '/@/settings/siteSetting';
   import { openWindow } from '/@/utils';
 
@@ -47,7 +38,7 @@
 
   export default defineComponent({
     name: 'LayoutFooter',
-    components: { Footer: Layout.Footer, GithubFilled },
+    components: { Footer: Layout.Footer },
     setup() {
       const { t } = useI18n();
       const { getShowFooter } = useRootSetting();
@@ -89,6 +80,7 @@
   .@{prefix-cls} {
     color: @normal-link-color;
     text-align: center;
+    padding: 10px 50px;
 
     &__links {
       margin-bottom: 8px;
diff --git 
a/streampark-console/streampark-console-webapp/src/layouts/default/menu/index.vue
 
b/streampark-console/streampark-console-webapp/src/layouts/default/menu/index.vue
index 4c94b2356..38f874553 100644
--- 
a/streampark-console/streampark-console-webapp/src/layouts/default/menu/index.vue
+++ 
b/streampark-console/streampark-console-webapp/src/layouts/default/menu/index.vue
@@ -78,7 +78,7 @@
 
       const getWrapperStyle = computed((): CSSProperties => {
         return {
-          height: `calc(100% - ${unref(getIsShowLogo) ? '48px' : '0px'})`,
+          height: `calc(100% - ${unref(getIsShowLogo) ? '100px' : '50px'})`,
         };
       });
 
diff --git 
a/streampark-console/streampark-console-webapp/src/layouts/default/sider/LayoutSider.vue
 
b/streampark-console/streampark-console-webapp/src/layouts/default/sider/LayoutSider.vue
index 9fd435594..3c57237ba 100644
--- 
a/streampark-console/streampark-console-webapp/src/layouts/default/sider/LayoutSider.vue
+++ 
b/streampark-console/streampark-console-webapp/src/layouts/default/sider/LayoutSider.vue
@@ -23,6 +23,23 @@
     </template>
     <LayoutMenu :theme="getMenuTheme" :menuMode="getMode" 
:splitType="getSplitType" />
     <DragBar ref="dragBarRef" />
+    <div
+      class="flex justify-between items-center border-t-1 border-[#c0c0c01a] 
h-50px px-4"
+      v-if="!getCollapsed"
+    >
+      <a @click="openWindow(SITE_URL)" class="text-gray-400 
hover:text-white">{{
+        t('layout.footer.website')
+      }}</a>
+
+      <GithubFilled
+        class="text-14px !text-gray-400 !hover:text-white cursor-pointer"
+        @click="openWindow(GITHUB_URL)"
+      />
+
+      <a @click="openWindow(DOC_URL)" class="text-gray-400 hover:text-white">{{
+        t('layout.footer.onlineDocument')
+      }}</a>
+    </div>
   </Sider>
 </template>
 <script lang="ts">
@@ -31,6 +48,7 @@
   import { Layout } from 'ant-design-vue';
   import LayoutMenu from '../menu/index.vue';
   import LayoutTrigger from '/@/layouts/default/trigger/index.vue';
+  import { version } from '../../../../package.json';
 
   import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
 
@@ -38,14 +56,19 @@
   import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider';
   import { useAppInject } from '/@/hooks/web/useAppInject';
   import { useDesign } from '/@/hooks/web/useDesign';
+  import { openWindow } from '/@/utils';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { DOC_URL, GITHUB_URL, SITE_URL } from '/@/settings/siteSetting';
+  import { GithubFilled } from '@ant-design/icons-vue';
 
   import DragBar from './DragBar.vue';
   export default defineComponent({
     name: 'LayoutSideBar',
-    components: { Sider: Layout.Sider, LayoutMenu, DragBar, LayoutTrigger },
+    components: { Sider: Layout.Sider, LayoutMenu, DragBar, LayoutTrigger, 
GithubFilled },
     setup() {
       const dragBarRef = ref<ElRef>(null);
       const sideRef = ref<ElRef>(null);
+      const { t } = useI18n();
 
       const {
         getCollapsed,
@@ -108,6 +131,8 @@
       const getTrigger = h(LayoutTrigger);
 
       return {
+        t,
+        version,
         prefixCls,
         sideRef,
         dragBarRef,
@@ -127,6 +152,10 @@
         getSplitType,
         getShowTrigger,
         toggleCollapsed,
+        openWindow,
+        DOC_URL,
+        GITHUB_URL,
+        SITE_URL,
       };
     },
   });
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue 
b/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue
index b06c8fb77..aa80de585 100644
--- a/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue
+++ b/streampark-console/streampark-console-webapp/src/views/flink/app/View.vue
@@ -14,16 +14,11 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<script lang="ts">
-  export default defineComponent({
-    name: 'AppView',
-  });
-</script>
 <script lang="ts" setup name="AppView">
-  import { defineComponent, nextTick, ref, onUnmounted, onMounted } from 'vue';
+  import { nextTick, ref, onUnmounted, onMounted } from 'vue';
   import { useAppTableAction } from './hooks/useAppTableAction';
   import { useI18n } from '/@/hooks/web/useI18n';
-  import { AppStateEnum, JobTypeEnum, OptionStateEnum, ReleaseStateEnum } from 
'/@/enums/flinkEnum';
+  import { JobTypeEnum, OptionStateEnum, ReleaseStateEnum } from 
'/@/enums/flinkEnum';
   import { useTimeoutFn } from '@vueuse/core';
   import { Tooltip, Badge, Tag, Popover } from 'ant-design-vue';
   import { fetchAppRecord } from '/@/api/flink/app/app';
@@ -50,6 +45,9 @@
   import { useSavepoint } from './hooks/useSavepoint';
   import { useAppTableColumns } from './hooks/useAppTableColumns';
   import AppTableResize from './components/AppResize.vue';
+  defineOptions({
+    name: 'AppView',
+  });
   const { t } = useI18n();
   const optionApps = {
     starting: new Map(),
@@ -238,13 +236,13 @@
   });
 </script>
 <template>
-  <PageWrapper contentFullHeight>
+  <PageWrapper contentFullHeight content-class="flex flex-col">
     <AppDashboard ref="appDashboardRef" />
     <BasicTable
       @register="registerTable"
       :columns="getAppColumns"
       @resize-column="onTableColumnResize"
-      class="app_list !px-0 pt-20px"
+      class="app_list !px-0 table-searchbar flex-1 pt-20px !px-0 flex flex-col"
       :formConfig="formConfig"
     >
       <template #bodyCell="{ column, record }">
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useAppTableColumns.ts
 
b/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useAppTableColumns.ts
index 6a52840f8..bab251f3e 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useAppTableColumns.ts
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/app/hooks/useAppTableColumns.ts
@@ -53,8 +53,17 @@ export const useAppTableColumns = () => {
       resizable: true,
       width: unref(tableColumnWidth).jobName,
     },
-    { title: t('flink.app.flinkVersion'), dataIndex: 'flinkVersion' },
-    { title: t('flink.app.tags'), ellipsis: true, dataIndex: 'tags', width: 
150 },
+    {
+      title: t('flink.app.flinkVersion'),
+      dataIndex: 'flinkVersion',
+      width: unref(tableColumnWidth).flinkVersion,
+    },
+    {
+      title: t('flink.app.tags'),
+      ellipsis: true,
+      dataIndex: 'tags',
+      width: unref(tableColumnWidth).tags,
+    },
     { title: t('flink.app.owner'), dataIndex: 'nickName', width: 
unref(tableColumnWidth).nickName },
     {
       title: t('flink.app.runStatus'),
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/project/View.vue 
b/streampark-console/streampark-console-webapp/src/views/flink/project/View.vue
index 6c2ebc2e9..ea3e2ca29 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/project/View.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/project/View.vue
@@ -15,74 +15,140 @@
   limitations under the License.
 -->
 <template>
-  <PageWrapper contentFullHeight contentBackground contentClass="px-20px">
+  <PageWrapper contentFullHeight fixed-height content-background 
contentClass="px-20px">
     <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-search
+              v-model:value="queryParams.name"
+              @search="() => reload()"
+              :placeholder="t('flink.project.searchPlaceholder')"
+              class="search-input"
+            />
+          </div>
+          <div class="operate pl-20px bg-white" v-auth="'project:create'">
+            <a-button type="primary" @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 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'">
+          <a-tag
+            v-if="record.refs.startsWith('refs/tags/') > 0"
+            color="#108ee9"
+            style="border-radius: 4px"
+          >
+            {{ record.refs.replace('refs/tags/', '') }}
+          </a-tag>
+          <a-tag v-else color="#2db7f5" style="border-radius: 4px">
+            {{ record.refs.replace('refs/heads/', '') }}
+          </a-tag>
+        </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="processing"
+            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
+            :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,
+                tooltip: t('flink.project.operationTips.buildProjectMessage'),
+                onClick: handleBuild.bind(null, record),
+              },
+              {
+                icon: 'ant-design:edit-outlined',
+                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'),
+                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/flink/project';
+  import { RadioGroup, Radio, Input, Card, Tag, Badge } from 'ant-design-vue';
+  import { buildStateMap } from './project.data';
+  import { buildProject, deleteProject, getList } from '/@/api/flink/project';
   import { ProjectRecord } from '/@/api/flink/project/model/projectModel';
-  import ListItem from './components/ListItem.vue';
-  import Icon from '/@/components/Icon/src/Icon.vue';
+  import Icon, { SvgIcon } from '/@/components/Icon';
   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';
 
   export default defineComponent({
     name: 'ProjectView',
@@ -91,18 +157,21 @@
       ARadioGroup: RadioGroup,
       ARadioButton: Radio.Button,
       AInputSearch: Input.Search,
-      APagination: Pagination,
       ACard: Card,
-      AList: List,
-      ListItem,
-      ASpin: Spin,
+      ATag: Tag,
+      ABadge: Badge,
       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);
@@ -116,49 +185,106 @@
 
       const queryParams = reactive<{ buildState: string; name?: string }>({
         buildState: '',
+        name: '',
       });
 
       let projectDataSource = ref<Array<ProjectRecord>>([]);
+      const projectMap = {
+        [ProjectTypeEnum.FLINK]: 'flink',
+        [ProjectTypeEnum.SPARK]: 'spark',
+      };
 
       function onAdd() {
         go(`/flink/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') 
},
+        ],
+        useSearchForm: false,
+        striped: false,
+        canResize: false,
+        bordered: false,
+        showIndexColumn: false,
+        actionColumn: {
+          width: 200,
+          title: t('component.table.operation'),
+          dataIndex: 'action',
+        },
+      });
+      function handlePageDataReload(polling = false) {
+        nextTick(() => {
+          reload({ polling });
         });
       }
+      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: '/project/edit', query: { id: record.id } });
+      };
+      async function handleDelete(record: ProjectRecord) {
+        try {
+          const { data } = await deleteProject({ id: record.id });
+          if (data.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 },
-      );
+      const { start, stop } = useTimeoutFn(() => {
+        if (!getLoading()) {
+          handlePageDataReload(true);
+        }
+        start();
+      }, 2000);
+
       /* View log */
       function handleViewLog(value: Recordable) {
         openLogModal(true, { project: value });
@@ -167,26 +293,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 +319,12 @@
         projectDataSource,
         loading,
         onAdd,
-        handleSearch,
         registerLogModal,
         handleViewLog,
-        queryData,
-        handlePageChange,
-        handleListItemSuccess,
+        handleBuild,
+        handleEdit,
+        handleDelete,
+        projectMap,
       };
     },
   });
diff --git 
a/streampark-console/streampark-console-webapp/src/views/flink/variable/View.vue
 
b/streampark-console/streampark-console-webapp/src/views/flink/variable/View.vue
index 0fa6a97b2..cfa3b0d36 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/flink/variable/View.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/flink/variable/View.vue
@@ -15,15 +15,14 @@
   limitations under the License.
 -->
 <template>
-  <div>
-    <BasicTable @register="registerTable">
+  <PageWrapper contentFullHeight fixed-height>
+    <BasicTable @register="registerTable" class="flex flex-col">
       <template #toolbar>
         <a-button type="primary" @click="handleCreate" v-auth="'variable:add'">
           <Icon icon="ant-design:plus-outlined" />
           {{ t('common.add') }}
         </a-button>
       </template>
-      <template #resetBefore> 1111 </template>
       <template #bodyCell="{ column, record }">
         <template v-if="column.dataIndex === 'action'">
           <TableAction
@@ -62,16 +61,10 @@
     </BasicTable>
     <VariableDrawer @register="registerDrawer" @success="handleSuccess" />
     <VariableInfo @register="registerInfo" />
-  </div>
+  </PageWrapper>
 </template>
-<script lang="ts">
-  export default defineComponent({
-    name: 'Variable',
-  });
-</script>
 
 <script lang="ts" setup>
-  import { defineComponent } from 'vue';
   import { BasicTable, useTable, TableAction, SorterResult } from 
'/@/components/Table';
   import VariableDrawer from './components/VariableDrawer.vue';
   import VariableInfo from './components/VariableInfo.vue';
@@ -82,7 +75,11 @@
   import { fetchVariableDelete, fetchVariableList } from 
'/@/api/flink/variable';
   import Icon from '/@/components/Icon';
   import { useRouter } from 'vue-router';
+  import { PageWrapper } from '/@/components/Page';
 
+  defineOptions({
+    name: 'Variable',
+  });
   const router = useRouter();
   const [registerDrawer, { openDrawer }] = useDrawer();
   const [registerInfo, { openDrawer: openInfoDraw }] = useDrawer();
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/index.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/Alarm/index.vue
index 74f995316..27ddcd682 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/setting/Alarm/index.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/Alarm/index.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: 'AlertSetting',
-  });
-</script>
 <script setup lang="ts" name="AlertSetting">
   import { onMounted, ref } from 'vue';
   import { List, Popconfirm, Tooltip, Card, Tag } from 'ant-design-vue';
@@ -39,6 +32,9 @@
   import { useMessage } from '/@/hooks/web/useMessage';
   import { AlertTypeInfo, DetailModal, AlertModal } from './components';
   import { PageWrapper } from '/@/components/Page';
+  defineOptions({
+    name: 'AlertSetting',
+  });
   const ListItem = List.Item;
 
   const { t } = useI18n();
@@ -168,127 +164,127 @@
 </script>
 
 <template>
-  <PageWrapper contentFullHeight>
-    <div v-auth="'project:create'" class="bg-white py-10px px-24px">
+  <PageWrapper contentFullHeight fixed-height content-class="flex flex-col">
+    <div class="bg-white py-16px px-24px">
       <span class="alarm-title">{{ t('setting.alarm.alertSetting') }}</span>
-      <a-button
-        type="dashed"
-        style="width: 100%; margin-top: 10px"
-        @click="openAlertModal(true, {})"
-      >
-        <PlusOutlined />
-        {{ t('common.add') }}
-      </a-button>
+      <div v-auth="'project:create'">
+        <a-button type="dashed" class="w-full mt-10px" 
@click="openAlertModal(true, {})">
+          <PlusOutlined />
+          {{ t('common.add') }}
+        </a-button>
+      </div>
     </div>
 
-    <List
-      class="alert-card-list !mt-10px"
-      :grid="{ gutter: 40, xs: 1, sm: 2, md: 4, lg: 4, xl: 4, xxl: 4, xxxl: 4 
}"
-      :data-source="alerts"
-      :pagination="false"
-    >
-      <template #renderItem="{ item }">
-        <ListItem>
-          <Card
-            class="shadow-xl alert-card"
-            :bordered="false"
-            :bodyStyle="{ height: '240px', padding: '15px', overflowY: 'auto' 
}"
-          >
-            <template #title>
-              {{ item.alertName }}
-              <div class="tag-list mt-4px">
-                <Tag
-                  color="blue"
-                  size="small"
-                  v-for="type in item.alertTypeTags"
-                  :key="type"
-                  class="!leading-15px"
-                >
-                  {{ getAlertTypeName(type) }}
-                </Tag>
-              </div>
-            </template>
-            <template #actions>
-              <Tooltip :title="t('setting.alarm.tooltip.test')">
-                <a-button
-                  @click="handleTestAlarm(item)"
-                  shape="circle"
-                  size="large"
-                  style="margin-left: 3px"
-                  class="control-button ctl-btn-color"
-                >
-                  <ThunderboltOutlined />
-                </a-button>
-              </Tooltip>
-              <Tooltip :title="t('setting.alarm.tooltip.detail')">
-                <a-button
-                  @click="openAlertDetailModal(true, item)"
-                  shape="circle"
-                  size="large"
-                  style="margin-left: 3px"
-                  class="control-button ctl-btn-color"
-                >
-                  <EyeOutlined />
-                </a-button>
-              </Tooltip>
-              <Tooltip :title="t('setting.alarm.tooltip.edit')">
-                <a-button
-                  @click="handleEditAlertConf(item)"
-                  shape="circle"
-                  size="large"
-                  style="margin-left: 3px"
-                  class="control-button ctl-btn-color"
-                >
-                  <EditOutlined />
-                </a-button>
-              </Tooltip>
-              <Popconfirm
-                :title="t('setting.alarm.tooltip.delete')"
-                :cancel-text="t('common.no')"
-                :ok-text="t('common.yes')"
-                @confirm="handleDeleteAlertConf(item)"
-              >
-                <a-button
-                  type="danger"
-                  shape="circle"
-                  size="large"
-                  style="margin-left: 3px"
-                  class="control-button"
+    <div class="flex-1">
+      <List
+        class="alert-card-list !mt-10px"
+        :grid="{ gutter: 40, xs: 1, sm: 2, md: 4, lg: 4, xl: 4, xxl: 4, xxxl: 
4 }"
+        :data-source="alerts"
+        :pagination="false"
+      >
+        <template #renderItem="{ item }">
+          <ListItem>
+            <Card
+              class="shadow-xl alert-card"
+              :bordered="false"
+              :bodyStyle="{ height: '240px', padding: '15px', overflowY: 
'auto' }"
+            >
+              <template #title>
+                {{ item.alertName }}
+                <div class="tag-list mt-4px">
+                  <Tag
+                    color="blue"
+                    size="small"
+                    v-for="type in item.alertTypeTags"
+                    :key="type"
+                    class="!leading-15px"
+                  >
+                    {{ getAlertTypeName(type) }}
+                  </Tag>
+                </div>
+              </template>
+              <template #actions>
+                <Tooltip :title="t('setting.alarm.tooltip.test')">
+                  <a-button
+                    @click="handleTestAlarm(item)"
+                    shape="circle"
+                    size="large"
+                    style="margin-left: 3px"
+                    class="control-button ctl-btn-color"
+                  >
+                    <ThunderboltOutlined />
+                  </a-button>
+                </Tooltip>
+                <Tooltip :title="t('setting.alarm.tooltip.detail')">
+                  <a-button
+                    @click="openAlertDetailModal(true, item)"
+                    shape="circle"
+                    size="large"
+                    style="margin-left: 3px"
+                    class="control-button ctl-btn-color"
+                  >
+                    <EyeOutlined />
+                  </a-button>
+                </Tooltip>
+                <Tooltip :title="t('setting.alarm.tooltip.edit')">
+                  <a-button
+                    @click="handleEditAlertConf(item)"
+                    shape="circle"
+                    size="large"
+                    style="margin-left: 3px"
+                    class="control-button ctl-btn-color"
+                  >
+                    <EditOutlined />
+                  </a-button>
+                </Tooltip>
+                <Popconfirm
+                  :title="t('setting.alarm.tooltip.delete')"
+                  :cancel-text="t('common.no')"
+                  :ok-text="t('common.yes')"
+                  @confirm="handleDeleteAlertConf(item)"
                 >
-                  <DeleteOutlined />
-                </a-button>
-              </Popconfirm>
-            </template>
+                  <a-button
+                    type="danger"
+                    shape="circle"
+                    size="large"
+                    style="margin-left: 3px"
+                    class="control-button"
+                  >
+                    <DeleteOutlined />
+                  </a-button>
+                </Popconfirm>
+              </template>
 
-            <AlertTypeInfo
-              :alertType="String(AlertTypeEnum.MAIL)"
-              :alertSource="item"
-              v-if="item.alertTypeTags.includes(String(AlertTypeEnum.MAIL))"
-            />
-            <AlertTypeInfo
-              :alertType="String(AlertTypeEnum.WECOM)"
-              :alertSource="item"
-              v-if="item.alertTypeTags.includes(String(AlertTypeEnum.WECOM))"
-            />
-            <AlertTypeInfo
-              :alertType="String(AlertTypeEnum.DINGTALK)"
-              :alertSource="item"
-              
v-if="item.alertTypeTags.includes(String(AlertTypeEnum.DINGTALK))"
-            />
-            <AlertTypeInfo
-              :alertType="String(AlertTypeEnum.MESSAGE)"
-              :alertSource="item"
-              v-if="item.alertTypeTags.includes(String(AlertTypeEnum.MESSAGE))"
-            />
-            <AlertTypeInfo
-              :alertType="String(AlertTypeEnum.LARK)"
-              :alertSource="item"
-              v-if="item.alertTypeTags.includes(String(AlertTypeEnum.LARK))"
-            />
-          </Card>
-        </ListItem>
-      </template>
-    </List>
+              <AlertTypeInfo
+                :alertType="String(AlertTypeEnum.MAIL)"
+                :alertSource="item"
+                v-if="item.alertTypeTags.includes(String(AlertTypeEnum.MAIL))"
+              />
+              <AlertTypeInfo
+                :alertType="String(AlertTypeEnum.WECOM)"
+                :alertSource="item"
+                v-if="item.alertTypeTags.includes(String(AlertTypeEnum.WECOM))"
+              />
+              <AlertTypeInfo
+                :alertType="String(AlertTypeEnum.DINGTALK)"
+                :alertSource="item"
+                
v-if="item.alertTypeTags.includes(String(AlertTypeEnum.DINGTALK))"
+              />
+              <AlertTypeInfo
+                :alertType="String(AlertTypeEnum.MESSAGE)"
+                :alertSource="item"
+                
v-if="item.alertTypeTags.includes(String(AlertTypeEnum.MESSAGE))"
+              />
+              <AlertTypeInfo
+                :alertType="String(AlertTypeEnum.LARK)"
+                :alertSource="item"
+                v-if="item.alertTypeTags.includes(String(AlertTypeEnum.LARK))"
+              />
+            </Card>
+          </ListItem>
+        </template>
+      </List>
+    </div>
     <AlertModal @register="registerAlertModal" @reload="getAlertSetting" 
width="850px" />
     <DetailModal @register="registerAlertDetailModal" width="850px" />
   </PageWrapper>
@@ -302,17 +298,14 @@
 
   .alarm-title {
     background-color: @background-color-base;
-    height: 100%;
     font-size: 14px;
     display: table;
     font-weight: normal;
-    margin: 14px 0;
     padding: 8px 12px;
   }
 
   .alert-card-list {
-    .ant-list-empty-text {
-      background-color: @component-background;
-    }
+    background-color: @component-background;
+    height: 100%;
   }
 </style>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/ExternalLink/index.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/ExternalLink/index.vue
index 464945269..5e3ef2078 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/setting/ExternalLink/index.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/ExternalLink/index.vue
@@ -14,28 +14,22 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-
-<script lang="ts">
-  import { defineComponent } from 'vue';
-
+<script lang="ts" setup name="ExternalLinkSetting">
   import { useI18n } from '/@/hooks/web/useI18n';
   import { ExternalLink } from '/@/api/flink/setting/types/externalLink.type';
   import { useMessage } from '/@/hooks/web/useMessage';
-
-  export default defineComponent({
-    name: 'ExternalLinkSetting',
-  });
-</script>
-<script lang="ts" setup name="ExternalLinkSetting">
   import { onMounted, ref } from 'vue';
   import { PlusOutlined } from '@ant-design/icons-vue';
   import { ColumnsType } from 'ant-design-vue/lib/table';
   import { useModal } from '/@/components/Modal';
   import { ExternalLinkModal, ExternalLinkBadge } from './components';
   import { PageWrapper } from '/@/components/Page';
-  import { Table, Popconfirm, Card } from 'ant-design-vue';
+  import { Table, Popconfirm } from 'ant-design-vue';
   import { fetchExternalLink, fetchExternalLinkDelete } from 
'/@/api/flink/setting/externalLink';
   import { BasicTitle } from '/@/components/Basic';
+  defineOptions({
+    name: 'ExternalLinkSetting',
+  });
   const [registerLinkModal, { openModal: openLinkModal }] = useModal();
   const { Swal } = useMessage();
   const { t } = useI18n();
@@ -87,19 +81,19 @@
   const APopconfirm = Popconfirm;
 </script>
 <template>
-  <PageWrapper contentFullHeight>
-    <Card :bordered="false">
-      <BasicTitle>{{ t('setting.externalLink.externalLinkSetting') 
}}</BasicTitle>
-      <div v-auth="'externalLink:create'" style="margin-bottom: 20px">
-        <a-button
-          type="dashed"
-          style="width: 100%; margin-top: 20px"
-          @click="openLinkModal(true, {})"
-        >
-          <plus-outlined />
+  <PageWrapper contentFullHeight fixed-height content-class="flex flex-col">
+    <div class="bg-white py-16px px-24px">
+      <BasicTitle class="!inline-block" style="margin: 0 !important; height: 
initial">
+        {{ t('setting.externalLink.externalLinkSetting') }}
+      </BasicTitle>
+      <div v-auth="'externalLink:create'">
+        <a-button type="dashed" class="w-full mt-10px" 
@click="openLinkModal(true, {})">
+          <PlusOutlined />
           {{ t('common.add') }}
         </a-button>
       </div>
+    </div>
+    <div class="flex-1 mt-10px bg-white">
       <a-table
         :showHeader="false"
         :data-source="externalLinks"
@@ -135,7 +129,7 @@
           </template>
         </template>
       </a-table>
-    </Card>
+    </div>
     <ExternalLinkModal @register="registerLinkModal" width="850px" 
@reload="getExternalLink" />
   </PageWrapper>
 </template>
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/FlinkCluster/index.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/FlinkCluster/index.vue
index a27d0be49..360132b98 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/setting/FlinkCluster/index.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/FlinkCluster/index.vue
@@ -14,18 +14,12 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<script lang="ts">
-  import { defineComponent, onUnmounted } from 'vue';
-  import { useTimeoutFn } from '@vueuse/core';
-
-  export default defineComponent({
-    name: 'FlinkClusterSetting',
-  });
-</script>
 <script lang="ts" setup name="FlinkClusterSetting">
+  import { onUnmounted } from 'vue';
+  import { useTimeoutFn } from '@vueuse/core';
   import { onMounted, ref } from 'vue';
   import { SvgIcon } from '/@/components/Icon';
-  import { List, Popconfirm, Tooltip, Card, Tag } from 'ant-design-vue';
+  import { List, Popconfirm, Tooltip, Tag } from 'ant-design-vue';
   import { ClusterStateEnum, ExecModeEnum } from '/@/enums/flinkEnum';
   import {
     PauseCircleOutlined,
@@ -47,7 +41,9 @@
   import { useI18n } from '/@/hooks/web/useI18n';
   import { PageWrapper } from '/@/components/Page';
   import { BasicTitle } from '/@/components/Basic';
-
+  defineOptions({
+    name: 'FlinkClusterSetting',
+  });
   const ListItem = List.Item;
   const ListItemMeta = ListItem.Meta;
 
@@ -129,20 +125,20 @@
   });
 </script>
 <template>
-  <PageWrapper contentFullHeight>
-    <Card :bordered="false">
-      <BasicTitle>{{ t('setting.flinkCluster.title') }}</BasicTitle>
+  <PageWrapper contentFullHeight fixed-height content-class="flex flex-col">
+    <div class="bg-white py-16px px-24px">
+      <BasicTitle class="!inline-block" style="margin: 0 !important; height: 
initial">
+        {{ t('setting.flinkCluster.title') }}
+      </BasicTitle>
       <div v-auth="'project:create'">
-        <a-button
-          type="dashed"
-          style="width: 100%; margin-top: 20px"
-          @click="go('/setting/add_cluster')"
-        >
+        <a-button type="dashed" class="w-full mt-10px" 
@click="go('/setting/add_cluster')">
           <PlusOutlined />
           {{ t('common.add') }}
         </a-button>
       </div>
-      <List>
+    </div>
+    <div class="flex-1">
+      <List class="cluster-card-list !mt-10px">
         <ListItem v-for="(item, index) in clusters" :key="index">
           <ListItemMeta
             :title="item.clusterName"
@@ -259,6 +255,12 @@
           </template>
         </ListItem>
       </List>
-    </Card>
+    </div>
   </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/setting/FlinkHome/index.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/FlinkHome/index.vue
index 6715dd307..16981252c 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/setting/FlinkHome/index.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/FlinkHome/index.vue
@@ -14,18 +14,12 @@
   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 { useModal } from '/@/components/Modal';
+  import { useI18n } from '/@/hooks/web/useI18n';
   import { SvgIcon } from '/@/components/Icon';
-  import { List, Switch, Card, Popconfirm, Tooltip } from 'ant-design-vue';
+  import { List, Switch, Popconfirm, Tooltip } from 'ant-design-vue';
   import {
     CheckOutlined,
     CloseOutlined,
@@ -47,7 +41,9 @@
   import { useDrawer } from '/@/components/Drawer';
   import { PageWrapper } from '/@/components/Page';
   import { BasicTitle } from '/@/components/Basic';
-
+  defineOptions({
+    name: 'FlinkEnvSetting',
+  });
   const ListItem = List.Item;
   const ListItemMeta = ListItem.Meta;
 
@@ -110,20 +106,20 @@
   });
 </script>
 <template>
-  <PageWrapper contentFullHeight>
-    <Card :bordered="false">
-      <BasicTitle>{{ t('setting.flinkHome.title') }}</BasicTitle>
+  <PageWrapper contentFullHeight fixed-height content-class="flex flex-col">
+    <div class="bg-white py-16px px-24px">
+      <BasicTitle class="!inline-block" style="margin: 0 !important; height: 
initial">
+        {{ t('setting.flinkHome.title') }}
+      </BasicTitle>
       <div v-auth="'project:create'">
-        <a-button
-          type="dashed"
-          style="width: 100%; margin-top: 20px"
-          @click="openFlinkModal(true, {})"
-        >
+        <a-button type="dashed" class="w-full mt-10px" 
@click="openFlinkModal(true, {})">
           <PlusOutlined />
           {{ t('common.add') }}
         </a-button>
       </div>
-      <List>
+    </div>
+    <div class="flex-1">
+      <List class="home-card-list !mt-10px">
         <ListItem v-for="(item, index) in flinks" :key="index">
           <ListItemMeta style="width: 60%" :title="item.flinkName" 
:description="item.description">
             <template #avatar>
@@ -198,10 +194,15 @@
           </template>
         </ListItem>
       </List>
-    </Card>
+    </div>
 
     <FlinkEnvModal @register="registerModal" @reload="getFlinkSetting" />
     <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/setting/System/index.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/System/index.vue
index 82bb8270a..849714afb 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/setting/System/index.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/System/index.vue
@@ -14,12 +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: 'SystemSetting',
-  });
-</script>
 <script setup lang="ts" name="SystemSetting">
   import { Collapse, Card } from 'ant-design-vue';
   import { ref, onMounted, computed } from 'vue';
@@ -30,7 +24,9 @@
   import { useI18n } from '/@/hooks/web/useI18n';
   import SettingList from './SettingList.vue';
   import { PageWrapper } from '/@/components/Page';
-
+  defineOptions({
+    name: 'SystemSetting',
+  });
   const { t } = useI18n();
   const CollapsePane = Collapse.Panel;
   const { createMessage } = useMessage();
@@ -111,7 +107,7 @@
 <style lang="less">
   .system-setting {
     .ant-card-body {
-      padding: 0 24px;
+      padding: 16px 24px;
     }
 
     .streampark-basic-title {
diff --git 
a/streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/index.vue
 
b/streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/index.vue
index 1eefbf5bb..0a2b123b7 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/index.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/setting/YarnQueue/index.vue
@@ -15,8 +15,8 @@
   limitations under the License.
 -->
 <template>
-  <div>
-    <BasicTable @register="registerTable">
+  <PageWrapper content-full-height fixed-height>
+    <BasicTable @register="registerTable" class="flex flex-col">
       <template #toolbar>
         <a-button type="primary" @click="handleCreate" 
v-auth="'yarnQueue:create'">
           <Icon icon="ant-design:plus-outlined" />
@@ -53,7 +53,7 @@
       @register="registerDrawer"
       @success="handleSuccess"
     />
-  </div>
+  </PageWrapper>
 </template>
 <script lang="ts">
   import { defineComponent } from 'vue';
@@ -66,10 +66,11 @@
   import { fetchYarnQueueList, fetchYarnQueueDelete } from 
'/@/api/flink/setting/yarnQueue';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useI18n } from '/@/hooks/web/useI18n';
+  import { PageWrapper } from '/@/components/Page';
 
   export default defineComponent({
     name: 'YarnQueue',
-    components: { BasicTable, YarnQueueDrawer, TableAction, Icon },
+    components: { BasicTable, YarnQueueDrawer, TableAction, Icon, PageWrapper 
},
     setup() {
       const [registerDrawer, { openDrawer }] = useDrawer();
       const { createMessage } = useMessage();
diff --git 
a/streampark-console/streampark-console-webapp/src/views/system/member/Member.vue
 
b/streampark-console/streampark-console-webapp/src/views/system/member/Member.vue
index bf9e305a6..7503498c7 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/system/member/Member.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/system/member/Member.vue
@@ -16,8 +16,8 @@
 -->
 
 <template>
-  <div>
-    <BasicTable @register="registerTable" :formConfig="formConfig">
+  <PageWrapper content-full-height fixed-height>
+    <BasicTable @register="registerTable" :formConfig="formConfig" class="flex 
flex-col">
       <template #toolbar>
         <a-button type="primary" @click="handleCreate" v-auth="'member:add'">
           <Icon icon="ant-design:plus-outlined" />
@@ -55,15 +55,8 @@
       :roleOptions="roleListOptions"
       okText="Submit"
     />
-  </div>
+  </PageWrapper>
 </template>
-<script lang="ts">
-  import { defineComponent } from 'vue';
-
-  export default defineComponent({
-    name: 'Member',
-  });
-</script>
 
 <script setup lang="ts" name="member">
   import { computed, onMounted, ref, unref } from 'vue';
@@ -78,7 +71,10 @@
   import { getRoleListByPage } from '/@/api/base/system';
   import { fetchMemberDelete, fetchMemberList } from '/@/api/system/member';
   import Icon from '/@/components/Icon';
-
+  import { PageWrapper } from '/@/components/Page';
+  defineOptions({
+    name: 'Member',
+  });
   const roleListOptions = ref<Array<Partial<RoleListItem>>>([]);
 
   const [registerDrawer, { openDrawer }] = useDrawer();
diff --git 
a/streampark-console/streampark-console-webapp/src/views/system/menu/Menu.vue 
b/streampark-console/streampark-console-webapp/src/views/system/menu/Menu.vue
index b70294f7d..1eb138d16 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/system/menu/Menu.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/system/menu/Menu.vue
@@ -15,14 +15,14 @@
   limitations under the License.
 -->
 <template>
-  <div>
-    <BasicTable @register="registerTable" @fetch-success="onFetchSuccess" />
+  <PageWrapper content-full-height fixed-height>
+    <BasicTable @register="registerTable" @fetch-success="onFetchSuccess" 
class="flex flex-col" />
     <MenuDrawer
       :okText="t('common.submitText')"
       @register="registerDrawer"
       @success="handleSuccess"
     />
-  </div>
+  </PageWrapper>
 </template>
 <script lang="ts">
   import { defineComponent, nextTick } from 'vue';
@@ -37,10 +37,11 @@
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useI18n } from '/@/hooks/web/useI18n';
   import { isArray } from '/@/utils/is';
+  import { PageWrapper } from '/@/components/Page';
 
   export default defineComponent({
     name: 'MenuManagement',
-    components: { BasicTable, MenuDrawer },
+    components: { BasicTable, MenuDrawer, PageWrapper },
     setup() {
       const [registerDrawer, { openDrawer }] = useDrawer();
       const { createMessage } = useMessage();
diff --git 
a/streampark-console/streampark-console-webapp/src/views/system/role/Role.vue 
b/streampark-console/streampark-console-webapp/src/views/system/role/Role.vue
index 8c7d34cc1..ad1fbbd08 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/system/role/Role.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/system/role/Role.vue
@@ -15,8 +15,8 @@
   limitations under the License.
 -->
 <template>
-  <div>
-    <BasicTable @register="registerTable">
+  <PageWrapper content-full-height fixed-height>
+    <BasicTable @register="registerTable" class="flex flex-col">
       <template #toolbar>
         <a-button type="primary" @click="handleCreate" v-auth="'role:add'">
           <Icon icon="ant-design:plus-outlined" />
@@ -62,7 +62,7 @@
       @success="handleSuccess"
     />
     <RoleInfo @register="registerInfo" />
-  </div>
+  </PageWrapper>
 </template>
 
 <script lang="ts">
@@ -83,10 +83,11 @@
   import { RoleListRecord } from '/@/api/system/model/roleModel';
   import { useI18n } from '/@/hooks/web/useI18n';
   import Icon from '/@/components/Icon';
+  import { PageWrapper } from '/@/components/Page';
 
   export default defineComponent({
     name: 'RoleManagement',
-    components: { BasicTable, RoleInfo, RoleDrawer, TableAction, Icon },
+    components: { BasicTable, RoleInfo, RoleDrawer, TableAction, Icon, 
PageWrapper },
     setup() {
       const { t } = useI18n();
       const [registerDrawer, { openDrawer }] = useDrawer();
diff --git 
a/streampark-console/streampark-console-webapp/src/views/system/team/Team.vue 
b/streampark-console/streampark-console-webapp/src/views/system/team/Team.vue
index 49509c870..3afb94853 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/system/team/Team.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/system/team/Team.vue
@@ -15,8 +15,8 @@
   limitations under the License.
 -->
 <template>
-  <div>
-    <BasicTable @register="registerTable">
+  <PageWrapper content-full-height fixed-height>
+    <BasicTable @register="registerTable" class="flex flex-col">
       <template #toolbar>
         <a-button type="primary" @click="handleCreate" v-auth="'team:add'">
           <Icon icon="ant-design:plus-outlined" />
@@ -49,7 +49,7 @@
       </template>
     </BasicTable>
     <TeamDrawer okText="Submit" @register="registerDrawer" 
@success="handleSuccess" />
-  </div>
+  </PageWrapper>
 </template>
 <script lang="ts">
   import { defineComponent } from 'vue';
@@ -62,9 +62,10 @@
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useI18n } from '/@/hooks/web/useI18n';
   import Icon from '/@/components/Icon';
+  import { PageWrapper } from '/@/components/Page';
   export default defineComponent({
     name: 'Team',
-    components: { BasicTable, TeamDrawer, TableAction, Icon },
+    components: { BasicTable, TeamDrawer, TableAction, Icon, PageWrapper },
     setup() {
       const [registerDrawer, { openDrawer }] = useDrawer();
       const { createMessage } = useMessage();
diff --git 
a/streampark-console/streampark-console-webapp/src/views/system/token/Token.vue 
b/streampark-console/streampark-console-webapp/src/views/system/token/Token.vue
index 0e2b93f78..0da2360f7 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/system/token/Token.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/system/token/Token.vue
@@ -15,8 +15,8 @@
   limitations under the License.
 -->
 <template>
-  <PageWrapper>
-    <BasicTable @register="registerTable">
+  <PageWrapper content-full-height fixed-height>
+    <BasicTable @register="registerTable" class="flex flex-col">
       <template #toolbar>
         <a-button type="primary" @click="handleCreate" v-auth="'token:add'">
           <Icon icon="ant-design:plus-outlined" />
diff --git 
a/streampark-console/streampark-console-webapp/src/views/system/user/User.vue 
b/streampark-console/streampark-console-webapp/src/views/system/user/User.vue
index 1d9d34a8f..763b04d64 100644
--- 
a/streampark-console/streampark-console-webapp/src/views/system/user/User.vue
+++ 
b/streampark-console/streampark-console-webapp/src/views/system/user/User.vue
@@ -15,8 +15,8 @@
   limitations under the License.
 -->
 <template>
-  <div>
-    <BasicTable @register="registerTable">
+  <PageWrapper content-full-height fixed-height>
+    <BasicTable @register="registerTable" class="flex flex-col">
       <template #toolbar>
         <a-button type="primary" @click="handleCreate" v-auth="'user:add'">
           <Icon icon="ant-design:plus-outlined" />
@@ -31,7 +31,7 @@
     </BasicTable>
     <UserDrawer @register="registerDrawer" @success="handleSuccess" />
     <UserModal @register="registerModal" />
-  </div>
+  </PageWrapper>
 </template>
 <script lang="ts">
   import { computed, defineComponent } from 'vue';
@@ -50,10 +50,11 @@
   import { useI18n } from '/@/hooks/web/useI18n';
   import Icon from '/@/components/Icon';
   import { LoginTypeEnum } from '/@/views/base/login/useLogin';
+  import { PageWrapper } from '/@/components/Page';
 
   export default defineComponent({
     name: 'User',
-    components: { BasicTable, UserModal, UserDrawer, TableAction, Icon },
+    components: { BasicTable, UserModal, UserDrawer, TableAction, Icon, 
PageWrapper },
     setup() {
       const { t } = useI18n();
       const userStore = useUserStoreWithOut();

Reply via email to