Merge pull request #256 from sjmshsh/feat_fixbug
remove redundant codes
diff --git a/ui-vue3/src/api/mock/mockInstance.ts b/ui-vue3/src/api/mock/mockInstance.ts
index 7c723f3..54b5e5f 100644
--- a/ui-vue3/src/api/mock/mockInstance.ts
+++ b/ui-vue3/src/api/mock/mockInstance.ts
@@ -17,87 +17,101 @@
import Mock from 'mockjs'
-Mock.mock('/mock/instance/search', 'get', {
- code: 200,
- message: 'laborum qui',
- data: {
- total: 66,
- curPage: 82,
- pageSize: 31,
- data: [
- {
- ip: '205.216.185.96',
- name: '用省中际解理',
- deployState: {
- label: 'dolor',
- value: 'in amet',
- level: 'amet nisi incididunt',
- tip: '133.16.55.40'
- },
- deployCluster: 'veniam elit irure',
- registerStates: [
- {
- label: 'in consequat est',
- value: 'esse non Lorem',
- level: 'sit',
- tip: '122.249.164.252'
- }
- ],
- registerClusters: ['cupidatat'],
- cpu: 'officia cupidatat reprehenderit magna ex',
- memory: 'mollit',
- startTime: '2016-07-31 19:20:31',
- registerTime: '2014-02-09 04:02:41',
- labels: ['cupidat']
- },
- {
- ip: '117.23.142.162',
- name: '之受力即此',
- deployState: {
- label: 'sint culpa elit quis id',
- value: 'amet',
- level: 'adipisicing do',
- tip: '112.176.231.68'
- },
- deployCluster: 'esse sit',
- registerStates: [
- {
- label: 'ut',
- value: 'eu sit',
- level: 'in eiusmod ullamco',
- tip: '220.153.108.236'
- }
- ],
- registerClusters: ['ea consectetur'],
- cpu: 'dolor sint deserunt',
- memory: 'sint eu commodo proident',
- startTime: '1994-12-22 18:24:57',
- registerTime: '1986-07-24 03:18:24'
- },
- {
- ip: '41.215.196.61',
- name: '值青给值',
- deployState: {
- label: 'sunt',
- value: 'consectetur in',
- level: 'culpa dolore',
- tip: '142.182.249.124'
- },
- deployCluster: 'cupidatat eu nostrud',
- registerStates: [
- {
- label: 'ad quis',
- value: 'Excepteur esse dolore Ut dolore',
- level: 'ipsum ad quis',
- tip: '220.55.203.4'
- }
- ],
- registerClusters: ['Excepteur sit laboris'],
- cpu: 'fugiat pariatur laborum ut',
- memory: 'Lorem adipisicing sunt',
- startTime: '1984-04-25 12:22:51',
- registerTime: '1976-06-06 19:58:58'
+Mock.mock('/mock/instance/search', '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/instance/detail', 'get', () => {
+ return {
+ code: 200,
+ message: 'success',
+ data: {
+ deployState: {
+ label: 'pariatur do nulla',
+ value: 'ut',
+ level: 'ullamco veniam laboris ex',
+ tip: '246.179.217.170'
+ },
+ registerStates: [
+ {
+ label: 'magna Duis non',
+ value: 'laboris',
+ level: 'et dolore pariatur ipsum adipisicing',
+ tip: '204.174.144.51'
+ }
+ ],
+ dubboPort: 35,
+ ip: '15.1.144.52',
+ appName: '式团划',
+ workload: 'in labore enim',
+ labels: [null],
+ createTime: '2000-11-12 05:59:48',
+ startTime: '1986-03-29 11:48:17',
+ registerTime: '2000-01-26 19:09:48',
+ registerCluster: 'qui commodo dolore',
+ deployCluster: 'dolore laborum',
+ node: 'aliquip',
+ image: 'http://dummyimage.com/400x400',
+ probes: {
+ startupProbe: {
+ type: 'pariatur in quis',
+ port: 92,
+ open: false
+ },
+ readinessProbe: {
+ type: 'aute',
+ port: 52,
+ open: false
+ },
+ livenessPronbe: {
+ type: 'reprehenderit aute nostrud',
+ port: 66,
+ open: false
+ }
+ }
+ }
+ }
+})
+
+Mock.mock('/mock/instance/metrics', 'get', () => {
+ return {
+ code: 200,
+ message: 'success',
+ data: 'http://8.147.104.101:3000/d/dcf5defe-d198-4704-9edf-6520838880e9/instance?orgId=1&refresh=1m&from=1710644821536&to=1710731221536&theme=light'
}
})
diff --git a/ui-vue3/src/api/mock/mockService.ts b/ui-vue3/src/api/mock/mockService.ts
index c10e0cd..2383bcd 100644
--- a/ui-vue3/src/api/mock/mockService.ts
+++ b/ui-vue3/src/api/mock/mockService.ts
@@ -28,56 +28,192 @@
data: [
{
serviceName: 'org.apache.dubbo.samples.UserService',
- interfaceNum: 4,
+ versionGroup: [
+ {
+ version: '1.0.0',
+ group: 'group1'
+ },
+ {
+ version: '1.0.0',
+ group: null
+ },
+ {
+ version: null,
+ group: 'group1'
+ },
+ {
+ version: null,
+ group: null
+ }
+ ],
avgQPS: 6,
avgRT: '194ms',
requestTotal: 200
},
{
serviceName: 'org.apache.dubbo.samples.OrderService',
- interfaceNum: 12,
+ versionGroup: [
+ {
+ version: '1.0.0',
+ group: 'group1'
+ },
+ {
+ version: '1.0.0',
+ group: null
+ },
+ {
+ version: null,
+ group: 'group1'
+ },
+ {
+ version: null,
+ group: null
+ }
+ ],
avgQPS: 13,
avgRT: '189ms',
requestTotal: 164
},
{
serviceName: 'org.apache.dubbo.samples.DetailService',
- interfaceNum: 14,
+ versionGroup: [
+ {
+ version: '1.0.0',
+ group: 'group1'
+ },
+ {
+ version: '1.0.0',
+ group: null
+ },
+ {
+ version: null,
+ group: 'group1'
+ },
+ {
+ version: null,
+ group: null
+ }
+ ],
avgQPS: 0.5,
avgRT: '268ms',
requestTotal: 1324
},
{
serviceName: 'org.apache.dubbo.samples.PayService',
- interfaceNum: 8,
+ versionGroup: [
+ {
+ version: '1.0.0',
+ group: 'group1'
+ },
+ {
+ version: '1.0.0',
+ group: null
+ },
+ {
+ version: null,
+ group: 'group1'
+ },
+ {
+ version: null,
+ group: null
+ }
+ ],
avgQPS: 9,
avgRT: '346ms',
requestTotal: 189
},
{
serviceName: 'org.apache.dubbo.samples.CommentService',
- interfaceNum: 9,
+ versionGroup: [
+ {
+ version: '1.0.0',
+ group: 'group1'
+ },
+ {
+ version: '1.0.0',
+ group: null
+ },
+ {
+ version: null,
+ group: 'group1'
+ },
+ {
+ version: null,
+ group: null
+ }
+ ],
avgQPS: 8,
avgRT: '936ms',
requestTotal: 200
},
{
serviceName: 'org.apache.dubbo.samples.RepayService',
- interfaceNum: 16,
+ versionGroup: [
+ {
+ version: '1.0.0',
+ group: 'group1'
+ },
+ {
+ version: '1.0.0',
+ group: null
+ },
+ {
+ version: null,
+ group: 'group1'
+ },
+ {
+ version: null,
+ group: null
+ }
+ ],
avgQPS: 17,
avgRT: '240ms',
requestTotal: 146
},
{
serviceName: 'org.apche.dubbo.samples.TransportService',
- interfaceNum: 5,
+ versionGroup: [
+ {
+ version: '1.0.0',
+ group: 'group1'
+ },
+ {
+ version: '1.0.0',
+ group: null
+ },
+ {
+ version: null,
+ group: 'group1'
+ },
+ {
+ version: null,
+ group: null
+ }
+ ],
avgQPS: 43,
avgRT: '89ms',
requestTotal: 367
},
{
serviceName: 'org.apche.dubbo.samples.DistributionService',
- interfaceNum: 5,
+ versionGroup: [
+ {
+ version: '1.0.0',
+ group: 'group1'
+ },
+ {
+ version: '1.0.0',
+ group: null
+ },
+ {
+ version: null,
+ group: 'group1'
+ },
+ {
+ version: null,
+ group: null
+ }
+ ],
avgQPS: 4,
avgRT: '78ms',
requestTotal: 145
diff --git a/ui-vue3/src/api/mock/mockServiceDistribution.ts b/ui-vue3/src/api/mock/mockServiceDistribution.ts
index 4a88fa4..23b74aa 100644
--- a/ui-vue3/src/api/mock/mockServiceDistribution.ts
+++ b/ui-vue3/src/api/mock/mockServiceDistribution.ts
@@ -28,67 +28,101 @@
{
applicationName: 'shop-order',
instanceNum: 15,
- instanceIP: [
- '192.168.32.28:8697',
- '192.168.32.26:20880',
- '192.168.32.24:28080',
- '192.168.32.22:20880'
- ]
+ instanceName: 'shop-order0',
+ rpcPort: '172.168.45.89:20888',
+ timeout: '1000ms',
+ retryNum: '2',
+ label: 'region=beijing'
},
{
applicationName: 'shop-order',
instanceNum: 15,
- instanceIP: ['192.168.32.28:8697', '192.168.32.26:20880', '192.168.32.24:28080']
+ instanceName: 'shop-order1',
+ rpcPort: '172.168.45.24:20888',
+ timeout: '500ms',
+ retryNum: '1',
+ label: 'region=wuhan'
},
{
applicationName: 'shop-user',
instanceNum: 12,
- instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+ instanceName: 'shop-order2',
+ rpcPort: '172.161.23.89:20888',
+ timeout: '200ms',
+ retryNum: '1',
+ label: 'region=shanghai'
},
{
applicationName: 'shop-order',
instanceNum: 15,
- instanceIP: [
- '192.168.32.28:8697',
- '192.168.32.26:20880',
- '192.168.32.24:28080',
- '192.168.32.22:20880'
- ]
+ instanceName: 'shop-order3',
+ rpcPort: '172.168.45.89:12423',
+ timeout: '2000ms',
+ retryNum: '2',
+ label: 'region=hangzhou'
},
{
applicationName: 'shop-order',
instanceNum: 15,
- instanceIP: ['192.168.32.28:8697', '192.168.32.26:20880', '192.168.32.24:28080']
+ instanceName: 'shop-order4',
+ rpcPort: '172.168.45.89:20888',
+ timeout: '100ms',
+ retryNum: '0',
+ label: 'region=wuxi'
},
{
applicationName: 'shop-user',
instanceNum: 12,
- instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+ instanceName: 'shop-order5',
+ rpcPort: '172.168.45.89:20888',
+ timeout: '1000ms',
+ retryNum: '2',
+ label: 'region=beijing'
},
{
applicationName: 'shop-order',
instanceNum: 15,
- instanceIP: ['192.168.32.28:8697', '192.168.32.26:20880', '192.168.32.24:28080']
+ instanceName: 'shop-order6',
+ rpcPort: '172.168.45.89:20888',
+ timeout: '1000ms',
+ retryNum: '2',
+ label: 'region=ningbo'
},
{
applicationName: 'shop-user',
instanceNum: 12,
- instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+ instanceName: 'shop-order7',
+ rpcPort: '172.168.45.89:20888',
+ timeout: '1000ms',
+ retryNum: '2',
+ label: 'region=shenzhen'
},
{
applicationName: 'shop-user',
instanceNum: 12,
- instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+ instanceName: 'shop-order8',
+ rpcPort: '172.168.45.89:20888',
+ timeout: '1000ms',
+ retryNum: '2',
+ label: 'region=guangzhou'
},
{
applicationName: 'shop-order',
instanceNum: 15,
- instanceIP: ['192.168.32.28:8697', '192.168.32.26:20880', '192.168.32.24:28080']
+ instanceName: 'shop-order9',
+ rpcPort: '172.168.45.89:20888',
+ timeout: '1000ms',
+ retryNum: '2',
+ label: 'region=nanjing'
},
{
applicationName: 'shop-user',
instanceNum: 12,
- instanceIP: ['192.168.32.28:8697', '192.168.32.24:28080']
+ instanceName: 'shop-order10',
+ rpcPort: '172.168.45.89:20888',
+ timeout: '1000ms',
+ retryNum: '2',
+ label: 'region=beijing'
}
]
}
diff --git a/ui-vue3/src/api/service/instance.ts b/ui-vue3/src/api/service/instance.ts
index c07ef80..e133c4a 100644
--- a/ui-vue3/src/api/service/instance.ts
+++ b/ui-vue3/src/api/service/instance.ts
@@ -24,3 +24,19 @@
params
})
}
+
+export const getInstanceDetail = (params: any): Promise<any> => {
+ return request({
+ url: '/instance/detail',
+ method: 'get',
+ params
+ })
+}
+
+export const getInstanceMetricsInfo = (params: any): Promise<any> => {
+ return request({
+ url: '/instance/metrics',
+ method: 'get',
+ params
+ })
+}
diff --git a/ui-vue3/src/base/i18n/en.ts b/ui-vue3/src/base/i18n/en.ts
index 03deca7..f197248 100644
--- a/ui-vue3/src/base/i18n/en.ts
+++ b/ui-vue3/src/base/i18n/en.ts
@@ -18,7 +18,68 @@
import type { I18nType } from './type.ts'
const words: I18nType = {
+ destinationRuleDomain: {
+ YAMLView: 'YAML view',
+ formView: 'Form view'
+ },
+ virtualServiceDomain: {
+ YAMLView: 'YAML view',
+ formView: 'Form view'
+ },
+ dynamicConfigDomain: {
+ YAMLView: 'YAML view',
+ formView: 'Form view'
+ },
+ routingRuleDomain: {
+ YAMLView: 'YAML view',
+ formView: 'Form view'
+ },
+ tagRuleDomain: {
+ YAMLView: 'YAML view',
+ formView: 'Form view'
+ },
+ flowControlDomain: {
+ actuatingRange: 'Actuating range',
+ notSet: 'Not set',
+ versionRecords: 'Version records',
+ YAMLView: 'YAML View',
+ addConfiguration: 'Add configuration',
+ addConfigurationItem: 'Add configurationItem',
+ addFilter: 'Add filter',
+ configurationItem: 'Configuration item',
+ scopeScreening: 'Scope screening',
+ endOfAction: 'End of action',
+ addLabel: 'Add label',
+ actions: 'Actions',
+ filterType: 'Filter type',
+ labelName: 'Label name',
+ formView: 'Form view',
+ addMatch: 'Add match',
+ addRouter: 'Add router',
+ addressSubsetMatching: 'Address subset matching',
+ value: 'Value',
+ relation: 'Relation',
+ parameter: 'Parameter',
+ matchingDimension: 'Matching dimension',
+ requestParameterMatching: 'Request parameter matching',
+ ruleName: 'Rule name',
+ actionObject: 'Action object',
+ faultTolerantProtection: 'Fault-tolerant protection',
+ runTimeEffective: 'Run time effective',
+ ruleGranularity: 'Rule granularity',
+ timeOfTakingEffect: 'Time of taking effect',
+ enabledStatus: 'Enabled status',
+ priority: 'Priority',
+ off: 'off',
+ on: 'on',
+ opened: 'Opened',
+ closed: 'Closed',
+ enabled: 'Enabled',
+ disabled: 'Disabled'
+ },
instanceDomain: {
+ flowDisabled: 'Flow disabled',
+ operatorLog: 'Operator log',
enableAppInstanceLogs: 'Enable access logs for all instances of this application',
appServiceRetries: 'Adjust the number of retries for the service provided by this application',
appServiceLoadBalance:
@@ -61,6 +122,9 @@
labels: 'Labels',
startTime_k8s: 'StartTime(k8s)'
},
+ serviceDomain: {
+ name: 'Name'
+ },
appServiceTimeout: 'Adjusting the timeout for application service provision',
enableAppInstanceLogs: 'Enable access logs for all instances of this application',
appServiceLoadBalance: 'Adjusting the load balancing strategy for application service provision',
@@ -99,6 +163,10 @@
instance: 'Instance',
resourceDetails: 'Resource Details',
service: 'Service',
+ versionGroup: 'Version & Group',
+ avgQPS: 'last 1min QPS',
+ avgRT: 'last 1min RT',
+ requestTotal: 'last 1min request total',
serviceSearch: 'Search Service',
serviceGovernance: 'Routing Rule',
trafficManagement: 'Traffic Management',
@@ -391,6 +459,7 @@
debug: 'Debug',
distribution: 'Distribution',
tracing: 'Tracing',
+ sceneConfig: 'Scene Config',
provideService: 'Provide Service',
dependentService: 'Dependent Service',
diff --git a/ui-vue3/src/base/i18n/zh.ts b/ui-vue3/src/base/i18n/zh.ts
index 8428a8c..28e68a7 100644
--- a/ui-vue3/src/base/i18n/zh.ts
+++ b/ui-vue3/src/base/i18n/zh.ts
@@ -18,7 +18,68 @@
import type { I18nType } from './type.ts'
const words: I18nType = {
+ destinationRuleDomain: {
+ YAMLView: 'YAML视图',
+ formView: '表单视图'
+ },
+ virtualServiceDomain: {
+ YAMLView: 'YAML视图',
+ formView: '表单视图'
+ },
+ dynamicConfigDomain: {
+ YAMLView: 'YAML视图',
+ formView: '表单视图'
+ },
+ routingRuleDomain: {
+ YAMLView: 'YAML视图',
+ formView: '表单视图'
+ },
+ tagRuleDomain: {
+ YAMLView: 'YAML视图',
+ formView: '表单视图'
+ },
+ flowControlDomain: {
+ notSet: '未设置',
+ versionRecords: '版本记录',
+ YAMLView: 'YAML视图',
+ addConfiguration: '增加配置',
+ addConfigurationItem: '增加配置项',
+ addFilter: '增加筛选',
+ configurationItem: '配置项',
+ actuatingRange: '作用范围',
+ scopeScreening: '作用范围筛选',
+ endOfAction: '作用端',
+ actions: '操作',
+ filterType: '筛选类型',
+ labelName: '标签名',
+ formView: '表单视图',
+ addMatch: '增加匹配',
+ addRouter: '增加路由',
+ addLabel: '增加标签',
+ addressSubsetMatching: '地址子集匹配',
+ value: '值',
+ relation: '关系',
+ requestParameterMatching: '请求参数匹配',
+ matchingDimension: '匹配维度',
+ parameter: '参数',
+ ruleName: '规则名',
+ actionObject: '作用对象',
+ faultTolerantProtection: '容错保护',
+ runTimeEffective: '运行时生效',
+ ruleGranularity: '规则粒度',
+ timeOfTakingEffect: '生效时间',
+ enabledStatus: '启用状态',
+ priority: '优先级',
+ off: '关',
+ on: '开',
+ opened: '开启',
+ closed: '关闭',
+ enabled: '启用',
+ disabled: '禁用'
+ },
instanceDomain: {
+ flowDisabled: '流量禁用',
+ operatorLog: '执行日志',
CPU: 'CPU',
enableAppInstanceLogs: '开启该应用所有实例的访问日志',
appServiceLoadBalance: '调整应用提供服务的负载均衡策略',
@@ -35,7 +96,7 @@
loadBalance: '负载均衡',
monitor: '监控',
linkTracking: '链路追踪',
- configuration: '配置',
+ configuration: '场景配置',
event: '事件',
healthExamination_k8s: '健康检查(k8s)',
instanceLabel: '实例标签',
@@ -61,7 +122,14 @@
creationTime_k8s: '创建时间(k8s)',
startTime_k8s: '启动时间(k8s)'
},
+ serviceDomain: {
+ name: '服务名'
+ },
service: '服务',
+ versionGroup: '版本&分组',
+ avgQPS: '近1min QPS',
+ avgRT: '近1min RT',
+ requestTotal: '近1min 请求总量',
serviceSearch: '服务查询',
serviceGovernance: '路由规则',
trafficManagement: '流量管控',
@@ -363,6 +431,7 @@
distribution: '分布',
monitor: '监控',
tracing: '链路追踪',
+ sceneConfig: '场景配置',
event: '事件',
provideService: '提供服务',
diff --git a/ui-vue3/src/router/defaultRoutes.ts b/ui-vue3/src/router/defaultRoutes.ts
index d67c200..a33feef 100644
--- a/ui-vue3/src/router/defaultRoutes.ts
+++ b/ui-vue3/src/router/defaultRoutes.ts
@@ -20,6 +20,7 @@
import LayoutTab from '../layout/tab/layout_tab.vue'
import _ from 'lodash'
import AppTabHeaderSlot from '@/views/resources/applications/slots/AppTabHeaderSlot.vue'
+import ServiceTabHeaderSlot from '@/views/resources/services/slots/ServiceTabHeaderSlot.vue'
export declare type RouteRecordType = RouteRecordRaw & {
key?: string
@@ -208,7 +209,10 @@
redirect: 'search',
component: LayoutTab,
meta: {
- tab_parent: true
+ tab_parent: true,
+ slots: {
+ header: ServiceTabHeaderSlot
+ }
},
children: [
{
@@ -220,7 +224,7 @@
}
},
{
- path: '/detail/:serviceName',
+ path: '/detail/:pathId',
name: 'detail',
component: () => import('../views/resources/services/tabs/detail.vue'),
meta: {
@@ -228,7 +232,7 @@
}
},
{
- path: '/debug/:serviceName',
+ path: '/debug/:pathId',
name: 'debug',
component: () => import('../views/resources/services/tabs/debug.vue'),
meta: {
@@ -236,7 +240,7 @@
}
},
{
- path: '/distribution/:serviceName',
+ path: '/distribution/:pathId',
name: 'distribution',
component: () => import('../views/resources/services/tabs/distribution.vue'),
meta: {
@@ -244,7 +248,7 @@
}
},
{
- path: '/monitor/:serviceName',
+ path: '/monitor/:pathId',
name: 'monitor',
component: () => import('../views/resources/services/tabs/monitor.vue'),
meta: {
@@ -252,7 +256,7 @@
}
},
{
- path: '/tracing/:serviceName',
+ path: '/tracing/:pathId',
name: 'tracing',
component: () => import('../views/resources/services/tabs/tracing.vue'),
meta: {
@@ -260,7 +264,15 @@
}
},
{
- path: '/event/:serviceName',
+ path: '/sceneConfig/:pathId',
+ name: 'sceneConfig',
+ component: () => import('../views/resources/services/tabs/sceneConfig.vue'),
+ meta: {
+ tab: true
+ }
+ },
+ {
+ path: '/event/:pathId',
name: 'event',
component: () => import('../views/resources/services/tabs/event.vue'),
meta: {
@@ -281,19 +293,113 @@
{
path: '/routingRule',
name: 'routingRule',
- component: () => import('../views/traffic/routingRule/index.vue')
+ redirect: 'index',
+ component: LayoutTab,
+ meta: {
+ tab_parent: true
+ },
+ children: [
+ {
+ path: '/index',
+ name: 'routingRuleIndex',
+ component: () => import('../views/traffic/routingRule/index.vue'),
+ meta: {
+ hidden: true
+ }
+ },
+ {
+ path: '/formview/:ruleName',
+ name: 'routingRuleDomain.formView',
+ component: () => import('../views/traffic/routingRule/tabs/formView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:apm-trace'
+ }
+ },
+ {
+ path: '/yamlview/:ruleName',
+ name: 'routingRuleDomain.YAMLView',
+ component: () => import('../views/traffic/routingRule/tabs/YAMLView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:app-console'
+ }
+ }
+ ]
},
{
path: '/tagRule',
name: 'tagRule',
- component: () => import('../views/traffic/tagRule/index.vue'),
- meta: {}
+ redirect: 'index',
+ component: LayoutTab,
+ meta: {
+ tab_parent: true
+ },
+ children: [
+ {
+ path: '/index',
+ name: 'tagRuleIndex',
+ component: () => import('../views/traffic/tagRule/index.vue'),
+ meta: {
+ hidden: true
+ }
+ },
+ {
+ path: '/formview/:ruleName',
+ name: 'tagRuleDomain.formView',
+ component: () => import('../views/traffic/tagRule/tabs/formView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:apm-trace'
+ }
+ },
+ {
+ path: '/yamlview/:ruleName',
+ name: 'tagRuleDomain.YAMLView',
+ component: () => import('../views/traffic/tagRule/tabs/YAMLView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:app-console'
+ }
+ }
+ ]
},
{
path: '/dynamicConfig',
name: 'dynamicConfig',
- component: () => import('../views/traffic/dynamicConfig/index.vue'),
- meta: {}
+ redirect: 'index',
+ component: LayoutTab,
+ meta: {
+ tab_parent: true
+ },
+ children: [
+ {
+ path: '/index',
+ name: 'dynamicConfigIndex',
+ component: () => import('../views/traffic/dynamicConfig/index.vue'),
+ meta: {
+ hidden: true
+ }
+ },
+ {
+ path: '/formview/:ruleName',
+ name: 'dynamicConfigDomain.formView',
+ component: () => import('../views/traffic/dynamicConfig/tabs/formView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:apm-trace'
+ }
+ },
+ {
+ path: '/yamlview/:ruleName',
+ name: 'dynamicConfigDomain.YAMLView',
+ component: () => import('../views/traffic/dynamicConfig/tabs/YAMLView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:app-console'
+ }
+ }
+ ]
},
{
path: '/meshRule',
@@ -302,12 +408,76 @@
{
path: '/virtualService',
name: 'virtualService',
- component: () => import('../views/traffic/virtualService/index.vue')
+ redirect: 'index',
+ component: LayoutTab,
+ meta: {
+ tab_parent: true
+ },
+ children: [
+ {
+ path: '/index',
+ name: 'virtualServiceIndex',
+ component: () => import('../views/traffic/virtualService/index.vue'),
+ meta: {
+ hidden: true
+ }
+ },
+ {
+ path: '/formview/:ruleName',
+ name: 'virtualServiceDomain.formView',
+ component: () => import('../views/traffic/virtualService/tabs/formView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:apm-trace'
+ }
+ },
+ {
+ path: '/yamlview/:ruleName',
+ name: 'virtualServiceDomain.YAMLView',
+ component: () => import('../views/traffic/virtualService/tabs/YAMLView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:app-console'
+ }
+ }
+ ]
},
{
path: '/destinationRule',
name: 'destinationRule',
- component: () => import('../views/traffic/destinationRule/index.vue')
+ redirect: 'index',
+ component: LayoutTab,
+ meta: {
+ tab_parent: true
+ },
+ children: [
+ {
+ path: '/index',
+ name: 'destinationRuleIndex',
+ component: () => import('../views/traffic/destinationRule/index.vue'),
+ meta: {
+ hidden: true
+ }
+ },
+ {
+ path: '/formview/:ruleName',
+ name: 'destinationRuleDomain.formView',
+ component: () => import('../views/traffic/destinationRule/tabs/formView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:apm-trace'
+ }
+ },
+ {
+ path: '/yamlview/:ruleName',
+ name: 'destinationRuleDomain.YAMLView',
+ component: () => import('../views/traffic/destinationRule/tabs/YAMLView.vue'),
+ meta: {
+ tab: true,
+ icon: 'oui:app-console'
+ }
+ }
+ ]
}
]
}
diff --git a/ui-vue3/src/utils/SearchUtil.ts b/ui-vue3/src/utils/SearchUtil.ts
index aeb39ec..e6f0fcb 100644
--- a/ui-vue3/src/utils/SearchUtil.ts
+++ b/ui-vue3/src/utils/SearchUtil.ts
@@ -41,6 +41,7 @@
]
searchApi: Function
result: any
+ handleResult?: Function
tableStyle: any
table: {
loading?: boolean
@@ -57,7 +58,8 @@
searchApi: any,
columns: TableColumnsType | any,
paged?: any | undefined,
- noPaged?: boolean
+ noPaged?: boolean,
+ handleResult?: Function
) {
this.params = query
this.noPaged = noPaged
@@ -72,16 +74,17 @@
this.paged = { ...this.paged, ...paged }
}
this.searchApi = searchApi
- this.onSearch()
+ handleResult && this.onSearch(handleResult)
}
- async onSearch() {
+ async onSearch(handleResult: Function) {
this.table.loading = true
setTimeout(() => {
this.table.loading = false
}, 5000)
- let res = (await this.searchApi(this.queryForm || {})).data
- this.result = res.data
+ const res = (await this.searchApi(this.queryForm || {})).data
+ this.result = handleResult ? handleResult(res.data) : res.data
+ console.log(this.result)
this.paged.total = res.total
this.table.loading = false
}
diff --git a/ui-vue3/src/views/resources/instances/index.vue b/ui-vue3/src/views/resources/instances/index.vue
index cf6e201..b4fdac8 100644
--- a/ui-vue3/src/views/resources/instances/index.vue
+++ b/ui-vue3/src/views/resources/instances/index.vue
@@ -23,31 +23,40 @@
</template>
<template v-if="column.dataIndex === 'deployCluster'">
- <a-tag color="success">
+ <a-tag color="grey">
{{ text }}
</a-tag>
</template>
+ <template v-if="column.dataIndex === 'deployState'">
+ <a-tag :color="INSTANCE_DEPLOY_COLOR[text.toUpperCase()]">{{ text }}</a-tag>
+ </template>
+
<template v-if="column.dataIndex === 'registerStates'">
- <a-typography-text type="success" v-for="t in text">{{ t.label }}</a-typography-text>
+ <a-tag :color="INSTANCE_REGISTER_COLOR[t.level.toUpperCase()]" v-for="t in text">
+ {{ t.label }}
+ </a-tag>
</template>
<template v-if="column.dataIndex === 'registerClusters'">
- <a-tag v-for="t in text" color="warning">
+ <a-tag v-for="t in text" color="grey">
{{ t }}
</a-tag>
</template>
<template v-if="column.dataIndex === 'labels'">
- <a-tag v-for="t in text" color="warning">
+ <a-tag v-for="t in text" color="grey">
{{ t }}
</a-tag>
</template>
- <template v-if="column.dataIndex === 'name'">
- <router-link :to="`detail/${record[column.key]}`">{{ text }}</router-link>
- </template>
+
<template v-if="column.dataIndex === 'ip'">
- <router-link :to="`detail/${record[column.key]}`">{{ text }}</router-link>
+ <span class="app-link" @click="router.replace(`detail/${record[column.key]}`)">
+ <b>
+ <Icon style="margin-bottom: -2px" icon="material-symbols:attach-file-rounded"></Icon>
+ {{ text }}
+ </b>
+ </span>
</template>
</template>
</search-table>
@@ -60,14 +69,19 @@
import SearchTable from '@/components/SearchTable.vue'
import { SearchDomain, sortString } from '@/utils/SearchUtil'
import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+import { INSTANCE_DEPLOY_COLOR, INSTANCE_REGISTER_COLOR } from '@/base/constants'
+import router from '@/router'
+import { Icon } from '@iconify/vue'
+import { PRIMARY_COLOR } from '@/base/constants'
+let __null = PRIMARY_COLOR
let columns = [
{
title: 'instanceDomain.instanceIP',
key: 'ip',
dataIndex: 'ip',
sorter: (a: any, b: any) => sortString(a.ip, b.ip),
- width: 140
+ width: 200
},
{
title: 'instanceDomain.instanceName',
@@ -169,5 +183,15 @@
<style lang="less" scoped>
.search-table-container {
min-height: 60vh;
+
+ .app-link {
+ padding: 4px 10px 4px 4px;
+ border-radius: 4px;
+ color: v-bind('PRIMARY_COLOR');
+ &:hover {
+ cursor: pointer;
+ background: rgba(133, 131, 131, 0.13);
+ }
+ }
}
</style>
diff --git a/ui-vue3/src/views/resources/instances/tabs/configuration.vue b/ui-vue3/src/views/resources/instances/tabs/configuration.vue
index 26e4c98..b590e61 100644
--- a/ui-vue3/src/views/resources/instances/tabs/configuration.vue
+++ b/ui-vue3/src/views/resources/instances/tabs/configuration.vue
@@ -16,105 +16,71 @@
-->
<template>
- <a-descriptions title="" layout="vertical" :column="2">
- <!-- execution log -->
- <a-descriptions-item :labelStyle="{ fontWeight: 'bold' }">
- <template v-slot:label>
- {{ $t('instanceDomain.executionLog') }}
- <a-tooltip placement="topLeft">
- <template #title>
- {{ $t('instanceDomain.enableAppInstanceLogs') }}(provider.accesslog)
- </template>
- <Icon icon="bitcoin-icons:info-circle-outline" class="iconStyle" />
- </a-tooltip>
+ <div class="__container_app_config">
+ <config-page :options="options">
+ <template v-slot:form_log="{ current }">
+ <a-form-item :label="$t('instanceDomain.operatorLog')" name="logFlag">
+ <a-switch v-model:checked="current.form.logFlag"></a-switch>
+ </a-form-item>
</template>
- <span :class="{ active: !state }" :style="{ color: 'black' }">
- {{ $t('instanceDomain.close') }}
- </span>
- <a-switch v-model:checked="state" :loading="loading" />
- <span :class="{ active: state }" :style="{ color: state ? PRIMARY_COLOR : 'black' }">
- {{ $t('instanceDomain.enable') }}
- </span>
- </a-descriptions-item>
-
- <!-- retry count -->
- <a-descriptions-item :labelStyle="{ fontWeight: 'bold' }">
- <template v-slot:label>
- {{ $t('instanceDomain.retryCount') }}
- <a-tooltip placement="topLeft">
- <template #title>{{ $t('instanceDomain.appServiceRetries') }}</template>
- <Icon icon="bitcoin-icons:info-circle-outline" class="iconStyle" />
- </a-tooltip>
+ <template v-slot:form_flowDisabled="{ current }">
+ <a-form-item :label="$t('instanceDomain.flowDisabled')" name="flowDisabledFlag">
+ <a-switch v-model:checked="current.form.flowDisabledFlag"></a-switch>
+ </a-form-item>
</template>
- <a-typography-paragraph editable v-model:content="retryCount"> </a-typography-paragraph>
- </a-descriptions-item>
-
- <!-- Load Balance -->
- <a-descriptions-item :labelStyle="{ fontWeight: 'bold' }">
- <template v-slot:label>
- {{ $t('instanceDomain.loadBalance') }}
- <a-tooltip placement="topLeft">
- <template #title
- >{{ $t('instanceDomain.appServiceLoadBalance') }}(provider.loadbalance)</template
- >
- <Icon icon="bitcoin-icons:info-circle-outline" class="iconStyle" />
- </a-tooltip>
- </template>
- <a-typography-paragraph editable v-model:content="loadBalance"> </a-typography-paragraph>
- </a-descriptions-item>
-
- <!-- timeout -->
- <a-descriptions-item :labelStyle="{ fontWeight: 'bold' }">
- <template v-slot:label>
- {{ $t('instanceDomain.timeout_ms') }}
- <a-tooltip placement="topLeft">
- <template #title>
- {{ $t('instanceDomain.appServiceTimeout') }}(provider.timeout)
- </template>
- <Icon icon="bitcoin-icons:info-circle-outline" class="iconStyle" />
- </a-tooltip>
- </template>
-
- <a-typography-paragraph editable v-model:content="timeout"> </a-typography-paragraph>
- </a-descriptions-item>
-
- <!-- Cluster approach -->
- <a-descriptions-item :labelStyle="{ fontWeight: 'bold' }">
- <template v-slot:label>
- {{ $t('instanceDomain.clusterApproach') }}
- <a-tooltip placement="topLeft">
- <template #title>{{ $t('instanceDomain.appServiceNegativeClusteringMethod') }}</template>
- <Icon icon="bitcoin-icons:info-circle-outline" class="iconStyle" />
- </a-tooltip>
- </template>
-
- <a-typography-paragraph editable v-model:content="clusterApproach"> </a-typography-paragraph>
- </a-descriptions-item>
- </a-descriptions>
+ </config-page>
+ </div>
</template>
<script lang="ts" setup>
-import { ref, onMounted } from 'vue'
-import { Icon } from '@iconify/vue'
-import { PRIMARY_COLOR } from '@/base/constants'
-
-// 执行日志开关
-const state = ref('')
-const loading = ref(false)
-const loadBalance = ref('random')
-const clusterApproach = ref('failover')
-const retryCount = ref('2次')
-const timeout = ref('1000 ms')
+import { onMounted, reactive, ref } from 'vue'
+import ConfigPage from '@/components/ConfigPage.vue'
+let options: any = reactive({
+ list: [
+ {
+ title: 'instanceDomain.operatorLog',
+ key: 'log',
+ form: {
+ logFlag: false
+ },
+ submit: (form: {}) => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(1)
+ }, 1000)
+ })
+ },
+ reset(form: any) {
+ form.logFlag = false
+ }
+ },
+ {
+ title: 'instanceDomain.flowDisabled',
+ form: {
+ flowDisabledFlag: false
+ },
+ key: 'flowDisabled',
+ submit: (form: {}) => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(1)
+ }, 1000)
+ })
+ },
+ reset(form: any) {
+ form.logFlag = false
+ }
+ }
+ ],
+ current: [0]
+})
+onMounted(() => {
+ console.log(333)
+})
</script>
<style lang="less" scoped>
-.active {
- font-size: 13px;
- font-weight: bold;
-}
-
-.iconStyle {
- font-size: 17px;
+.__container_app_config {
}
</style>
diff --git a/ui-vue3/src/views/resources/instances/tabs/details.vue b/ui-vue3/src/views/resources/instances/tabs/details.vue
index dbac10f..179ad36 100644
--- a/ui-vue3/src/views/resources/instances/tabs/details.vue
+++ b/ui-vue3/src/views/resources/instances/tabs/details.vue
@@ -18,161 +18,237 @@
<template>
<div class="__container_app_detail">
<a-flex>
- <a-descriptions title="" layout="vertical" :column="2">
- <!-- instanceName -->
- <a-descriptions-item
- :label="$t('instanceDomain.instanceName')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph copyable>
- {{ instanceName }}
- </a-typography-paragraph>
- </a-descriptions-item>
+ <a-card-grid>
+ <a-row :gutter="10">
+ <a-col :span="12">
+ <a-card class="_detail">
+ <a-descriptions class="description-column" :column="1">
+ <!-- instanceName -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.instanceName')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p @click="copyIt(instanceName)" class="description-item-content with-card">
+ {{ instanceName }}
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
- <!-- Creation time -->
- <a-descriptions-item
- :label="$t('instanceDomain.creationTime_k8s')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph> 20230/12/19 22:09:34 </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- Creation time -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.creationTime_k8s')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> 20230/12/19 22:09:34</a-typography-paragraph>
+ </a-descriptions-item>
- <!-- deployState -->
- <a-descriptions-item
- :label="$t('instanceDomain.deployState')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph type="success"> Running </a-typography-paragraph>
- <a-typography-paragraph type="danger"> Stop </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- deployState -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.deployState')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph type="success"> Running</a-typography-paragraph>
+ <a-typography-paragraph type="danger"> Stop</a-typography-paragraph>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-card>
+ </a-col>
- <!-- Start time -->
- <a-descriptions-item
- :label="$t('instanceDomain.startTime_k8s')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph> 20230/12/19 22:09:34 </a-typography-paragraph>
- </a-descriptions-item>
+ <a-col :span="12">
+ <a-card class="_detail">
+ <a-descriptions class="description-column" :column="1">
+ <!-- Start time -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.startTime_k8s')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> 20230/12/19 22:09:34</a-typography-paragraph>
+ </a-descriptions-item>
- <!-- registerStates -->
- <a-descriptions-item
- :label="$t('instanceDomain.registerStates')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph type="success"> Registed </a-typography-paragraph>
- <a-typography-paragraph type="danger"> UnRegisted </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- registerStates -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.registerStates')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph type="success"> Registed</a-typography-paragraph>
+ <a-typography-paragraph type="danger"> UnRegisted</a-typography-paragraph>
+ </a-descriptions-item>
- <!-- Register Time -->
- <a-descriptions-item
- :label="$t('instanceDomain.registerTime')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph> 20230/12/19 22:09:34 </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- Register Time -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.registerTime')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> 20230/12/19 22:09:34</a-typography-paragraph>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-card>
+ </a-col>
+ </a-row>
- <!-- instanceIP -->
- <a-descriptions-item
- :label="$t('instanceDomain.instanceIP')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph copyable> 192.168.0.1 </a-typography-paragraph>
- </a-descriptions-item>
+ <a-card style="margin-top: 10px" class="_detail">
+ <a-descriptions class="description-column" :column="1">
+ <!-- instanceIP -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.instanceIP')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p @click="copyIt('192.168.0.1')" class="description-item-content with-card">
+ 192.168.0.1
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
- <!-- deploy cluster -->
- <a-descriptions-item
- :label="$t('instanceDomain.deployCluster')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph> sz-ali-zk-f8otyo4r </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- deploy cluster -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.deployCluster')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> sz-ali-zk-f8otyo4r</a-typography-paragraph>
+ </a-descriptions-item>
- <!-- Dubbo Port -->
- <a-descriptions-item
- :label="$t('instanceDomain.dubboPort')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph copyable> 2088 </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- Dubbo Port -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.dubboPort')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p @click="copyIt('2088')" class="description-item-content with-card">
+ 2088
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
- <!-- Register cluster -->
- <a-descriptions-item
- :label="$t('instanceDomain.registerCluster')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph> sz-ali-zk-f8otyo4r </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- Register cluster -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.registerCluster')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> sz-ali-zk-f8otyo4r</a-typography-paragraph>
+ </a-descriptions-item>
- <!-- whichApplication -->
- <a-descriptions-item
- :label="$t('instanceDomain.whichApplication')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-link @click="checkApplication()"> shop-b </a-typography-link>
- </a-descriptions-item>
+ <!-- whichApplication -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.whichApplication')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-link @click="checkApplication()"> shop-b</a-typography-link>
+ </a-descriptions-item>
- <!-- Node IP -->
- <a-descriptions-item
- :label="$t('instanceDomain.node')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph copyable> 30.33.0.1 </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- Node IP -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.node')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p @click="copyIt('30.33.0.1')" class="description-item-content with-card">
+ 30.33.0.1
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
- <!-- Owning workload(k8s) -->
- <a-descriptions-item
- :label="$t('instanceDomain.owningWorkload_k8s')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph> shop-user-base </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- Owning workload(k8s) -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.owningWorkload_k8s')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> shop-user-base</a-typography-paragraph>
+ </a-descriptions-item>
- <!-- image -->
- <a-descriptions-item
- :label="$t('instanceDomain.instanceImage_k8s')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-typography-paragraph copyable>
- apache/org.apahce.dubbo.samples.shop-user:v1
- </a-typography-paragraph>
- </a-descriptions-item>
+ <!-- image -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.instanceImage_k8s')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-card class="description-item-card">
+ <p
+ @click="copyIt(' apache/org.apahce.dubbo.samples.shop-user:v1')"
+ class="description-item-content with-card"
+ >
+ apache/org.apahce.dubbo.samples.shop-user:v1
+ <CopyOutlined />
+ </p>
+ </a-card>
+ </a-descriptions-item>
- <!-- instanceLabel -->
- <a-descriptions-item
- :label="$t('instanceDomain.instanceLabel')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-card class="description-item-card">
- <a-tag>app=shop-user</a-tag>
- <a-tag>version=v1</a-tag>
- <a-tag>region=shenzhen</a-tag>
- </a-card>
- </a-descriptions-item>
+ <!-- instanceLabel -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.instanceLabel')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-card class="description-item-card">
+ <a-tag>app=shop-user</a-tag>
+ <a-tag>version=v1</a-tag>
+ <a-tag>region=shenzhen</a-tag>
+ </a-card>
+ </a-descriptions-item>
- <!-- health examination -->
- <a-descriptions-item
- :label="$t('instanceDomain.healthExamination_k8s')"
- :labelStyle="{ fontWeight: 'bold' }"
- >
- <a-card class="description-item-card">
- <p class="white_space">启动探针(StartupProbe):关闭</p>
- <p class="white_space">就绪探针(ReadinessProbe):开启 类型: Http 端口:22222</p>
- <p class="white_space">存活探针(LivenessProbe):开启 类型: Http 端口:22222</p>
- </a-card>
- </a-descriptions-item>
- </a-descriptions>
+ <!-- health examination -->
+ <a-descriptions-item
+ :label="$t('instanceDomain.healthExamination_k8s')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-card class="description-item-card">
+ <p class="white_space">启动探针(StartupProbe):关闭</p>
+ <p class="white_space">就绪探针(ReadinessProbe):开启 类型: Http 端口:22222</p>
+ <p class="white_space">存活探针(LivenessProbe):开启 类型: Http 端口:22222</p>
+ </a-card>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-card>
+ </a-card-grid>
</a-flex>
</div>
</template>
<script lang="ts" setup>
-import { ref } from 'vue'
+import { type ComponentInternalInstance, getCurrentInstance, ref } from 'vue'
+import { CopyOutlined } from '@ant-design/icons-vue'
+import useClipboard from 'vue-clipboard3'
+import { message } from 'ant-design-vue'
+import { PRIMARY_COLOR, PRIMARY_COLOR_T } from '@/base/constants'
+
+const {
+ appContext: {
+ config: { globalProperties }
+ }
+} = <ComponentInternalInstance>getCurrentInstance()
+
+let __ = PRIMARY_COLOR
+let PRIMARY_COLOR_20 = PRIMARY_COLOR_T('20')
// instance name
const instanceName = ref('shop-user-base-7e33f1e')
// Click on the application name to view the application
const checkApplication = () => {}
+
+const toClipboard = useClipboard().toClipboard
+
+function copyIt(v: string) {
+ message.success(globalProperties.$t('messageDomain.success.copy'))
+ toClipboard(v)
+}
</script>
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+.__container_app_detail {
+ ._detail {
+ box-shadow: 8px 8px 4px rgba(162, 162, 162, 0.19);
+ }
+ .description-item-content {
+ &.no-card {
+ padding-left: 20px;
+ }
+ &.with-card:hover {
+ color: v-bind('PRIMARY_COLOR');
+ }
+ }
+ .description-item-card {
+ :deep(.ant-card-body) {
+ padding: 10px;
+ }
+ width: 80%;
+ margin-left: 20px;
+ border: 1px dashed rgba(162, 162, 162, 0.19);
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/instances/tabs/monitor.vue b/ui-vue3/src/views/resources/instances/tabs/monitor.vue
index bd1abc2..66cd44a 100644
--- a/ui-vue3/src/views/resources/instances/tabs/monitor.vue
+++ b/ui-vue3/src/views/resources/instances/tabs/monitor.vue
@@ -16,11 +16,66 @@
-->
<template>
- <div>monitor todo</div>
+ <div class="__container_tabDemo3">
+ <div class="option">
+ <a-button class="btn" @click="refresh"> refresh </a-button>
+ <a-button class="btn" @click="newPageForGrafana"> grafana </a-button>
+ </div>
+ <a-spin class="spin" :spinning="!showIframe">
+ <div class="__container_iframe_container">
+ <iframe v-if="showIframe" id="grafanaIframe" :src="grafanaUrl" frameborder="0"></iframe>
+ </div>
+ </a-spin>
+ </div>
</template>
-<script lang="ts" setup>
-import { ref } from 'vue'
-</script>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import { getInstanceMetricsInfo } from '@/api/service/instance'
-<style lang="less" scoped></style>
+let grafanaUrl = ref('')
+let showIframe = ref(true)
+onMounted(async () => {
+ let res = await getInstanceMetricsInfo({})
+ grafanaUrl.value = res.data
+})
+
+function refresh() {
+ showIframe.value = false
+ setTimeout(() => {
+ showIframe.value = true
+ }, 200)
+}
+
+function newPageForGrafana() {
+ window.open(grafanaUrl.value, '_blank')
+}
+</script>
+<style lang="less" scoped>
+.__container_tabDemo3 {
+ .option {
+ padding-left: 16px;
+ .btn {
+ margin-right: 10px;
+ }
+ }
+ :deep(.spin) {
+ margin-top: 30px;
+ }
+ .__container_iframe_container {
+ z-index: 1;
+ position: relative;
+ width: calc(100vw - 332px);
+ height: calc(100vh - 200px);
+ clip-path: inset(20px 10px);
+
+ #grafanaIframe {
+ z-index: 0;
+ top: -112px;
+ position: absolute;
+ width: calc(100vw - 332px);
+ height: calc(100vh - 200px);
+ }
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/search.vue b/ui-vue3/src/views/resources/services/search.vue
index 73ef847..64db7ad 100644
--- a/ui-vue3/src/views/resources/services/search.vue
+++ b/ui-vue3/src/views/resources/services/search.vue
@@ -19,7 +19,23 @@
<search-table :search-domain="searchDomain">
<template #bodyCell="{ column, text }">
<template v-if="column.dataIndex === 'serviceName'">
- <a-button type="link" @click="viewDetail(text)">{{ text }}</a-button>
+ <span class="service-link" @click="viewDetail(text)">
+ <b>
+ <Icon style="margin-bottom: -2px" icon="material-symbols:attach-file-rounded"></Icon>
+ {{ text }}
+ </b>
+ </span>
+ </template>
+ <template v-else-if="column.dataIndex === 'versionGroupSelect'">
+ <a-select v-model:value="text.versionGroupValue" :bordered="false" style="width: 80%">
+ <a-select-option
+ v-for="(item, index) in text.versionGroupArr"
+ :value="item"
+ :key="index"
+ >
+ {{ item }}
+ </a-select-option>
+ </a-select>
</template>
</template>
</search-table>
@@ -33,69 +49,99 @@
import { SearchDomain } from '@/utils/SearchUtil'
import SearchTable from '@/components/SearchTable.vue'
import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+import { PRIMARY_COLOR } from '@/base/constants'
+import { Icon } from '@iconify/vue'
+let __null = PRIMARY_COLOR
const router = useRouter()
const columns = [
{
- title: '服务',
+ title: 'service',
+ key: 'service',
dataIndex: 'serviceName',
- key: 'serviceName',
sorter: true,
width: '30%'
},
{
- title: '接口数',
- dataIndex: 'interfaceNum',
- key: 'interfaceNum',
- sorter: true,
- width: '10%'
+ title: 'versionGroup',
+ key: 'versionGroup',
+ dataIndex: 'versionGroupSelect',
+ width: '25%'
},
{
- title: '近 1min QPS',
- dataIndex: 'avgQPS',
+ title: 'avgQPS',
key: 'avgQPS',
+ dataIndex: 'avgQPS',
sorter: true,
width: '15%'
},
{
- title: '近 1min RT',
- dataIndex: 'avgRT',
+ title: 'avgRT',
key: 'avgRT',
+ dataIndex: 'avgRT',
sorter: true,
width: '15%'
},
{
- title: '近 1min 请求总量',
- dataIndex: 'requestTotal',
+ title: 'requestTotal',
key: 'requestTotal',
+ dataIndex: 'requestTotal',
sorter: true,
width: '15%'
}
]
+const handleResult = (result: any) => {
+ return result.map((service) => {
+ service.versionGroupSelect = {}
+ service.versionGroupSelect.versionGroupArr = service.versionGroup.map((item: any) => {
+ return (item.versionGroup =
+ (item.version ? 'version: ' + item.version + ', ' : '') +
+ (item.group ? 'group: ' + item.group : '') || '无')
+ })
+ service.versionGroupSelect.versionGroupValue = service.versionGroupSelect.versionGroupArr[0]
+ return service
+ })
+}
+
const searchDomain = reactive(
new SearchDomain(
[
{
- label: '服务名',
+ label: 'serviceName',
param: 'serviceName',
- placeholder: '请输入',
+ placeholder: 'typeAppName',
style: {
width: '200px'
}
}
],
searchService,
- columns
+ columns,
+ undefined,
+ undefined,
+ handleResult
)
)
-searchDomain.onSearch()
+searchDomain.onSearch(handleResult)
const viewDetail = (serviceName: string) => {
- router.push({ name: 'detail', params: { serviceName } })
+ router.push({ name: 'detail', params: { pathId: serviceName } })
}
provide(PROVIDE_INJECT_KEY.SEARCH_DOMAIN, searchDomain)
</script>
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+.__container_services_index {
+ .service-link {
+ padding: 4px 10px 4px 4px;
+ border-radius: 4px;
+ color: v-bind('PRIMARY_COLOR');
+ &:hover {
+ cursor: pointer;
+ background: rgba(133, 131, 131, 0.13);
+ }
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/slots/ServiceTabHeaderSlot.vue b/ui-vue3/src/views/resources/services/slots/ServiceTabHeaderSlot.vue
new file mode 100644
index 0000000..2962a4e
--- /dev/null
+++ b/ui-vue3/src/views/resources/services/slots/ServiceTabHeaderSlot.vue
@@ -0,0 +1,46 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<template>
+ <!-- example like blow-->
+ <div class="__container_ServiceTabHeaderSlot">
+ <span class="header-desc">{{ $t('serviceDomain.name') }}: {{ route.params?.pathId }}</span>
+ <!-- <a-select
+ v-model:value="versionGroupSelect.versionGroupValue"
+ :bordered="false"
+ style="width: 80%"
+ >
+ <a-select-option v-for="(item, index) in versionGroupSelect.versionGroupArr" :value="item" :key="index">
+ {{ item }}
+ </a-select-option>
+ </a-select> -->
+ </div>
+</template>
+
+<script setup lang="ts">
+import { inject } from 'vue'
+import { useRoute } from 'vue-router'
+import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+const route = useRoute()
+</script>
+<style lang="less" scoped>
+.__container_ServiceTabHeaderSlot {
+ .header-desc {
+ line-height: 30px;
+ vertical-align: center;
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/tabs/debug.vue b/ui-vue3/src/views/resources/services/tabs/debug.vue
index 6e67003..796349f 100644
--- a/ui-vue3/src/views/resources/services/tabs/debug.vue
+++ b/ui-vue3/src/views/resources/services/tabs/debug.vue
@@ -19,20 +19,12 @@
<div class="tabs-title">方法列表</div>
<a-tabs v-model:activeKey="activeKey" tab-position="left" :tabBarStyle="{ width: '200px' }">
<a-tab-pane v-for="tabName in methodTabs" :key="tabName" :tab="`${tabName}`">
- <a-descriptions :column="4" layout="vertical">
- <a-descriptions-item label="接口" :span="2">
+ <a-descriptions :column="2" layout="vertical">
+ <a-descriptions-item label="接口">
<p class="description-item-content">
org.apache.dubbo.samples.UserService:getPermissions
</p>
</a-descriptions-item>
- <a-descriptions-item label="版本&分组">
- <a-select
- v-model:value="versionAndGroup"
- size="large"
- :options="versionAndGroupOptions"
- class="description-item-content"
- ></a-select>
- </a-descriptions-item>
<a-descriptions-item label="指定生产者">
<a-select
v-model:value="versionAndGroup"
@@ -41,16 +33,16 @@
class="description-item-content"
></a-select>
</a-descriptions-item>
- <a-descriptions-item label="入参类型" :span="2">
+ <a-descriptions-item label="入参类型">
<a-tree block-node :tree-data="enterParamType" class="description-item-content" />
</a-descriptions-item>
- <a-descriptions-item label="出参类型" :span="2">
+ <a-descriptions-item label="出参类型">
<a-tree block-node :tree-data="outputParamType" class="description-item-content" />
</a-descriptions-item>
- <a-descriptions-item label="请求" :span="2">
+ <a-descriptions-item label="请求">
<monaco-editor editorId="requestEditor" width="90%" height="300px" />
</a-descriptions-item>
- <a-descriptions-item label="响应" :span="2">
+ <a-descriptions-item label="响应">
<monaco-editor editorId="responseEditor" width="90%" height="300px" />
</a-descriptions-item>
</a-descriptions>
@@ -152,7 +144,7 @@
}
.description-item-content {
margin-left: 20px;
- width: 90%;
+ width: 80%;
}
}
</style>
diff --git a/ui-vue3/src/views/resources/services/tabs/detail.vue b/ui-vue3/src/views/resources/services/tabs/detail.vue
index 9420d9c..4c8452b 100644
--- a/ui-vue3/src/views/resources/services/tabs/detail.vue
+++ b/ui-vue3/src/views/resources/services/tabs/detail.vue
@@ -16,46 +16,55 @@
-->
<template>
<div class="__container_services_tabs_detail">
- <a-flex>
- <a-descriptions class="description-column" :column="1" layout="vertical">
- <a-descriptions-item label="服务名">
- <p class="description-item-content">{{ serviceDetail.serviceName }}</p>
- </a-descriptions-item>
- <a-descriptions-item label="服务版本&分组">
- <div class="description-item-content">
- <a-tag color="cyan" v-for="item in serviceDetail.versionGroup" :key="item">{{
- item
- }}</a-tag>
- </div>
- </a-descriptions-item>
- <a-descriptions-item label="RPC协议">
- <p class="description-item-content">{{ serviceDetail.protocol }}</p>
- </a-descriptions-item>
- <a-descriptions-item label="延迟注册时间">
- <p class="description-item-content">{{ serviceDetail.delay }}</p>
- </a-descriptions-item>
- <a-descriptions-item label="超时时间">
- <p class="description-item-content">{{ serviceDetail.timeOut }}</p>
- </a-descriptions-item>
- <a-descriptions-item label="重试次数">
- <p class="description-item-content">{{ serviceDetail.retry }}</p>
- </a-descriptions-item>
- </a-descriptions>
- <a-descriptions class="description-column" :column="1" layout="vertical">
- <a-descriptions-item label="请求总量(1min)">
- <p class="description-item-content">{{ serviceDetail.requestTotal }}</p>
- </a-descriptions-item>
- <a-descriptions-item label="平均RT(1min)">
- <p class="description-item-content">{{ serviceDetail.avgRT }}</p>
- </a-descriptions-item>
- <a-descriptions-item label="平均qps(1min)">
- <p class="description-item-content">{{ serviceDetail.avgQPS }}</p>
- </a-descriptions-item>
- <a-descriptions-item label="是否过时">
- <p class="description-item-content">{{ serviceDetail.obsolete }}</p>
- </a-descriptions-item>
- </a-descriptions>
- </a-flex>
+ <a-card-grid>
+ <a-card title="生产者详情">
+ <a-flex>
+ <a-descriptions class="description-column" :column="1" layout="vertical">
+ <a-descriptions-item label="超时时间">
+ <p class="description-item-content">{{ serviceDetail.timeOut }}</p>
+ </a-descriptions-item>
+ <a-descriptions-item label="重试次数">
+ <p class="description-item-content">{{ serviceDetail.retry }}</p>
+ </a-descriptions-item>
+ <a-descriptions-item label="是否过时">
+ <p class="description-item-content">{{ serviceDetail.obsolete }}</p>
+ </a-descriptions-item>
+ </a-descriptions>
+ <a-descriptions class="description-column" :column="1" layout="vertical">
+ <a-descriptions-item label="RPC协议">
+ <p class="description-item-content">{{ serviceDetail.protocol }}</p>
+ </a-descriptions-item>
+ <a-descriptions-item label="延迟注册时间">
+ <p class="description-item-content">{{ serviceDetail.delay }}</p>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-flex>
+ </a-card>
+ <a-card title="消费者详情" style="margin-top: 10px">
+ <a-flex>
+ <a-descriptions class="description-column" :column="1" layout="vertical">
+ <a-descriptions-item label="超时时间">
+ <p class="description-item-content">{{ serviceDetail.timeOut }}</p>
+ </a-descriptions-item>
+ <a-descriptions-item label="重试次数">
+ <p class="description-item-content">{{ serviceDetail.retry }}</p>
+ </a-descriptions-item>
+ <a-descriptions-item label="是否过时">
+ <p class="description-item-content">{{ serviceDetail.obsolete }}</p>
+ </a-descriptions-item>
+ </a-descriptions>
+ <a-descriptions class="description-column" :column="1" layout="vertical">
+ <a-descriptions-item label="RPC协议">
+ <p class="description-item-content">{{ serviceDetail.protocol }}</p>
+ </a-descriptions-item>
+ <a-descriptions-item label="延迟注册时间">
+ <p class="description-item-content">{{ serviceDetail.delay }}</p>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-flex>
+ </a-card>
+ </a-card-grid>
+ <a-flex> </a-flex>
</div>
</template>
diff --git a/ui-vue3/src/views/resources/services/tabs/distribution.vue b/ui-vue3/src/views/resources/services/tabs/distribution.vue
index 4b985a2..ef7a335 100644
--- a/ui-vue3/src/views/resources/services/tabs/distribution.vue
+++ b/ui-vue3/src/views/resources/services/tabs/distribution.vue
@@ -18,30 +18,17 @@
<div class="__container_services_tabs_distribution">
<a-flex vertical>
<a-flex class="service-filter">
- <a-flex>
- <div>
- <span>版本&分组:</span>
- <a-select
- v-model:value="versionAndGroup"
- :options="versionAndGroupOptions"
- class="service-filter-select"
- @change="debounceSearch"
- ></a-select>
- </div>
- <a-input-search
- v-model:value="searchValue"
- placeholder="搜索应用,ip,支持前缀搜索"
- class="service-filter-input"
- @search="debounceSearch"
- enter-button
- />
- </a-flex>
- <div>
- <a-radio-group v-model:value="type" button-style="solid" @click="debounceSearch">
- <a-radio-button value="producer">生产者</a-radio-button>
- <a-radio-button value="consumer">消费者</a-radio-button>
- </a-radio-group>
- </div>
+ <a-radio-group v-model:value="type" button-style="solid" @click="debounceSearch">
+ <a-radio-button value="producer">生产者</a-radio-button>
+ <a-radio-button value="consumer">消费者</a-radio-button>
+ </a-radio-group>
+ <a-input-search
+ v-model:value="searchValue"
+ placeholder="搜索应用,ip,支持前缀搜索"
+ class="service-filter-input"
+ @search="debounceSearch"
+ enter-button
+ />
</a-flex>
<a-table
:columns="tableColumns"
@@ -51,13 +38,18 @@
>
<template #bodyCell="{ column, text }">
<template v-if="column.dataIndex === 'applicationName'">
- <a-button type="link">{{ text }}</a-button>
+ <span class="app-link" @click="viewDetail(text)">
+ <b>
+ <Icon
+ style="margin-bottom: -2px"
+ icon="material-symbols:attach-file-rounded"
+ ></Icon>
+ {{ text }}
+ </b>
+ </span>
</template>
- <template v-if="column.dataIndex === 'instanceIP'">
- <a-flex justify="">
- <a-button v-for="ip in text.slice(0, 3)" :key="ip" type="link">{{ ip }}</a-button>
- <a-button v-if="text.length > 3" type="link">更多</a-button>
- </a-flex>
+ <template v-if="column.dataIndex === 'label'">
+ <a-tag :color="PRIMARY_COLOR">{{ text }}</a-tag>
</template>
</template>
</a-table>
@@ -70,7 +62,10 @@
import { ref, reactive, getCurrentInstance } from 'vue'
import { getServiceDistribution } from '@/api/service/service'
import { debounce } from 'lodash'
+import { PRIMARY_COLOR } from '@/base/constants'
+import { Icon } from '@iconify/vue'
+let __null = PRIMARY_COLOR
const {
appContext: {
config: { globalProperties }
@@ -103,19 +98,51 @@
{
title: '应用名',
dataIndex: 'applicationName',
- width: '25%',
- sorter: true
+ width: '20%',
+ customCell: (_, index) => {
+ if (index === 0) {
+ return { rowSpan: tableData.value.length }
+ } else {
+ return { rowSpan: 0 }
+ }
+ }
},
{
title: '实例数',
dataIndex: 'instanceNum',
- width: '25%',
- sorter: true
+ width: '15%',
+ customCell: (_, index) => {
+ if (index === 0) {
+ return { rowSpan: tableData.value.length }
+ } else {
+ return { rowSpan: 0 }
+ }
+ }
},
{
- title: '实例ip',
- dataIndex: 'instanceIP',
- width: '50%'
+ title: '实例名',
+ dataIndex: 'instanceName',
+ width: '15%'
+ },
+ {
+ title: 'RPC端口',
+ dataIndex: 'rpcPort',
+ width: '15%'
+ },
+ {
+ title: '超时时间',
+ dataIndex: 'timeout',
+ width: '10%'
+ },
+ {
+ title: '重试次数',
+ dataIndex: 'retryNum',
+ width: '10%'
+ },
+ {
+ title: '标签',
+ dataIndex: 'label',
+ width: '15%'
}
]
@@ -138,10 +165,10 @@
globalProperties.$t('searchDomain.unit')
}
</script>
+
<style lang="less" scoped>
.__container_services_tabs_distribution {
.service-filter {
- justify-content: space-between;
margin-bottom: 20px;
.service-filter-select {
margin-left: 10px;
@@ -152,5 +179,14 @@
width: 300px;
}
}
+ .app-link {
+ padding: 4px 10px 4px 4px;
+ border-radius: 4px;
+ color: v-bind('PRIMARY_COLOR');
+ &:hover {
+ cursor: pointer;
+ background: rgba(133, 131, 131, 0.13);
+ }
+ }
}
</style>
diff --git a/ui-vue3/src/views/resources/services/tabs/event.vue b/ui-vue3/src/views/resources/services/tabs/event.vue
index d9a86c0..11a5eaa 100644
--- a/ui-vue3/src/views/resources/services/tabs/event.vue
+++ b/ui-vue3/src/views/resources/services/tabs/event.vue
@@ -15,8 +15,52 @@
~ limitations under the License.
-->
<template>
- <div class="__container_services_tabs_event">事件todo</div>
+ <div class="__container_services_tabs_event">
+ <a-timeline class="timeline">
+ <a-timeline-item v-for="(item, index) in eventData" :key="index">
+ <a-tag class="time" :color="PRIMARY_COLOR">{{ item.time }}</a-tag>
+ <span class="description">{{ item.description }}</span>
+ </a-timeline-item>
+ </a-timeline>
+ </div>
</template>
-<script setup lang="ts"></script>
-<style lang="less" scoped></style>
+<script setup lang="ts">
+import { PRIMARY_COLOR } from '@/base/constants'
+
+let __null = PRIMARY_COLOR
+const eventData = [
+ {
+ time: '2022-01-01',
+ description: 'description'
+ },
+ {
+ time: '2022-01-02',
+ description: 'description'
+ },
+ {
+ time: '2022-01-03',
+ description: 'description'
+ },
+ {
+ time: '2022-01-04',
+ description: 'description'
+ },
+ {
+ time: '2022-01-05',
+ description: 'description'
+ }
+]
+</script>
+
+<style lang="less" scoped>
+.__container_services_tabs_event {
+ display: flex;
+ justify-content: center;
+ .timeline {
+ .description {
+ font-size: 16px;
+ }
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/tabs/paramRoute.vue b/ui-vue3/src/views/resources/services/tabs/paramRoute.vue
new file mode 100644
index 0000000..6bcfce3
--- /dev/null
+++ b/ui-vue3/src/views/resources/services/tabs/paramRoute.vue
@@ -0,0 +1,198 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<template>
+ <div class="__container_services_tabs_param_route">
+ <a-card :bordered="false" style="width: 1000px">
+ <template #title>
+ <a-flex justify="space-between">
+ <span>路由</span>
+ <a-flex class="handle-form">
+ <EditOutlined class="edit-icon" />
+ <DeleteOutlined class="edit-icon" />
+ </a-flex>
+ </a-flex>
+ </template>
+ <a-form :labelCol="{ span: 3 }">
+ <a-form-item label="选择方法">
+ <a-select v-model:value="method.value" style="width: 120px">
+ <a-select-option v-for="(item, index) in method.selectArr" :value="item" :key="index">
+ {{ item }}
+ </a-select-option>
+ </a-select>
+ </a-form-item>
+ <a-form-item label="指定方法参数">
+ <a-table
+ :columns="functionParamsColumn"
+ :data-source="props.paramRouteForm.functionParams"
+ :pagination="false"
+ >
+ <template #bodyCell="{ column, index: idx }">
+ <template v-if="column.dataIndex === 'param'">
+ <a-input v-model:value="functionParamsEdit[idx].param" />
+ </template>
+ <template v-if="column.dataIndex === 'relation'">
+ <a-input v-model:value="functionParamsEdit[idx].relation" />
+ </template>
+ <template v-if="column.dataIndex === 'value'">
+ <a-input v-model:value="functionParamsEdit[idx].value" />
+ </template>
+ <template v-if="column.dataIndex === 'handle'">
+ <a-flex justify="space-between">
+ <PlusOutlined
+ class="edit-icon"
+ @click="emit('addRow', 'functionParams', props.index, idx)"
+ />
+ <MinusOutlined
+ class="edit-icon"
+ @click="emit('deleteRow', 'functionParams', props.index, idx)"
+ />
+ </a-flex>
+ </template>
+ </template>
+ </a-table>
+ </a-form-item>
+ <a-form-item label="路由目的地">
+ <a-table
+ :columns="destinationColumn"
+ :data-source="props.paramRouteForm.destination"
+ :pagination="false"
+ >
+ <template #bodyCell="{ column, index: idx }">
+ <template v-if="column.dataIndex === 'label'">
+ <a-input v-model:value="destinationEdit[idx].label" />
+ </template>
+ <template v-if="column.dataIndex === 'relation'">
+ <a-input v-model:value="destinationEdit[idx].relation" />
+ </template>
+ <template v-if="column.dataIndex === 'value'">
+ <a-input v-model:value="destinationEdit[idx].value" />
+ </template>
+ <template v-if="column.dataIndex === 'weight'">
+ <a-input v-model:value="destinationEdit[idx].weight" />
+ </template>
+ <template v-if="column.dataIndex === 'handle'">
+ <a-flex justify="space-between">
+ <PlusOutlined
+ class="edit-icon"
+ @click="emit('addRow', 'destination', props.index, idx)"
+ />
+ <MinusOutlined
+ class="edit-icon"
+ @click="emit('deleteRow', 'destination', props.index, idx)"
+ />
+ </a-flex>
+ </template>
+ </template>
+ </a-table>
+ </a-form-item>
+ </a-form>
+ </a-card>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { EditOutlined, DeleteOutlined, PlusOutlined, MinusOutlined } from '@ant-design/icons-vue'
+import { ref } from 'vue'
+
+const props = defineProps<{
+ paramRouteForm: {
+ type: Object
+ default: {}
+ }
+ index: {
+ type: Number
+ }
+}>()
+
+console.log('props', props.paramRouteForm)
+
+const method = ref(JSON.parse(JSON.stringify(props.paramRouteForm.method)))
+const functionParamsEdit = ref(JSON.parse(JSON.stringify(props.paramRouteForm.functionParams)))
+const destinationEdit = ref(JSON.parse(JSON.stringify(props.paramRouteForm.destination)))
+
+const functionParamsColumn = [
+ {
+ title: '参数索引',
+ key: 'param',
+ dataIndex: 'param',
+ width: '30%'
+ },
+ {
+ title: '关系',
+ key: 'relation',
+ dataIndex: 'relation',
+ width: '30%'
+ },
+ {
+ title: '值',
+ key: 'value',
+ dataIndex: 'value',
+ width: '30%'
+ },
+ {
+ title: '操作',
+ key: 'handle',
+ dataIndex: 'handle',
+ width: '10%'
+ }
+]
+
+const destinationColumn = [
+ {
+ title: '标签',
+ key: 'label',
+ dataIndex: 'label',
+ width: '25%'
+ },
+ {
+ title: '关系',
+ key: 'relation',
+ dataIndex: 'relation',
+ width: '25%'
+ },
+ {
+ title: '值',
+ key: 'value',
+ dataIndex: 'value',
+ width: '25%'
+ },
+ {
+ title: '权重',
+ key: 'weight',
+ dataIndex: 'weight',
+ width: '15%'
+ },
+ {
+ title: '操作',
+ key: 'handle',
+ dataIndex: 'handle',
+ width: '10%'
+ }
+]
+</script>
+
+<style lang="less" scoped>
+.__container_services_tabs_param_route {
+ .handle-form {
+ width: 50px;
+ justify-content: space-between;
+ }
+ .edit-icon {
+ font-size: 18px;
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/resources/services/tabs/sceneConfig.vue b/ui-vue3/src/views/resources/services/tabs/sceneConfig.vue
new file mode 100644
index 0000000..2fc5d3e
--- /dev/null
+++ b/ui-vue3/src/views/resources/services/tabs/sceneConfig.vue
@@ -0,0 +1,146 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<template>
+ <div class="__container_services_tabs_scene_config">
+ <a-tabs v-model:activeKey="activeKey" :tab-position="'left'" animated>
+ <a-tab-pane key="timeout" tab="超时时间">
+ <a-descriptions layout="vertical">
+ <a-descriptions-item label="超时时间">
+ <a-flex v-if="!editForm.timeout.isEdit">
+ <span class="item-content">1000ms</span>
+ <EditOutlined @click="showEdit('timeout')" class="item-icon" />
+ </a-flex>
+ <a-flex v-else align="center">
+ <a-input v-model:value="editForm.timeout.value" class="item-input" />
+ <span style="margin-left: 5px">ms</span>
+ <CheckOutlined @click="hideEdit('timeout')" class="item-icon" />
+ <CloseOutlined @click="hideEdit('timeout')" class="item-icon" />
+ </a-flex>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-tab-pane>
+ <a-tab-pane key="retryNum" tab="重试次数">
+ <a-descriptions layout="vertical">
+ <a-descriptions-item label="重试次数">
+ <a-flex v-if="!editForm.retryNum.isEdit">
+ <span class="item-content">1000次</span>
+ <EditOutlined @click="showEdit('retryNum')" class="item-icon" />
+ </a-flex>
+ <a-flex v-else align="center">
+ <a-input v-model:value="editForm.retryNum.value" class="item-input" />
+ <span style="margin-left: 5px">次</span>
+ <CheckOutlined @click="hideEdit('retryNum')" class="item-icon" />
+ <CloseOutlined @click="hideEdit('retryNum')" class="item-icon" />
+ </a-flex>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-tab-pane>
+ <a-tab-pane key="sameArea" tab="同区域优先">
+ <a-descriptions layout="vertical">
+ <a-descriptions-item label="同区域优先">
+ <a-radio-group v-model:value="editForm.sameArea.value" button-style="solid">
+ <a-radio-button value="close">关闭</a-radio-button>
+ <a-radio-button value="open">开启</a-radio-button>
+ </a-radio-group>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-tab-pane>
+ <a-tab-pane key="paramRoute" tab="参数路由">
+ <paramRoute
+ v-for="(item, index) in paramRouteForms"
+ :key="index"
+ :paramRouteForm="item"
+ :index="index"
+ @addRow="() => {}"
+ @deleteRow="() => {}"
+ />
+ <a-button type="primary" style="margin-top: 20px">增加路由</a-button>
+ </a-tab-pane>
+ </a-tabs>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive } from 'vue'
+import { EditOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons-vue'
+import paramRoute from './paramRoute.vue'
+
+const editForm = reactive({
+ timeout: {
+ isEdit: false,
+ value: ''
+ },
+ retryNum: {
+ isEdit: false,
+ value: ''
+ },
+ sameArea: {
+ value: 'close'
+ },
+ paramRoute: {
+ isEdit: false,
+ value: {}
+ }
+})
+
+const activeKey = ref('timeout')
+const showEdit = (param: string) => {
+ editForm[param].isEdit = true
+}
+const hideEdit = (param: string) => {
+ editForm[param].isEdit = false
+}
+
+const paramRouteForms = [
+ {
+ method: {
+ value: 'getUserInfo',
+ selectArr: ['getUserInfo', 'register', 'login']
+ },
+ functionParams: [
+ {
+ param: '',
+ relation: '',
+ value: ''
+ }
+ ],
+ destination: [
+ {
+ label: '',
+ relation: '',
+ value: '',
+ weight: ''
+ }
+ ]
+ }
+]
+</script>
+
+<style lang="less" scoped>
+.__container_services_tabs_scene_config {
+ .item-content {
+ margin-right: 20px;
+ }
+ .item-input {
+ width: 200px;
+ }
+ .item-icon {
+ margin-left: 15px;
+ font-size: 18px;
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/destinationRule/index.vue b/ui-vue3/src/views/traffic/destinationRule/index.vue
index 05b92be..dc1fb10 100644
--- a/ui-vue3/src/views/traffic/destinationRule/index.vue
+++ b/ui-vue3/src/views/traffic/destinationRule/index.vue
@@ -20,9 +20,11 @@
<template #customOperation>
<a-button type="primary">新增 Destination Rule</a-button>
</template>
- <template #bodyCell="{ text, column }">
+ <template #bodyCell="{ text, column, record }">
<template v-if="column.dataIndex === 'ruleName'">
- <a-button type="link">{{ text }}</a-button>
+ <a-button type="link" @click="router.replace(`formview/${record[column.key]}`)">{{
+ text
+ }}</a-button>
</template>
<template v-if="column.dataIndex === 'operation'">
<a-button type="link">查看</a-button>
@@ -47,6 +49,7 @@
import SearchTable from '@/components/SearchTable.vue'
import { SearchDomain, sortString } from '@/utils/SearchUtil'
import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+import router from '@/router'
let columns = [
{
diff --git a/ui-vue3/src/views/traffic/destinationRule/tabs/YAMLView.vue b/ui-vue3/src/views/traffic/destinationRule/tabs/YAMLView.vue
new file mode 100644
index 0000000..785fa05
--- /dev/null
+++ b/ui-vue3/src/views/traffic/destinationRule/tabs/YAMLView.vue
@@ -0,0 +1,103 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <a-card>
+ <a-flex style="width: 100%">
+ <a-col :span="isDrawerOpened ? 24 - sliderSpan : 24" class="left">
+ <a-flex vertical align="end">
+ <a-button type="text" style="color: #0a90d5" @click="isDrawerOpened = !isDrawerOpened">
+ {{ $t('flowControlDomain.versionRecords') }}
+ <DoubleLeftOutlined v-if="!isDrawerOpened" />
+ <DoubleRightOutlined v-else />
+ </a-button>
+
+ <div class="editorBox">
+ <MonacoEditor
+ :modelValue="YAMLValue"
+ theme="vs-dark"
+ :height="500"
+ language="yaml"
+ :readonly="isReadonly"
+ />
+ </div>
+ </a-flex>
+ </a-col>
+
+ <a-col :span="isDrawerOpened ? sliderSpan : 0" class="right">
+ <a-card v-if="isDrawerOpened" class="sliderBox">
+ <a-card v-for="i in 2" :key="i">
+ <p>修改时间: 2024/3/20 15:20:31</p>
+ <p>版本号: xo842xqpx834</p>
+
+ <a-flex justify="flex-end">
+ <a-button type="text" style="color: #0a90d5">查看</a-button>
+ <a-button type="text" style="color: #0a90d5">回滚</a-button>
+ </a-flex>
+ </a-card>
+ </a-card>
+ </a-col>
+ </a-flex>
+ </a-card>
+</template>
+
+<script setup lang="ts">
+import MonacoEditor from '@/components/editor/MonacoEditor.vue'
+import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue'
+import { computed, ref } from 'vue'
+
+const isReadonly = ref(true)
+
+const isDrawerOpened = ref(false)
+
+const sliderSpan = ref(8)
+
+const YAMLValue = ref(
+ 'configVersion: v3.0\n' +
+ 'force: true\n' +
+ 'enabled: true\n' +
+ 'key: shop-detail\n' +
+ 'tags:\n' +
+ ' - name: gray\n' +
+ ' match:\n' +
+ ' - key: env\n' +
+ ' value:\n' +
+ ' exact: gray'
+)
+</script>
+
+<style scoped lang="less">
+.editorBox {
+ border-radius: 0.3rem;
+ overflow: hidden;
+ width: 100%;
+}
+
+.sliderBox {
+ margin-left: 5px;
+ max-height: 530px;
+ overflow: auto;
+}
+
+&:deep(.left.ant-col) {
+ transition: all 0.5s ease;
+}
+
+&:deep(.right.ant-col) {
+ transition: all 0.5s ease;
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/destinationRule/tabs/formView.vue b/ui-vue3/src/views/traffic/destinationRule/tabs/formView.vue
new file mode 100644
index 0000000..83ad05b
--- /dev/null
+++ b/ui-vue3/src/views/traffic/destinationRule/tabs/formView.vue
@@ -0,0 +1,162 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <a-card>
+ <a-row>
+ <a-descriptions title="基础信息" />
+ <a-col :span="12">
+ <a-descriptions title="" layout="vertical" :column="1">
+ <a-descriptions-item label="规则名" :labelStyle="{ fontWeight: 'bold', width: '100px' }">
+ <p
+ @click="copyIt('shop-user/StandardRouter')"
+ class="description-item-content with-card"
+ >
+ shop-user/StandardRouter
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
+ <a-descriptions-item
+ label="作用对象"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ shop-user
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-col>
+ <a-col :span="12">
+ <a-descriptions title="" layout="vertical" :column="1">
+ <a-descriptions-item
+ label="创建时间"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ 2024/2/19/ 11:38:21
+ </a-descriptions-item>
+ <a-descriptions-item
+ label="最近修改时间"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ 2024/3/20 15:20:31
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-col>
+ </a-row>
+
+ <a-row>
+ <a-descriptions title="流量策略" />
+ <a-col :span="12">
+ <a-descriptions title="" layout="vertical" :column="1">
+ <a-descriptions-item
+ label="负载均衡"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ 不指定
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-col>
+ </a-row>
+ </a-card>
+
+ <a-card>
+ <a-descriptions title="地址子集" />
+
+ <a-card title="地址子集【1】">
+ <a-space size="middle" direction="vertical">
+ <a-descriptions :column="1">
+ <a-descriptions-item
+ label="子集名称"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ v1-beijing
+ </a-descriptions-item>
+ </a-descriptions>
+
+ <a-descriptions :column="1">
+ <a-descriptions-item
+ label="标签匹配"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ <a-space>
+ <a-tag color="#2db7f5">version=v1</a-tag>
+ <a-tag color="#2db7f5">region=beijing</a-tag>
+ </a-space>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-space>
+ </a-card>
+
+ <a-card title="地址子集【2】">
+ <a-space size="middle" direction="vertical">
+ <a-descriptions :column="1">
+ <a-descriptions-item
+ label="子集名称"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ v1-beijing
+ </a-descriptions-item>
+ </a-descriptions>
+
+ <a-descriptions :column="1">
+ <a-descriptions-item
+ label="标签匹配"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ <a-space>
+ <a-tag color="#2db7f5">version=v1</a-tag>
+ <a-tag color="#2db7f5">region=beijing</a-tag>
+ </a-space>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-space>
+ </a-card>
+ </a-card>
+</template>
+
+<script setup lang="ts">
+import { CopyOutlined } from '@ant-design/icons-vue'
+import { type ComponentInternalInstance, getCurrentInstance } from 'vue'
+import { PRIMARY_COLOR, PRIMARY_COLOR_T } from '@/base/constants'
+import useClipboard from 'vue-clipboard3'
+import { message } from 'ant-design-vue'
+
+const {
+ appContext: {
+ config: { globalProperties }
+ }
+} = <ComponentInternalInstance>getCurrentInstance()
+
+let __ = PRIMARY_COLOR
+let PRIMARY_COLOR_20 = PRIMARY_COLOR_T('20')
+
+const toClipboard = useClipboard().toClipboard
+
+function copyIt(v: string) {
+ message.success(globalProperties.$t('messageDomain.success.copy'))
+ toClipboard(v)
+}
+</script>
+
+<style scoped lang="less">
+.description-item-content {
+ &.no-card {
+ padding-left: 20px;
+ }
+ &.with-card:hover {
+ color: v-bind('PRIMARY_COLOR');
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/dynamicConfig/index.vue b/ui-vue3/src/views/traffic/dynamicConfig/index.vue
index fbef790..31cfe98 100644
--- a/ui-vue3/src/views/traffic/dynamicConfig/index.vue
+++ b/ui-vue3/src/views/traffic/dynamicConfig/index.vue
@@ -21,9 +21,11 @@
<a-button type="primary">新增动态配置</a-button>
<a-button type="primary" style="margin-left: 10px">从模版创建</a-button>
</template>
- <template #bodyCell="{ text, column }">
+ <template #bodyCell="{ text, column, record }">
<template v-if="column.dataIndex === 'ruleName'">
- <a-button type="link">{{ text }}</a-button>
+ <a-button type="link" @click="router.replace(`formview/${record[column.key]}`)">{{
+ text
+ }}</a-button>
</template>
<template v-if="column.dataIndex === 'ruleGranularity'">
{{ text ? '服务' : '应用' }}
@@ -54,6 +56,7 @@
import SearchTable from '@/components/SearchTable.vue'
import { SearchDomain, sortString } from '@/utils/SearchUtil'
import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+import router from '@/router'
let columns = [
{
diff --git a/ui-vue3/src/views/traffic/dynamicConfig/tabs/YAMLView.vue b/ui-vue3/src/views/traffic/dynamicConfig/tabs/YAMLView.vue
new file mode 100644
index 0000000..785fa05
--- /dev/null
+++ b/ui-vue3/src/views/traffic/dynamicConfig/tabs/YAMLView.vue
@@ -0,0 +1,103 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <a-card>
+ <a-flex style="width: 100%">
+ <a-col :span="isDrawerOpened ? 24 - sliderSpan : 24" class="left">
+ <a-flex vertical align="end">
+ <a-button type="text" style="color: #0a90d5" @click="isDrawerOpened = !isDrawerOpened">
+ {{ $t('flowControlDomain.versionRecords') }}
+ <DoubleLeftOutlined v-if="!isDrawerOpened" />
+ <DoubleRightOutlined v-else />
+ </a-button>
+
+ <div class="editorBox">
+ <MonacoEditor
+ :modelValue="YAMLValue"
+ theme="vs-dark"
+ :height="500"
+ language="yaml"
+ :readonly="isReadonly"
+ />
+ </div>
+ </a-flex>
+ </a-col>
+
+ <a-col :span="isDrawerOpened ? sliderSpan : 0" class="right">
+ <a-card v-if="isDrawerOpened" class="sliderBox">
+ <a-card v-for="i in 2" :key="i">
+ <p>修改时间: 2024/3/20 15:20:31</p>
+ <p>版本号: xo842xqpx834</p>
+
+ <a-flex justify="flex-end">
+ <a-button type="text" style="color: #0a90d5">查看</a-button>
+ <a-button type="text" style="color: #0a90d5">回滚</a-button>
+ </a-flex>
+ </a-card>
+ </a-card>
+ </a-col>
+ </a-flex>
+ </a-card>
+</template>
+
+<script setup lang="ts">
+import MonacoEditor from '@/components/editor/MonacoEditor.vue'
+import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue'
+import { computed, ref } from 'vue'
+
+const isReadonly = ref(true)
+
+const isDrawerOpened = ref(false)
+
+const sliderSpan = ref(8)
+
+const YAMLValue = ref(
+ 'configVersion: v3.0\n' +
+ 'force: true\n' +
+ 'enabled: true\n' +
+ 'key: shop-detail\n' +
+ 'tags:\n' +
+ ' - name: gray\n' +
+ ' match:\n' +
+ ' - key: env\n' +
+ ' value:\n' +
+ ' exact: gray'
+)
+</script>
+
+<style scoped lang="less">
+.editorBox {
+ border-radius: 0.3rem;
+ overflow: hidden;
+ width: 100%;
+}
+
+.sliderBox {
+ margin-left: 5px;
+ max-height: 530px;
+ overflow: auto;
+}
+
+&:deep(.left.ant-col) {
+ transition: all 0.5s ease;
+}
+
+&:deep(.right.ant-col) {
+ transition: all 0.5s ease;
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/dynamicConfig/tabs/formView.vue b/ui-vue3/src/views/traffic/dynamicConfig/tabs/formView.vue
new file mode 100644
index 0000000..77185b5
--- /dev/null
+++ b/ui-vue3/src/views/traffic/dynamicConfig/tabs/formView.vue
@@ -0,0 +1,146 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <div class="__container_app_detail">
+ <a-flex>
+ <a-card>
+ <a-descriptions :column="2" layout="vertical" title="">
+ <!-- ruleName -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.ruleName')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p
+ @click="copyIt('org.apache.dubbo.samples.UserService::.condition-router')"
+ class="description-item-content with-card"
+ >
+ org.apache.dubbo.samples.UserService::.condition-router
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
+
+ <!-- ruleGranularity -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.ruleGranularity')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> 服务 </a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- actionObject -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.actionObject')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p
+ @click="copyIt('org.apache.dubbo.samples.UserService')"
+ class="description-item-content with-card"
+ >
+ org.apache.dubbo.samples.UserService
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
+
+ <!-- timeOfTakingEffect -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.timeOfTakingEffect')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> 20230/12/19 22:09:34</a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- enabledStatus -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.enabledStatus')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph>
+ {{ $t('flowControlDomain.enabled') }}
+ </a-typography-paragraph>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-card>
+ </a-flex>
+
+ <a-card title="配置【1】">
+ <a-space align="center" size="large">
+ <!-- enabledStatus-->
+ <a-typography-title :level="5">
+ {{ $t('flowControlDomain.enabledStatus') }}:
+ <a-typography-text>
+ {{ $t('flowControlDomain.enabled') }}
+ </a-typography-text>
+ </a-typography-title>
+ <!--endOfAction-->
+ <a-typography-title :level="5">
+ {{ $t('flowControlDomain.endOfAction') }}:
+ <a-typography-text>
+ {{ 'provider' }}
+ </a-typography-text>
+ </a-typography-title>
+ </a-space>
+ <a-space align="start" style="width: 100%">
+ <a-typography-title :level="5"
+ >{{ $t('flowControlDomain.actuatingRange') }}:</a-typography-title
+ >
+ <a-tag color="#2db7f5">address=10.255.10.11</a-tag>
+ </a-space>
+
+ <a-space align="start" style="width: 100%">
+ <a-typography-title :level="5"
+ >{{ $t('flowControlDomain.configurationItem') }}:</a-typography-title
+ >
+ <a-tag color="#87d068">retries=3</a-tag>
+ </a-space>
+ </a-card>
+ </div>
+</template>
+
+<script lang="ts" setup>
+import { type ComponentInternalInstance, getCurrentInstance } from 'vue'
+import { CopyOutlined } from '@ant-design/icons-vue'
+import { PRIMARY_COLOR } from '@/base/constants'
+import useClipboard from 'vue-clipboard3'
+import { message } from 'ant-design-vue'
+
+const {
+ appContext: {
+ config: { globalProperties }
+ }
+} = <ComponentInternalInstance>getCurrentInstance()
+
+let __ = PRIMARY_COLOR
+const toClipboard = useClipboard().toClipboard
+
+function copyIt(v: string) {
+ message.success(globalProperties.$t('messageDomain.success.copy'))
+ toClipboard(v)
+}
+</script>
+
+<style lang="less" scoped>
+.description-item-content {
+ &.no-card {
+ padding-left: 20px;
+ }
+
+ &.with-card:hover {
+ color: v-bind('PRIMARY_COLOR');
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/routingRule/index.vue b/ui-vue3/src/views/traffic/routingRule/index.vue
index 14e82ed..1942c0b 100644
--- a/ui-vue3/src/views/traffic/routingRule/index.vue
+++ b/ui-vue3/src/views/traffic/routingRule/index.vue
@@ -20,9 +20,11 @@
<template #customOperation>
<a-button type="primary">新增条件路由规则</a-button>
</template>
- <template #bodyCell="{ text, column }">
+ <template #bodyCell="{ text, column, record }">
<template v-if="column.dataIndex === 'ruleName'">
- <a-button type="link">{{ text }}</a-button>
+ <a-button type="link" @click="router.replace(`formview/${record[column.key]}`)">{{
+ text
+ }}</a-button>
</template>
<template v-if="column.dataIndex === 'ruleGranularity'">
{{ text ? '服务' : '应用' }}
@@ -53,6 +55,7 @@
import SearchTable from '@/components/SearchTable.vue'
import { SearchDomain, sortString } from '@/utils/SearchUtil'
import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+import router from '@/router'
let columns = [
{
diff --git a/ui-vue3/src/views/traffic/routingRule/tabs/YAMLView.vue b/ui-vue3/src/views/traffic/routingRule/tabs/YAMLView.vue
new file mode 100644
index 0000000..785fa05
--- /dev/null
+++ b/ui-vue3/src/views/traffic/routingRule/tabs/YAMLView.vue
@@ -0,0 +1,103 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <a-card>
+ <a-flex style="width: 100%">
+ <a-col :span="isDrawerOpened ? 24 - sliderSpan : 24" class="left">
+ <a-flex vertical align="end">
+ <a-button type="text" style="color: #0a90d5" @click="isDrawerOpened = !isDrawerOpened">
+ {{ $t('flowControlDomain.versionRecords') }}
+ <DoubleLeftOutlined v-if="!isDrawerOpened" />
+ <DoubleRightOutlined v-else />
+ </a-button>
+
+ <div class="editorBox">
+ <MonacoEditor
+ :modelValue="YAMLValue"
+ theme="vs-dark"
+ :height="500"
+ language="yaml"
+ :readonly="isReadonly"
+ />
+ </div>
+ </a-flex>
+ </a-col>
+
+ <a-col :span="isDrawerOpened ? sliderSpan : 0" class="right">
+ <a-card v-if="isDrawerOpened" class="sliderBox">
+ <a-card v-for="i in 2" :key="i">
+ <p>修改时间: 2024/3/20 15:20:31</p>
+ <p>版本号: xo842xqpx834</p>
+
+ <a-flex justify="flex-end">
+ <a-button type="text" style="color: #0a90d5">查看</a-button>
+ <a-button type="text" style="color: #0a90d5">回滚</a-button>
+ </a-flex>
+ </a-card>
+ </a-card>
+ </a-col>
+ </a-flex>
+ </a-card>
+</template>
+
+<script setup lang="ts">
+import MonacoEditor from '@/components/editor/MonacoEditor.vue'
+import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue'
+import { computed, ref } from 'vue'
+
+const isReadonly = ref(true)
+
+const isDrawerOpened = ref(false)
+
+const sliderSpan = ref(8)
+
+const YAMLValue = ref(
+ 'configVersion: v3.0\n' +
+ 'force: true\n' +
+ 'enabled: true\n' +
+ 'key: shop-detail\n' +
+ 'tags:\n' +
+ ' - name: gray\n' +
+ ' match:\n' +
+ ' - key: env\n' +
+ ' value:\n' +
+ ' exact: gray'
+)
+</script>
+
+<style scoped lang="less">
+.editorBox {
+ border-radius: 0.3rem;
+ overflow: hidden;
+ width: 100%;
+}
+
+.sliderBox {
+ margin-left: 5px;
+ max-height: 530px;
+ overflow: auto;
+}
+
+&:deep(.left.ant-col) {
+ transition: all 0.5s ease;
+}
+
+&:deep(.right.ant-col) {
+ transition: all 0.5s ease;
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/routingRule/tabs/formView.vue b/ui-vue3/src/views/traffic/routingRule/tabs/formView.vue
new file mode 100644
index 0000000..e6ef312
--- /dev/null
+++ b/ui-vue3/src/views/traffic/routingRule/tabs/formView.vue
@@ -0,0 +1,165 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <div class="__container_app_detail">
+ <a-flex>
+ <a-card>
+ <a-descriptions :column="2" layout="vertical" title="">
+ <!-- ruleName -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.ruleName')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p
+ @click="copyIt('org.apache.dubbo.samples.UserService::.condition-router')"
+ class="description-item-content with-card"
+ >
+ org.apache.dubbo.samples.UserService::.condition-router
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
+
+ <!-- ruleGranularity -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.ruleGranularity')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> 服务 </a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- actionObject -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.actionObject')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p
+ @click="copyIt('org.apache.dubbo.samples.UserService')"
+ class="description-item-content with-card"
+ >
+ org.apache.dubbo.samples.UserService
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
+
+ <!-- timeOfTakingEffect -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.timeOfTakingEffect')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> 20230/12/19 22:09:34</a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- faultTolerantProtection -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.faultTolerantProtection')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph>
+ {{ $t('flowControlDomain.opened') }}
+ </a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- enabledStatus -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.enabledStatus')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph>
+ {{ $t('flowControlDomain.enabled') }}
+ </a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- runTimeEffective -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.runTimeEffective')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph>
+ {{ $t('flowControlDomain.opened') }}
+ </a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- priority -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.priority')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph>
+ {{ $t('flowControlDomain.notSet') }}
+ </a-typography-paragraph>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-card>
+ </a-flex>
+
+ <a-card title="路由【1】">
+ <a-space align="start" style="width: 100%">
+ <a-typography-title :level="5"
+ >{{ $t('flowControlDomain.requestParameterMatching') }}:</a-typography-title
+ >
+
+ <a-space align="center" direction="horizontal" size="middle">
+ <a-tag color="#2db7f5">method=login</a-tag>
+ <a-tag color="#2db7f5">arguments[0]=dubbo</a-tag>
+ </a-space>
+ </a-space>
+
+ <a-space align="start" style="width: 100%">
+ <a-typography-title :level="5"
+ >{{ $t('flowControlDomain.addressSubsetMatching') }}:</a-typography-title
+ >
+ <a-tag color="#87d068">version=v1</a-tag>
+ </a-space>
+ </a-card>
+ </div>
+</template>
+
+<script lang="ts" setup>
+import { type ComponentInternalInstance, getCurrentInstance, ref } from 'vue'
+import { CopyOutlined } from '@ant-design/icons-vue'
+import useClipboard from 'vue-clipboard3'
+import { message } from 'ant-design-vue'
+import { PRIMARY_COLOR } from '@/base/constants'
+
+const {
+ appContext: {
+ config: { globalProperties }
+ }
+} = <ComponentInternalInstance>getCurrentInstance()
+
+let __ = PRIMARY_COLOR
+
+const toClipboard = useClipboard().toClipboard
+
+function copyIt(v: string) {
+ message.success(globalProperties.$t('messageDomain.success.copy'))
+ toClipboard(v)
+}
+</script>
+
+<style lang="less" scoped>
+.description-item-content {
+ &.no-card {
+ padding-left: 20px;
+ }
+
+ &.with-card:hover {
+ color: v-bind('PRIMARY_COLOR');
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/tagRule/index.vue b/ui-vue3/src/views/traffic/tagRule/index.vue
index d72618b..5532e04 100644
--- a/ui-vue3/src/views/traffic/tagRule/index.vue
+++ b/ui-vue3/src/views/traffic/tagRule/index.vue
@@ -20,9 +20,11 @@
<template #customOperation>
<a-button type="primary">新增标签路由规则</a-button>
</template>
- <template #bodyCell="{ text, column }">
+ <template #bodyCell="{ text, column, record }">
<template v-if="column.dataIndex === 'ruleName'">
- <a-button type="link">{{ text }}</a-button>
+ <a-button type="link" @click="router.replace(`formview/${record[column.key]}`)">{{
+ text
+ }}</a-button>
</template>
<template v-if="column.dataIndex === 'enable'">
{{ text ? '启用' : '禁用' }}
@@ -50,6 +52,7 @@
import SearchTable from '@/components/SearchTable.vue'
import { SearchDomain, sortString } from '@/utils/SearchUtil'
import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+import router from '@/router'
let columns = [
{
diff --git a/ui-vue3/src/views/traffic/tagRule/tabs/YAMLView.vue b/ui-vue3/src/views/traffic/tagRule/tabs/YAMLView.vue
new file mode 100644
index 0000000..785fa05
--- /dev/null
+++ b/ui-vue3/src/views/traffic/tagRule/tabs/YAMLView.vue
@@ -0,0 +1,103 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <a-card>
+ <a-flex style="width: 100%">
+ <a-col :span="isDrawerOpened ? 24 - sliderSpan : 24" class="left">
+ <a-flex vertical align="end">
+ <a-button type="text" style="color: #0a90d5" @click="isDrawerOpened = !isDrawerOpened">
+ {{ $t('flowControlDomain.versionRecords') }}
+ <DoubleLeftOutlined v-if="!isDrawerOpened" />
+ <DoubleRightOutlined v-else />
+ </a-button>
+
+ <div class="editorBox">
+ <MonacoEditor
+ :modelValue="YAMLValue"
+ theme="vs-dark"
+ :height="500"
+ language="yaml"
+ :readonly="isReadonly"
+ />
+ </div>
+ </a-flex>
+ </a-col>
+
+ <a-col :span="isDrawerOpened ? sliderSpan : 0" class="right">
+ <a-card v-if="isDrawerOpened" class="sliderBox">
+ <a-card v-for="i in 2" :key="i">
+ <p>修改时间: 2024/3/20 15:20:31</p>
+ <p>版本号: xo842xqpx834</p>
+
+ <a-flex justify="flex-end">
+ <a-button type="text" style="color: #0a90d5">查看</a-button>
+ <a-button type="text" style="color: #0a90d5">回滚</a-button>
+ </a-flex>
+ </a-card>
+ </a-card>
+ </a-col>
+ </a-flex>
+ </a-card>
+</template>
+
+<script setup lang="ts">
+import MonacoEditor from '@/components/editor/MonacoEditor.vue'
+import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue'
+import { computed, ref } from 'vue'
+
+const isReadonly = ref(true)
+
+const isDrawerOpened = ref(false)
+
+const sliderSpan = ref(8)
+
+const YAMLValue = ref(
+ 'configVersion: v3.0\n' +
+ 'force: true\n' +
+ 'enabled: true\n' +
+ 'key: shop-detail\n' +
+ 'tags:\n' +
+ ' - name: gray\n' +
+ ' match:\n' +
+ ' - key: env\n' +
+ ' value:\n' +
+ ' exact: gray'
+)
+</script>
+
+<style scoped lang="less">
+.editorBox {
+ border-radius: 0.3rem;
+ overflow: hidden;
+ width: 100%;
+}
+
+.sliderBox {
+ margin-left: 5px;
+ max-height: 530px;
+ overflow: auto;
+}
+
+&:deep(.left.ant-col) {
+ transition: all 0.5s ease;
+}
+
+&:deep(.right.ant-col) {
+ transition: all 0.5s ease;
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/tagRule/tabs/formView.vue b/ui-vue3/src/views/traffic/tagRule/tabs/formView.vue
new file mode 100644
index 0000000..618e2b7
--- /dev/null
+++ b/ui-vue3/src/views/traffic/tagRule/tabs/formView.vue
@@ -0,0 +1,156 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <div class="__container_app_detail">
+ <a-flex>
+ <a-card>
+ <a-descriptions :column="2" layout="vertical" title="">
+ <!-- ruleName -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.ruleName')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p
+ @click="copyIt('org.apache.dubbo.samples.UserService::.condition-router')"
+ class="description-item-content with-card"
+ >
+ org.apache.dubbo.samples.UserService::.condition-router
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
+
+ <!-- ruleGranularity -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.ruleGranularity')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> 服务 </a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- actionObject -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.actionObject')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <p @click="copyIt('shop-user')" class="description-item-content with-card">
+ shop-user
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
+
+ <!-- timeOfTakingEffect -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.timeOfTakingEffect')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph> 20230/12/19 22:09:34</a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- faultTolerantProtection -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.faultTolerantProtection')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph>
+ {{ $t('flowControlDomain.opened') }}
+ </a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- enabledStatus -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.enabledStatus')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph>
+ {{ $t('flowControlDomain.enabled') }}
+ </a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- runTimeEffective -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.runTimeEffective')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph>
+ {{ $t('flowControlDomain.opened') }}
+ </a-typography-paragraph>
+ </a-descriptions-item>
+
+ <!-- priority -->
+ <a-descriptions-item
+ :label="$t('flowControlDomain.priority')"
+ :labelStyle="{ fontWeight: 'bold' }"
+ >
+ <a-typography-paragraph>
+ {{ '未设置' }}
+ </a-typography-paragraph>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-card>
+ </a-flex>
+
+ <a-card title="标签【1】">
+ <a-space align="center">
+ <a-typography-title :level="5">
+ {{ $t('flowControlDomain.labelName') }}:
+ <a-typography-text class="labelName">gray</a-typography-text>
+ </a-typography-title>
+ </a-space>
+ <a-space align="start" style="width: 100%">
+ <a-typography-title :level="5"
+ >{{ $t('flowControlDomain.actuatingRange') }}:</a-typography-title
+ >
+ <a-tag color="#2db7f5">version=v1</a-tag>
+ </a-space>
+ </a-card>
+ </div>
+</template>
+
+<script lang="ts" setup>
+import { CopyOutlined } from '@ant-design/icons-vue'
+import useClipboard from 'vue-clipboard3'
+import { message } from 'ant-design-vue'
+import { type ComponentInternalInstance, getCurrentInstance } from 'vue'
+import { PRIMARY_COLOR } from '@/base/constants'
+
+const {
+ appContext: {
+ config: { globalProperties }
+ }
+} = <ComponentInternalInstance>getCurrentInstance()
+
+let __ = PRIMARY_COLOR
+const toClipboard = useClipboard().toClipboard
+
+function copyIt(v: string) {
+ message.success(globalProperties.$t('messageDomain.success.copy'))
+ toClipboard(v)
+}
+</script>
+
+<style lang="less" scoped>
+.description-item-content {
+ &.no-card {
+ padding-left: 20px;
+ }
+
+ &.with-card:hover {
+ color: v-bind('PRIMARY_COLOR');
+ }
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/virtualService/index.vue b/ui-vue3/src/views/traffic/virtualService/index.vue
index 4e1f049..d75a4fc 100644
--- a/ui-vue3/src/views/traffic/virtualService/index.vue
+++ b/ui-vue3/src/views/traffic/virtualService/index.vue
@@ -20,9 +20,11 @@
<template #customOperation>
<a-button type="primary">新增路由规则</a-button>
</template>
- <template #bodyCell="{ text, column }">
+ <template #bodyCell="{ text, column, record }">
<template v-if="column.dataIndex === 'ruleName'">
- <a-button type="link">{{ text }}</a-button>
+ <a-button type="link" @click="router.replace(`formview/${record[column.key]}`)">{{
+ text
+ }}</a-button>
</template>
<template v-if="column.dataIndex === 'operation'">
<a-button type="link">查看</a-button>
@@ -47,6 +49,7 @@
import SearchTable from '@/components/SearchTable.vue'
import { SearchDomain, sortString } from '@/utils/SearchUtil'
import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
+import router from '@/router'
let columns = [
{
diff --git a/ui-vue3/src/views/traffic/virtualService/tabs/YAMLView.vue b/ui-vue3/src/views/traffic/virtualService/tabs/YAMLView.vue
new file mode 100644
index 0000000..785fa05
--- /dev/null
+++ b/ui-vue3/src/views/traffic/virtualService/tabs/YAMLView.vue
@@ -0,0 +1,103 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <a-card>
+ <a-flex style="width: 100%">
+ <a-col :span="isDrawerOpened ? 24 - sliderSpan : 24" class="left">
+ <a-flex vertical align="end">
+ <a-button type="text" style="color: #0a90d5" @click="isDrawerOpened = !isDrawerOpened">
+ {{ $t('flowControlDomain.versionRecords') }}
+ <DoubleLeftOutlined v-if="!isDrawerOpened" />
+ <DoubleRightOutlined v-else />
+ </a-button>
+
+ <div class="editorBox">
+ <MonacoEditor
+ :modelValue="YAMLValue"
+ theme="vs-dark"
+ :height="500"
+ language="yaml"
+ :readonly="isReadonly"
+ />
+ </div>
+ </a-flex>
+ </a-col>
+
+ <a-col :span="isDrawerOpened ? sliderSpan : 0" class="right">
+ <a-card v-if="isDrawerOpened" class="sliderBox">
+ <a-card v-for="i in 2" :key="i">
+ <p>修改时间: 2024/3/20 15:20:31</p>
+ <p>版本号: xo842xqpx834</p>
+
+ <a-flex justify="flex-end">
+ <a-button type="text" style="color: #0a90d5">查看</a-button>
+ <a-button type="text" style="color: #0a90d5">回滚</a-button>
+ </a-flex>
+ </a-card>
+ </a-card>
+ </a-col>
+ </a-flex>
+ </a-card>
+</template>
+
+<script setup lang="ts">
+import MonacoEditor from '@/components/editor/MonacoEditor.vue'
+import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue'
+import { computed, ref } from 'vue'
+
+const isReadonly = ref(true)
+
+const isDrawerOpened = ref(false)
+
+const sliderSpan = ref(8)
+
+const YAMLValue = ref(
+ 'configVersion: v3.0\n' +
+ 'force: true\n' +
+ 'enabled: true\n' +
+ 'key: shop-detail\n' +
+ 'tags:\n' +
+ ' - name: gray\n' +
+ ' match:\n' +
+ ' - key: env\n' +
+ ' value:\n' +
+ ' exact: gray'
+)
+</script>
+
+<style scoped lang="less">
+.editorBox {
+ border-radius: 0.3rem;
+ overflow: hidden;
+ width: 100%;
+}
+
+.sliderBox {
+ margin-left: 5px;
+ max-height: 530px;
+ overflow: auto;
+}
+
+&:deep(.left.ant-col) {
+ transition: all 0.5s ease;
+}
+
+&:deep(.right.ant-col) {
+ transition: all 0.5s ease;
+}
+</style>
diff --git a/ui-vue3/src/views/traffic/virtualService/tabs/formView.vue b/ui-vue3/src/views/traffic/virtualService/tabs/formView.vue
new file mode 100644
index 0000000..67b5644
--- /dev/null
+++ b/ui-vue3/src/views/traffic/virtualService/tabs/formView.vue
@@ -0,0 +1,178 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<template>
+ <a-card>
+ <a-row>
+ <a-descriptions title="基础信息" />
+ <a-col :span="12">
+ <a-descriptions title="" layout="vertical" :column="1">
+ <a-descriptions-item label="规则名" :labelStyle="{ fontWeight: 'bold', width: '100px' }">
+ <p
+ @click="copyIt('shop-user/StandardRouter')"
+ class="description-item-content with-card"
+ >
+ shop-user/StandardRouter
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
+ <a-descriptions-item
+ label="作用对象"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ <p @click="copyIt('shop-user')" class="description-item-content with-card">
+ shop-user
+ <CopyOutlined />
+ </p>
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-col>
+ <a-col :span="12">
+ <a-descriptions title="" layout="vertical" :column="1">
+ <a-descriptions-item
+ label="创建时间"
+ :labelStyle="{ fontWeight: 'bold', width: '100px' }"
+ >
+ 2024/2/19/ 11:38:21
+ </a-descriptions-item>
+ </a-descriptions>
+ </a-col>
+ </a-row>
+ </a-card>
+
+ <a-card>
+ <a-descriptions title="路由列表" />
+
+ <a-card title="路由【1】">
+ <a-space size="middle" direction="vertical">
+ <span>名称:未指定</span>
+ <a-space size="small">
+ <span>服务生效范围:</span>
+ <a-tooltip>
+ <template #title>服务精确匹配org.apache.dubbo.samples.UserService</template>
+ <a-space>
+ <a-space-compact direction="horizontal">
+ <a-button type="primary">service</a-button>
+ <a-button type="primary">exact</a-button>
+ <a-button type="primary">org.apache.dubbo.samples.UserService</a-button>
+ </a-space-compact>
+ </a-space>
+ </a-tooltip>
+ </a-space>
+ <a-space size="middle">
+ <span>路由:</span>
+ <a-card style="min-width: 400px">
+ <a-space direction="vertical" size="middle">
+ <a-space size="middle">
+ <span>名称</span>
+ <span>未指定</span>
+ </a-space>
+
+ <a-space size="middle" align="start">
+ <span>匹配条件</span>
+ <a-space size="middle" direction="vertical">
+ <a-space size="middle" v-for="i in 3">
+ {{ i + '.' }}
+ <a-tooltip>
+ <template #title>方法前缀匹配get</template>
+ <a-space>
+ <a-space-compact direction="horizontal">
+ <a-button type="primary">method</a-button>
+ <a-button type="primary">prefix</a-button>
+ <a-button type="primary">get</a-button>
+ </a-space-compact>
+ </a-space>
+ </a-tooltip>
+ <a-tooltip>
+ <template #title>参数范围匹配1-100</template>
+ <a-space>
+ <a-space-compact direction="horizontal">
+ <a-button type="primary">arg[1]</a-button>
+ <a-button type="primary">range</a-button>
+ <a-button type="primary"> 1-100</a-button>
+ </a-space-compact>
+ </a-space>
+ </a-tooltip>
+ </a-space>
+ </a-space>
+ </a-space>
+ <a-space size="middle">
+ <span>路由分发</span>
+ <a-tooltip>
+ <template #title>subset=-v1的地址子集赋予权重70</template>
+ <a-space>
+ <a-space>
+ <a-space-compact direction="horizontal">
+ <a-button type="primary">subset=v1</a-button>
+ <a-button type="primary">weight=70</a-button>
+ </a-space-compact>
+ </a-space>
+ </a-space>
+ </a-tooltip>
+ <a-tooltip>
+ <template #title>subset=-v2的地址子集赋予权重30</template>
+ <a-space>
+ <a-space-compact direction="horizontal">
+ <a-button type="primary">subset=v2</a-button>
+ <a-button type="primary">weight=30</a-button>
+ </a-space-compact>
+ </a-space>
+ </a-tooltip>
+ </a-space>
+ </a-space>
+ </a-card>
+ </a-space>
+ </a-space>
+ </a-card>
+ </a-card>
+</template>
+
+<script setup lang="ts">
+import { CopyOutlined } from '@ant-design/icons-vue'
+import useClipboard from 'vue-clipboard3'
+import { message } from 'ant-design-vue'
+import { type ComponentInternalInstance, getCurrentInstance } from 'vue'
+import { PRIMARY_COLOR, PRIMARY_COLOR_T } from '@/base/constants'
+
+const {
+ appContext: {
+ config: { globalProperties }
+ }
+} = <ComponentInternalInstance>getCurrentInstance()
+
+let __ = PRIMARY_COLOR
+let PRIMARY_COLOR_20 = PRIMARY_COLOR_T('20')
+
+const toClipboard = useClipboard().toClipboard
+
+function copyIt(v: string) {
+ message.success(globalProperties.$t('messageDomain.success.copy'))
+ toClipboard(v)
+}
+</script>
+
+<style scoped lang="less">
+.description-item-content {
+ &.no-card {
+ padding-left: 20px;
+ }
+
+ &.with-card:hover {
+ color: v-bind('PRIMARY_COLOR');
+ }
+}
+</style>