Merge pull request #260 from sjmshsh/feat_gc

Fix memory leak
diff --git a/app/dubboctl/cmd/build.go b/app/dubboctl/cmd/build.go
index 68d9d82..44d9cb0 100644
--- a/app/dubboctl/cmd/build.go
+++ b/app/dubboctl/cmd/build.go
@@ -79,9 +79,6 @@
 	if err != nil {
 		return err
 	}
-	if err != nil {
-		return err
-	}
 	if !f.Initialized() {
 		return dubbo.NewErrNotInitialized(f.Root)
 	}
diff --git a/app/dubboctl/cmd/proxy.go b/app/dubboctl/cmd/proxy.go
index 54aa98b..31fdf76 100644
--- a/app/dubboctl/cmd/proxy.go
+++ b/app/dubboctl/cmd/proxy.go
@@ -97,7 +97,6 @@
 
 func addProxy(opts dubbo_cmd.RunCmdOpts, cmd *cobra.Command) {
 	proxyArgs := DefaultProxyConfig()
-
 	cfg := proxyArgs.Config
 	var proxyResource model.Resource
 	arg := struct {
@@ -113,6 +112,11 @@
 		Short: "Commands related to proxy",
 		Long:  "Commands help user to generate Ingress and Egress",
 		RunE: func(cmd *cobra.Command, args []string) error {
+
+			return nil
+		},
+		PreRunE: func(cmd *cobra.Command, args []string) error {
+			var err error
 			logger.InitCmdSugar(zapcore.AddSync(cmd.OutOrStdout()))
 			level, err := dubbo_log.ParseLogLevel(arg.logLevel)
 			if err != nil {
@@ -130,9 +134,6 @@
 			} else {
 				core.SetLogger(core.NewLogger(level))
 			}
-			return nil
-		},
-		PreRunE: func(cmd *cobra.Command, args []string) error {
 			proxyTypeMap := map[string]model.ResourceType{
 				string(mesh_proto.IngressProxyType): mesh.ZoneIngressType,
 				string(mesh_proto.EgressProxyType):  mesh.ZoneEgressType,
@@ -144,7 +145,7 @@
 				cfg.DataplaneRuntime.EnvoyLogLevel = proxyArgs.LogLevel.String()
 			}
 
-			proxyResource, err := readResource(cmd, &cfg.DataplaneRuntime)
+			proxyResource, err = readResource(cmd, &cfg.DataplaneRuntime)
 			if err != nil {
 				runLog.Error(err, "failed to read policy", "proxyType", cfg.Dataplane.ProxyType)
 				return err
@@ -220,7 +221,7 @@
 	cmd.PersistentFlags().IntVar(&arg.maxBackups, "log-max-retained-files", 1000, "maximum number of the old log files to retain")
 	cmd.PersistentFlags().IntVar(&arg.maxSize, "log-max-size", 100, "maximum size in megabytes of a log file before it gets rotated")
 	cmd.PersistentFlags().IntVar(&arg.maxAge, "log-max-age", 30, "maximum number of days to retain old log files based on the timestamp encoded in their filename")
-
+	cmd.PersistentFlags().StringVar(&cfg.ControlPlane.URL, "cp-address", cfg.ControlPlane.URL, "URL of the Control Plane Dataplane Server. Example: https://localhost:5678")
 	proxyCmd.PersistentFlags().StringVar(&cfg.Dataplane.Name, "name", cfg.Dataplane.Name, "Name of the Dataplane")
 	proxyCmd.PersistentFlags().StringVar(&cfg.Dataplane.Mesh, "mesh", cfg.Dataplane.Mesh, "Mesh that Dataplane belongs to")
 	proxyCmd.PersistentFlags().StringVar(&cfg.Dataplane.ProxyType, "proxy-type", "dataplane", `type of the Dataplane ("dataplane", "ingress")`)
diff --git a/pkg/config/dp-server/config.go b/pkg/config/dp-server/config.go
index e0d1688..1256b61 100644
--- a/pkg/config/dp-server/config.go
+++ b/pkg/config/dp-server/config.go
@@ -48,8 +48,10 @@
 	// were failing to reconnect (we observed this in Projected Service Account
 	// Tokens e2e tests, which started flaking a lot after introducing explicit
 	// 1s timeout)
-	// TlsCertFile defines a path to a file with PEM-encoded TLS cert. If empty, autoconfigured from general.tlsCertFile
-	TlsCertFile       string                `json:"tlsCertFile" envconfig:"dubbo_dp_server_tls_cert_file"`
+	// TlsCertFile defines a path to a file with PEM-encoded TLS cert. If empty, start the plain HTTP/2 server (h2c).
+	TlsCertFile string `json:"tlsCertFile" envconfig:"dubbo_dp_server_tls_cert_file"`
+	// TlsKeyFile defines a path to a file with PEM-encoded TLS key. If empty, start the plain HTTP/2 server (h2c).
+	TlsKeyFile        string                `json:"tlsKeyFile" envconfig:"kuma_diagnostics_tls_key_file"`
 	ReadHeaderTimeout config_types.Duration `json:"readHeaderTimeout" envconfig:"dubbo_dp_server_read_header_timeout"`
 	// Port of the DP Server
 	Port int `json:"port" envconfig:"dubbo_dp_server_port"`
diff --git a/pkg/core/runtime/builder.go b/pkg/core/runtime/builder.go
index 55122a4..d86617c 100644
--- a/pkg/core/runtime/builder.go
+++ b/pkg/core/runtime/builder.go
@@ -208,6 +208,10 @@
 	return b
 }
 
+func (b *Builder) MeshCache() *mesh.Cache {
+	return b.meshCache
+}
+
 func (b *Builder) WithDDSContext(ddsctx *dds_context.Context) *Builder {
 	b.ddsctx = ddsctx
 	return b
@@ -281,6 +285,9 @@
 	if b.dps == nil {
 		return nil, errors.Errorf("DpServer has not been configured")
 	}
+	if b.meshCache == nil {
+		return nil, errors.Errorf("MeshCache has not been configured")
+	}
 
 	return &runtime{
 		RuntimeInfo: b.runtimeInfo,
@@ -304,6 +311,7 @@
 			serviceDiscovery:     b.serviceDiscover,
 			rv:                   b.rv,
 			appCtx:               b.appCtx,
+			meshCache:            b.meshCache,
 			regClient:            b.regClient,
 		},
 		Manager: b.cm,
diff --git a/pkg/core/runtime/runtime.go b/pkg/core/runtime/runtime.go
index e2e6496..4d380e1 100644
--- a/pkg/core/runtime/runtime.go
+++ b/pkg/core/runtime/runtime.go
@@ -44,6 +44,7 @@
 	dds_context "github.com/apache/dubbo-kubernetes/pkg/dds/context"
 	dp_server "github.com/apache/dubbo-kubernetes/pkg/dp-server/server"
 	"github.com/apache/dubbo-kubernetes/pkg/events"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/cache/mesh"
 	xds_runtime "github.com/apache/dubbo-kubernetes/pkg/xds/runtime"
 )
 
@@ -87,6 +88,7 @@
 	// AppContext returns a context.Context which tracks the lifetime of the apps, it gets cancelled when the app is starting to shutdown.
 	AppContext() context.Context
 	XDS() xds_runtime.XDSRuntimeContext
+	MeshCache() *mesh.Cache
 }
 
 type ResourceValidators struct {
@@ -167,6 +169,7 @@
 	adminRegistry        *registry.Registry
 	governance           governance.GovernanceConfig
 	appCtx               context.Context
+	meshCache            *mesh.Cache
 	regClient            reg_client.RegClient
 	serviceDiscovery     dubboRegistry.ServiceDiscovery
 }
@@ -203,6 +206,10 @@
 	return b.metadataReportCenter
 }
 
+func (b *runtimeContext) MeshCache() *mesh.Cache {
+	return b.meshCache
+}
+
 func (rc *runtimeContext) DDSContext() *dds_context.Context {
 	return rc.ddsctx
 }
diff --git a/pkg/core/xds/types.go b/pkg/core/xds/types.go
index 3380391..4035d13 100644
--- a/pkg/core/xds/types.go
+++ b/pkg/core/xds/types.go
@@ -163,6 +163,7 @@
 type ExternalServiceDynamicPolicies map[ServiceName]PluginOriginatedPolicies
 
 type MeshIngressResources struct {
+	Mesh        *core_mesh.MeshResource
 	EndpointMap EndpointMap
 	Resources   map[core_model.ResourceType]core_model.ResourceList
 }
diff --git a/pkg/dds/reconcile/reconciler.go b/pkg/dds/reconcile/reconciler.go
index c1fb527..9121efc 100644
--- a/pkg/dds/reconcile/reconciler.go
+++ b/pkg/dds/reconcile/reconciler.go
@@ -133,6 +133,7 @@
 			continue
 		}
 
+		// 如果旧版本不为空, 并且新版本和旧版本的资源是一样的, 那么以旧版本为主
 		if old != nil && r.equal(new.GetResources(typ), old.GetResources(typ)) {
 			version = old.GetVersion(typ)
 		}
diff --git a/pkg/plugins/common/zookeeper/connection.go b/pkg/plugins/common/zookeeper/connection.go
deleted file mode 100644
index af4cc0c..0000000
--- a/pkg/plugins/common/zookeeper/connection.go
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-
-package zookeeper
-
-import (
-	gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
-)
-
-import (
-	config "github.com/apache/dubbo-kubernetes/pkg/config/plugins/resources/zookeeper"
-)
-
-func ConnectToZK(cfg config.ZookeeperStoreConfig) (*gxzookeeper.ZookeeperClient, error) {
-	client, err := gxzookeeper.NewZookeeperClient("default", cfg.Servers, true)
-	if err != nil {
-		return nil, err
-	}
-	return client, nil
-}
diff --git a/pkg/plugins/common/zookeeper/listener.go b/pkg/plugins/common/zookeeper/listener.go
deleted file mode 100644
index fae637d..0000000
--- a/pkg/plugins/common/zookeeper/listener.go
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-package zookeeper
-
-type Listener interface {
-	Notify() chan *Notification
-	Error() <-chan error
-	Close() error
-}
-
-// Notification represents a single notification from the database.
-type Notification struct {
-	// Payload, or the empty string if unspecified.
-	Payload string
-}
diff --git a/pkg/plugins/common/zookeeper/zk_listener.go b/pkg/plugins/common/zookeeper/zk_listener.go
deleted file mode 100644
index 70cc299..0000000
--- a/pkg/plugins/common/zookeeper/zk_listener.go
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.
- */
-
-package zookeeper
-
-import (
-	"sync"
-)
-
-import (
-	gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
-
-	"github.com/go-logr/logr"
-
-	"go.uber.org/atomic"
-)
-
-import (
-	"github.com/apache/dubbo-kubernetes/pkg/config/plugins/resources/zookeeper"
-)
-
-type zkListener struct {
-	Client        *gxzookeeper.ZookeeperClient
-	pathMapLock   sync.Mutex
-	pathMap       map[string]*atomic.Int32
-	wg            sync.WaitGroup
-	err           chan error
-	notifications chan *Notification
-	stop          chan struct{}
-}
-
-func NewListener(cfg zookeeper.ZookeeperStoreConfig, log logr.Logger) (Listener, error) {
-	return nil, nil
-}
-
-func (z *zkListener) Error() <-chan error {
-	return z.err
-}
-
-func (z *zkListener) Notify() chan *Notification {
-	return z.notifications
-}
-
-func (z *zkListener) Close() error {
-	close(z.stop)
-	z.wg.Wait()
-	return nil
-}
diff --git a/pkg/xds/bootstrap/template_v3.go b/pkg/xds/bootstrap/template_v3.go
index 71034f1..5fbc406 100644
--- a/pkg/xds/bootstrap/template_v3.go
+++ b/pkg/xds/bootstrap/template_v3.go
@@ -221,16 +221,16 @@
 	for _, r := range res.StaticResources.Clusters {
 		if r.Name == adsClusterName {
 			transport := &envoy_tls.UpstreamTlsContext{
-				Sni: parameters.XdsHost,
+				Sni:              parameters.XdsHost,
 				CommonTlsContext: &envoy_tls.CommonTlsContext{
-					TlsParams: &envoy_tls.TlsParameters{
-						TlsMinimumProtocolVersion: envoy_tls.TlsParameters_TLSv1_2,
-					},
-					ValidationContextType: &envoy_tls.CommonTlsContext_ValidationContextSdsSecretConfig{
-						//ValidationContextSdsSecretConfig: &envoy_tls.SdsSecretConfig{
-						//	Name: tls.CpValidationCtx,
-						//},
-					},
+					//TlsParams: &envoy_tls.TlsParameters{
+					//	TlsMinimumProtocolVersion: envoy_tls.TlsParameters_TLSv1_2,
+					//},
+					//ValidationContextType: &envoy_tls.CommonTlsContext_ValidationContextSdsSecretConfig{
+					//ValidationContextSdsSecretConfig: &envoy_tls.SdsSecretConfig{
+					//	Name: tls.CpValidationCtx,
+					//},
+					//},
 				},
 			}
 			any, err := util_proto.MarshalAnyDeterministic(transport)
diff --git a/pkg/xds/context/aggregate_mesh_context.go b/pkg/xds/context/aggregate_mesh_context.go
new file mode 100644
index 0000000..fb2e5cb
--- /dev/null
+++ b/pkg/xds/context/aggregate_mesh_context.go
@@ -0,0 +1,57 @@
+package context
+
+import (
+	"context"
+)
+
+import (
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/cache/sha256"
+)
+
+type meshContextFetcher = func(ctx context.Context, meshName string) (MeshContext, error)
+
+func AggregateMeshContexts(
+	ctx context.Context,
+	resManager manager.ReadOnlyResourceManager,
+	fetcher meshContextFetcher,
+) (AggregatedMeshContexts, error) {
+	var meshList core_mesh.MeshResourceList
+	if err := resManager.List(ctx, &meshList, core_store.ListOrdered()); err != nil {
+		return AggregatedMeshContexts{}, err
+	}
+
+	var meshContexts []MeshContext
+	meshContextsByName := map[string]MeshContext{}
+	for _, mesh := range meshList.Items {
+		meshCtx, err := fetcher(ctx, mesh.GetMeta().GetName())
+		if err != nil {
+			if core_store.IsResourceNotFound(err) {
+				// When the mesh no longer exists it's likely because it was removed since, let's just skip it.
+				continue
+			}
+			return AggregatedMeshContexts{}, err
+		}
+		meshContexts = append(meshContexts, meshCtx)
+		meshContextsByName[mesh.Meta.GetName()] = meshCtx
+	}
+
+	hash := aggregatedHash(meshContexts)
+
+	result := AggregatedMeshContexts{
+		Hash:               hash,
+		Meshes:             meshList.Items,
+		MeshContextsByName: meshContextsByName,
+	}
+	return result, nil
+}
+
+func aggregatedHash(meshContexts []MeshContext) string {
+	var hash string
+	for _, meshCtx := range meshContexts {
+		hash += meshCtx.Hash
+	}
+	return sha256.Hash(hash)
+}
diff --git a/pkg/xds/context/context.go b/pkg/xds/context/context.go
index ea3a57c..8537db0 100644
--- a/pkg/xds/context/context.go
+++ b/pkg/xds/context/context.go
@@ -55,6 +55,18 @@
 	return base64.StdEncoding.EncodeToString(g.hash)
 }
 
+// BaseMeshContext holds for a Mesh a set of resources that are changing less often (policies, external services...)
+type BaseMeshContext struct {
+	Mesh        *core_mesh.MeshResource
+	ResourceMap ResourceMap
+	hash        []byte
+}
+
+// Hash base64 version of the hash mostly useed for testing
+func (g BaseMeshContext) Hash() string {
+	return base64.StdEncoding.EncodeToString(g.hash)
+}
+
 type MeshContext struct {
 	Hash                string
 	Resource            *core_mesh.MeshResource
@@ -76,3 +88,36 @@
 	}
 	return core_mesh.ProtocolUnknown
 }
+
+// AggregatedMeshContexts is an aggregate of all MeshContext across all meshes
+type AggregatedMeshContexts struct {
+	Hash               string
+	Meshes             []*core_mesh.MeshResource
+	MeshContextsByName map[string]MeshContext
+}
+
+// MustGetMeshContext panics if there is no mesh context for given mesh. Call it when iterating over .Meshes
+// There is a guarantee that for every Mesh in .Meshes there is a MeshContext.
+func (m AggregatedMeshContexts) MustGetMeshContext(meshName string) MeshContext {
+	meshCtx, ok := m.MeshContextsByName[meshName]
+	if !ok {
+		panic("there should be a corresponding mesh context for every mesh in mesh contexts")
+	}
+	return meshCtx
+}
+
+func (m AggregatedMeshContexts) AllDataplanes() []*core_mesh.DataplaneResource {
+	var resources []*core_mesh.DataplaneResource
+	for _, mesh := range m.Meshes {
+		meshCtx := m.MustGetMeshContext(mesh.Meta.GetName())
+		resources = append(resources, meshCtx.Resources.Dataplanes().Items...)
+	}
+	return resources
+}
+
+func (m AggregatedMeshContexts) ZoneIngresses() []*core_mesh.ZoneIngressResource {
+	for _, meshCtx := range m.MeshContextsByName {
+		return meshCtx.Resources.ZoneIngresses().Items // all mesh contexts has the same list
+	}
+	return nil
+}
diff --git a/pkg/xds/context/mesh_context_builder.go b/pkg/xds/context/mesh_context_builder.go
index e17c737..733dc2d 100644
--- a/pkg/xds/context/mesh_context_builder.go
+++ b/pkg/xds/context/mesh_context_builder.go
@@ -21,7 +21,9 @@
 	"bytes"
 	"context"
 	"encoding/base64"
+	"fmt"
 	"hash/fnv"
+	"slices"
 )
 
 import (
@@ -85,7 +87,7 @@
 			return nil, err
 		}
 		if desc.Scope == core_model.ScopeGlobal && desc.Name != system.ConfigType { // For config we ignore them atm and prefer to rely on more specific filters.
-			rmap[t], err = m.fetchResourceList(ctx, t, nil)
+			rmap[t], err = m.fetchResourceList(ctx, t, nil, nil)
 			if err != nil {
 				return nil, errors.Wrap(err, "failed to build global context")
 			}
@@ -101,22 +103,77 @@
 	}, nil
 }
 
+func (m *meshContextBuilder) BuildBaseMeshContextIfChanged(ctx context.Context, meshName string, latest *BaseMeshContext) (*BaseMeshContext, error) {
+	mesh := core_mesh.NewMeshResource()
+	if err := m.rm.Get(ctx, mesh, core_store.GetByKey(meshName, core_model.NoMesh)); err != nil {
+		return nil, err
+	}
+	rmap := ResourceMap{}
+	// Add the mesh to the resourceMap
+	rmap[core_mesh.MeshType] = mesh.Descriptor().NewList()
+	_ = rmap[core_mesh.MeshType].AddItem(mesh)
+	rmap[core_mesh.MeshType].GetPagination().SetTotal(1)
+	for t := range m.typeSet {
+		desc, err := registry.Global().DescriptorFor(t)
+		if err != nil {
+			return nil, err
+		}
+		// Only pick the policies, gateways, external services and the vip config map
+		switch {
+		case desc.IsPolicy:
+			rmap[t], err = m.fetchResourceList(ctx, t, mesh, nil)
+		// ignore system.ConfigType for now
+		default:
+			// DO nothing we're not interested in this type
+		}
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to build base mesh context")
+		}
+	}
+	newHash := rmap.Hash()
+	if latest != nil && bytes.Equal(newHash, latest.hash) {
+		return latest, nil
+	}
+	return &BaseMeshContext{
+		hash:        newHash,
+		Mesh:        mesh,
+		ResourceMap: rmap,
+	}, nil
+}
+
 func (m meshContextBuilder) BuildIfChanged(ctx context.Context, meshName string, latestMeshCtx *MeshContext) (*MeshContext, error) {
 	globalContext, err := m.BuildGlobalContextIfChanged(ctx, nil)
 	if err != nil {
 		return nil, err
 	}
+	baseMeshContext, err := m.BuildBaseMeshContextIfChanged(ctx, meshName, nil)
+	if err != nil {
+		return nil, err
+	}
 
+	var managedTypes []core_model.ResourceType // The types not managed by global
 	resources := NewResources()
 	for resType := range m.typeSet {
 		rl, ok := globalContext.ResourceMap[resType]
 		if ok {
 			// Exists in global context take it from there
 			resources.MeshLocalResources[resType] = rl
+		} else {
+			rl, ok = baseMeshContext.ResourceMap[resType]
+			if ok { // Exist in the baseMeshContext take it from there
+				resources.MeshLocalResources[resType] = rl
+			} else { // absent from all parent contexts get it now
+				managedTypes = append(managedTypes, resType)
+				rl, err = m.fetchResourceList(ctx, resType, baseMeshContext.Mesh, nil)
+				if err != nil {
+					return nil, errors.Wrap(err, fmt.Sprintf("could not fetch resources of type:%s", resType))
+				}
+				resources.MeshLocalResources[resType] = rl
+			}
 		}
 	}
 
-	newHash := base64.StdEncoding.EncodeToString(m.hash(globalContext))
+	newHash := base64.StdEncoding.EncodeToString(m.hash(globalContext, managedTypes, resources))
 	if latestMeshCtx != nil && newHash == latestMeshCtx.Hash {
 		return latestMeshCtx, nil
 	}
@@ -127,10 +184,13 @@
 		dataplanesByName[dp.Meta.GetName()] = dp
 	}
 
-	endpointMap := xds_topology.BuildEdsEndpoint(m.zone, dataplanes, nil)
+	mesh := baseMeshContext.Mesh
+	zoneIngresses := resources.ZoneIngresses().Items
+	endpointMap := xds_topology.BuildEdsEndpoint(m.zone, dataplanes, zoneIngresses)
 
 	return &MeshContext{
 		Hash:             newHash,
+		Resource:         mesh,
 		Resources:        resources,
 		DataplanesByName: dataplanesByName,
 		EndpointMap:      endpointMap,
@@ -139,14 +199,34 @@
 
 type filterFn = func(rs core_model.Resource) bool
 
-func (m *meshContextBuilder) fetchResourceList(ctx context.Context, resType core_model.ResourceType, filterFn filterFn) (core_model.ResourceList, error) {
+func (m *meshContextBuilder) fetchResourceList(ctx context.Context, resType core_model.ResourceType, mesh *core_mesh.MeshResource, filterFn filterFn) (core_model.ResourceList, error) {
 	var listOptsFunc []core_store.ListOptionsFunc
 	desc, err := registry.Global().DescriptorFor(resType)
 	if err != nil {
 		return nil, err
 	}
+	switch desc.Scope {
+	case core_model.ScopeGlobal:
+	case core_model.ScopeMesh:
+		if mesh != nil {
+			listOptsFunc = append(listOptsFunc, core_store.ListByMesh(mesh.GetMeta().GetName()))
+		}
+	default:
+		return nil, fmt.Errorf("unknown resource scope:%s", desc.Scope)
+	}
 	listOptsFunc = append(listOptsFunc, core_store.ListOrdered())
 	list := desc.NewList()
+	// TODO: Currently, We only interested in Dataplane, Mapping, Mesh and MetaData
+	acceptedTypes := map[core_model.ResourceType]struct{}{
+		core_mesh.DataplaneType: {},
+		core_mesh.MappingType:   {},
+		core_mesh.MeshType:      {},
+		core_mesh.MetaDataType:  {},
+	}
+	if _, ok := acceptedTypes[resType]; !ok {
+		// ignore non-dataplane resources
+		return list, nil
+	}
 	if err := m.rm.List(ctx, list, listOptsFunc...); err != nil {
 		return nil, err
 	}
@@ -154,6 +234,7 @@
 		// No post processing stuff so return the list as is
 		return list, nil
 	}
+
 	list, err = modifyAllEntries(list, func(resource core_model.Resource) (core_model.Resource, error) {
 		if filterFn != nil && !filterFn(resource) {
 			return nil, nil
@@ -200,8 +281,13 @@
 	return newList, nil
 }
 
-func (m *meshContextBuilder) hash(globalContext *GlobalContext) []byte {
+func (m *meshContextBuilder) hash(globalContext *GlobalContext, managedTypes []core_model.ResourceType, resources Resources) []byte {
+	slices.Sort(managedTypes)
 	hasher := fnv.New128a()
 	_, _ = hasher.Write(globalContext.hash)
+	for _, resType := range managedTypes {
+		_, _ = hasher.Write(core_model.ResourceListHash(resources.MeshLocalResources[resType]))
+	}
+
 	return hasher.Sum(nil)
 }
diff --git a/pkg/xds/envoy/endpoints/endpoints.go b/pkg/xds/envoy/endpoints/endpoints.go
index 89afc9a..7e12baa 100644
--- a/pkg/xds/envoy/endpoints/endpoints.go
+++ b/pkg/xds/envoy/endpoints/endpoints.go
@@ -22,7 +22,7 @@
 )
 
 import (
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/proto"
 )
 
 import (
diff --git a/pkg/xds/generator/ingress_proxy_generator.go b/pkg/xds/generator/ingress_proxy_generator.go
index 943e3c2..1ae70db 100644
--- a/pkg/xds/generator/ingress_proxy_generator.go
+++ b/pkg/xds/generator/ingress_proxy_generator.go
@@ -19,21 +19,80 @@
 
 import (
 	"context"
+	"sort"
 )
 
 import (
+	envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+
+	"golang.org/x/exp/maps"
+)
+
+import (
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
 	"github.com/apache/dubbo-kubernetes/pkg/core"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
 	model "github.com/apache/dubbo-kubernetes/pkg/core/xds"
 	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+	envoy_listeners "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/generator/zoneproxy"
 )
 
 var ingressLog = core.Log.WithName("ingress-proxy-generator")
 
 // Ingress is a marker to indicate by which ProxyGenerator resources were generated.
-const Ingress = "outbound"
+const Ingress = "ingress"
 
 type IngressGenerator struct{}
 
 func (g IngressGenerator) Generator(ctx context.Context, _ *model.ResourceSet, xdsCtx xds_context.Context, proxy *model.Proxy) (*model.ResourceSet, error) {
-	return nil, nil
+	resources := core_xds.NewResourceSet()
+
+	networking := proxy.ZoneIngressProxy.ZoneIngressResource.Spec.GetNetworking()
+	address, port := networking.GetAddress(), networking.GetPort()
+	listenerBuilder := envoy_listeners.NewInboundListenerBuilder(proxy.APIVersion, address, port, core_xds.SocketAddressProtocolTCP).
+		Configure(envoy_listeners.TLSInspector())
+
+	availableSvcsByMesh := map[string][]*mesh_proto.ZoneIngress_AvailableService{}
+	for _, service := range proxy.ZoneIngressProxy.ZoneIngressResource.Spec.AvailableServices {
+		availableSvcsByMesh[service.Mesh] = append(availableSvcsByMesh[service.Mesh], service)
+	}
+
+	for _, mr := range proxy.ZoneIngressProxy.MeshResourceList {
+		meshName := mr.Mesh.GetMeta().GetName()
+		serviceList := maps.Keys(mr.EndpointMap)
+		sort.Strings(serviceList)
+		dest := zoneproxy.BuildMeshDestinations(
+			availableSvcsByMesh[meshName],
+			xds_context.Resources{MeshLocalResources: mr.Resources},
+		)
+
+		services := zoneproxy.AddFilterChains(availableSvcsByMesh[meshName], proxy.APIVersion, listenerBuilder, dest, mr.EndpointMap)
+
+		cdsResources, err := zoneproxy.GenerateCDS(dest, services, proxy.APIVersion, meshName, Ingress)
+		if err != nil {
+			return nil, err
+		}
+		resources.Add(cdsResources...)
+
+		edsResources, err := zoneproxy.GenerateEDS(services, mr.EndpointMap, proxy.APIVersion, meshName, Ingress)
+		if err != nil {
+			return nil, err
+		}
+		resources.Add(edsResources...)
+	}
+
+	listener, err := listenerBuilder.Build()
+	if err != nil {
+		return nil, err
+	}
+	if len(listener.(*envoy_listener_v3.Listener).FilterChains) > 0 {
+		resources.Add(&core_xds.Resource{
+			Name:     listener.GetName(),
+			Origin:   Ingress,
+			Resource: listener,
+		})
+	}
+
+	return resources, nil
 }
diff --git a/pkg/xds/generator/zoneproxy/destinations.go b/pkg/xds/generator/zoneproxy/destinations.go
new file mode 100644
index 0000000..1c2ec56
--- /dev/null
+++ b/pkg/xds/generator/zoneproxy/destinations.go
@@ -0,0 +1,48 @@
+package zoneproxy
+
+import (
+	"reflect"
+)
+
+import (
+	"golang.org/x/exp/slices"
+)
+
+import (
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+	envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+)
+
+func BuildMeshDestinations(
+	availableServices []*mesh_proto.ZoneIngress_AvailableService, // available services for a single mesh
+	res xds_context.Resources,
+) map[string][]envoy_tags.Tags {
+	destForMesh := map[string][]envoy_tags.Tags{}
+	addTrafficFlowByDefaultDestination(destForMesh)
+	return destForMesh
+}
+
+// addTrafficFlowByDefaultDestination Make sure that when
+// at least one MeshHTTPRoute policy exists there will be a "match all"
+// destination pointing to all services (dubbo.io/service:* -> dubbo.io/service:*)
+func addTrafficFlowByDefaultDestination(
+	destinations map[string][]envoy_tags.Tags,
+) {
+	// We need to add a destination to route any service to any instance of
+	// that service
+	matchAllTags := envoy_tags.Tags{mesh_proto.ServiceTag: mesh_proto.MatchAllTag}
+	matchAllDestinations := destinations[mesh_proto.MatchAllTag]
+	foundAllServicesDestination := slices.ContainsFunc(
+		matchAllDestinations,
+		func(tagsElem envoy_tags.Tags) bool {
+			return reflect.DeepEqual(tagsElem, matchAllTags)
+		},
+	)
+
+	if !foundAllServicesDestination {
+		matchAllDestinations = append(matchAllDestinations, matchAllTags)
+	}
+
+	destinations[mesh_proto.MatchAllTag] = matchAllDestinations
+}
diff --git a/pkg/xds/generator/zoneproxy/generator.go b/pkg/xds/generator/zoneproxy/generator.go
new file mode 100644
index 0000000..9db2262
--- /dev/null
+++ b/pkg/xds/generator/zoneproxy/generator.go
@@ -0,0 +1,155 @@
+package zoneproxy
+
+import (
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	envoy_common "github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
+	envoy_clusters "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/clusters"
+	envoy_endpoints "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/endpoints"
+	envoy_listeners "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners"
+	envoy_names "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/names"
+	envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+)
+
+func GenerateCDS(
+	destinationsPerService map[string][]envoy_tags.Tags,
+	services envoy_common.Services,
+	apiVersion core_xds.APIVersion,
+	meshName string,
+	origin string,
+) ([]*core_xds.Resource, error) {
+	matchAllDestinations := destinationsPerService[mesh_proto.MatchAllTag]
+
+	var resources []*core_xds.Resource
+	for _, service := range services.Sorted() {
+		clusters := services[service]
+
+		var tagsSlice envoy_tags.TagsSlice
+		for _, cluster := range clusters.Clusters() {
+			tagsSlice = append(tagsSlice, cluster.Tags())
+		}
+		tagSlice := append(tagsSlice, matchAllDestinations...)
+
+		tagKeySlice := tagSlice.ToTagKeysSlice().Transform(
+			envoy_tags.Without(mesh_proto.ServiceTag),
+		)
+
+		clusterName := envoy_names.GetMeshClusterName(meshName, service)
+		edsCluster, err := envoy_clusters.NewClusterBuilder(apiVersion, clusterName).
+			Configure(envoy_clusters.EdsCluster()).
+			Configure(envoy_clusters.LbSubset(tagKeySlice)).
+			Build()
+		if err != nil {
+			return nil, err
+		}
+		resources = append(resources, &core_xds.Resource{
+			Name:     clusterName,
+			Origin:   origin,
+			Resource: edsCluster,
+		})
+	}
+
+	return resources, nil
+}
+
+func GenerateEDS(
+	services envoy_common.Services,
+	endpointMap core_xds.EndpointMap,
+	apiVersion core_xds.APIVersion,
+	meshName string,
+	origin string,
+) ([]*core_xds.Resource, error) {
+	var resources []*core_xds.Resource
+
+	for _, service := range services.Sorted() {
+		endpoints := endpointMap[service]
+
+		clusterName := envoy_names.GetMeshClusterName(meshName, service)
+		cla, err := envoy_endpoints.CreateClusterLoadAssignment(clusterName, endpoints, apiVersion)
+		if err != nil {
+			return nil, err
+		}
+		resources = append(resources, &core_xds.Resource{
+			Name:     clusterName,
+			Origin:   origin,
+			Resource: cla,
+		})
+	}
+
+	return resources, nil
+}
+
+// AddFilterChains adds filter chains to a listener. We generate
+// FilterChainsMatcher for each unique destination. This approach has
+// a limitation: additional tags on outbound in Universal mode won't work across
+// different zones.
+func AddFilterChains(
+	availableServices []*mesh_proto.ZoneIngress_AvailableService,
+	apiVersion core_xds.APIVersion,
+	listenerBuilder *envoy_listeners.ListenerBuilder,
+	destinationsPerService map[string][]envoy_tags.Tags,
+	endpointMap core_xds.EndpointMap,
+) envoy_common.Services {
+	servicesAcc := envoy_common.NewServicesAccumulator(nil)
+
+	for _, service := range availableServices {
+		serviceName := service.Tags[mesh_proto.ServiceTag]
+		destinations := destinationsPerService[serviceName]
+		destinations = append(destinations, destinationsPerService[mesh_proto.MatchAllTag]...)
+		clusterName := envoy_names.GetMeshClusterName(service.Mesh, serviceName)
+		serviceEndpoints := endpointMap[serviceName]
+
+		for _, destination := range destinations {
+
+			// relevantTags is a set of tags for which it actually makes sense to do LB split on.
+			// If the endpoint list is the same with or without the tag, we should just not do the split.
+			// This solves the problem that Envoy deduplicate endpoints of the same address and different metadata.
+			// example 1:
+			// Ingress1 (10.0.0.1) supports service:a,version:1 and service:a,version:2
+			// Ingress2 (10.0.0.2) supports service:a,version:1 and service:a,version:2
+			// If we want to split by version, we don't need to do LB subset on version.
+			//
+			// example 2:
+			// Ingress1 (10.0.0.1) supports service:a,version:1
+			// Ingress2 (10.0.0.2) supports service:a,version:2
+			// If we want to split by version, we need LB subset.
+			relevantTags := envoy_tags.Tags{}
+			for key, value := range destination {
+				matchedTargets := map[string]struct{}{}
+				allTargets := map[string]struct{}{}
+				for _, endpoint := range serviceEndpoints {
+					address := endpoint.Address()
+					if endpoint.Tags[key] == value || value == mesh_proto.MatchAllTag {
+						matchedTargets[address] = struct{}{}
+					}
+					allTargets[address] = struct{}{}
+				}
+				if len(matchedTargets) < len(allTargets) {
+					relevantTags[key] = value
+				}
+			}
+
+			cluster := envoy_common.NewCluster(
+				envoy_common.WithName(clusterName),
+				envoy_common.WithService(serviceName),
+				envoy_common.WithTags(relevantTags),
+			)
+			cluster.SetMesh(service.Mesh)
+
+			filterChain := envoy_listeners.FilterChain(
+				envoy_listeners.NewFilterChainBuilder(apiVersion, envoy_common.AnonymousResource).Configure(
+					envoy_listeners.TcpProxyDeprecatedWithMetadata(
+						clusterName,
+						cluster,
+					),
+				),
+			)
+
+			listenerBuilder.Configure(filterChain)
+
+			servicesAcc.Add(cluster)
+		}
+	}
+
+	return servicesAcc.Services()
+}
diff --git a/pkg/xds/ingress/dataplane.go b/pkg/xds/ingress/dataplane.go
new file mode 100644
index 0000000..744bd48
--- /dev/null
+++ b/pkg/xds/ingress/dataplane.go
@@ -0,0 +1,116 @@
+package ingress
+
+import (
+	"context"
+	"fmt"
+	"sort"
+	"strings"
+)
+
+import (
+	"golang.org/x/exp/slices"
+
+	"google.golang.org/protobuf/proto"
+)
+
+import (
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	envoy "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+)
+
+// tagSets represent map from tags (encoded as string) to number of instances
+type tagSets map[serviceKey]uint32
+
+type serviceKey struct {
+	mesh string
+	tags string
+}
+
+type serviceKeySlice []serviceKey
+
+func (s serviceKeySlice) Len() int { return len(s) }
+func (s serviceKeySlice) Less(i, j int) bool {
+	return s[i].mesh < s[j].mesh || (s[i].mesh == s[j].mesh && s[i].tags < s[j].tags)
+}
+func (s serviceKeySlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func (sk *serviceKey) String() string {
+	return fmt.Sprintf("%s.%s", sk.tags, sk.mesh)
+}
+
+func (s tagSets) addInstanceOfTags(mesh string, tags envoy.Tags) {
+	strTags := tags.String()
+	s[serviceKey{tags: strTags, mesh: mesh}]++
+}
+
+func (s tagSets) toAvailableServices() []*mesh_proto.ZoneIngress_AvailableService {
+	var result []*mesh_proto.ZoneIngress_AvailableService
+
+	var keys []serviceKey
+	for key := range s {
+		keys = append(keys, key)
+	}
+	sort.Sort(serviceKeySlice(keys))
+
+	for _, key := range keys {
+		tags, _ := envoy.TagsFromString(key.tags) // ignore error since we control how string looks like
+		result = append(result, &mesh_proto.ZoneIngress_AvailableService{
+			Tags:      tags,
+			Instances: s[key],
+			Mesh:      key.mesh,
+		})
+	}
+	return result
+}
+
+func UpdateAvailableServices(
+	ctx context.Context,
+	rm manager.ResourceManager,
+	ingress *core_mesh.ZoneIngressResource,
+	otherDataplanes []*core_mesh.DataplaneResource,
+	tagFilters []string,
+) error {
+	availableServices := GetIngressAvailableServices(otherDataplanes, tagFilters)
+
+	if availableServicesEqual(availableServices, ingress.Spec.GetAvailableServices()) {
+		return nil
+	}
+	ingress.Spec.AvailableServices = availableServices
+	if err := rm.Update(ctx, ingress); err != nil {
+		return err
+	}
+	return nil
+}
+
+func availableServicesEqual(services []*mesh_proto.ZoneIngress_AvailableService, other []*mesh_proto.ZoneIngress_AvailableService) bool {
+	if len(services) != len(other) {
+		return false
+	}
+	for i := range services {
+		if !proto.Equal(services[i], other[i]) {
+			return false
+		}
+	}
+	return true
+}
+
+func GetIngressAvailableServices(others []*core_mesh.DataplaneResource, tagFilters []string) []*mesh_proto.ZoneIngress_AvailableService {
+	tagSets := tagSets{}
+	for _, dp := range others {
+		for _, dpInbound := range dp.Spec.GetNetworking().GetHealthyInbounds() {
+			tags := map[string]string{}
+			for key, value := range dpInbound.Tags {
+				hasPrefix := func(tagFilter string) bool {
+					return strings.HasPrefix(key, tagFilter)
+				}
+				if len(tagFilters) == 0 || slices.ContainsFunc(tagFilters, hasPrefix) {
+					tags[key] = value
+				}
+			}
+			tagSets.addInstanceOfTags(dp.GetMeta().GetMesh(), tags)
+		}
+	}
+	return tagSets.toAvailableServices()
+}
diff --git a/pkg/xds/ingress/outbound.go b/pkg/xds/ingress/outbound.go
new file mode 100644
index 0000000..b128e88
--- /dev/null
+++ b/pkg/xds/ingress/outbound.go
@@ -0,0 +1,39 @@
+package ingress
+
+import (
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+func BuildEndpointMap(
+	destinations core_xds.DestinationMap,
+	dataplanes []*core_mesh.DataplaneResource,
+) core_xds.EndpointMap {
+	if len(destinations) == 0 {
+		return nil
+	}
+
+	outbound := core_xds.EndpointMap{}
+	for _, dataplane := range dataplanes {
+		for _, inbound := range dataplane.Spec.GetNetworking().GetHealthyInbounds() {
+			service := inbound.Tags[mesh_proto.ServiceTag]
+			selectors, ok := destinations[service]
+			if !ok {
+				continue
+			}
+			if !selectors.Matches(inbound.Tags) {
+				continue
+			}
+			iface := dataplane.Spec.GetNetworking().ToInboundInterface(inbound)
+			outbound[service] = append(outbound[service], core_xds.Endpoint{
+				Target: iface.DataplaneAdvertisedIP,
+				Port:   iface.DataplanePort,
+				Tags:   inbound.Tags,
+				Weight: 1,
+			})
+		}
+	}
+
+	return outbound
+}
diff --git a/pkg/xds/ingress/router.go b/pkg/xds/ingress/router.go
new file mode 100644
index 0000000..604961a
--- /dev/null
+++ b/pkg/xds/ingress/router.go
@@ -0,0 +1,19 @@
+package ingress
+
+import (
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+func BuildDestinationMap(mesh string, ingress *core_mesh.ZoneIngressResource) core_xds.DestinationMap {
+	destinations := core_xds.DestinationMap{}
+	for _, svc := range ingress.Spec.GetAvailableServices() {
+		if mesh != svc.GetMesh() {
+			continue
+		}
+		service := svc.Tags[mesh_proto.ServiceTag]
+		destinations[service] = destinations[service].Add(mesh_proto.MatchTags(svc.Tags))
+	}
+	return destinations
+}
diff --git a/pkg/xds/sync/componenets.go b/pkg/xds/sync/componenets.go
index 4a8e8be..cdf79c8 100644
--- a/pkg/xds/sync/componenets.go
+++ b/pkg/xds/sync/componenets.go
@@ -73,6 +73,7 @@
 		IngressProxyBuilder:   ingressProxyBuilder,
 		IngressReconciler:     ingressReconciler,
 		EnvoyCpCtx:            envoyCpCtx,
+		MeshCache:             rt.MeshCache(),
 		MetadataTracker:       metadataTracker,
 		ResManager:            rt.ReadOnlyResourceManager(),
 	}
diff --git a/pkg/xds/sync/dataplane_watchdog.go b/pkg/xds/sync/dataplane_watchdog.go
index e9b443d..72d239f 100644
--- a/pkg/xds/sync/dataplane_watchdog.go
+++ b/pkg/xds/sync/dataplane_watchdog.go
@@ -116,7 +116,43 @@
 }
 
 func (d *DataplaneWatchdog) syncIngress(ctx context.Context, metadata *core_xds.DataplaneMetadata) (SyncResult, error) {
-	return SyncResult{}, nil
+	envoyCtx := &xds_context.Context{
+		ControlPlane: d.EnvoyCpCtx,
+		Mesh:         xds_context.MeshContext{}, // ZoneIngress does not have a mesh!
+	}
+
+	aggregatedMeshCtxs, err := xds_context.AggregateMeshContexts(ctx, d.ResManager, d.MeshCache.GetMeshContext)
+	if err != nil {
+		return SyncResult{}, errors.Wrap(err, "could not aggregate mesh contexts")
+	}
+
+	result := SyncResult{
+		ProxyType: mesh_proto.IngressProxyType,
+	}
+	syncForConfig := aggregatedMeshCtxs.Hash != d.lastHash
+	if !syncForConfig {
+		result.Status = SkipStatus
+		return result, nil
+	}
+
+	d.log.V(1).Info("snapshot hash updated, reconcile", "prev", d.lastHash, "current", aggregatedMeshCtxs.Hash)
+	d.lastHash = aggregatedMeshCtxs.Hash
+
+	proxy, err := d.IngressProxyBuilder.Build(ctx, d.key, aggregatedMeshCtxs)
+	if err != nil {
+		return SyncResult{}, errors.Wrap(err, "could not build ingress proxy")
+	}
+	proxy.Metadata = metadata
+	changed, err := d.IngressReconciler.Reconcile(ctx, *envoyCtx, proxy)
+	if err != nil {
+		return SyncResult{}, errors.Wrap(err, "could not reconcile")
+	}
+	if changed {
+		result.Status = ChangedStatus
+	} else {
+		result.Status = GeneratedStatus
+	}
+	return result, nil
 }
 
 func (d *DataplaneWatchdog) syncEgress(ctx context.Context, metadata *core_xds.DataplaneMetadata) (SyncResult, error) {
diff --git a/pkg/xds/sync/ingress_proxy_builder.go b/pkg/xds/sync/ingress_proxy_builder.go
index 6cdecb8..5657978 100644
--- a/pkg/xds/sync/ingress_proxy_builder.go
+++ b/pkg/xds/sync/ingress_proxy_builder.go
@@ -22,14 +22,20 @@
 )
 
 import (
+	"github.com/apache/dubbo-kubernetes/pkg/core/dns/lookup"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
 	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
 	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
 	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
 	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/ingress"
+	xds_topology "github.com/apache/dubbo-kubernetes/pkg/xds/topology"
 )
 
 type IngressProxyBuilder struct {
 	ResManager manager.ResourceManager
+	LookupIP   lookup.LookupIPFunc
 
 	apiVersion        core_xds.APIVersion
 	zone              string
@@ -39,7 +45,86 @@
 func (p *IngressProxyBuilder) Build(
 	ctx context.Context,
 	key core_model.ResourceKey,
-	aggregatedMeshCtxs xds_context.MeshContext,
+	aggregatedMeshCtxs xds_context.AggregatedMeshContexts,
 ) (*core_xds.Proxy, error) {
-	return &core_xds.Proxy{}, nil
+	zoneIngress, err := p.getZoneIngress(ctx, key, aggregatedMeshCtxs)
+	if err != nil {
+		return nil, err
+	}
+
+	zoneIngress, err = xds_topology.ResolveZoneIngressPublicAddress(p.LookupIP, zoneIngress)
+	if err != nil {
+		return nil, err
+	}
+
+	proxy := &core_xds.Proxy{
+		Id:               core_xds.FromResourceKey(key),
+		APIVersion:       p.apiVersion,
+		Zone:             p.zone,
+		ZoneIngressProxy: p.buildZoneIngressProxy(zoneIngress, aggregatedMeshCtxs),
+	}
+
+	return proxy, nil
+}
+
+func (p *IngressProxyBuilder) buildZoneIngressProxy(
+	zoneIngress *core_mesh.ZoneIngressResource,
+	aggregatedMeshCtxs xds_context.AggregatedMeshContexts,
+) *core_xds.ZoneIngressProxy {
+	var meshResourceList []*core_xds.MeshIngressResources
+
+	for _, mesh := range aggregatedMeshCtxs.Meshes {
+		meshName := mesh.GetMeta().GetName()
+		meshCtx := aggregatedMeshCtxs.MustGetMeshContext(meshName)
+
+		meshResources := &core_xds.MeshIngressResources{
+			Mesh: mesh,
+			EndpointMap: ingress.BuildEndpointMap(
+				ingress.BuildDestinationMap(meshName, zoneIngress),
+				meshCtx.Resources.Dataplanes().Items,
+			),
+			Resources: meshCtx.Resources.MeshLocalResources,
+		}
+
+		meshResourceList = append(meshResourceList, meshResources)
+	}
+
+	return &core_xds.ZoneIngressProxy{
+		ZoneIngressResource: zoneIngress,
+		MeshResourceList:    meshResourceList,
+	}
+}
+
+func (p *IngressProxyBuilder) getZoneIngress(
+	ctx context.Context,
+	key core_model.ResourceKey,
+	aggregatedMeshCtxs xds_context.AggregatedMeshContexts,
+) (*core_mesh.ZoneIngressResource, error) {
+	zoneIngress := core_mesh.NewZoneIngressResource()
+	if err := p.ResManager.Get(ctx, zoneIngress, core_store.GetBy(key)); err != nil {
+		return nil, err
+	}
+	// Update Ingress' Available Services
+	// This was placed as an operation of DataplaneWatchdog out of the convenience.
+	// Consider moving to the outside of this component
+	if err := p.updateIngress(ctx, zoneIngress, aggregatedMeshCtxs); err != nil {
+		return nil, err
+	}
+	return zoneIngress, nil
+}
+
+func (p *IngressProxyBuilder) updateIngress(
+	ctx context.Context, zoneIngress *core_mesh.ZoneIngressResource,
+	aggregatedMeshCtxs xds_context.AggregatedMeshContexts,
+) error {
+	// Update Ingress' Available Services
+	// This was placed as an operation of DataplaneWatchdog out of the convenience.
+	// Consider moving to the outside of this component (follow the pattern of updating VIP outbounds)
+	return ingress.UpdateAvailableServices(
+		ctx,
+		p.ResManager,
+		zoneIngress,
+		aggregatedMeshCtxs.AllDataplanes(),
+		p.ingressTagFilters,
+	)
 }
diff --git a/pkg/xds/topology/dataplane.go b/pkg/xds/topology/dataplane.go
index 66683d5..9ece62c 100644
--- a/pkg/xds/topology/dataplane.go
+++ b/pkg/xds/topology/dataplane.go
@@ -65,6 +65,22 @@
 	return dataplane, nil
 }
 
+func ResolveZoneIngressPublicAddress(lookupIPFunc lookup.LookupIPFunc, zoneIngress *core_mesh.ZoneIngressResource) (*core_mesh.ZoneIngressResource, error) {
+	ip, err := lookupFirstIp(lookupIPFunc, zoneIngress.Spec.GetNetworking().GetAdvertisedAddress())
+	if err != nil {
+		return nil, err
+	}
+	if ip != "" { // only if we resolve any address, in most cases this is IP not a hostname
+		ziSpec := proto.Clone(zoneIngress.Spec).(*mesh_proto.ZoneIngress)
+		ziSpec.Networking.AdvertisedAddress = ip
+		return &core_mesh.ZoneIngressResource{
+			Meta: zoneIngress.Meta,
+			Spec: ziSpec,
+		}, nil
+	}
+	return zoneIngress, nil
+}
+
 func lookupFirstIp(lookupIPFunc lookup.LookupIPFunc, address string) (string, error) {
 	if address == "" || net.ParseIP(address) != nil { // There's either no address or it's already an ip so nothing to do
 		return "", nil
diff --git a/test/proxy/dp-bootstrap.yaml b/test/proxy/dp-bootstrap.yaml
new file mode 100644
index 0000000..a887e7b
--- /dev/null
+++ b/test/proxy/dp-bootstrap.yaml
@@ -0,0 +1,151 @@
+# 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.
+
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: dubbo-system
+  labels:
+    dubbo.apache/system-namespace: "true"
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: dubbo-ingress
+  namespace: dubbo-system
+  labels:
+    app: dubbo-ingress
+    app.kubernetes.io/name: dubbo
+    app.kubernetes.io/instance: dubbo
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: dubbo-ingress
+  namespace: dubbo-system
+  labels:
+    app: dubbo-ingress
+    app.kubernetes.io/name: dubbo
+    app.kubernetes.io/instance: dubbo
+spec:
+  type: LoadBalancer
+  ports:
+    - port: 10001
+      protocol: TCP
+      targetPort: 10001
+  selector:
+    app: dubbo-ingress
+    app.kubernetes.io/name: dubbo
+    app.kubernetes.io/instance: dubbo
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: dubbo-ingress
+  namespace: dubbo-system
+  labels:
+    app: dubbo-ingress
+    app.kubernetes.io/name: dubbo
+    app.kubernetes.io/instance: dubbo
+spec:
+  strategy:
+    rollingUpdate:
+      maxSurge: 1
+      maxUnavailable: 0
+  replicas: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/name: dubbo
+      app.kubernetes.io/instance: dubbo
+      app: dubbo-ingress
+  template:
+    metadata:
+      annotations:
+        dubbo.io/ingress: enabled
+      labels:
+        app: dubbo-ingress
+        app.kubernetes.io/name: dubbo
+        app.kubernetes.io/instance: dubbo
+    spec:
+      hostNetwork: true
+      serviceAccountName: dubbo-ingress
+      automountServiceAccountToken: true
+      nodeSelector:
+        kubernetes.io/os: linux
+      terminationGracePeriodSeconds: 40
+      containers:
+        - name: ingress
+          image: "dubboctl/proxy:latest"
+          imagePullPolicy: IfNotPresent
+          args:
+            - --cp-address=http://127.0.0.1:5678
+          ports:
+            - containerPort: 10001
+          livenessProbe:
+            httpGet:
+              path: "/ready"
+              port: 9901
+            failureThreshold: 12
+            initialDelaySeconds: 60
+            periodSeconds: 5
+            successThreshold: 1
+            timeoutSeconds: 3
+          readinessProbe:
+            httpGet:
+              path: "/ready"
+              port: 9901
+            failureThreshold: 12
+            initialDelaySeconds: 1
+            periodSeconds: 5
+            successThreshold: 1
+            timeoutSeconds: 3
+          resources:
+            limits:
+              cpu: 1000m
+              memory: 512Mi
+            requests:
+              cpu: 50m
+              memory: 64Mi
+          volumeMounts:
+            - name: tmp
+              mountPath: /tmp
+            - name: ingress-config
+              mountPath: /config
+      volumes:
+        - name: tmp
+          emptyDir: { }
+        - name: ingress-config
+          configMap:
+            name: ingress-config
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: ingress-config
+  namespace: dubbo-system
+  labels:
+    app: dubbo-control-plane
+    app.kubernetes.io/name: dubbo
+    app.kubernetes.io/instance: dubbo
+data:
+  ingress.yaml: |
+    type: ZoneIngress
+    name: zoneingress-1
+    networking:
+      address: 192.168.0.1
+      port: 10001
+      advertisedAddress: 10.0.0.1
+      advertisedPort: 10000
\ No newline at end of file
diff --git a/test/proxy/dp.Dockerfile b/test/proxy/dp.Dockerfile
new file mode 100644
index 0000000..60b4c5a
--- /dev/null
+++ b/test/proxy/dp.Dockerfile
@@ -0,0 +1,67 @@
+# 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.
+
+# Build the image binary
+FROM golang:1.20.1-alpine3.17 as builder
+
+
+# Build argments
+ARG TARGETOS=linux
+ARG TARGETARCH=amd64
+ARG LDFLAGS="-s -w"
+ARG BUILD
+
+WORKDIR /go/src/github.com/apache/dubbo-kubernetes
+
+# Copy the Go Modules manifests
+COPY go.mod go.mod
+COPY go.sum go.sum
+
+#RUN if [[ "${PKGNAME}" == "authority" ]]; then apk --update add gcc libc-dev upx ca-certificates && update-ca-certificates; fi
+
+# cache deps before building and copying source so that we don't need to re-download as much
+# and so that source changes don't invalidate our downloaded layer
+RUN if [[ "${BUILD}" != "CI" ]]; then go env -w GOPROXY=https://goproxy.cn,direct; fi
+RUN go env
+RUN go mod download
+
+
+# Copy the go source
+COPY pkg pkg/
+COPY app app/
+COPY api api/
+COPY conf conf/
+COPY deploy deploy/
+COPY generate generate/
+COPY test/proxy/dp test/proxy/dp
+
+# Build
+RUN env
+RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="${LDFLAGS:- -s -w}" -a -o dubboctl /go/src/github.com/apache/dubbo-kubernetes/app/dubboctl/main.go
+
+FROM envoyproxy/envoy:v1.29.2
+
+
+
+# Build
+WORKDIR /
+ARG PKGNAME
+COPY --from=builder /go/src/github.com/apache/dubbo-kubernetes/dubboctl .
+COPY --from=builder /go/src/github.com/apache/dubbo-kubernetes/test/proxy/dp .
+#COPY --from=builder /go/src/github.com/apache/dubbo-kubernetes/conf/admin.yml .
+#ENV ADMIN_CONFIG_PATH=./admin.yml
+
+
+ENTRYPOINT ["./dubboctl", "proxy","--proxy-type=ingress","--dataplane-file=/ingress.yaml"]
diff --git a/test/proxy/dp/ingress.yaml b/test/proxy/dp/ingress.yaml
new file mode 100644
index 0000000..6080785
--- /dev/null
+++ b/test/proxy/dp/ingress.yaml
@@ -0,0 +1,22 @@
+# 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.
+
+type: ZoneIngress
+name: zoneingress-1
+networking:
+  address: 192.168.0.1
+  port: 10001
+  advertisedAddress: 10.0.0.1
+  advertisedPort: 10000
\ No newline at end of file
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>