diff --git a/ui-vue3/package.json b/ui-vue3/package.json index 322cec677..89e72851c 100644 --- a/ui-vue3/package.json +++ b/ui-vue3/package.json @@ -37,6 +37,7 @@ "ts-node": "^10.9.2", "tslib": "^2.6.2", "vue": "^3.3.10", + "vue-clipboard3": "^2.0.0", "vue-i18n": "^9.8.0", "vue-router": "^4.2.5", "vue3-colorpicker": "^2.2.3" diff --git a/ui-vue3/src/api/mock/mockApp.ts b/ui-vue3/src/api/mock/mockApp.ts index 0178c2137..3c9b61422 100644 --- a/ui-vue3/src/api/mock/mockApp.ts +++ b/ui-vue3/src/api/mock/mockApp.ts @@ -16,26 +16,135 @@ */ import Mock from 'mockjs' +import devTool from "@/utils/DevToolUtil"; Mock.mock('/mock/application/search', 'get', () => { - let total = Mock.mock('@integer(8, 1000)') - let list = [] - for (let i = 0; i < total; i++) { - list.push({ - appName: 'app_' + Mock.mock('@string(2,10)'), - instanceNum: Mock.mock('@integer(80, 200)'), - deployCluster: 'cluster_' + Mock.mock('@string(5)'), - 'registerClusters|1-3': ['cluster_' + Mock.mock('@string(5)')] - }) - } - return { - code: 200, - message: 'success', - data: Mock.mock({ - total: total, - curPage: 1, - pageSize: 10, - data: list - }) - } + let total = Mock.mock('@integer(8, 1000)') + let list = [] + for (let i = 0; i < total; i++) { + list.push({ + appName: 'app_' + Mock.mock('@string(2,10)'), + instanceNum: Mock.mock('@integer(80, 200)'), + deployCluster: 'cluster_' + Mock.mock('@string(5)'), + 'registerClusters|1-3': ['cluster_' + Mock.mock('@string(5)')] + }) + } + return { + code: 200, + message: 'success', + data: Mock.mock({ + total: total, + curPage: 1, + pageSize: 10, + data: list + }) + } +}) +Mock.mock('/mock/application/instance/statistics', 'get', () => { + return { + "code": 1000, + "message": "success", + "data": { + "instanceTotal": 43, + "versionTotal": 4, + "cpuTotal": "56c", + "memoryTotal": "108.2GB" + } + } +}) + +Mock.mock(devTool.mockUrl('/mock/application/instance/info'), 'get', () => { + + let total = Mock.mock('@integer(8, 1000)') + let list = [] + for (let i = 0; i < total; i++) { + list.push( + { + "ip": "121.90.211.162", + "name": "shop-user", + "deployState": Mock.Random.pick(['Running','Pending', 'Terminating', 'Crashing']), + "deployCluster": "tx-shanghai-1", + "registerStates": [ + { + "label": "Registed", + "value": "Registed", + "level": "healthy" + } + ], + "registerClusters": [ + "ali-hangzhou-1", + "ali-hangzhou-2" + ], + "cpu": "1.2c", + "memory": "2349MB", + "startTime": "2023-06-09 03:47:10", + "registerTime": "2023-06-09 03:48:20", + "labels": { + "region": "beijing", + "version": "v1" + } + } + ) + } + return { + code: 200, + message: 'success', + data: Mock.mock({ + total: total, + curPage: 1, + pageSize: 10, + data: list + }) + } +}) + +Mock.mock('/mock/application/detail', 'get', () => { + return { + "code": 200, + "message": "success", + "data": { + "appName": [ + "shop-user" + ], + "rpcProtocols": [ + "dubbo 2.0.2" + ], + "dubboVersions": [ + "Dubbo 3.2.10", + "Dubbo 2.7.4.1" + ], + "dubboPorts": [ + "20880" + ], + "serialProtocols": [ + "fastjson2" + ], + "appTypes": [ + "无状态" + ], + "images": [ + "harbor.apche.org/dubbo-samples-shop-user:v1.0", + "harbor.apche.org/dubbo-samples-shop-user:v1.1", + "harbor.apche.org/dubbo-samples-shop-user:v1.2", + ], + "workloads": [ + "dubbo-samples-shop-user-base", + "dubbo-samples-shop-user-gray", + "dubbo-samples-shop-user-gray", + "dubbo-samples-shop-user-gray", + ], + "deployCluster": [ + "ali-shanghai-1", + "tx-shanghai-2" + ], + "registerCluster": [ + "nacos-cluster-1", + "nacos-cluster-2" + ], + "registerMode": [ + "应用级", + "接口级" + ] + } + } }) diff --git a/ui-vue3/src/api/mock/mockService.ts b/ui-vue3/src/api/mock/mockService.ts index f5e975eb6..d6962b1f5 100644 --- a/ui-vue3/src/api/mock/mockService.ts +++ b/ui-vue3/src/api/mock/mockService.ts @@ -16,14 +16,15 @@ */ import Mock from 'mockjs' +import devTool from "@/utils/DevToolUtil"; -Mock.mock('/mock/service/search', 'get', { +Mock.mock(devTool.mockUrl('/mock/service/search'), 'get', { code: 200, message: 'success', data: { total: 8, curPage: 1, - pageSize: 1, + pageSize: 5, data: [ { serviceName: 'org.apache.dubbo.samples.UserService', diff --git a/ui-vue3/src/api/service/app.ts b/ui-vue3/src/api/service/app.ts index 079f0e5ff..a908e1807 100644 --- a/ui-vue3/src/api/service/app.ts +++ b/ui-vue3/src/api/service/app.ts @@ -24,3 +24,29 @@ export const searchApplications = (params: any): Promise => { params }) } + + +export const getApplicationDetail = (params: any): Promise => { + return request({ + url: '/application/detail', + method: 'get', + params + }) +} + +export const getApplicationInstanceStatistics = (params: any): Promise => { + return request({ + url: '/application/instance/statistics', + method: 'get', + params + }) +} +export const getApplicationInstanceInfo = (params: any): Promise => { + return request({ + url: '/application/instance/info', + method: 'get', + params + }) +} + + diff --git a/ui-vue3/src/base/constants.ts b/ui-vue3/src/base/constants.ts index 0d00fe2ce..4b68a57be 100644 --- a/ui-vue3/src/base/constants.ts +++ b/ui-vue3/src/base/constants.ts @@ -25,3 +25,19 @@ export const LOCAL_STORAGE_THEME = 'LOCAL_STORAGE_THEME' let item = localStorage.getItem(LOCAL_STORAGE_THEME) export const PRIMARY_COLOR = ref(item || PRIMARY_COLOR_DEFAULT) + + + +export const INSTANCE_REGISTER_COLOR:{ [key: string]: string } = { + HEALTHY: 'green' +} + +/** + * 'Running','Pending', 'Terminating', 'Crashing' + */ +export const INSTANCE_DEPLOY_COLOR:{ [key: string]: string } = { + RUNNING: 'green', + PENDING: 'yellow', + TERMINATING: 'red', + CRASHING: 'darkRed', +} \ No newline at end of file diff --git a/ui-vue3/src/base/i18n/en.ts b/ui-vue3/src/base/i18n/en.ts index bd8dfd2b4..23b480cb3 100644 --- a/ui-vue3/src/base/i18n/en.ts +++ b/ui-vue3/src/base/i18n/en.ts @@ -268,13 +268,48 @@ const words: I18nType = { applications: 'Applications', instances: 'Instances', applicationDomain: { - name: 'Application Name', - detail: 'Application Detail' + + detail: 'Detail', + instance: 'Instance', + service: 'Service', + monitor: 'Monitor', + tracing: 'Tracing', + config: 'Config', + event: 'Event', + appName: 'Application Name', + rpcProtocols: 'Rpc Protocols', + dubboVersions: 'Dubbo Versions', + dubboPorts: 'Dubbo Ports', + serialProtocols: 'Serial Protocols', + appTypes: 'Application Types', + images: 'Images', + workloads: 'Workloads', + deployCluster: 'Deploy Cluster', + registerCluster: 'Register Cluster', + registerMode: 'Register Mode', }, + instanceDomain:{ + ip: 'Ip', + name: 'Name', + deployState: 'Deploy State', + deployCluster: 'Deploy Cluster', + registerStates: 'Register States', + registerClusters: 'Register Clusters', + cpu: 'Cpu', + memory: 'Memory', + startTime: 'Start Time', + registerTime: 'Register Time', + labels: 'Labels', +}, searchDomain: { total: 'Total', unit: 'items' }, + messageDomain: { + success: { + copy: "You have successfully copied a piece of information" + } + }, backHome: 'Back Home', noPageTip: 'Sorry, the page you visited does not exist.', globalSearchTip: 'Search ip, application, instance, service', diff --git a/ui-vue3/src/base/i18n/zh.ts b/ui-vue3/src/base/i18n/zh.ts index 0553d2d75..782c94c4a 100644 --- a/ui-vue3/src/base/i18n/zh.ts +++ b/ui-vue3/src/base/i18n/zh.ts @@ -267,12 +267,47 @@ const words: I18nType = { instances: '实例', applicationDomain: { name: '应用名', - detail: '应用详情' + detail: '详情', + instance: '实例', + service: '服务', + monitor: '监控', + tracing: '链路追踪', + config: '配置', + event: '事件', + appName: '应用名', + rpcProtocols: 'RPC 协议', + dubboVersions: 'Dubbo 版本', + dubboPorts: 'Dubbo 端口', + serialProtocols: '序列化协议', + appTypes: '应用类型', + images: '应用镜像', + workloads: '工作负载', + deployCluster: '部署集群', + registerCluster: '注册集群', + registerMode: '注册模式', + }, + instanceDomain:{ + ip: 'IP', + name: '实例名称', + deployState: '部署状态', + deployCluster: '部署集群', + registerStates: '注册状态', + registerClusters: '注册集群', + cpu: 'CPU', + memory: '内存', + startTime: '启动时间', + registerTime: '注册时间', + labels: '标签', }, searchDomain: { total: '共计', unit: '条' }, + messageDomain: { + success: { + copy: "您已经成功复制一条信息" + } + }, backHome: '回到首页', noPageTip: '抱歉,你访问的页面不存在', globalSearchTip: '搜索ip,应用,实例,服务', diff --git a/ui-vue3/src/components/SearchTable.vue b/ui-vue3/src/components/SearchTable.vue index 5fca6a0eb..3d8338d62 100644 --- a/ui-vue3/src/components/SearchTable.vue +++ b/ui-vue3/src/components/SearchTable.vue @@ -17,35 +17,51 @@ @@ -54,20 +70,25 @@
@@ -77,16 +98,16 @@ diff --git a/ui-vue3/src/layout/menu/layout_menu.vue b/ui-vue3/src/layout/menu/layout_menu.vue index d37516ddc..8496a1a01 100644 --- a/ui-vue3/src/layout/menu/layout_menu.vue +++ b/ui-vue3/src/layout/menu/layout_menu.vue @@ -49,7 +49,7 @@ const routesForMenu = defaultRoutes const nowRoute = useRoute() // load active menu -let selectedKeys = reactive([getLoadSelectedKeys(nowRoute.meta)]) +let selectedKeys = computed(()=>[getLoadSelectedKeys(nowRoute.meta)]) let openKeys: any = reactive([]) function getLoadSelectedKeys(meta: RouterMeta): string { return meta.tab || meta.hidden ? getLoadSelectedKeys(meta.parent?.meta!) : meta._router_key! diff --git a/ui-vue3/src/layout/tab/layout_tab.vue b/ui-vue3/src/layout/tab/layout_tab.vue index e6a4df5d9..b5be87555 100644 --- a/ui-vue3/src/layout/tab/layout_tab.vue +++ b/ui-vue3/src/layout/tab/layout_tab.vue @@ -31,7 +31,9 @@ - + + +
@@ -50,16 +52,23 @@ const tabRouters = computed(() => { return meta?.parent?.children?.filter((x: any): any => x.meta.tab) }) let activeKey = ref(tabRoute.name) -let transitionFlag = ref(true) +let transitionFlag = ref(false) let key = _.uniqueId('__tab_page') router.beforeEach((to, from, next) => { key = _.uniqueId('__tab_page') - transitionFlag.value = false + transitionFlag.value = true activeKey.value = to.name next() setTimeout(() => { - transitionFlag.value = true + transitionFlag.value = false }, 500) }) - + diff --git a/ui-vue3/src/router/defaultRoutes.ts b/ui-vue3/src/router/defaultRoutes.ts index a63af5de1..2c96fdcf3 100644 --- a/ui-vue3/src/router/defaultRoutes.ts +++ b/ui-vue3/src/router/defaultRoutes.ts @@ -72,28 +72,69 @@ export const routes: Readonly = [ { path: '/detail/:pathId', name: 'applicationDomain.detail', - component: () => import('../views/resources/applications/tabs/tab1.vue'), + component: () => import('../views/resources/applications/tabs/detail.vue'), meta: { tab: true, - icon: 'material-symbols:view-in-ar' + icon: 'tabler:list-details' } }, { - path: '/detail2/:pathId', - name: 'application-tab2', - component: () => import('../views/resources/applications/tabs/tab2.vue'), + path: '/instance/:pathId', + name: 'applicationDomain.instance', + component: () => import('../views/resources/applications/tabs/instance.vue'), meta: { - tab: true + tab: true, + icon: 'ooui:instance-ltr' } }, { - path: '/detail3/:pathId', - name: 'application-tab3', - component: () => import('../views/resources/applications/tabs/tab3.vue'), + path: '/service/:pathId', + name: 'applicationDomain.service', + component: () => import('../views/resources/applications/tabs/service.vue'), meta: { - tab: true + tab: true, + icon: 'carbon:web-services-definition' } - } + }, + + + { + path: '/monitor/:pathId', + name: 'applicationDomain.monitor', + component: () => import('../views/resources/applications/tabs/monitor.vue'), + meta: { + tab: true, + icon: 'material-symbols-light:monitor-heart-outline' + } + }, + { + path: '/tracing/:pathId', + name: 'applicationDomain.tracing', + component: () => import('../views/resources/applications/tabs/tracing.vue'), + meta: { + tab: true, + icon: 'game-icons:digital-trace' + } + }, + { + path: '/config/:pathId', + name: 'applicationDomain.config', + component: () => import('../views/resources/applications/tabs/config.vue'), + meta: { + tab: true, + icon: 'material-symbols:settings' + } + }, + { + path: '/event/:pathId', + name: 'applicationDomain.event', + component: () => import('../views/resources/applications/tabs/event.vue'), + meta: { + tab: true, + icon: 'material-symbols:date-range' + } + }, + ] }, { diff --git a/ui-vue3/src/utils/DevToolUtil.ts b/ui-vue3/src/utils/DevToolUtil.ts index 5a80ef13d..fbcd7b1ad 100644 --- a/ui-vue3/src/utils/DevToolUtil.ts +++ b/ui-vue3/src/utils/DevToolUtil.ts @@ -15,25 +15,25 @@ * limitations under the License. */ -import { notification } from 'ant-design-vue' +import {notification} from 'ant-design-vue' /** * get function invoke stack * @param more Offset relative to the default stack number */ function getCurrentFunctionLocation(more = 0): string { - try { - throw new Error() - } catch (error: any) { - // error.stack - const stackLines = error.stack.split('\n') - // The forth line typically contains information about the calling location. - const offset = 2 + more - if (offset >= 0 && stackLines.length >= offset) { - return stackLines[offset].trim() + try { + throw new Error() + } catch (error: any) { + // error.stack + const stackLines = error.stack.split('\n') + // The forth line typically contains information about the calling location. + const offset = 2 + more + if (offset >= 0 && stackLines.length >= offset) { + return stackLines[offset].trim() + } + return 'wrong location' } - return 'wrong location' - } } /** @@ -42,14 +42,20 @@ function getCurrentFunctionLocation(more = 0): string { * @param todoDetail */ const todo = (todoDetail: string) => { - notification.warn({ - message: `TODO: ${todoDetail} =>: ${devTool.getCurrentFunctionLocation(1)}` - }) + notification.warn({ + message: `TODO: ${todoDetail} =>: ${devTool.getCurrentFunctionLocation(1)}` + }) +} + +const mockUrl = (raw: string) => { + return RegExp(raw + ".*") } const devTool = { - getCurrentFunctionLocation, - todo + getCurrentFunctionLocation, + todo, + mockUrl, } + export default devTool diff --git a/ui-vue3/src/utils/SearchUtil.ts b/ui-vue3/src/utils/SearchUtil.ts index 0be57b708..3a0c07928 100644 --- a/ui-vue3/src/utils/SearchUtil.ts +++ b/ui-vue3/src/utils/SearchUtil.ts @@ -18,6 +18,12 @@ import type { TableColumnsType } from 'ant-design-vue' import { reactive } from 'vue' +export type DICT_TYPE = + 'SELECT'| + 'BUTTON'| + 'RADIO' + ; + export class SearchDomain { // form of search queryForm: any @@ -26,18 +32,21 @@ export class SearchDomain { label: string param: string defaultValue: string + style?: any dict: [ { label: string value: string } - ] + ], + dictType: DICT_TYPE } ] searchApi: Function result: any tableStyle: any table: { + loading?: boolean, columns: TableColumnsType } = { columns: [] } paged = { @@ -46,20 +55,31 @@ export class SearchDomain { pageSize: 10 } - constructor(query: any, searchApi: any, columns: TableColumnsType) { + constructor(query: any, searchApi: any, columns: TableColumnsType|any, paged?: any|undefined) { this.params = query this.queryForm = reactive({}) this.table.columns = columns + query.forEach((c:any)=>{ + if(c.defaultValue) { + this.queryForm[c.param] = c.defaultValue + } + }) + if(paged) { + this.paged = {...this.paged, ...paged} + } this.searchApi = searchApi this.onSearch() } async onSearch() { + this.table.loading = true; + setTimeout(()=>{ + this.table.loading = false; + }, 5000) let res = (await this.searchApi(this.queryForm || {})).data this.result = res.data this.paged.total = res.total - this.paged.curPage = res.curPage - this.paged.pageSize = res.pageSize + this.table.loading = false; } } diff --git a/ui-vue3/src/views/resources/applications/index.vue b/ui-vue3/src/views/resources/applications/index.vue index b8328e3f1..a003ebe00 100644 --- a/ui-vue3/src/views/resources/applications/index.vue +++ b/ui-vue3/src/views/resources/applications/index.vue @@ -97,7 +97,7 @@ const searchDomain = reactive( onMounted(() => { searchDomain.tableStyle = { - scrollY: '40vh' + scrollY: '40vh', } searchDomain.onSearch() }) diff --git a/ui-vue3/src/views/resources/applications/tabs/tab2.vue b/ui-vue3/src/views/resources/applications/tabs/config.vue similarity index 93% rename from ui-vue3/src/views/resources/applications/tabs/tab2.vue rename to ui-vue3/src/views/resources/applications/tabs/config.vue index c20afd741..081aa0554 100644 --- a/ui-vue3/src/views/resources/applications/tabs/tab2.vue +++ b/ui-vue3/src/views/resources/applications/tabs/config.vue @@ -15,14 +15,14 @@ ~ limitations under the License. --> diff --git a/ui-vue3/src/views/resources/applications/tabs/detail.vue b/ui-vue3/src/views/resources/applications/tabs/detail.vue new file mode 100644 index 000000000..9a31f60a4 --- /dev/null +++ b/ui-vue3/src/views/resources/applications/tabs/detail.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/ui-vue3/src/views/resources/applications/tabs/tab1.vue b/ui-vue3/src/views/resources/applications/tabs/event.vue similarity index 93% rename from ui-vue3/src/views/resources/applications/tabs/tab1.vue rename to ui-vue3/src/views/resources/applications/tabs/event.vue index 1b67290fa..a3b5c81fb 100644 --- a/ui-vue3/src/views/resources/applications/tabs/tab1.vue +++ b/ui-vue3/src/views/resources/applications/tabs/event.vue @@ -15,15 +15,15 @@ ~ limitations under the License. --> diff --git a/ui-vue3/src/views/resources/applications/tabs/instance.vue b/ui-vue3/src/views/resources/applications/tabs/instance.vue new file mode 100644 index 000000000..ac5f7da9a --- /dev/null +++ b/ui-vue3/src/views/resources/applications/tabs/instance.vue @@ -0,0 +1,276 @@ + + + + + diff --git a/ui-vue3/src/views/resources/applications/tabs/tab3.vue b/ui-vue3/src/views/resources/applications/tabs/monitor.vue similarity index 100% rename from ui-vue3/src/views/resources/applications/tabs/tab3.vue rename to ui-vue3/src/views/resources/applications/tabs/monitor.vue diff --git a/ui-vue3/src/views/resources/applications/tabs/service.vue b/ui-vue3/src/views/resources/applications/tabs/service.vue new file mode 100644 index 000000000..8ea700250 --- /dev/null +++ b/ui-vue3/src/views/resources/applications/tabs/service.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/ui-vue3/src/views/resources/applications/tabs/tracing.vue b/ui-vue3/src/views/resources/applications/tabs/tracing.vue new file mode 100644 index 000000000..798cc4b4c --- /dev/null +++ b/ui-vue3/src/views/resources/applications/tabs/tracing.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/ui-vue3/src/views/resources/services/search.vue b/ui-vue3/src/views/resources/services/search.vue index 64f62e99b..73ef84793 100644 --- a/ui-vue3/src/views/resources/services/search.vue +++ b/ui-vue3/src/views/resources/services/search.vue @@ -29,7 +29,7 @@