You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2022/07/27 08:24:38 UTC

[skywalking-booster-ui] branch main updated: feat: the log widget and the trace widget associate with each other, remove log tables on the trace widget (#128)

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

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-booster-ui.git


The following commit(s) were added to refs/heads/main by this push:
     new 2ba3c67  feat: the log widget and the trace widget associate with each other, remove log tables on the trace widget (#128)
2ba3c67 is described below

commit 2ba3c67d314377138dfb4a4b74e3782feb7be07c
Author: Fine0830 <fi...@outlook.com>
AuthorDate: Wed Jul 27 16:24:34 2022 +0800

    feat: the log widget and the trace widget associate with each other, remove log tables on the trace widget (#128)
---
 src/hooks/useDashboardsSession.ts                  | 46 ++++++++++++--
 src/store/modules/dashboard.ts                     |  3 +
 src/store/modules/trace.ts                         |  8 +++
 src/types/dashboard.d.ts                           |  3 +
 src/views/components/ConditionTags.vue             | 15 +----
 src/views/dashboard/controls/Log.vue               | 11 +++-
 src/views/dashboard/controls/Tab.vue               | 20 ++++++
 src/views/dashboard/controls/Trace.vue             |  4 +-
 src/views/dashboard/controls/Widget.vue            |  2 +-
 src/views/dashboard/related/log/Header.vue         | 42 +++++++++++--
 src/views/dashboard/related/log/List.vue           |  4 +-
 .../related/{components => log}/LogTable/Index.vue |  0
 .../{components => log}/LogTable/LogBrowser.vue    |  0
 .../{components => log}/LogTable/LogDetail.vue     |  0
 .../{components => log}/LogTable/LogService.vue    | 44 +++++++++----
 .../related/{components => log}/LogTable/data.ts   |  8 +--
 src/views/dashboard/related/trace/Detail.vue       | 72 ++++------------------
 src/views/dashboard/related/trace/Filter.vue       | 37 +++++++++--
 .../trace/components/D3Graph/SpanDetail.vue        | 71 +++++----------------
 .../dashboard/related/trace/utils/d3-trace-list.ts |  4 +-
 .../dashboard/related/trace/utils/d3-trace-tree.ts |  4 +-
 21 files changed, 230 insertions(+), 168 deletions(-)

diff --git a/src/hooks/useDashboardsSession.ts b/src/hooks/useDashboardsSession.ts
index e7c332b..027e27e 100644
--- a/src/hooks/useDashboardsSession.ts
+++ b/src/hooks/useDashboardsSession.ts
@@ -14,24 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import { ElMessage } from "element-plus";
 import { useDashboardStore } from "@/store/modules/dashboard";
-export default function getDashboard(param: {
+import { LayoutConfig } from "@/types/dashboard";
+
+export default function getDashboard(param?: {
   name: string;
   layer: string;
   entity: string;
 }) {
   const dashboardStore = useDashboardStore();
+  const opt = param || dashboardStore.currentDashboard;
   const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
   const dashboard = list.find(
     (d: { name: string; layer: string; entity: string }) =>
-      d.name === param.name &&
-      d.entity === param.entity &&
-      d.layer === param.layer
+      d.name === opt.name && d.entity === opt.entity && d.layer === opt.layer
   );
   const all = dashboardStore.layout;
-  const widgets = [];
+  const widgets: LayoutConfig[] = [];
   for (const item of all) {
     if (item.type === "Tab") {
+      widgets.push(item);
       if (item.children && item.children.length) {
         for (const child of item.children) {
           if (child.children && child.children.length) {
@@ -43,5 +46,36 @@ export default function getDashboard(param: {
       widgets.push(item);
     }
   }
-  return { dashboard, widgets };
+  function associationWidget(sourceId: string, filters: unknown, type: string) {
+    const widget = widgets.find((d: { type: string }) => d.type === type);
+    if (!widget) {
+      return ElMessage.info(`There has no a ${type} widget in the dashboard`);
+    }
+    const item = {
+      ...widget,
+      filters,
+    };
+    dashboardStore.setWidget(item);
+    const targetTabIndex = (widget.id || "").split("-");
+    const sourceTabindex = (sourceId || "").split("-") || [];
+    let container: Nullable<Element>;
+
+    if (targetTabIndex[1] === undefined) {
+      container = document.querySelector(".ds-main");
+    } else {
+      const w = widgets.find((d: any) => d.id === targetTabIndex[0]);
+      container = document.querySelector(".tab-layout");
+      const layout: Nullable<Element> = document.querySelector(".ds-main");
+      if (w && layout) {
+        layout.scrollTop = w.y * 10 + w.h * 5;
+      }
+    }
+    if (targetTabIndex[1] && targetTabIndex[1] !== sourceTabindex[1]) {
+      dashboardStore.setActiveTabIndex(Number(targetTabIndex[1]));
+    }
+    if (container && widget) {
+      container.scrollTop = widget.y * 10 + widget.h * 5;
+    }
+  }
+  return { dashboard, widgets, associationWidget };
 }
diff --git a/src/store/modules/dashboard.ts b/src/store/modules/dashboard.ts
index 9809221..450c237 100644
--- a/src/store/modules/dashboard.ts
+++ b/src/store/modules/dashboard.ts
@@ -39,6 +39,7 @@ interface DashboardState {
   dashboards: DashboardItem[];
   currentDashboard: Nullable<DashboardItem>;
   editMode: boolean;
+  currentTabIndex: number;
 }
 
 export const dashboardStore = defineStore({
@@ -56,6 +57,7 @@ export const dashboardStore = defineStore({
     dashboards: [],
     currentDashboard: null,
     editMode: false,
+    currentTabIndex: 0,
   }),
   actions: {
     setLayout(data: LayoutConfig[]) {
@@ -189,6 +191,7 @@ export const dashboardStore = defineStore({
       this.activedGridItem = index;
     },
     setActiveTabIndex(index: number, target?: number) {
+      this.currentTabIndex = index;
       const m = target || this.activedGridItem;
       const idx = this.layout.findIndex((d: LayoutConfig) => d.i === m);
       if (idx < 0) {
diff --git a/src/store/modules/trace.ts b/src/store/modules/trace.ts
index 4e33735..16aa422 100644
--- a/src/store/modules/trace.ts
+++ b/src/store/modules/trace.ts
@@ -63,6 +63,14 @@ export const traceStore = defineStore({
     setTraceSpans(spans: Span) {
       this.traceSpans = spans;
     },
+    resetCondition() {
+      this.conditions = {
+        queryDuration: useAppStoreWithOut().durationTime,
+        paging: { pageNum: 1, pageSize: 20 },
+        traceState: "ALL",
+        queryOrder: "BY_START_TIME",
+      };
+    },
     async getServices(layer: string) {
       const res: AxiosResponse = await graphql.query("queryServices").params({
         layer,
diff --git a/src/types/dashboard.d.ts b/src/types/dashboard.d.ts
index 1f3ecac..e8cd4b1 100644
--- a/src/types/dashboard.d.ts
+++ b/src/types/dashboard.d.ts
@@ -47,6 +47,9 @@ export interface LayoutConfig {
       startTime: string;
       endTime: string;
     };
+    traceId?: string;
+    spanId?: string;
+    segmentId?: string;
   };
 }
 
diff --git a/src/views/components/ConditionTags.vue b/src/views/components/ConditionTags.vue
index f7e2ae6..d1f56ff 100644
--- a/src/views/components/ConditionTags.vue
+++ b/src/views/components/ConditionTags.vue
@@ -15,11 +15,7 @@ limitations under the License. -->
 <template>
   <div>
     <span class="grey">{{ t("tags") }}: </span>
-    <span
-      v-if="tagsList.length"
-      class="trace-tags"
-      :style="type === 'LOG' ? `min-width: 122px;` : ''"
-    >
+    <span v-if="tagsList.length" class="trace-tags">
       <span class="selected" v-for="(item, index) in tagsList" :key="index">
         <span>{{ item }}</span>
         <span class="remove-icon" @click="removeTags(index)">×</span>
@@ -33,13 +29,7 @@ limitations under the License. -->
       @change="addLabels"
       :placeholder="t('addTags')"
     />
-    <el-popover
-      v-else
-      trigger="click"
-      style="margin: 10px 0 0 -130px"
-      :visible="visible"
-      width="300px"
-    >
+    <el-popover v-else trigger="click" :visible="visible" width="300px">
       <template #reference>
         <el-input
           size="small"
@@ -226,7 +216,6 @@ watch(
   padding: 2px 5px;
   border-radius: 3px;
   width: 250px;
-  z-index: 999;
 }
 
 .remove-icon {
diff --git a/src/views/dashboard/controls/Log.vue b/src/views/dashboard/controls/Log.vue
index 0d6708e..f379d43 100644
--- a/src/views/dashboard/controls/Log.vue
+++ b/src/views/dashboard/controls/Log.vue
@@ -30,7 +30,7 @@ limitations under the License. -->
       </div>
     </el-popover>
     <div class="header">
-      <Header :needQuery="needQuery" />
+      <Header :needQuery="needQuery" :data="data" />
     </div>
     <div class="log">
       <List />
@@ -38,20 +38,24 @@ limitations under the License. -->
   </div>
 </template>
 <script lang="ts" setup>
+import { provide } from "vue";
 import { useI18n } from "vue-i18n";
 import { useDashboardStore } from "@/store/modules/dashboard";
 import Header from "../related/log/Header.vue";
 import List from "../related/log/List.vue";
+import { LayoutConfig } from "@/types/dashboard";
+import type { PropType } from "vue";
 
 /*global defineProps */
 const props = defineProps({
   data: {
-    type: Object,
-    default: () => ({}),
+    type: Object as PropType<LayoutConfig>,
+    default: () => ({ graph: {} }),
   },
   activeIndex: { type: String, default: "" },
   needQuery: { type: Boolean, default: true },
 });
+provide("options", props.data);
 const { t } = useI18n();
 const dashboardStore = useDashboardStore();
 
@@ -72,6 +76,7 @@ function removeWidget() {
   position: absolute;
   top: 5px;
   right: 3px;
+  z-index: 1000;
 }
 
 .header {
diff --git a/src/views/dashboard/controls/Tab.vue b/src/views/dashboard/controls/Tab.vue
index 8c35399..370b28a 100644
--- a/src/views/dashboard/controls/Tab.vue
+++ b/src/views/dashboard/controls/Tab.vue
@@ -259,6 +259,26 @@ export default defineComponent({
         }
       }
     );
+    watch(
+      () => dashboardStore.currentTabIndex,
+      () => {
+        activeTabIndex.value = dashboardStore.currentTabIndex;
+        dashboardStore.activeGridItem(props.data.i);
+        dashboardStore.selectWidget(props.data);
+        const l = dashboardStore.layout.findIndex(
+          (d: LayoutConfig) => d.i === props.data.i
+        );
+        dashboardStore.setCurrentTabItems(
+          dashboardStore.layout[l].children[activeTabIndex.value].children
+        );
+        needQuery.value = true;
+        if (route.params.activeTabIndex) {
+          let p = location.href.split("/tab/")[0];
+          p = p + "/tab/" + activeTabIndex.value;
+          history.replaceState({}, "", p);
+        }
+      }
+    );
     return {
       handleClick,
       layoutUpdatedEvent,
diff --git a/src/views/dashboard/controls/Trace.vue b/src/views/dashboard/controls/Trace.vue
index 724b57f..9279a8a 100644
--- a/src/views/dashboard/controls/Trace.vue
+++ b/src/views/dashboard/controls/Trace.vue
@@ -30,7 +30,7 @@ limitations under the License. -->
       </div>
     </el-popover>
     <div class="header">
-      <Filter :needQuery="needQuery" />
+      <Filter :needQuery="needQuery" :data="data" />
     </div>
     <div class="trace flex-h">
       <TraceList />
@@ -39,6 +39,7 @@ limitations under the License. -->
   </div>
 </template>
 <script lang="ts" setup>
+import { provide } from "vue";
 import type { PropType } from "vue";
 import Filter from "../related/trace/Filter.vue";
 import TraceList from "../related/trace/TraceList.vue";
@@ -55,6 +56,7 @@ const props = defineProps({
   activeIndex: { type: String, default: "" },
   needQuery: { type: Boolean, default: true },
 });
+provide("options", props.data);
 const { t } = useI18n();
 const dashboardStore = useDashboardStore();
 function removeWidget() {
diff --git a/src/views/dashboard/controls/Widget.vue b/src/views/dashboard/controls/Widget.vue
index 1598e87..c165389 100644
--- a/src/views/dashboard/controls/Widget.vue
+++ b/src/views/dashboard/controls/Widget.vue
@@ -168,7 +168,7 @@ export default defineComponent({
 
       for (const item of associate) {
         const widget = widgets.find(
-          (d: { id: string }) => d.id === item.widgetId
+          (d: LayoutConfig) => d.id === item.widgetId
         );
         if (widget) {
           widget.filters = {
diff --git a/src/views/dashboard/related/log/Header.vue b/src/views/dashboard/related/log/Header.vue
index 0efc382..7b16c4d 100644
--- a/src/views/dashboard/related/log/Header.vue
+++ b/src/views/dashboard/related/log/Header.vue
@@ -131,6 +131,7 @@ limitations under the License. -->
 </template>
 <script lang="ts" setup>
 import { ref, reactive, watch, onUnmounted } from "vue";
+import type { PropType } from "vue";
 import { useI18n } from "vue-i18n";
 import { Option } from "@/types/app";
 import { useLogStore } from "@/store/modules/log";
@@ -141,17 +142,24 @@ import ConditionTags from "@/views/components/ConditionTags.vue";
 import { ElMessage } from "element-plus";
 import { EntityType } from "../../data";
 import { ErrorCategory } from "./data";
+import { LayoutConfig } from "@/types/dashboard";
 
-/*global defineProps */
+/*global  defineProps, Recordable */
 const props = defineProps({
   needQuery: { type: Boolean, default: true },
+  data: {
+    type: Object as PropType<LayoutConfig>,
+    default: () => ({ graph: {} }),
+  },
 });
 const { t } = useI18n();
 const appStore = useAppStoreWithOut();
 const selectorStore = useSelectorStore();
 const dashboardStore = useDashboardStore();
 const logStore = useLogStore();
-const traceId = ref<string>("");
+const traceId = ref<string>(
+  (props.data.filters && props.data.filters.traceId) || ""
+);
 const keywordsOfContent = ref<string[]>([]);
 const excludingKeywordsOfContent = ref<string[]>([]);
 const tagsList = ref<string[]>([]);
@@ -159,7 +167,7 @@ const tagsMap = ref<Option[]>([]);
 const contentStr = ref<string>("");
 const excludingContentStr = ref<string>("");
 const isBrowser = ref<boolean>(dashboardStore.layerId === "BROWSER");
-const state = reactive<any>({
+const state = reactive<Recordable>({
   instance: { value: "0", label: "All" },
   endpoint: { value: "0", label: "All" },
   service: { value: "", label: "" },
@@ -248,6 +256,11 @@ function searchLogs() {
       category: state.category.value,
     });
   } else {
+    let segmentId, spanId;
+    if (props.data.filters) {
+      segmentId = props.data.filters.segmentId;
+      spanId = props.data.filters.spanId;
+    }
     logStore.setLogCondition({
       serviceId: selectorStore.currentService
         ? selectorStore.currentService.id
@@ -259,7 +272,9 @@ function searchLogs() {
       excludingKeywordsOfContent: excludingKeywordsOfContent.value,
       tags: tagsMap.value.length ? tagsMap.value : undefined,
       paging: { pageNum: 1, pageSize: 15 },
-      relatedTrace: traceId.value ? { traceId: traceId.value } : undefined,
+      relatedTrace: traceId.value
+        ? { traceId: traceId.value, segmentId, spanId }
+        : undefined,
     });
   }
   queryLogs();
@@ -325,6 +340,12 @@ function removeExcludeContent(index: number) {
 }
 onUnmounted(() => {
   logStore.resetCondition();
+  const item = {
+    ...props.data,
+    filters: undefined,
+  };
+  dashboardStore.setWidget(item);
+  traceId.value = "";
 });
 watch(
   () => selectorStore.currentService,
@@ -351,6 +372,19 @@ watch(
     }
   }
 );
+watch(
+  () => props.data.filters,
+  (newJson, oldJson) => {
+    console.log(props.data.filters);
+    if (props.data.filters) {
+      if (JSON.stringify(newJson) === JSON.stringify(oldJson)) {
+        return;
+      }
+      traceId.value = props.data.filters.traceId || "";
+      init();
+    }
+  }
+);
 </script>
 <style lang="scss" scoped>
 .inputs {
diff --git a/src/views/dashboard/related/log/List.vue b/src/views/dashboard/related/log/List.vue
index 583b30a..448fd58 100644
--- a/src/views/dashboard/related/log/List.vue
+++ b/src/views/dashboard/related/log/List.vue
@@ -18,7 +18,7 @@ limitations under the License. -->
       v-loading="logStore.loadLogs"
       :tableData="logStore.logs || []"
       :type="type"
-      :noLink="true"
+      :noLink="false"
     >
       <div class="log-tips" v-if="!logStore.logs.length">{{ t("noData") }}</div>
     </LogTable>
@@ -39,7 +39,7 @@ limitations under the License. -->
 <script lang="ts" setup>
 import { ref, computed } from "vue";
 import { useI18n } from "vue-i18n";
-import LogTable from "@/views/dashboard/related/components/LogTable/Index.vue";
+import LogTable from "./LogTable/Index.vue";
 import { useLogStore } from "@/store/modules/log";
 import { useDashboardStore } from "@/store/modules/dashboard";
 import { ElMessage } from "element-plus";
diff --git a/src/views/dashboard/related/components/LogTable/Index.vue b/src/views/dashboard/related/log/LogTable/Index.vue
similarity index 100%
rename from src/views/dashboard/related/components/LogTable/Index.vue
rename to src/views/dashboard/related/log/LogTable/Index.vue
diff --git a/src/views/dashboard/related/components/LogTable/LogBrowser.vue b/src/views/dashboard/related/log/LogTable/LogBrowser.vue
similarity index 100%
rename from src/views/dashboard/related/components/LogTable/LogBrowser.vue
rename to src/views/dashboard/related/log/LogTable/LogBrowser.vue
diff --git a/src/views/dashboard/related/components/LogTable/LogDetail.vue b/src/views/dashboard/related/log/LogTable/LogDetail.vue
similarity index 100%
rename from src/views/dashboard/related/components/LogTable/LogDetail.vue
rename to src/views/dashboard/related/log/LogTable/LogDetail.vue
diff --git a/src/views/dashboard/related/components/LogTable/LogService.vue b/src/views/dashboard/related/log/LogTable/LogService.vue
similarity index 69%
rename from src/views/dashboard/related/components/LogTable/LogService.vue
rename to src/views/dashboard/related/log/LogTable/LogService.vue
index 4b40ff8..a24e1ce 100644
--- a/src/views/dashboard/related/components/LogTable/LogService.vue
+++ b/src/views/dashboard/related/log/LogTable/LogService.vue
@@ -14,46 +14,68 @@ See the License for the specific language governing permissions and
 limitations under the License. -->
 
 <template>
-  <div @click="showSelectSpan" class="log-item">
+  <div class="log-item">
     <div v-for="(item, index) in columns" :key="index" :class="item.label">
-      <span v-if="item.label === 'timestamp'">
+      <span v-if="item.label === 'timestamp'" @click="showSelectSpan">
         {{ dateFormat(data.timestamp) }}
       </span>
-      <span v-else-if="item.label === 'tags'">
+      <span v-else-if="item.label === 'tags'" @click="showSelectSpan">
         {{ tags }}
       </span>
-      <!-- <router-link
+      <span
         v-else-if="item.label === 'traceId' && !noLink"
-        :to="{ name: 'trace', query: { traceid: data[item.label] } }"
+        :class="noLink ? '' : 'blue'"
+        @click="linkTrace(data[item.label])"
       >
-        <span :class="noLink ? '' : 'blue'">{{ data[item.label] }}</span>
-      </router-link> -->
-      <span v-else>{{ data[item.label] }}</span>
+        {{ data[item.label] }}
+      </span>
+      <span v-else @click="showSelectSpan">{{ data[item.label] }}</span>
     </div>
   </div>
 </template>
 <script lang="ts" setup>
-import { computed } from "vue";
+import { computed, inject } from "vue";
 import dayjs from "dayjs";
 import { ServiceLogConstants } from "./data";
-/*global defineProps, defineEmits */
+import getDashboard from "@/hooks/useDashboardsSession";
+import { useDashboardStore } from "@/store/modules/dashboard";
+import { LayoutConfig } from "@/types/dashboard";
+
+/*global defineProps, defineEmits, Recordable */
 const props = defineProps({
   data: { type: Object as any, default: () => ({}) },
   noLink: { type: Boolean, default: true },
 });
+const dashboardStore = useDashboardStore();
+const options: Recordable<LayoutConfig> = inject("options") || {};
 const emit = defineEmits(["select"]);
 const columns = ServiceLogConstants;
 const tags = computed(() => {
   if (!props.data.tags) {
     return "";
   }
-  return String(props.data.tags.map((d: any) => `${d.key}=${d.value}`));
+  return String(
+    props.data.tags.map(
+      (d: { key: string; value: string }) => `${d.key}=${d.value}`
+    )
+  );
 });
 const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
   dayjs(date).format(pattern);
 function showSelectSpan() {
   emit("select", props.data);
 }
+function linkTrace(id: string) {
+  const { associationWidget } = getDashboard(dashboardStore.currentDashboard);
+  associationWidget(
+    (options.id as any) || "",
+    {
+      sourceId: options.id || "",
+      traceId: id,
+    },
+    "Trace"
+  );
+}
 </script>
 <style lang="scss" scoped>
 .log-item {
diff --git a/src/views/dashboard/related/components/LogTable/data.ts b/src/views/dashboard/related/log/LogTable/data.ts
similarity index 100%
rename from src/views/dashboard/related/components/LogTable/data.ts
rename to src/views/dashboard/related/log/LogTable/data.ts
index 58009b2..efb4e6e 100644
--- a/src/views/dashboard/related/components/LogTable/data.ts
+++ b/src/views/dashboard/related/log/LogTable/data.ts
@@ -40,14 +40,14 @@ export const ServiceLogConstants = [
     label: "tags",
     value: "tags",
   },
-  {
-    label: "content",
-    value: "content",
-  },
   {
     label: "traceId",
     value: "traceID",
   },
+  {
+    label: "content",
+    value: "content",
+  },
 ];
 export const ServiceLogDetail = [
   {
diff --git a/src/views/dashboard/related/trace/Detail.vue b/src/views/dashboard/related/trace/Detail.vue
index 4d217ce..19b1bb9 100644
--- a/src/views/dashboard/related/trace/Detail.vue
+++ b/src/views/dashboard/related/trace/Detail.vue
@@ -33,33 +33,6 @@ limitations under the License. -->
             {{ t("viewLogs") }}
           </el-button>
         </div>
-        <el-dialog
-          v-model="showTraceLogs"
-          :destroy-on-close="true"
-          fullscreen
-          @closed="showTraceLogs = false"
-        >
-          <div>
-            <el-pagination
-              v-model:currentPage="pageNum"
-              v-model:page-size="pageSize"
-              :small="true"
-              layout="prev, pager, next"
-              :pager-count="5"
-              :total="total"
-              @current-change="turnLogsPage"
-            />
-            <LogTable
-              :tableData="traceStore.traceSpanLogs || []"
-              :type="`service`"
-              :noLink="true"
-            >
-              <div class="log-tips" v-if="!traceStore.traceSpanLogs.length">
-                {{ t("noData") }}
-              </div>
-            </LogTable>
-          </div>
-        </el-dialog>
       </h5>
       <div class="mb-5 blue sm">
         <Selector
@@ -148,37 +121,31 @@ limitations under the License. -->
 </template>
 <script lang="ts">
 import dayjs from "dayjs";
-import { ref, defineComponent, computed } from "vue";
+import { ref, defineComponent, inject } from "vue";
 import { useI18n } from "vue-i18n";
 import { useTraceStore } from "@/store/modules/trace";
 import { Option } from "@/types/app";
 import copy from "@/utils/copy";
 import graphs from "./components/index";
-import LogTable from "@/views/dashboard/related/components/LogTable/Index.vue";
 import { ElMessage } from "element-plus";
+import getDashboard from "@/hooks/useDashboardsSession";
+import { LayoutConfig } from "@/types/dashboard";
 
 export default defineComponent({
   name: "TraceDetail",
   components: {
     ...graphs,
-    LogTable,
   },
   setup() {
+    /*global Recordable */
+    const options: Recordable<LayoutConfig> = inject("options") || {};
     const { t } = useI18n();
     const traceStore = useTraceStore();
     const loading = ref<boolean>(false);
     const traceId = ref<string>("");
     const displayMode = ref<string>("List");
-    const pageNum = ref<number>(1);
-    const pageSize = 10;
-    const total = computed(() =>
-      traceStore.traceList.length === pageSize
-        ? pageSize * pageNum.value + 1
-        : pageSize * pageNum.value
-    );
     const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
       dayjs(date).format(pattern);
-    const showTraceLogs = ref<boolean>(false);
 
     function handleClick() {
       copy(traceId.value || traceStore.currentTrace.traceIds[0].value);
@@ -195,23 +162,15 @@ export default defineComponent({
     }
 
     async function searchTraceLogs() {
-      showTraceLogs.value = true;
-      const res = await traceStore.getSpanLogs({
-        condition: {
-          relatedTrace: {
-            traceId: traceId.value || traceStore.currentTrace.traceIds[0].value,
-          },
-          paging: { pageNum: pageNum.value, pageSize },
+      const { associationWidget } = getDashboard();
+      associationWidget(
+        (options.id as any) || "",
+        {
+          sourceId: options?.id || "",
+          traceId: traceId.value || traceStore.currentTrace.traceIds[0].value,
         },
-      });
-      if (res.errors) {
-        ElMessage.error(res.errors);
-      }
-    }
-
-    function turnLogsPage(page: number) {
-      pageNum.value = page;
-      searchTraceLogs();
+        "Log"
+      );
     }
     return {
       traceStore,
@@ -221,12 +180,7 @@ export default defineComponent({
       handleClick,
       t,
       searchTraceLogs,
-      showTraceLogs,
-      turnLogsPage,
-      pageSize,
-      pageNum,
       loading,
-      total,
       traceId,
     };
   },
diff --git a/src/views/dashboard/related/trace/Filter.vue b/src/views/dashboard/related/trace/Filter.vue
index 4901953..77753b4 100644
--- a/src/views/dashboard/related/trace/Filter.vue
+++ b/src/views/dashboard/related/trace/Filter.vue
@@ -92,7 +92,8 @@ limitations under the License. -->
   </div>
 </template>
 <script lang="ts" setup>
-import { ref, reactive, watch } from "vue";
+import { ref, reactive, watch, onUnmounted } from "vue";
+import type { PropType } from "vue";
 import { useI18n } from "vue-i18n";
 import { Option } from "@/types/app";
 import { Status } from "../../data";
@@ -103,22 +104,29 @@ import { useSelectorStore } from "@/store/modules/selectors";
 import ConditionTags from "@/views/components/ConditionTags.vue";
 import { ElMessage } from "element-plus";
 import { EntityType } from "../../data";
+import { LayoutConfig } from "@/types/dashboard";
 
-/*global defineProps */
+/*global defineProps, Recordable */
 const props = defineProps({
   needQuery: { type: Boolean, default: true },
+  data: {
+    type: Object as PropType<LayoutConfig>,
+    default: () => ({ graph: {} }),
+  },
 });
+const traceId = ref<string>(
+  (props.data.filters && props.data.filters.traceId) || ""
+);
 const { t } = useI18n();
 const appStore = useAppStoreWithOut();
 const selectorStore = useSelectorStore();
 const dashboardStore = useDashboardStore();
 const traceStore = useTraceStore();
-const traceId = ref<string>("");
 const minTraceDuration = ref<number>();
 const maxTraceDuration = ref<number>();
 const tagsList = ref<string[]>([]);
 const tagsMap = ref<Option[]>([]);
-const state = reactive<any>({
+const state = reactive<Recordable>({
   status: { label: "All", value: "ALL" },
   instance: { value: "0", label: "All" },
   endpoint: { value: "0", label: "All" },
@@ -222,6 +230,15 @@ async function searchEndpoints(keyword: string) {
     ElMessage.error(resp.errors);
   }
 }
+onUnmounted(() => {
+  traceStore.resetCondition();
+  const item = {
+    ...props.data,
+    filters: undefined,
+  };
+  dashboardStore.setWidget(item);
+  traceId.value = "";
+});
 watch(
   () => [selectorStore.currentPod],
   () => {
@@ -248,6 +265,18 @@ watch(
     }
   }
 );
+watch(
+  () => props.data.filters,
+  (newJson, oldJson) => {
+    if (props.data.filters) {
+      if (JSON.stringify(newJson) === JSON.stringify(oldJson)) {
+        return;
+      }
+      traceId.value = props.data.filters.traceId || "";
+      init();
+    }
+  }
+);
 </script>
 <style lang="scss" scoped>
 .inputs {
diff --git a/src/views/dashboard/related/trace/components/D3Graph/SpanDetail.vue b/src/views/dashboard/related/trace/components/D3Graph/SpanDetail.vue
index 33617f9..38e1682 100644
--- a/src/views/dashboard/related/trace/components/D3Graph/SpanDetail.vue
+++ b/src/views/dashboard/related/trace/components/D3Graph/SpanDetail.vue
@@ -81,77 +81,36 @@ limitations under the License. -->
       {{ t("relatedTraceLogs") }}
     </el-button>
   </div>
-  <el-dialog
-    v-model="showRelatedLogs"
-    :destroy-on-close="true"
-    fullscreen
-    @closed="showRelatedLogs = false"
-  >
-    <el-pagination
-      v-model:currentPage="pageNum"
-      v-model:page-size="pageSize"
-      :small="true"
-      layout="prev, pager, next"
-      :pager-count="5"
-      :total="total"
-      @current-change="turnPage"
-    />
-    <LogTable
-      :tableData="traceStore.traceSpanLogs || []"
-      :type="`service`"
-      :noLink="true"
-    >
-      <div class="log-tips" v-if="!traceStore.traceSpanLogs.length">
-        {{ t("noData") }}
-      </div>
-    </LogTable>
-  </el-dialog>
 </template>
 <script lang="ts" setup>
-import { ref, computed } from "vue";
+import { inject } from "vue";
 import { useI18n } from "vue-i18n";
 import type { PropType } from "vue";
 import dayjs from "dayjs";
-import { useTraceStore } from "@/store/modules/trace";
 import copy from "@/utils/copy";
-import { ElMessage } from "element-plus";
-import LogTable from "@/views/dashboard/related/components/LogTable/Index.vue";
+import getDashboard from "@/hooks/useDashboardsSession";
+import { LayoutConfig } from "@/types/dashboard";
 
-/* global defineProps */
+/*global defineProps, Recordable */
+const options: Recordable<LayoutConfig> = inject("options") || {};
 const props = defineProps({
   currentSpan: { type: Object as PropType<any>, default: () => ({}) },
 });
 const { t } = useI18n();
-const traceStore = useTraceStore();
-const pageNum = ref<number>(1);
-const showRelatedLogs = ref<boolean>(false);
-const pageSize = 10;
-const total = computed(() =>
-  traceStore.traceList.length === pageSize
-    ? pageSize * pageNum.value + 1
-    : pageSize * pageNum.value
-);
 const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
   dayjs(date).format(pattern);
 async function getTaceLogs() {
-  showRelatedLogs.value = true;
-  const res = await traceStore.getSpanLogs({
-    condition: {
-      relatedTrace: {
-        traceId: props.currentSpan.traceId,
-        segmentId: props.currentSpan.segmentId,
-        spanId: props.currentSpan.spanId,
-      },
-      paging: { pageNum: pageNum.value, pageSize },
+  const { associationWidget } = getDashboard();
+  associationWidget(
+    (options.id as any) || "",
+    {
+      sourceId: options?.id || "",
+      traceId: props.currentSpan.traceId,
+      segmentId: props.currentSpan.segmentId,
+      spanId: props.currentSpan.spanId,
     },
-  });
-  if (res.errors) {
-    ElMessage.error(res.errors);
-  }
-}
-function turnPage(p: number) {
-  pageNum.value = p;
-  getTaceLogs();
+    "Log"
+  );
 }
 function showCurrentSpanDetail(text: string) {
   copy(text);
diff --git a/src/views/dashboard/related/trace/utils/d3-trace-list.ts b/src/views/dashboard/related/trace/utils/d3-trace-list.ts
index 8d90dc6..ddd7043 100644
--- a/src/views/dashboard/related/trace/utils/d3-trace-list.ts
+++ b/src/views/dashboard/related/trace/utils/d3-trace-list.ts
@@ -46,8 +46,8 @@ export default class ListGraph {
       .select(this.el)
       .append("svg")
       .attr("class", "trace-list-dowanload")
-      .attr("width", this.width)
-      .attr("height", this.height)
+      .attr("width", this.width > 0 ? this.width : 10)
+      .attr("height", this.height > 0 ? this.height : 10)
       .attr("transform", `translate(-5, 0)`);
     this.tip = (d3tip as any)()
       .attr("class", "d3-tip")
diff --git a/src/views/dashboard/related/trace/utils/d3-trace-tree.ts b/src/views/dashboard/related/trace/utils/d3-trace-tree.ts
index 0238283..fab33bf 100644
--- a/src/views/dashboard/related/trace/utils/d3-trace-tree.ts
+++ b/src/views/dashboard/related/trace/utils/d3-trace-tree.ts
@@ -57,8 +57,8 @@ export default class TraceMap {
       .select(this.el)
       .append("svg")
       .attr("class", "d3-trace-tree")
-      .attr("width", this.width)
-      .attr("height", this.height);
+      .attr("width", this.width > 0 ? this.width : 10)
+      .attr("height", this.height > 0 ? this.height : 10);
     this.tip = (d3tip as any)()
       .attr("class", "d3-tip")
       .offset([-8, 0])