diff --git a/bigtop-manager-ui/README.md b/bigtop-manager-ui/README.md
index c93033201..70f775d2d 100644
--- a/bigtop-manager-ui/README.md
+++ b/bigtop-manager-ui/README.md
@@ -15,43 +15,53 @@
limitations under the License.
--->
-# Bigtop-Manager-ui
+# Bigtop Manager UI
-Bigtop-Manager-ui is the front-end UI of the Manager platform, which stores code about the interaction between the platform and users, interface display, control styles, etc.
+Bigtop Manager UI is the front-end UI of the Manager platform, which stores code about the interaction between the platform and users, interface display, control styles, etc.
## Prerequisites
-Vite: Version 4.4.5
+- Node.js:`v18.17.0`
-Typescript: Version 5.0.2
+- pnpm: `v8.6.9`
-Vue: Version 3.3.4
+- Vite:`v5.4.19`
-Editor: VsCode
+- Typescript:`v5.8.3`
+
+- Vue:`v3.4.37`
+
+- Editor Recommendation: [Visual Studio Code](https://code.visualstudio.com/)
## Project Structure
-```
-—————————————————— public static resources
-—————————————————— src project source code
-——————————— api calls the backend interface
-——————————— assets static assets
-——————————— components customize components
-——————————— composables composable
-——————————— directives customize directive
-——————————— layouts overall page layout
-——————————— locales internationalization
-——————————— pages components
-——————————— plugins global registration configuration
-——————————— router router
-——————————— store global persistence
-——————————— styles style file
-——————————— types data type
-——————————— utils utility function
-——————————— App.vue project root component
-——————————— main.ts Project packaging portal
-—————————————————— index.html Project page
-—————————————————— package.json Package management profile for your project
-—————————————————— vite.config.ts Project configuration file
-—————————————————— postcss.config.ts postcss configuration file
+```plaintext
+├── public/ # Static assets
+├── src/ # Source code root
+│ ├── api/ # API request functions
+│ ├── assets/ # Static assets
+│ ├── components/ # Reusable component directory
+│ │ ├── base/ # Basic UI components
+│ │ ├── common/ # Common composite components
+│ ├── composables/ # Vue composables
+│ ├── directives/ # Custom directives
+│ ├── features/ # Feature-based components
+│ ├── layouts/ # Layout components
+│ ├── locales/ # locale files (i18n)
+│ ├── pages/ # Page components
+│ ├── plugins/ # Plugins and global registrations
+│ ├── router/ # Routing configuration
+│ ├── store/ # Global state management
+│ ├── styles/ # Global styles
+│ ├── types/ # Global TypeScript declarations (.d.ts)
+│ ├── utils/ # Utility functions
+│ ├── App.vue # Root component
+│ └── main.ts # Project entry point
+├── tests/ # Unit and integration tests
+├── index.html # HTML template
+├── package.json # Project metadata and dependencies
+├── tsconfig.json # TypeScript config file
+├── vite.config.ts # Vite config
+├── postcss.config.ts # PostCSS config
+└── vitest.config.ts # Vitest config
```
diff --git a/bigtop-manager-ui/README.zh.md b/bigtop-manager-ui/README.zh.md
index 241ca73e2..cac951494 100644
--- a/bigtop-manager-ui/README.zh.md
+++ b/bigtop-manager-ui/README.zh.md
@@ -15,43 +15,53 @@
limitations under the License.
--->
-# Bigtop-Manager-ui
+# Bigtop Manager UI
-Bigtop-Manager-ui是Manager平台的前端UI,存放有关平台与用户交互,界面展示,控件样式等代码。
+Bigtop Manager UI 是 Manager 平台的前端 UI,存放有关平台与用户交互,界面展示,控件样式等代码。
## 先决条件
-Vite:版本4.4.5
+- Node.js:`v18.17.0`
-Typescript:版本5.0.2
+- pnpm: `v8.6.9`
-Vue:版本3.3.4
+- Vite:`v5.4.19`
-编辑器:VsCode
+- Typescript:`v5.8.3`
+
+- Vue:`v3.4.37`
+
+- 推荐编辑器:[Visual Studio Code](https://code.visualstudio.com/)
## 项目结构
-```
-——————————————————public 公共静态资源
-——————————————————src 项目源代码
-——————————— api 调用后端接口
-——————————— assets 静态资源
-——————————— components 自定义组件
-——————————— composables 组合式函数
-——————————— directives 自定义指令
-——————————— layouts 整体页面布局
-——————————— locales 国际化
-——————————— pages 组件
-——————————— plugins 全局注册配置
-——————————— router 路由
-——————————— store 全局持久化
-——————————— styles 样式文件
-——————————— types 数据类型
-——————————— utils 工具函数
-——————————— App.vue 项目根组件
-——————————— main.ts 项目打包入口
-—————————————————— index.html 项目页面
-—————————————————— package.json 项目的包管理配置文件
-—————————————————— vite.config.ts 项目配置文件
-—————————————————— postcss.config.ts postcss 配置文件
+```plaintext
+├── public/ # 公共静态资源
+├── src/ # 项目源代码目录
+│ ├── api/ # 后端接口封装
+│ ├── assets/ # 静态资源
+│ ├── components/ # 可复用组件目录
+│ │ ├── base/ # 基础组件
+│ │ ├── common/ # 常用复合组件
+│ ├── composables/ # Vue 组合式函数
+│ ├── directives/ # 自定义指令
+│ ├── features/ # 业务组件
+│ ├── layouts/ # 页面布局组件
+│ ├── locales/ # 国际化资源(i18n)
+│ ├── pages/ # 页面级组件
+│ ├── plugins/ # 插件与全局注册逻辑
+│ ├── router/ # 路由配置
+│ ├── store/ # 全局状态管理
+│ ├── styles/ # 全局样式
+│ ├── types/ # 全局类型声明(.d.ts)
+│ ├── utils/ # 工具函数
+│ ├── App.vue # 根组件
+│ └── main.ts # 项目入口文件
+├── tests/ # 单元测试或集成测试用例
+├── index.html # HTML 模板文件
+├── package.json # 包管理与脚本配置文件
+├── tsconfig.json # TypeScript 配置文件
+├── vite.config.ts # Vite 配置文件
+├── postcss.config.ts # PostCSS 配置
+└── vitest.config.ts # Vitest 测试配置文件
```
diff --git a/bigtop-manager-ui/src/App.vue b/bigtop-manager-ui/src/App.vue
index df598c764..091cd6a4e 100644
--- a/bigtop-manager-ui/src/App.vue
+++ b/bigtop-manager-ui/src/App.vue
@@ -18,10 +18,9 @@
-->
diff --git a/bigtop-manager-ui/src/components/charts/category-chart.vue b/bigtop-manager-ui/src/features/metric/category-chart.vue
similarity index 98%
rename from bigtop-manager-ui/src/components/charts/category-chart.vue
rename to bigtop-manager-ui/src/features/metric/category-chart.vue
index c8035c754..3ed6cdbfa 100644
--- a/bigtop-manager-ui/src/components/charts/category-chart.vue
+++ b/bigtop-manager-ui/src/features/metric/category-chart.vue
@@ -19,7 +19,6 @@
diff --git a/bigtop-manager-ui/src/components/service-management/components.vue b/bigtop-manager-ui/src/features/service-management/components.vue
similarity index 93%
rename from bigtop-manager-ui/src/components/service-management/components.vue
rename to bigtop-manager-ui/src/features/service-management/components.vue
index 8f846ccfc..2f8bcfa7c 100644
--- a/bigtop-manager-ui/src/components/service-management/components.vue
+++ b/bigtop-manager-ui/src/features/service-management/components.vue
@@ -18,16 +18,11 @@
-->
![]()
-
{{ $t('cluster.cluster_unavailable_message') }}
+
{{ t('cluster.cluster_unavailable_message') }}
$router.push({ name: 'CreateCluster' })">
- {{ $t('menu.create') }}
+ {{ t('menu.create') }}
diff --git a/bigtop-manager-ui/src/layouts/header.vue b/bigtop-manager-ui/src/layouts/header.vue
index 7af58b489..aac8e11fe 100644
--- a/bigtop-manager-ui/src/layouts/header.vue
+++ b/bigtop-manager-ui/src/layouts/header.vue
@@ -18,16 +18,15 @@
-->
-
+
- {{ $t('common.back_home') }}
+ {{ t('common.back_home') }}
diff --git a/bigtop-manager-ui/src/pages/login/index.vue b/bigtop-manager-ui/src/pages/login/index.vue
index 7c87db83a..fa7f76448 100644
--- a/bigtop-manager-ui/src/pages/login/index.vue
+++ b/bigtop-manager-ui/src/pages/login/index.vue
@@ -18,18 +18,15 @@
-->
-
+
- {{ $t('common.edit') }}
+ {{ t('common.edit') }}
-
+
{{ userVO?.username }}
-
+
{{ userVO?.nickname }}
-
+
{{ userVO?.createTime }}
-
+
{{ userVO?.updateTime }}
-
+
{{ userVO?.status }}
@@ -97,7 +94,7 @@
@@ -119,7 +116,7 @@
- {{ $t('common.cancel') }}
+ {{ t('common.cancel') }}
- {{ $t('common.submit') }}
+ {{ t('common.submit') }}
diff --git a/bigtop-manager-ui/src/plugins/index.ts b/bigtop-manager-ui/src/plugins/index.ts
index b81d6464c..81df8ec17 100644
--- a/bigtop-manager-ui/src/plugins/index.ts
+++ b/bigtop-manager-ui/src/plugins/index.ts
@@ -21,8 +21,8 @@ import type { App } from 'vue'
import router from '@/router'
import pinia from '@/store'
import i18n from '@/locales'
-import Antd, { message } from 'ant-design-vue'
-import components from '@/components/common'
+import { message } from 'ant-design-vue'
+import components from '@/components'
import directives from '@/directives'
import VueDOMPurifyHTML from 'vue-dompurify-html'
@@ -33,7 +33,6 @@ interface PluginOptions {
export default {
install(app: App, options: PluginOptions) {
app.use(pinia)
- app.use(Antd)
app.use(router)
app.use(i18n)
app.use(directives)
diff --git a/bigtop-manager-ui/src/router/routes/modules/clusters.ts b/bigtop-manager-ui/src/router/routes/modules/clusters.ts
index 1863d4121..0f9c4432a 100644
--- a/bigtop-manager-ui/src/router/routes/modules/clusters.ts
+++ b/bigtop-manager-ui/src/router/routes/modules/clusters.ts
@@ -59,7 +59,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'CreateCluster',
path: 'create-cluster',
- component: () => import('@/components/create-cluster/index.vue'),
+ component: () => import('@/features/create-cluster/index.vue'),
meta: {
hidden: true
}
@@ -67,7 +67,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'CreateService',
path: ':id/create-service/:creationMode?',
- component: () => import('@/components/create-service/index.vue'),
+ component: () => import('@/features/create-service/index.vue'),
meta: {
hidden: true
}
@@ -75,7 +75,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'ServiceDetail',
path: ':id/service-detail/:serviceId',
- component: () => import('@/components/service-management/index.vue'),
+ component: () => import('@/features/service-management/index.vue'),
meta: {
hidden: true
}
@@ -83,7 +83,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'CreateComponent',
path: ':id/create-component/:serviceId/:creationMode?/:type',
- component: () => import('@/components/create-service/index.vue'),
+ component: () => import('@/features/create-service/index.vue'),
meta: {
hidden: true
}
@@ -111,7 +111,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'CreateInfraService',
path: 'create-infra-service/:id/:creationMode',
- component: () => import('@/components/create-service/index.vue'),
+ component: () => import('@/features/create-service/index.vue'),
meta: {
hidden: true,
activeMenu: '/cluster-manage/infrastructures/list'
@@ -120,7 +120,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'InfraServiceDetail',
path: 'create-infra-service/service-detail/:id/:serviceId',
- component: () => import('@/components/service-management/index.vue'),
+ component: () => import('@/features/service-management/index.vue'),
meta: {
hidden: true,
activeMenu: '/cluster-manage/infrastructures/list'
@@ -129,7 +129,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'CreateInfraComponent',
path: '/create-infra-service/create-infra-component/:id/:serviceId/:creationMode/:type',
- component: () => import('@/components/create-service/index.vue'),
+ component: () => import('@/features/create-service/index.vue'),
meta: {
hidden: true,
activeMenu: '/cluster-manage/infrastructures/list'
@@ -178,7 +178,7 @@ const routes: RouteRecordRaw[] = [
{
name: 'HostCreation',
path: 'add',
- component: () => import('@/components/create-host/index.vue'),
+ component: () => import('@/features/create-host/index.vue'),
meta: {
hidden: true,
activeMenu: '/cluster-manage/hosts/list'
diff --git a/bigtop-manager-ui/src/store/ai-assistant/index.ts b/bigtop-manager-ui/src/store/ai-assistant/index.ts
index 35fb6b812..9d4347818 100644
--- a/bigtop-manager-ui/src/store/ai-assistant/index.ts
+++ b/bigtop-manager-ui/src/store/ai-assistant/index.ts
@@ -19,11 +19,10 @@
import dayjs from 'dayjs'
import * as ai from '@/api/ai-assistant/index'
-import { defineStore, storeToRefs } from 'pinia'
-import { computed, ref, watch } from 'vue'
import { SenderType } from '@/api/ai-assistant/types'
import { getRandomFromTimestamp } from '@/utils/tools'
import { useLlmConfigStore } from '../llm-config'
+
import type { AxiosProgressEvent, Canceler } from 'axios'
import type { ChatMessageItem, ChatThread, ReceivedMessageItem, ThreadId } from '@/api/ai-assistant/types'
diff --git a/bigtop-manager-ui/src/store/cluster/index.ts b/bigtop-manager-ui/src/store/cluster/index.ts
index 461047e70..4e7eb4193 100644
--- a/bigtop-manager-ui/src/store/cluster/index.ts
+++ b/bigtop-manager-ui/src/store/cluster/index.ts
@@ -17,10 +17,9 @@
* under the License.
*/
-import { computed, ref } from 'vue'
-import { defineStore } from 'pinia'
import { getCluster, getClusterList } from '@/api/cluster'
import { useServiceStore } from '@/store/service'
+
import type { ClusterVO } from '@/api/cluster/types.ts'
export const useClusterStore = defineStore(
diff --git a/bigtop-manager-ui/src/store/create-service/index.ts b/bigtop-manager-ui/src/store/create-service/index.ts
index 1f4132e0d..fddffa069 100644
--- a/bigtop-manager-ui/src/store/create-service/index.ts
+++ b/bigtop-manager-ui/src/store/create-service/index.ts
@@ -16,16 +16,22 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { computed, ref, shallowRef } from 'vue'
-import { defineStore } from 'pinia'
-import useSteps from '@/composables/use-steps'
import { useValidations } from './validation'
+import { cloneDeep } from 'lodash-es'
import { execCommand } from '@/api/command'
+import { createKeyedItem } from '@/utils/tools'
import { type ExpandServiceVO, useStackStore } from '@/store/stack'
-import type { ServiceVO } from '@/api/service/types'
-import type { CommandRequest, CommandVO, ComponentCommandReq, ServiceCommandReq } from '@/api/command/types'
+import type { Property, ServiceConfig, ServiceVO } from '@/api/service/types'
+import type {
+ CommandRequest,
+ CommandVO,
+ ComponentCommandReq,
+ ComponentHostReq,
+ ServiceCommandReq,
+ ServiceConfigReq
+} from '@/api/command/types'
import type { HostVO } from '@/api/hosts/types'
import type { ComponentVO } from '@/api/component/types'
@@ -61,42 +67,46 @@ export const useCreateServiceStore = defineStore(
const stackStore = useStackStore()
const steps = shallowRef(STEPS_TITLES)
+ const { current, stepsLimit, previousStep, nextStep } = useSteps(steps.value)
const selectedServices = ref([])
- const selectedServicesMeta = ref([])
+ const snapshotSelectedServices = ref([])
const createdPayload = ref({})
+
+ const commandRequest = ref({
+ command: 'Add',
+ commandLevel: 'service'
+ })
+
const stepContext = ref({
clusterId: 0,
serviceId: 0,
creationMode: 'internal'
})
- const commandRequest = ref({
- command: 'Add',
- commandLevel: 'service'
- })
- const { current, stepsLimit, previousStep, nextStep } = useSteps(steps.value)
+ const creationMode = computed(() => stepContext.value.creationMode)
+ const clusterId = computed(() => (creationMode.value === 'internal' ? stepContext.value.clusterId : 0))
const infraServices = computed(() => stackStore.getServicesByExclude(['bigtop', 'extra']) as ExpandServiceVO[])
- const excludeInfraServices = computed(() => stackStore.getServicesByExclude(['infra']))
const infraServiceNames = computed(() => infraServices.value.map((v) => v.name!))
+ const excludeInfraServices = computed(() => stackStore.getServicesByExclude(['infra']))
+
const processedServices = computed(() => new Set(selectedServices.value.map((v) => v.name)))
- const creationMode = computed(() => stepContext.value.creationMode)
- const currentClusterId = computed(() => (creationMode.value === 'internal' ? stepContext.value.clusterId : 0))
const allComps = computed(() => {
return new Map(
selectedServices.value.flatMap((s) =>
- s.components!.map((comp) => {
- return [comp.name, { serviceName: s.name, serviceDisplayName: s.displayName, serviceId: s.id, ...comp }]
- })
+ s.components!.map((comp) => [
+ comp.name,
+ { serviceName: s.name, serviceDisplayName: s.displayName, serviceId: s.id, ...comp }
+ ])
)
) as Map
})
- const allCompsMeta = computed(() => {
+ const componentSnapshot = computed(() => {
return new Map(
- selectedServicesMeta.value.flatMap((s) =>
+ snapshotSelectedServices.value.flatMap((s) =>
s.components!.map((comp) => [
comp.name,
{ serviceName: s.name, serviceDisplayName: s.displayName, serviceId: s.id, ...comp }
@@ -105,31 +115,106 @@ export const useCreateServiceStore = defineStore(
) as Map
})
+ const snapshotSelectedServiceMap = computed(() =>
+ snapshotSelectedServices.value.reduce(
+ (p, s) => {
+ if (!s.name) {
+ return p
+ }
+
+ if (!p[s.name]) {
+ s.configs && (p[s.name] = s.configs)
+ }
+
+ return p
+ },
+ {} as Record
+ )
+ )
+
+ function generateProperty(): Property {
+ return createKeyedItem({
+ name: '',
+ displayName: undefined,
+ value: '',
+ isManual: true,
+ action: 'add'
+ })
+ }
+
function getServiceMap(services: ServiceVO[]) {
return new Map(services.map((s) => [s.name as string, s as ExpandServiceVO]))
}
- function updateSelectedService(partial: ExpandServiceVO[]) {
- selectedServices.value = partial
+ function injectKeysToConfigs(configs: ServiceConfig[]): ServiceConfig[] {
+ return configs.map((c) => {
+ if (!c.properties) return c
+ return {
+ ...c,
+ properties: c.properties.map((p) => createKeyedItem(p))
+ }
+ })
+ }
+
+ function injectPropertyKeys(data: ExpandServiceVO[]) {
+ return data.map((s) => {
+ if (!s.configs) return s
+
+ return {
+ ...s,
+ configs: injectKeysToConfigs(s.configs)
+ }
+ })
}
- function setTempData(partial: ExpandServiceVO[]) {
- selectedServicesMeta.value = JSON.parse(JSON.stringify(partial))
+ function updateSelectedService(data: ExpandServiceVO[], updateSnapshot = false) {
+ selectedServices.value = injectPropertyKeys(data)
+
+ if (updateSnapshot) {
+ snapshotSelectedServices.value = cloneDeep(toRaw(data))
+ }
}
- function setStepContext(partial: StepContext) {
- stepContext.value = partial
+ function setStepContext(data: StepContext) {
+ stepContext.value = data
}
function updateInstalledStatus(state: string) {
createdPayload.value.state = state
}
+ function generateConfigsMap(arr: ServiceConfig[]): Record> {
+ const treeMap: Record> = {}
+
+ for (const { name, properties } of arr) {
+ if (!name) continue
+
+ const propMap: Record = {}
+
+ for (const prop of properties ?? []) {
+ if (prop.isManual) continue
+ const key = prop.name
+ propMap[key] = {
+ name: prop.name,
+ value: prop.value,
+ __key: prop.__key,
+ ...(prop.id !== undefined && { id: prop.id }),
+ ...(prop.displayName !== undefined && { displayName: prop.displayName })
+ }
+ }
+
+ treeMap[name] = propMap
+ }
+
+ return treeMap
+ }
+
async function confirmServiceDependencyAction(type: 'add' | 'remove', preSelectedService: ExpandServiceVO) {
const { requiredServices } = preSelectedService
if (!requiredServices && type === 'add') {
return [preSelectedService]
}
+
const valid = validations.validServiceFromInfra(preSelectedService, requiredServices!, infraServiceNames.value)
if (type === 'add' && creationMode.value === 'internal' && valid) {
return []
@@ -210,16 +295,70 @@ export const useCreateServiceStore = defineStore(
return { success: true }
}
- function formatServiceData(services: ExpandServiceVO[]) {
- return services.map((service) => ({
- serviceName: service.name,
- installed: service.isInstalled === undefined ? false : service.isInstalled,
- componentHosts: (service.components || []).map((component) => ({
- componentName: component.name,
- hostnames: (component.hosts || []).map((host: HostVO) => host.hostname)
- })),
- configs: service.configs
- })) as ServiceCommandReq[]
+ const getDiffConfigs = (configs: ServiceConfig[], snapshotConfigs: ServiceConfig[]) => {
+ const configMap = generateConfigsMap(snapshotConfigs)
+ const filterConfig = [] as ServiceConfig[]
+
+ for (const c of configs) {
+ const { name: configName, id } = c
+ if (!configName || !configMap[configName]) continue
+ const oldPropsMap = configMap[configName]
+
+ const diffProps = (c.properties ?? []).filter((prop) => {
+ if (prop.name === '') return false
+
+ if (prop.action === 'delete' || prop.action === 'add') return true
+
+ const oldProp = oldPropsMap[prop.name]
+ return oldProp && oldProp.value !== prop.value
+ })
+
+ if (diffProps.length > 0) {
+ filterConfig.push({
+ id,
+ name: configName,
+ properties: diffProps.map(({ name, value, action }) => ({ name, value, action: action ?? 'update' }))
+ })
+ }
+ }
+
+ return filterConfig
+ }
+
+ function extractComponentHosts(components: ComponentVO[]) {
+ return components.map((component) => ({
+ componentName: component.name,
+ hostnames: (component.hosts ?? []).map((host) => host.hostname)
+ })) as ComponentHostReq[]
+ }
+
+ /**
+ * Get modified configuration diffs for uninstalled services.
+ *
+ * @param services - Uninstalled services to be checked for config changes
+ * @returns A list of services with config differences to be sent to backend
+ */
+ function getUninstalledConfigDiffs(services: ExpandServiceVO[]) {
+ const diffs: ServiceCommandReq[] = []
+
+ for (const service of services) {
+ const { name, isInstalled, configs, components } = service
+ if (isInstalled || !name || !configs) continue
+
+ const snapshot = snapshotSelectedServiceMap.value[name]
+ if (!snapshot) continue
+
+ const configDiffs = getDiffConfigs(configs, snapshot)
+
+ diffs.push({
+ serviceName: name,
+ installed: false,
+ componentHosts: extractComponentHosts(components ?? []),
+ configs: configDiffs as ServiceConfigReq[]
+ })
+ }
+
+ return diffs
}
function formatComponentData(components: Map) {
@@ -244,9 +383,9 @@ export const useCreateServiceStore = defineStore(
async function createService() {
try {
- commandRequest.value.serviceCommands = formatServiceData(selectedServices.value)
- createdPayload.value = await execCommand({ ...commandRequest.value, clusterId: currentClusterId.value })
- Object.assign(createdPayload.value, { clusterId: currentClusterId.value })
+ commandRequest.value.serviceCommands = getUninstalledConfigDiffs(toRaw(selectedServices.value))
+ createdPayload.value = await execCommand({ ...commandRequest.value, clusterId: clusterId.value })
+ Object.assign(createdPayload.value, { clusterId: clusterId.value })
return true
} catch (error) {
console.log('error :>> ', error)
@@ -258,8 +397,8 @@ export const useCreateServiceStore = defineStore(
try {
commandRequest.value.commandLevel = 'component'
commandRequest.value.componentCommands = formatComponentData(allComps.value)
- createdPayload.value = await execCommand({ ...commandRequest.value, clusterId: currentClusterId.value })
- Object.assign(createdPayload.value, { clusterId: currentClusterId.value })
+ createdPayload.value = await execCommand({ ...commandRequest.value, clusterId: clusterId.value })
+ Object.assign(createdPayload.value, { clusterId: clusterId.value })
return true
} catch (error) {
console.log('error :>> ', error)
@@ -270,7 +409,7 @@ export const useCreateServiceStore = defineStore(
function $reset() {
current.value = 0
selectedServices.value = []
- selectedServicesMeta.value = []
+ snapshotSelectedServices.value = []
createdPayload.value = {}
stepContext.value = {
clusterId: 0,
@@ -294,15 +433,17 @@ export const useCreateServiceStore = defineStore(
nextStep,
previousStep,
allComps,
- allCompsMeta,
+ componentSnapshot,
updateSelectedService,
updateInstalledStatus,
setStepContext,
- setTempData,
confirmServiceDependencyAction,
setComponentHosts,
createService,
createdPayload,
+ generateProperty,
+ getDiffConfigs,
+ injectKeysToConfigs,
attachComponentToService,
$reset,
validCardinality: validations.validCardinality
diff --git a/bigtop-manager-ui/src/store/create-service/validation.ts b/bigtop-manager-ui/src/store/create-service/validation.ts
index 1501a9f2e..c2a6496fc 100644
--- a/bigtop-manager-ui/src/store/create-service/validation.ts
+++ b/bigtop-manager-ui/src/store/create-service/validation.ts
@@ -18,10 +18,9 @@
*/
import { message, Modal } from 'ant-design-vue'
-import { h } from 'vue'
-import { useI18n } from 'vue-i18n'
import { useServiceStore } from '@/store/service'
-import SvgIcon from '@/components/common/svg-icon/index.vue'
+import SvgIcon from '@/components/base/svg-icon/index.vue'
+
import type { ExpandServiceVO } from '@/store/stack'
export function useValidations() {
diff --git a/bigtop-manager-ui/src/store/index.ts b/bigtop-manager-ui/src/store/index.ts
index bdccf194d..8a0338998 100644
--- a/bigtop-manager-ui/src/store/index.ts
+++ b/bigtop-manager-ui/src/store/index.ts
@@ -17,7 +17,6 @@
* under the License.
*/
-import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
diff --git a/bigtop-manager-ui/src/store/job-progress/index.ts b/bigtop-manager-ui/src/store/job-progress/index.ts
index af92c4e47..65437b9a7 100644
--- a/bigtop-manager-ui/src/store/job-progress/index.ts
+++ b/bigtop-manager-ui/src/store/job-progress/index.ts
@@ -18,15 +18,12 @@
*/
import { notification, Progress, Avatar, Button, Modal } from 'ant-design-vue'
-import { computed, getCurrentInstance, h, reactive, shallowRef } from 'vue'
-import { useI18n } from 'vue-i18n'
-import { defineStore } from 'pinia'
import { useClusterStore } from '@/store/cluster'
import { execCommand } from '@/api/command'
import { getJobDetails } from '@/api/job'
-import SvgIcon from '@/components/common/svg-icon/index.vue'
-import JobModal from '@/components/job-modal/index.vue'
+import SvgIcon from '@/components/base/svg-icon/index.vue'
+import JobModal from '@/features/job-modal/index.vue'
import { type CommandRequest } from '@/api/command/types'
import type { JobParams, JobVO } from '@/api/job/types'
diff --git a/bigtop-manager-ui/src/store/llm-config/index.ts b/bigtop-manager-ui/src/store/llm-config/index.ts
index 7044ae02b..b8093dd53 100644
--- a/bigtop-manager-ui/src/store/llm-config/index.ts
+++ b/bigtop-manager-ui/src/store/llm-config/index.ts
@@ -17,8 +17,6 @@
* under the License.
*/
-import { defineStore } from 'pinia'
-import { computed, ref, shallowRef, watch } from 'vue'
import * as llmServer from '@/api/llm-config/index'
import type { AuthorizedPlatform, PlatformCredential, UpdateAuthorizedPlatformConfig } from '@/api/llm-config/types'
diff --git a/bigtop-manager-ui/src/store/locale/index.ts b/bigtop-manager-ui/src/store/locale/index.ts
index 385558f5d..e3a22d64c 100644
--- a/bigtop-manager-ui/src/store/locale/index.ts
+++ b/bigtop-manager-ui/src/store/locale/index.ts
@@ -17,14 +17,14 @@
* under the License.
*/
-import { watch, ref, computed } from 'vue'
import i18n from '@/locales'
import en_US from 'ant-design-vue/es/locale/en_US'
import zh_CN from 'ant-design-vue/es/locale/zh_CN'
import dayjs from 'dayjs'
+
import 'dayjs/locale/zh-cn'
import 'dayjs/locale/en'
-import { defineStore } from 'pinia'
+
import { Locale, defaultLocale } from './types'
export const useLocaleStore = defineStore(
diff --git a/bigtop-manager-ui/src/store/menu/index.ts b/bigtop-manager-ui/src/store/menu/index.ts
index b25dd069f..a0bed71fd 100644
--- a/bigtop-manager-ui/src/store/menu/index.ts
+++ b/bigtop-manager-ui/src/store/menu/index.ts
@@ -17,12 +17,11 @@
* under the License.
*/
-import { computed, nextTick, ref, shallowRef } from 'vue'
-import { RouteRecordRaw, useRoute, useRouter } from 'vue-router'
import { dynamicRoutes as dr } from '@/router/routes/index'
-import { defineStore } from 'pinia'
import { useClusterStore } from '../cluster'
+import type { RouteRecordRaw } from 'vue-router'
+
export const useMenuStore = defineStore(
'menu',
() => {
diff --git a/bigtop-manager-ui/src/store/service/index.ts b/bigtop-manager-ui/src/store/service/index.ts
index 79d3f7830..cca193e77 100644
--- a/bigtop-manager-ui/src/store/service/index.ts
+++ b/bigtop-manager-ui/src/store/service/index.ts
@@ -17,8 +17,6 @@
* under the License.
*/
-import { defineStore } from 'pinia'
-import { computed, ref } from 'vue'
import { getService, getServiceList } from '@/api/service'
import type { ServiceListParams, ServiceVO } from '@/api/service/types'
diff --git a/bigtop-manager-ui/src/store/stack/index.ts b/bigtop-manager-ui/src/store/stack/index.ts
index 32646c413..6fe578a77 100644
--- a/bigtop-manager-ui/src/store/stack/index.ts
+++ b/bigtop-manager-ui/src/store/stack/index.ts
@@ -18,8 +18,6 @@
*/
import { getStacks } from '@/api/stack'
-import { defineStore } from 'pinia'
-import { shallowRef } from 'vue'
import type { ComponentVO } from '@/api/component/types'
import type { ServiceConfig, ServiceVO } from '@/api/service/types'
import type { StackVO } from '@/api/stack/types'
diff --git a/bigtop-manager-ui/src/store/theme/index.ts b/bigtop-manager-ui/src/store/theme/index.ts
index e028713e3..cc3ce1d20 100644
--- a/bigtop-manager-ui/src/store/theme/index.ts
+++ b/bigtop-manager-ui/src/store/theme/index.ts
@@ -17,8 +17,6 @@
* under the License.
*/
-import { defineStore } from 'pinia'
-import { computed, ref, watch } from 'vue'
import { theme as antdTheme } from 'ant-design-vue'
import { GlobalToken } from 'ant-design-vue/es/theme'
import { componentsConfigProvider } from './config'
diff --git a/bigtop-manager-ui/src/store/ui/index.ts b/bigtop-manager-ui/src/store/ui/index.ts
index 7f9163f95..b33b1f8d7 100644
--- a/bigtop-manager-ui/src/store/ui/index.ts
+++ b/bigtop-manager-ui/src/store/ui/index.ts
@@ -17,9 +17,6 @@
* under the License.
*/
-import { ref } from 'vue'
-import { defineStore } from 'pinia'
-
export const useUIStore = defineStore(
'ui',
() => {
diff --git a/bigtop-manager-ui/src/store/user/index.ts b/bigtop-manager-ui/src/store/user/index.ts
index 4d38f0eef..333eaaeb5 100644
--- a/bigtop-manager-ui/src/store/user/index.ts
+++ b/bigtop-manager-ui/src/store/user/index.ts
@@ -17,9 +17,7 @@
* under the License.
*/
-import { defineStore } from 'pinia'
import { getCurrentUser, updateUser } from '@/api/user'
-import { shallowRef } from 'vue'
import { UserReq, UserVO } from '@/api/user/types.ts'
export const useUserStore = defineStore(
diff --git a/bigtop-manager-ui/src/types/global.d.ts b/bigtop-manager-ui/src/types/global.d.ts
index 0e2a61712..4db122813 100644
--- a/bigtop-manager-ui/src/types/global.d.ts
+++ b/bigtop-manager-ui/src/types/global.d.ts
@@ -17,12 +17,10 @@
* under the License.
*/
-import AutoForm from '@/components/common/auto-form/index.vue'
-import { FormBuilder } from '@/components/common/form-builder'
+import FormBuilder from '@/components/common/form-builder/index.vue'
declare global {
namespace Comp {
- type AutoFormInstance = InstanceType
type FormBuilderInstance = InstanceType
}
}
diff --git a/bigtop-manager-ui/src/utils/tools.ts b/bigtop-manager-ui/src/utils/tools.ts
index f057e3b7a..620c70fb8 100644
--- a/bigtop-manager-ui/src/utils/tools.ts
+++ b/bigtop-manager-ui/src/utils/tools.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-export const copyText = (text: string): Promise => {
+export function copyText(text: string): Promise {
if (navigator.clipboard) {
return navigator.clipboard.writeText(text)
}
@@ -48,11 +48,11 @@ export const copyText = (text: string): Promise => {
})
}
-export const usePngImage = (imageName: string): string => {
+export function usePngImage(imageName: string): string {
return new URL(`../assets/images/${imageName}.png`, import.meta.url).href
}
-export const scrollToBottom = (container: HTMLElement | null) => {
+export function scrollToBottom(container: HTMLElement | null) {
if (!container) {
return
}
@@ -61,17 +61,17 @@ export const scrollToBottom = (container: HTMLElement | null) => {
})
}
-export const getRandomFromTimestamp = (len: number = 6) => {
+export function getRandomFromTimestamp(len: number = 6) {
return Date.now().toString().slice(-len)
}
-export const generateRandomId = (length = 8) => {
+export function generateRandomId(length = 8) {
return Math.random()
.toString(36)
.substring(2, length + 2)
}
-export const pick = (obj: T, keys: K[]): Pick => {
+export function pick(obj: T, keys: K[]): Pick {
return keys.reduce(
(acc, key) => {
if (obj.hasOwnProperty(key)) {
@@ -82,3 +82,10 @@ export const pick = (obj: T, keys: K[]): Pi
{} as Pick
)
}
+
+export function createKeyedItem(item: T): T & { __key: string } {
+ return {
+ ...item,
+ __key: crypto.randomUUID?.() ?? `${Date.now()}-${Math.random()}`
+ }
+}
diff --git a/bigtop-manager-ui/tests/__components__/form-builder.test.ts b/bigtop-manager-ui/tests/__components__/form-builder.test.ts
index a9f81507e..1b44cad18 100644
--- a/bigtop-manager-ui/tests/__components__/form-builder.test.ts
+++ b/bigtop-manager-ui/tests/__components__/form-builder.test.ts
@@ -19,51 +19,49 @@
import { mount } from '@vue/test-utils'
import { describe, it, expect, vi } from 'vitest'
-import { createI18n, useI18n } from 'vue-i18n'
+import { useI18n } from 'vue-i18n'
import { computed, defineComponent, h, ref } from 'vue'
+import { i18nPlugins } from '../test-util.ts'
import FormBuilder from '../../src/components/common/form-builder/index.vue'
import Antd from 'ant-design-vue'
-const i18n = createI18n({
- legacy: false, // Use Composition API style
- locale: 'en',
- messages: {
- en: {
- form: {
- name: 'Translated Name'
- },
- common: {
- select_error: 'Please select ',
- enter_error: 'Please enter '
- }
+const i18n = i18nPlugins({
+ en: {
+ form: {
+ name: 'Translated Name'
+ },
+ common: {
+ select_error: 'Please select ',
+ enter_error: 'Please enter '
}
}
})
-const wrapper = mount(
- defineComponent({
- components: { FormBuilder },
- setup() {
- const { t } = useI18n()
- const formData = ref({ name: 'xxx' })
- const formRef = ref>()
- const formItems = computed(() => [{ field: 'name', label: t('form.name'), type: 'input', required: true }])
- return { formRef, formData, formItems }
- },
- template: `
+const wrapper = () =>
+ mount(
+ defineComponent({
+ components: { FormBuilder },
+ setup() {
+ const { t } = useI18n()
+ const formData = ref({ name: 'xxx' })
+ const formRef = ref>()
+ const formItems = computed(() => [{ field: 'name', label: t('form.name'), type: 'input', required: true }])
+ return { formRef, formData, formItems }
+ },
+ template: `
`
- }),
- {
- global: {
- plugins: [Antd, i18n]
+ }),
+ {
+ global: {
+ plugins: [Antd, i18n]
+ }
}
- }
-)
+ )
describe('FormBuilder.vue', () => {
it('renders form items based on formItems prop', () => {
@@ -80,7 +78,8 @@ describe('FormBuilder.vue', () => {
}
})
- expect(wrapper.findAll('input').length).toBe(3)
+ const inputs = wrapper.findAll('input').filter((input) => input.classes().includes('ant-input'))
+ expect(inputs.length).toBe(wrapper.vm.formItems.filter((item) => item.type === 'input').length)
wrapper.findAll('label').forEach((label, index) => {
const formItem = wrapper.vm.formItems[index]
@@ -88,15 +87,10 @@ describe('FormBuilder.vue', () => {
expect(label.text()).toBe(formItem.label)
}
})
-
- wrapper.findAll('input').forEach((input, index) => {
- const formItem = wrapper.vm.formItems[index]
- expect(input.attributes('id')).toBe(`form_item_${formItem.field}`)
- })
})
it('applies validation rules correctly', async () => {
- const formRef = wrapper.vm.formRef as InstanceType
+ const formRef = wrapper().vm.formRef as InstanceType
expect(formRef?.validate).toBeDefined()
const validSpy = vi.spyOn(formRef, 'validate')
@@ -105,7 +99,7 @@ describe('FormBuilder.vue', () => {
})
it('resets the form correctly', () => {
- const formRef = wrapper.vm.formRef as InstanceType
+ const formRef = wrapper().vm.formRef as InstanceType
const resetSpy = vi.spyOn(formRef, 'resetForm')
formRef.resetForm()
@@ -113,7 +107,7 @@ describe('FormBuilder.vue', () => {
})
it('renders translated labels using vue-i18n', () => {
- const label = wrapper.find('label')
+ const label = wrapper().find('label')
expect(label.exists()).toBe(true)
expect(label.text()).toBe('Translated Name')
diff --git a/bigtop-manager-ui/tests/__components__/form-filter.test.ts b/bigtop-manager-ui/tests/__components__/form-filter.test.ts
index 22eaf2381..105c1c78e 100644
--- a/bigtop-manager-ui/tests/__components__/form-filter.test.ts
+++ b/bigtop-manager-ui/tests/__components__/form-filter.test.ts
@@ -19,8 +19,11 @@
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
+import { i18nPlugins } from '../test-util.ts'
import FormFilter from '../../src/components/common/form-filter/index.vue'
+const i18n = i18nPlugins()
+
describe('FormFilter.vue', () => {
const filterItems = [
{
@@ -45,9 +48,7 @@ describe('FormFilter.vue', () => {
filterItems
},
global: {
- mocks: {
- $t: (key: string) => key
- }
+ plugins: [i18n]
}
})
@@ -62,9 +63,7 @@ describe('FormFilter.vue', () => {
filterItems
},
global: {
- mocks: {
- $t: (key: string) => key
- }
+ plugins: [i18n]
}
})
@@ -84,9 +83,7 @@ describe('FormFilter.vue', () => {
filterItems
},
global: {
- mocks: {
- $t: (key: string) => key
- }
+ plugins: [i18n]
}
})
diff --git a/bigtop-manager-ui/tests/test-util.ts b/bigtop-manager-ui/tests/test-util.ts
index 8adf8e696..7f2568204 100644
--- a/bigtop-manager-ui/tests/test-util.ts
+++ b/bigtop-manager-ui/tests/test-util.ts
@@ -18,6 +18,7 @@
*/
import { createApp } from 'vue'
+import { createI18n } from 'vue-i18n'
export function withSetup(composable: any, payload?: T) {
let result: any
@@ -30,3 +31,11 @@ export function withSetup(composable: any, payload?: T) {
app.mount(document.createElement('div'))
return [result, app]
}
+
+export function i18nPlugins(messages?: Record) {
+ return createI18n({
+ legacy: false, // Use Composition API style
+ locale: 'en',
+ messages: messages ?? {}
+ })
+}
diff --git a/bigtop-manager-ui/vite.config.ts b/bigtop-manager-ui/vite.config.ts
index d5cc8b4b4..a445724c5 100644
--- a/bigtop-manager-ui/vite.config.ts
+++ b/bigtop-manager-ui/vite.config.ts
@@ -20,11 +20,12 @@
import { loadEnv, defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'node:path'
-import Icons from 'unplugin-icons/vite'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
+import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
+import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
@@ -47,8 +48,12 @@ export default defineConfig(({ mode }) => {
}
}),
Components({
+ dirs: ['src/components', 'src/features'],
dts: 'src/types/components.d.ts',
resolvers: [
+ AntDesignVueResolver({
+ importStyle: false // css in js
+ }),
IconsResolver({
customCollections: ['svg'],
componentPrefix: 'icon'
@@ -56,13 +61,14 @@ export default defineConfig(({ mode }) => {
]
}),
AutoImport({
- imports: [],
+ imports: ['vue', 'vue-i18n', 'vue-router', '@vueuse/core', 'pinia'],
resolvers: [
IconsResolver({
customCollections: ['svg'],
componentPrefix: 'icon'
})
],
+ dirs: ['src/composables/**'],
dts: 'src/types/auto-imports.d.ts'
})
],