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

wuzhiguo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bigtop-manager.git


The following commit(s) were added to refs/heads/main by this push:
     new dd86f7d0 BIGTOP-4379: Add stack/infra page (#194)
dd86f7d0 is described below

commit dd86f7d0148db8e527a63122591118da824a5bd1
Author: pckinghao <[email protected]>
AuthorDate: Thu Mar 27 23:54:47 2025 +0800

    BIGTOP-4379: Add stack/infra page (#194)
---
 bigtop-manager-ui/src/api/command/types.ts         |   7 +-
 bigtop-manager-ui/src/locales/en_US/index.ts       |   4 +-
 .../src/locales/en_US/{index.ts => infra.ts}       |  27 +--
 bigtop-manager-ui/src/locales/zh_CN/index.ts       |   4 +-
 .../src/locales/{en_US/index.ts => zh_CN/infra.ts} |  27 +--
 .../cluster/components/category-chart.vue          |   2 +-
 .../src/pages/cluster-manage/components/index.vue  | 119 +++++++++-
 .../pages/cluster-manage/infrastructures/index.vue | 255 ++++++++++++++++++++-
 bigtop-manager-ui/src/utils/array.ts               |   2 +-
 bigtop-manager-ui/tsconfig.json                    |   3 +-
 10 files changed, 379 insertions(+), 71 deletions(-)

diff --git a/bigtop-manager-ui/src/api/command/types.ts 
b/bigtop-manager-ui/src/api/command/types.ts
index e5e876b4..97313362 100644
--- a/bigtop-manager-ui/src/api/command/types.ts
+++ b/bigtop-manager-ui/src/api/command/types.ts
@@ -66,7 +66,8 @@ export enum Command {
   Restart = 'Restart',
   Start = 'Start',
   Status = 'Status',
-  Stop = 'Stop'
+  Stop = 'Stop',
+  More = 'More'
 }
 
 export enum CommandLevel {
@@ -99,8 +100,8 @@ export interface HostCommandReq {
 }
 
 export interface ServiceCommandReq {
-  componentHosts: ComponentHostReq[]
-  configs: ServiceConfigReq[]
+  componentHosts?: ComponentHostReq[]
+  configs?: ServiceConfigReq[]
   serviceName: string
   [property: string]: any
 }
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts 
b/bigtop-manager-ui/src/locales/en_US/index.ts
index bc44b73b..e91a5f9f 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/locales/en_US/index.ts
@@ -28,6 +28,7 @@ import host from '@/locales/en_US/host.ts'
 import job from '@/locales/en_US/job.ts'
 import overview from '@/locales/en_US/overview'
 import service from '@/locales/en_US/service'
+import infra from '@/locales/en_US/infra.ts'
 
 export default {
   common,
@@ -40,5 +41,6 @@ export default {
   host,
   job,
   overview,
-  service
+  service,
+  infra
 }
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts 
b/bigtop-manager-ui/src/locales/en_US/infra.ts
similarity index 56%
copy from bigtop-manager-ui/src/locales/en_US/index.ts
copy to bigtop-manager-ui/src/locales/en_US/infra.ts
index bc44b73b..6f9f7384 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/locales/en_US/infra.ts
@@ -16,29 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
-import common from '@/locales/en_US/common.ts'
-import menu from '@/locales/en_US/menu.ts'
-import login from '@/locales/en_US/login'
-import user from '@/locales/en_US/user.ts'
-import llmConfig from '@/locales/en_US/llm-config.ts'
-import aiAssistant from '@/locales/en_US/ai-assistant.ts'
-import cluster from '@/locales/en_US/cluster.ts'
-import host from '@/locales/en_US/host.ts'
-import job from '@/locales/en_US/job.ts'
-import overview from '@/locales/en_US/overview'
-import service from '@/locales/en_US/service'
-
 export default {
-  common,
-  menu,
-  login,
-  user,
-  llmConfig,
-  aiAssistant,
-  cluster,
-  host,
-  job,
-  overview,
-  service
+  info: 'Only one instance of a basic service shared across clusters can 
exist',
+  action: 'Add service',
+  restart: 'Need to restart'
 }
diff --git a/bigtop-manager-ui/src/locales/zh_CN/index.ts 
b/bigtop-manager-ui/src/locales/zh_CN/index.ts
index 5aa15877..c2104cc2 100644
--- a/bigtop-manager-ui/src/locales/zh_CN/index.ts
+++ b/bigtop-manager-ui/src/locales/zh_CN/index.ts
@@ -28,6 +28,7 @@ import host from '@/locales/zh_CN/host.ts'
 import job from '@/locales/zh_CN/job.ts'
 import overview from '@/locales/zh_CN/overview.ts'
 import service from '@/locales/zh_CN/service.ts'
+import infra from '@/locales/zh_CN/infra.ts'
 
 export default {
   common,
@@ -40,5 +41,6 @@ export default {
   host,
   job,
   overview,
-  service
+  service,
+  infra
 }
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts 
b/bigtop-manager-ui/src/locales/zh_CN/infra.ts
similarity index 56%
copy from bigtop-manager-ui/src/locales/en_US/index.ts
copy to bigtop-manager-ui/src/locales/zh_CN/infra.ts
index bc44b73b..3ea8333f 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/locales/zh_CN/infra.ts
@@ -16,29 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
-import common from '@/locales/en_US/common.ts'
-import menu from '@/locales/en_US/menu.ts'
-import login from '@/locales/en_US/login'
-import user from '@/locales/en_US/user.ts'
-import llmConfig from '@/locales/en_US/llm-config.ts'
-import aiAssistant from '@/locales/en_US/ai-assistant.ts'
-import cluster from '@/locales/en_US/cluster.ts'
-import host from '@/locales/en_US/host.ts'
-import job from '@/locales/en_US/job.ts'
-import overview from '@/locales/en_US/overview'
-import service from '@/locales/en_US/service'
-
 export default {
-  common,
-  menu,
-  login,
-  user,
-  llmConfig,
-  aiAssistant,
-  cluster,
-  host,
-  job,
-  overview,
-  service
+  info: '跨集群共享的基础服务,只能存在一个实例',
+  action: '添加服务',
+  restart: '需要重启'
 }
diff --git 
a/bigtop-manager-ui/src/pages/cluster-manage/cluster/components/category-chart.vue
 
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/components/category-chart.vue
index c9abfd99..6c07487f 100644
--- 
a/bigtop-manager-ui/src/pages/cluster-manage/cluster/components/category-chart.vue
+++ 
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/components/category-chart.vue
@@ -37,7 +37,7 @@
   const myChart = shallowRef<echarts.ECharts | null>(null)
 
   const generateTimeLabels = () => {
-    const times = []
+    const times = [] as Array<string>
     for (let h = 0; h < 24; h++) {
       for (let m = 0; m < 60; m += 15) {
         const hour = h.toString().padStart(2, '0')
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/components/index.vue 
b/bigtop-manager-ui/src/pages/cluster-manage/components/index.vue
index 4782fe83..07e54bd9 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/components/index.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/components/index.vue
@@ -16,11 +16,124 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
 -->
+<script setup lang="ts">
+  import { ref, reactive, computed, onMounted, watchEffect } from 'vue'
+  import useBaseTable from '@/composables/use-base-table'
+  import type { TableColumnType } from 'ant-design-vue'
+  import { useI18n } from 'vue-i18n'
+  import { useStackStore } from '@/store/stack'
+  import { storeToRefs } from 'pinia'
+  import { ServiceVO } from '@/api/service/types'
 
-<script setup lang="ts"></script>
+  const { t } = useI18n()
+  const stackStore = useStackStore()
+  const { stacks } = storeToRefs(stackStore)
+
+  const store = reactive({
+    loading: false,
+    stackSelected: 'Bigtop',
+    stackGroup: ['Bigtop', 'Infra', 'Extra']
+  })
+  const currentStack = computed(() =>
+    stacks.value.find((stack) => stack.stackName.toUpperCase() === 
store.stackSelected.toUpperCase())
+  )
+  const data = ref<ServiceVO[]>([])
+  const columns = computed((): TableColumnType[] => [
+    {
+      title: '#',
+      width: '48px',
+      key: 'index',
+      customRender: ({ index }) => {
+        return `${index + 1}`
+      }
+    },
+    {
+      title: t('common.name'),
+      dataIndex: 'displayName',
+      width: '20%',
+      ellipsis: true
+    },
+    {
+      title: t('common.version'),
+      dataIndex: 'version',
+      width: '15%',
+      ellipsis: true
+    },
+    {
+      key: 'stack',
+      title: t('common.stack'),
+      dataIndex: 'stack',
+      width: '20%',
+      ellipsis: true
+    },
+    {
+      title: t('common.desc'),
+      dataIndex: 'desc',
+      ellipsis: true
+    }
+  ])
+
+  const { loading, paginationProps, onChange, resetState } = useBaseTable({
+    columns: columns.value,
+    rows: data.value
+  })
+
+  watchEffect(() => {
+    resetState()
+    data.value = currentStack.value?.services || []
+  })
+
+  const handleSetSource = () => {
+    // setSourceRef.value?.handleOpen()
+  }
+
+  onMounted(() => {
+    stackStore.loadStacks()
+  })
+</script>
 
 <template>
-  <div> components</div>
+  <div class="cluster-components">
+    <div>
+      <div class="menu-title">{{ $t('menu.component') }}</div>
+    </div>
+    <div class="cluster-components-header">
+      <a-radio-group v-model:value="store.stackSelected" button-style="solid">
+        <a-radio-button v-for="(stack, idx) in store.stackGroup" :key="idx" 
:value="stack">{{ stack }}</a-radio-button>
+      </a-radio-group>
+      <a-button type="primary" @click="handleSetSource">{{ 
$t('cluster.config_source') }}</a-button>
+    </div>
+    <div>
+      <a-table
+        :loading="loading"
+        :data-source="data"
+        :columns="columns"
+        :pagination="paginationProps"
+        @change="onChange"
+      >
+        <template #bodyCell="{ column }">
+          <template v-if="column.key === 'stack'">
+            <span> {{ `${store.stackSelected}-${currentStack?.stackVersion}` 
}} </span>
+          </template>
+        </template>
+      </a-table>
+    </div>
+  </div>
 </template>
 
-<style scoped></style>
+<style lang="scss" scoped>
+  .cluster-components {
+    padding: 16px;
+    background-color: #fff;
+    .menu-title {
+      font-size: 16px;
+      font-weight: 500;
+      line-height: 24px;
+    }
+    .cluster-components-header {
+      display: flex;
+      justify-content: space-between;
+      margin: $space-md 0;
+    }
+  }
+</style>
diff --git 
a/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/index.vue 
b/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/index.vue
index 2fc14d22..915374f4 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/index.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/index.vue
@@ -17,19 +17,250 @@
   ~ under the License.
 -->
 
-<script setup lang="ts"></script>
+<script setup lang="ts">
+  import { computed, shallowRef, onMounted, toRefs } from 'vue'
+  import { usePngImage } from '@/utils/tools'
+  import { useI18n } from 'vue-i18n'
+  import { CommonStatus, CommonStatusTexts } from '@/enums/state'
+  import { useServiceStore } from '@/store/service'
+  import type { ServiceListParams, ServiceStatusType } from 
'@/api/service/types'
+  import type { GroupItem } from '@/components/common/button-group/types'
+  import type { FilterFormItem } from '@/components/common/filter-form/types'
+  // import { execCommand } from '@/api/command';
+  import type { Command } from '@/api/command/types'
+
+  const { t } = useI18n()
+  const serviceStore = useServiceStore()
+  const { services, loading } = toRefs(serviceStore)
+
+  const statusColors = shallowRef<Record<ServiceStatusType, keyof typeof 
CommonStatusTexts>>({
+    1: 'healthy',
+    2: 'unhealthy',
+    3: 'unknown'
+  })
+  const filterFormItems = computed((): FilterFormItem[] => [
+    { type: 'search', key: 'serviceName', label: t('service.name') },
+    {
+      type: 'status',
+      key: 'restartFlag',
+      label: t('service.required_restart'),
+      options: [
+        { label: t('common.required'), value: 1 },
+        { label: t('common.not_required'), value: 2 }
+      ]
+    },
+    {
+      type: 'status',
+      key: 'status',
+      label: t('common.status'),
+      options: [
+        { label: t(`common.${statusColors.value[1]}`), value: 1 },
+        { label: t(`common.${statusColors.value[2]}`), value: 2 },
+        { label: t(`common.${statusColors.value[3]}`), value: 3 }
+      ]
+    }
+  ])
+  const actionGroups = shallowRef<GroupItem[]>([
+    {
+      action: 'start',
+      icon: 'start',
+      clickEvent: (item, args) => {
+        console.log('item :>> ', item?.action)
+        infraAction('Start', args.name)
+      }
+    },
+    {
+      action: 'stop',
+      icon: 'stop',
+      clickEvent: (item, args) => {
+        console.log('item :>> ', item?.action)
+        infraAction('Stop', args.name)
+      }
+    },
+    {
+      action: 'restart',
+      icon: 'restart',
+      clickEvent: (item, args) => {
+        console.log('item :>> ', item?.action)
+        infraAction('Restart', args.name)
+      }
+    },
+    {
+      action: 'more',
+      icon: 'more_line',
+      clickEvent: (item, args) => {
+        console.log('item :>> ', item?.action)
+        infraAction('More', args.name)
+      }
+    }
+  ])
+
+  const infraAction = async (command: keyof typeof Command, serviceName: 
string) => {
+    console.log(command, serviceName)
+    // await execCommand({
+    //   command: command,
+    //   clusterId: 0,
+    //   commandLevel: "service",
+    //   serviceCommands: [{ serviceName: serviceName }]
+    // })
+  }
+
+  const getServices = async (filters?: ServiceListParams) => {
+    await serviceStore.getServices(0, filters)
+  }
+
+  onMounted(async () => {
+    await getServices()
+  })
+</script>
 
 <template>
-  <div>
-    <a-button
-      @click="
-        () => $router.push({ name: 'CreateInfraService', params: { id: 0, 
cluster: '', creationMode: 'public' } })
-      "
-    >
-      add Service
-    </a-button>
-    <span> infra list </span>
-  </div>
+  <a-spin :spinning="loading" class="service">
+    <div class="infra-header">
+      <div>
+        <div class="menu-title">{{ $t('menu.infra') }}</div>
+        <div class="menu-info">{{ $t('infra.info') }}</div>
+      </div>
+      <a-button
+        type="primary"
+        @click="
+          () => $router.push({ name: 'CreateInfraService', params: { id: 0, 
cluster: '', creationMode: 'public' } })
+        "
+      >
+        {{ $t('infra.action') }}
+      </a-button>
+    </div>
+    <div class="infra-body">
+      <filter-form :filter-items="filterFormItems" @filter="getServices" />
+      <a-empty v-if="services.length == 0" style="width: 100%" />
+      <template v-else>
+        <a-card v-for="item in services" :key="item.name" :hoverable="true" 
class="service-item">
+          <div class="header">
+            <div class="header-base-wrp">
+              <a-avatar v-if="item.name" 
:src="usePngImage(item.name.toLowerCase())" :size="42" class="header-icon" />
+              <div class="header-base-title">
+                <span>{{ `${item.displayName}` }}</span>
+                <span class="small-gray">{{ item.version }}</span>
+              </div>
+              <div class="header-base-status">
+                <a-tag :color="CommonStatus[statusColors[item.status]]">
+                  <div class="header-base-status-inner">
+                    <status-dot 
:color="CommonStatus[statusColors[item.status]]" />
+                    <span class="small">{{ 
$t(`common.${statusColors[item.status]}`) }}</span>
+                  </div>
+                </a-tag>
+              </div>
+            </div>
+            <div class="header-restart-status">
+              <span class="small-gray">{{ `${$t('common.restart')}` }}</span>
+              <status-dot :color="CommonStatus[statusColors[item.status]]" />
+              <span class="small">{{ `${item.restartFlag ? 
$t('common.required') : $t('common.not_required')}` }}</span>
+            </div>
+          </div>
+          <div class="item-content">
+            <button-group :auto="true" :space="0" :args="item" 
:groups="actionGroups">
+              <template #icon="{ item: groupItem }">
+                <svg-icon :name="groupItem.icon || ''" />
+              </template>
+            </button-group>
+          </div>
+        </a-card>
+      </template>
+    </div>
+  </a-spin>
 </template>
 
-<style scoped></style>
+<style scoped lang="scss">
+  .infra-header {
+    padding: 16px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    background-color: #fff;
+    .menu-title {
+      font-size: 16px;
+      font-weight: 500;
+      line-height: 24px;
+    }
+    .menu-info {
+      margin-top: 5px;
+      color: #00000072;
+      font-size: 14px;
+      font-weight: 400;
+      line-height: 22px;
+    }
+  }
+  .small {
+    font-size: 12px;
+  }
+  .small-gray {
+    line-height: 12px;
+    font-size: 12px;
+    color: rgba(0, 0, 0, 0.45);
+  }
+  .infra-body {
+    margin-top: 16px;
+    padding: 16px;
+    background-color: #fff;
+    .service {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 16px;
+    }
+    .service-item {
+      width: 280px;
+      height: 150px;
+      border-radius: 8px;
+      @include flexbox($direction: column);
+      :deep(.ant-card-body) {
+        padding: 0;
+        height: 100%;
+        @include flexbox($direction: column);
+      }
+      .header {
+        padding: $space-md;
+        border-bottom: 1px solid $color-border-secondary;
+        &-base-wrp {
+          @include flexbox($gap: $space-sm);
+          margin-bottom: 10px;
+        }
+        &-base-title {
+          @include flexbox($direction: column);
+        }
+        &-base-status {
+          flex: 1;
+          text-align: end;
+          &-inner {
+            @include flexbox($align: center, $gap: 4px);
+          }
+          .ant-tag {
+            margin-inline-end: 0;
+          }
+        }
+        &-restart-status {
+          @include flexbox($align: center, $gap: 6px);
+          .dot {
+            margin-top: 2px;
+          }
+        }
+        &-icon {
+          flex-shrink: 0;
+          border-radius: 0;
+        }
+        :deep(.ant-avatar) {
+          img {
+            object-fit: contain !important;
+          }
+        }
+      }
+      .item-content {
+        flex: 1;
+        @include flexbox($align: center);
+        padding-inline: 8px;
+      }
+    }
+  }
+  :deep(.ant-dropdown-link) {
+    margin-right: 10px;
+  }
+</style>
diff --git a/bigtop-manager-ui/src/utils/array.ts 
b/bigtop-manager-ui/src/utils/array.ts
index 5f25684e..4cc0a1eb 100644
--- a/bigtop-manager-ui/src/utils/array.ts
+++ b/bigtop-manager-ui/src/utils/array.ts
@@ -33,7 +33,7 @@ const replacePatternInHosts = (rawHostNames: string[]): 
string[] => {
     extra: any,
     allHostNames: any = []
   rawHostNames.forEach(function (rawHostName) {
-    const hostNames = []
+    const hostNames = [] as Array<string>
     start = rawHostName.match(/\[\d*/)
     end = rawHostName.match(/\-\d*]/)
     extra = { 0: '' }
diff --git a/bigtop-manager-ui/tsconfig.json b/bigtop-manager-ui/tsconfig.json
index 741ccfd4..d33b1fa6 100644
--- a/bigtop-manager-ui/tsconfig.json
+++ b/bigtop-manager-ui/tsconfig.json
@@ -23,7 +23,8 @@
     "strict": true,
     "noUnusedLocals": true,
     "noUnusedParameters": true,
-    "noFallthroughCasesInSwitch": true
+    "noFallthroughCasesInSwitch": true,
+    "noImplicitAny": false,
   },
   "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", 
"src/types/**/*.d.ts"],
   "references": [

Reply via email to