INSERT INTO grafana.library_element
(id, org_id, folder_id, uid, name, kind, `type`, description, model, created, created_by, updated, updated_by, version, folder_uid)
VALUES(552, 1, 131, 'eel1ofjz6mdj4f', 'RT9901- 關連圖明細', 1, 'marcusolsson-dynamictext-panel', '', '{"datasource":{"type":"yesoreyeram-infinity-datasource","uid":"aegaeyjq187b4e"},"description":"","fieldConfig":{"defaults":{"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"red","value":80}]}},"overrides":[{"matcher":{"id":"byName","options":"create_datetime"},"properties":[]},{"matcher":{"id":"byName","options":"level"},"properties":[]},{"matcher":{"id":"byName","options":"diiaInfo__deviceInfo__ip_request"},"properties":[]},{"matcher":{"id":"byName","options":"diiaInfo__deviceInfo__udid"},"properties":[]},{"matcher":{"id":"byName","options":"txInfo__acctNumber"},"properties":[]},{"matcher":{"id":"byName","options":"veriid_trans_id"},"properties":[]},{"matcher":{"id":"byName","options":"建立時間"},"properties":[]},{"matcher":{"id":"byName","options":"交易 IP"},"properties":[]},{"matcher":{"id":"byName","options":"設備 ID"},"properties":[]},{"matcher":{"id":"byName","options":"交易 ID"},"properties":[]}]},"gridPos":{"h":12,"w":12,"x":0,"y":2},"id":13,"options":{"afterRender":"let currentSortField = ''merchantName''; // 預設排序欄位\\r\\nlet currentSortOrder = ''asc'';           // 預設排序方向\\r\\n\\r\\nfunction generateRelationItems(data, sortField = ''txCount'', sortOrder = ''desc'') {\\r\\n  const tableBody = document.getElementById(''relation-table-body'');\\r\\n  tableBody.innerHTML = ''''; // 清空舊資料\\r\\n\\r\\n  // 排序\\r\\n  data.sort((a, b) =\\u003e {\\r\\n    const rawA = a[sortField];\\r\\n    const rawB = b[sortField];\\r\\n\\r\\n    // 嘗試把逗號移除，轉成數字\\r\\n    const numA = typeof rawA === ''string'' ? Number(rawA.replace(/,/g, '''')) : rawA;\\r\\n    const numB = typeof rawB === ''string'' ? Number(rawB.replace(/,/g, '''')) : rawB;\\r\\n\\r\\n    if (!isNaN(numA) \\u0026\\u0026 !isNaN(numB)) {\\r\\n      // 都是有效數字，就用數字比較\\r\\n      return sortOrder === ''asc'' ? numA - numB : numB - numA;\\r\\n    } else {\\r\\n      // 有不是數字，就當字串比\\r\\n      return sortOrder === ''asc''\\r\\n        ? String(rawA).localeCompare(String(rawB))\\r\\n        : String(rawB).localeCompare(String(rawA));\\r\\n    }\\r\\n  });\\r\\n\\r\\n  // 產生每一列\\r\\n  data.forEach(item =\\u003e {\\r\\n    const row = document.createElement(''tr'');\\r\\n\\r\\n    // 商店名稱\\r\\n    const merchantNameCell = document.createElement(''td'');\\r\\n    merchantNameCell.className = ''kpi-merchant-name'';\\r\\n    merchantNameCell.textContent = item.create_datetime;\\r\\n    row.appendChild(merchantNameCell);\\r\\n\\r\\n    // 其他欄位\\r\\n    const kpiFields = [\\r\\n      item.diiaInfo__deviceInfo__udid,\\r\\n      item.diiaInfo__deviceInfo__ip_request,\\r\\n      item.veriid_trans_id,\\r\\n      item.txInfo__acctNumber,\\r\\n    ];\\r\\n\\r\\n    kpiFields.forEach(value =\\u003e {\\r\\n      const cell = document.createElement(''td'');\\r\\n      cell.className = ''kpi-value'';\\r\\n      cell.textContent = value;\\r\\n      row.appendChild(cell);\\r\\n    });\\r\\n\\r\\n    tableBody.appendChild(row);\\r\\n  });\\r\\n}\\r\\n\\r\\nfunction setupRelationTableHeader(data) {\\r\\n  const tableHeader = document.getElementById(''relation-table-header'');\\r\\n  tableHeader.innerHTML = ''''; // 清空舊表頭\\r\\n\\r\\n  // 中文表頭設定\\r\\n  const headers = [\\r\\n    { text: ''Date Time'', field: ''create_datetime'' },\\r\\n    { text: ''UDID'', field: ''diiaInfo__deviceInfo__udid'' },\\r\\n    { text: ''IP'', field: ''diiaInfo__deviceInfo__ip_request'' },\\r\\n    { text: ''Transaction ID'', field: ''veriid_trans_id'' },\\r\\n    { text: ''Account Number'', field: ''txInfo__acctNumber'' },\\r\\n  ];\\r\\n\\r\\n  const headerRow = document.createElement(''tr'');\\r\\n\\r\\n  headers.forEach(header =\\u003e {\\r\\n    const th = document.createElement(''th'');\\r\\n    th.textContent = header.text;\\r\\n    th.style.cursor = ''pointer''; // 鼠標提示可以點\\r\\n    th.addEventListener(''click'', () =\\u003e {\\r\\n      if (currentSortField === header.field) {\\r\\n        currentSortOrder = currentSortOrder === ''asc'' ? ''desc'' : ''asc'';\\r\\n      } else {\\r\\n        currentSortField = header.field;\\r\\n        currentSortOrder = ''asc'';\\r\\n      }\\r\\n      generateRelationItems(data, currentSortField, currentSortOrder);\\r\\n      updateTableHeaderSortIndicator(headers);\\r\\n    });\\r\\n    th.dataset.field = header.field; // 記錄欄位屬性 (方便加箭頭)\\r\\n    headerRow.appendChild(th);\\r\\n  });\\r\\n\\r\\n  tableHeader.appendChild(headerRow);\\r\\n}\\r\\n\\r\\nfunction updateTableHeaderSortIndicator(headers) {\\r\\n  const thElements = document.querySelectorAll(''#relation-table-header th'');\\r\\n  thElements.forEach(th =\\u003e {\\r\\n    const field = th.dataset.field;\\r\\n    const header = headers.find(h =\\u003e h.field === field);\\r\\n    if (!header) return;\\r\\n\\r\\n    if (field === currentSortField) {\\r\\n      th.textContent = ``${header.text} ${currentSortOrder === ''asc'' ? ''▲'' : ''▼''}``;\\r\\n    } else {\\r\\n      th.textContent = header.text;\\r\\n    }\\r\\n  });\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n// 初始化\\r\\nif (Array.isArray(context.data) \\u0026\\u0026 context.data.length \\u003e 0) {\\r\\n  const tableData = context.data[0];\\r\\n  setupRelationTableHeader(tableData);\\r\\n  generateRelationItems(tableData);\\r\\n}\\r\\n","content":"\\u003ctable id=\\"relation-table\\" class=\\"relation-table\\"\\u003e\\n  \\u003cthead id=\\"relation-table-header\\"\\u003e\\n    \\u003c!-- JS 會產生表頭 --\\u003e\\n  \\u003c/thead\\u003e\\n  \\u003ctbody id=\\"relation-table-body\\"\\u003e\\n    \\u003c!-- Data will be populated by JavaScript --\\u003e\\n  \\u003c/tbody\\u003e\\n  \\u003c!-- \\u003ctfoot id=\\"relation-table-footer\\"\\u003e\\u003c/tfoot\\u003e --\\u003e\\n\\u003c/table\\u003e","contentPartials":[],"defaultContent":"The query didn''t return any results.","editor":{"format":"auto","language":"markdown"},"editors":["afterRender","styles"],"externalStyles":[],"helpers":"","renderMode":"data","styles":"\\u003cstyle\\u003e\\r\\n/* 基本字型與排版 */\\r\\n.kpi-grid {\\r\\n  display: grid;\\r\\n  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\\r\\n  gap: 24px;\\r\\n  padding: 20px;\\r\\n  font-family: ''Segoe UI'', ''Roboto Mono'', ''Noto Sans TC'', monospace;\\r\\n}\\r\\n\\r\\n/* KPI 項目 */\\r\\n.kpi-item {\\r\\n  border: 1px solid rgba(100, 181, 246, 0.4);\\r\\n  border-radius: 12px;\\r\\n  padding: 16px 20px;\\r\\n  background: linear-gradient(160deg, rgba(30, 30, 30, 0.9), rgba(40, 40, 40, 0.6));\\r\\n  box-shadow: 0 0 12px rgba(66, 165, 245, 0.2);\\r\\n  transition: transform 0.2s ease;\\r\\n  color: #fff;\\r\\n}\\r\\n.kpi-item:hover {\\r\\n  transform: scale(1.02);\\r\\n  box-shadow: 0 0 20px rgba(66, 165, 245, 0.4);\\r\\n}\\r\\n\\r\\n/* 表格樣式 */\\r\\n.relation-table {\\r\\n  margin: auto;\\r\\n  border-collapse: collapse;\\r\\n  table-layout: fixed;\\r\\n  border: 1px solid rgba(100, 181, 246, 0.2);\\r\\n  font-family: ''Segoe UI'', ''Roboto Mono'', ''Noto Sans TC'', monospace;\\r\\n  // color: #fff;\\r\\n}\\r\\n\\r\\n/* Sticky 表頭和表尾 */\\r\\n.relation-table thead th,\\r\\n.relation-table tfoot td {\\r\\n  position: sticky;\\r\\n  z-index: 1;\\r\\n  color: #fff;  \\r\\n  background: rgba(30, 30, 30, 0.95);\\r\\n}\\r\\n\\r\\n.relation-table thead th {\\r\\n  top: 0;\\r\\n  font-size: 12px;\\r\\n  text-align: center;\\r\\n  border-bottom: 1px solid rgba(100, 181, 246, 0.4);\\r\\n}\\r\\n\\r\\n.relation-table tfoot td {\\r\\n  bottom: 0;\\r\\n  font-weight: bold;\\r\\n  border-top: 1px solid rgba(100, 181, 246, 0.4);\\r\\n}\\r\\n\\r\\n/* 表格內容 */\\r\\n.relation-table th,\\r\\n.relation-table td {\\r\\n  padding: 8px 12px;\\r\\n  min-width: 160px;\\r\\n  text-align: right;\\r\\n  white-space: nowrap;\\r\\n  width: auto;\\r\\n}\\r\\n\\r\\n.relation-table td:last-child {\\r\\n  text-align: left;\\r\\n}\\r\\n\\r\\n.relation-table td.kpi-merchant-name {\\r\\n  font-size: 14px;\\r\\n  letter-spacing: 1px;\\r\\n  text-transform: uppercase;\\r\\n  text-align: center;\\r\\n}\\r\\n\\r\\n.relation-table td.kpi-value {\\r\\n  font-size: 14px;\\r\\n  color: rgb(44, 132, 198);\\r\\n  font-variant-numeric: tabular-nums;\\r\\n}\\r\\n\\r\\n.relation-table td.kpi-label {\\r\\n  font-size: 14px;\\r\\n  letter-spacing: 1px;\\r\\n  text-transform: uppercase;\\r\\n  text-align: left;\\r\\n}\\r\\n\\r\\n.relation-table tbody tr:hover {\\r\\n  transform: scale(1.02);\\r\\n  box-shadow: 0 0 20px rgba(66, 165, 245, 0.4);\\r\\n  transition: all 0.2s ease;\\r\\n}\\r\\n\\r\\n\\r\\n","wrap":true},"pluginVersion":"5.7.0","targets":[{"columns":[],"datasource":{"type":"yesoreyeram-infinity-datasource","uid":"aegaeyjq187b4e"},"filters":[],"format":"table","global_query_id":"","parser":"backend","refId":"A","root_selector":"","source":"url","type":"json","url":"/smartfds-adm-web/report/api/relation-data","url_options":{"data":"","method":"GET","params":[{"key":"requestIP","value":"${query_ip}"},{"key":"from_time","value":"${__from}"},{"key":"to_time","value":"${__to}"},{"key":"acctNumber","value":"${query_acct_number}"},{"key":"udid","value":"${query_udid}"},{"key":"operator_id","value":"${operator_id}"},{"key":"institute_id","value":"${institute_id}"},{"key":"top10udid","value":"${top10Udid}"},{"key":"veriid_trans_id","value":"${query_veriid_trans_id}"},{"key":"merchant_id","value":"${merchant_id}"},{"key":"auth_token","value":"${auth_token}"}]}}],"title":" ","transformations":[{"id":"organize","options":{"excludeByName":{"diiaInfo__deviceInfo__ip_src_latitude":true,"diiaInfo__deviceInfo__ip_src_longitude":true},"includeByName":{},"indexByName":{"create_datetime":0,"diiaInfo__deviceInfo__ip_request":3,"diiaInfo__deviceInfo__udid":2,"level":5,"txInfo__acctNumber":4,"veriid_trans_id":1},"renameByName":{}}},{"id":"sortBy","options":{"fields":{},"sort":[{"desc":true,"field":"create_datetime"}]}}],"type":"marcusolsson-dynamictext-panel"}', '2025-05-06 15:18:14', 1, '2025-05-15 10:52:11', 1, 12, 'RT99-Library');
INSERT INTO grafana.library_element
(id, org_id, folder_id, uid, name, kind, `type`, description, model, created, created_by, updated, updated_by, version, folder_uid)
VALUES(554, 1, 131, 'fel1ogkqetzpca', 'RT9901-關連圖_force 佈局', 1, 'volkovlabs-echarts-panel', '', '{"datasource":{"type":"yesoreyeram-infinity-datasource","uid":"aegaeyjq187b4e"},"description":"","fieldConfig":{"defaults":{},"overrides":[]},"gridPos":{"h":12,"w":12,"x":0,"y":59},"id":2,"options":{"baidu":{"callback":"bmapReady","key":""},"editor":{"format":"auto"},"editorMode":"visual","gaode":{"key":"","plugin":"AMap.Scale,AMap.ToolBar"},"getOption":"const series = context.panel.data.series.map((s) =\\u003e {\\n  const sData = s.fields.find((f) =\\u003e f.type === ''number'').values.buffer || s.fields.find((f) =\\u003e f.type === ''number'').values;\\n  const sTime = s.fields.find((f) =\\u003e f.type === ''time'').values.buffer || s.fields.find((f) =\\u003e f.type === ''time'').values;\\n  \\n  return {\\n    name: s.refId,\\n    type: ''line'',\\n    showSymbol: false,\\n    areaStyle: {\\n      opacity: 0.1,\\n    },\\n    lineStyle: {\\n      width: 1,\\n    },\\n    data: sData.map((d, i) =\\u003e [sTime[i], d.toFixed(2)]),\\n  };\\n});\\n\\n/**\\n * Enable Data Zoom by default\\n */\\nsetTimeout(() =\\u003e context.panel.chart.dispatchAction({\\n  type: ''takeGlobalCursor'',\\n  key: ''dataZoomSelect'',\\n  dataZoomSelectActive: true,\\n}), 500);\\n\\n/**\\n * Update Time Range on Zoom\\n */\\ncontext.panel.chart.on(''datazoom'', function (params) {\\n  const startValue = params.batch[0]?.startValue;\\n  const endValue = params.batch[0]?.endValue;\\n  locationService.partial({ from: startValue, to: endValue });\\n});\\n\\nreturn {\\n  backgroundColor: ''transparent'',\\n  tooltip: {\\n    trigger: ''axis'',\\n  },\\n  legend: {\\n    left: ''0'',\\n    bottom: ''0'',\\n    data: context.panel.data.series.map((s) =\\u003e s.refId),\\n    textStyle: {\\n      color: ''rgba(128, 128, 128, .9)'',\\n    },\\n  },\\n  toolbox: {\\n    feature: {\\n      dataZoom: {\\n        yAxisIndex: ''none'',\\n        icon: {\\n          zoom: ''path://'',\\n          back: ''path://'',\\n        },\\n      },\\n      saveAsImage: {},\\n    }\\n  },\\n  xAxis: {\\n    type: ''time'',\\n  },\\n  yAxis: {\\n    type: ''value'',\\n    min: ''dataMin'',\\n  },\\n  grid: {\\n    left: ''2%'',\\n    right: ''2%'',\\n    top: ''2%'',\\n    bottom: 24,\\n    containLabel: true,\\n  },\\n  series,\\n};","google":{"callback":"gmapReady","key":""},"map":"none","renderer":"canvas","themeEditor":{"config":"{}","name":"default"},"visualEditor":{"code":"const data = context.panel.data;\\nlet graph = {\\n  nodes: [],\\n  links: [],\\n  categories: []\\n};\\n\\nif (data.series.length \\u003e 0) {\\n  data.series[0].fields.forEach((s) =\\u003e {\\n    console.log(``Processing field: ${s.name}``, JSON.parse(s.values));\\n    if (s.name === ''links'') graph.links = JSON.parse(s.values);\\n    else if (s.name === ''nodes'') graph.nodes = JSON.parse(s.values);\\n    else if (s.name === ''categories'') graph.categories = JSON.parse(s.values);\\n  });\\n}\\n\\ngraph.nodes.forEach(node =\\u003e {\\n  if (node.category == 0) node.category = 1;\\n  else if (node.category == 1) node.category = 0;\\n  else if (node.category == 3) node.category = 4;\\n  else if (node.category == 4) node.category = 3;\\n});\\n\\ngraph.categories.forEach(categorie =\\u003e {\\n  if (categorie.name === ''IP 1'') {\\n    categorie.name = ''設備ID-1'';\\n  } else if (categorie.name === ''IP 2'') {\\n    categorie.name = ''設備ID-2'';\\n  } else if (categorie.name === ''UDID 1'') {\\n    categorie.name = ''位址-1'';\\n  } else if (categorie.name === ''UDID 2'') {\\n    categorie.name = ''位址-2'';\\n  } else if (categorie.name === ''Account 1'') {\\n    categorie.name = ''帳號-1'';\\n  } else if (categorie.name === ''Account 2'') {\\n    categorie.name = ''帳號-2'';\\n  }\\n});\\n\\nconst categoryColors = {\\n  3: \\"#81D4FA\\", 4: \\"#AED581\\", 5: \\"#FFB74D\\",\\n  0: \\"#0288D1\\", 1: \\"#388E3C\\", 2: \\"#F57C00\\"\\n};\\n\\nconst triangleHeight = 1;     // 頂部與底部之間的垂直距離\\nconst triangleSpacing = 480;    // 左右兩個三角形之間的水平距離\\nconst nodeSpacing = 36;         // 節點垂直間距（由水平改成垂直）\\n\\nconst categoryGroups = {\\n  0: { baseX: -triangleSpacing, baseY: triangleHeight },  // 左頂\\n  1: { baseX: -triangleSpacing - 360, baseY: triangleHeight + 180 },            // 左底左\\n  2: { baseX: -triangleSpacing + 360, baseY: triangleHeight + 180 },            // 左底右\\n  3: { baseX: triangleSpacing, baseY: triangleHeight },    // 右頂\\n  4: { baseX: triangleSpacing - 360, baseY: triangleHeight + 180 },             // 右底左\\n  5: { baseX: triangleSpacing + 360, baseY: triangleHeight + 180 }              // 右底右\\n};\\n\\n// 初始化每類別的偏移量為 0\\nconst categoryOffsets = {};\\nObject.keys(categoryGroups).forEach(k =\\u003e categoryOffsets[k] = 0);\\n\\ngraph.nodes.forEach(node =\\u003e {\\n  const cat = node.category;\\n  const group = categoryGroups[cat];\\n  const offset = categoryOffsets[cat];\\n  node.symbol = \\"circle\\"; //circle，rect，roundRect，triangle，diamond\\n  node.symbolSize = [12, 12];\\n  node.label = { show: true, position: \\"inside\\" };\\n\\n  // 改為「固定 X、Y 軸上下堆疊」\\n  node.x = group.baseX;\\n  node.y = group.baseY + offset;\\n  categoryOffsets[cat] += nodeSpacing;\\n  console.log(cat + '':'' + categoryOffsets[cat]);\\n  // 節點顏色\\n  node.itemStyle = {\\n    color: categoryColors[cat] || \\"#cccccc\\"\\n  };\\n\\n  //categoryOffsets[cat] = categoryOffsets[cat] + 10;\\n\\n});\\n\\nconst option = {\\n  tooltip: {},\\n  legend: [\\n    {\\n      icon: ''circle'',\\n      data: graph.categories.map(cat =\\u003e cat.name),\\n      textStyle: {\\n        rich: graph.categories.reduce((acc, cat, index) =\\u003e {\\n          acc[cat.name] = { color: categoryColors[index] };\\n          return acc;\\n        }, {})\\n      }\\n    }\\n  ],\\n  animation: false,\\n  series: [\\n    {\\n      name: \\"Graph\\",\\n      type: \\"graph\\",\\n      layout: \\"force\\",\\n      zoom: 0.8,\\n      force: {\\n        repulsion: [100, 1200],\\n        gravity: 0.1,\\n        edgeLength: [100, 1200],\\n        friction: 0.2,\\n        preventOverlap: true\\n      },\\n      draggable: true,\\n      data: graph.nodes.map(node =\\u003e ({\\n        ...node,\\n        symbol: ''circle'',\\n        symbolSize: [12, 12],\\n        itemStyle: {\\n          color: categoryColors[node.category] || \\"#cccccc\\"\\n        },\\n        label: {\\n          show: true,\\n          position: \\"right\\",\\n          padding: [0, 0, 0, 5],\\n          formatter: params =\\u003e params.name.substring(0, 16),\\n        },\\n        labelLayout: {\\n          hideOverlap: false\\n        }\\n      })),\\n      links: graph.links,\\n      focusNodeAdjacency: false,\\n      categories: graph.categories.map((cat, index) =\\u003e ({\\n        ...cat,\\n        itemStyle: { color: categoryColors[index] }\\n      })),\\n      roam: true,\\n      lineStyle: { color: \\"source\\", curveness: 0.3 },\\n    }\\n  ],\\n  graphic: graph.nodes.length === 0 ? [\\n    {\\n      type: \\"text\\",\\n      left: \\"center\\",\\n      top: \\"middle\\",\\n      style: {\\n        text: \\"查無資訊\\",\\n        fill: \\"#999\\",\\n        font: \\"bold 20px sans-serif\\"\\n      }\\n    }\\n  ] : []\\n};\\n\\nreturn option;\\n\\n\\n\\n","dataset":[],"series":[]}},"pluginVersion":"6.6.0","targets":[{"columns":[],"datasource":{"type":"yesoreyeram-infinity-datasource","uid":"aegaeyjq187b4e"},"filters":[],"format":"table","global_query_id":"","parser":"backend","refId":"A","root_selector":"","source":"url","type":"json","url":"/smartfds-adm-web/report/api/relation","url_options":{"data":"","headers":[],"method":"GET","params":[{"key":"requestIP","value":"${query_ip}"},{"key":"from_time","value":"${__from}"},{"key":"to_time","value":"${__to}"},{"key":"acctNumber","value":"${query_acct_number}"},{"key":"udid","value":"${query_udid}"},{"key":"operator_id","value":"${operator_id}"},{"key":"institute_id","value":"${institute_id}"},{"key":"top10udid","value":"${top10Udid}"},{"key":"veriid_trans_id","value":"${query_veriid_trans_id}"},{"key":"merchant_id","value":"${merchant_id}"},{"key":"auth_token","value":"${auth_token}"}]}}],"title":"UDID Relation","transformations":[{"id":"organize","options":{}}],"type":"volkovlabs-echarts-panel"}', '2025-05-06 15:18:33', 1, '2025-06-13 16:12:22', 1, 15, 'RT99-Library');
INSERT INTO grafana.library_element
(id, org_id, folder_id, uid, name, kind, `type`, description, model, created, created_by, updated, updated_by, version, folder_uid)
VALUES(555, 1, 131, 'bel1ogy4i8i68c', 'RT9901-關連圖_none 佈局', 1, 'volkovlabs-echarts-panel', '', '{"datasource":{"type":"yesoreyeram-infinity-datasource","uid":"aegaeyjq187b4e"},"description":"","fieldConfig":{"defaults":{},"overrides":[]},"gridPos":{"h":12,"w":12,"x":12,"y":59},"id":5,"options":{"baidu":{"callback":"bmapReady","key":""},"editor":{"format":"auto"},"editorMode":"visual","gaode":{"key":"","plugin":"AMap.Scale,AMap.ToolBar"},"getOption":"const series = context.panel.data.series.map((s) =\\u003e {\\n  const sData = s.fields.find((f) =\\u003e f.type === ''number'').values.buffer || s.fields.find((f) =\\u003e f.type === ''number'').values;\\n  const sTime = s.fields.find((f) =\\u003e f.type === ''time'').values.buffer || s.fields.find((f) =\\u003e f.type === ''time'').values;\\n  \\n  return {\\n    name: s.refId,\\n    type: ''line'',\\n    showSymbol: false,\\n    areaStyle: {\\n      opacity: 0.1,\\n    },\\n    lineStyle: {\\n      width: 1,\\n    },\\n    data: sData.map((d, i) =\\u003e [sTime[i], d.toFixed(2)]),\\n  };\\n});\\n\\n/**\\n * Enable Data Zoom by default\\n */\\nsetTimeout(() =\\u003e context.panel.chart.dispatchAction({\\n  type: ''takeGlobalCursor'',\\n  key: ''dataZoomSelect'',\\n  dataZoomSelectActive: true,\\n}), 500);\\n\\n/**\\n * Update Time Range on Zoom\\n */\\ncontext.panel.chart.on(''datazoom'', function (params) {\\n  const startValue = params.batch[0]?.startValue;\\n  const endValue = params.batch[0]?.endValue;\\n  locationService.partial({ from: startValue, to: endValue });\\n});\\n\\nreturn {\\n  backgroundColor: ''transparent'',\\n  tooltip: {\\n    trigger: ''axis'',\\n  },\\n  legend: {\\n    left: ''0'',\\n    bottom: ''0'',\\n    data: context.panel.data.series.map((s) =\\u003e s.refId),\\n    textStyle: {\\n      color: ''rgba(128, 128, 128, .9)'',\\n    },\\n  },\\n  toolbox: {\\n    feature: {\\n      dataZoom: {\\n        yAxisIndex: ''none'',\\n        icon: {\\n          zoom: ''path://'',\\n          back: ''path://'',\\n        },\\n      },\\n      saveAsImage: {},\\n    }\\n  },\\n  xAxis: {\\n    type: ''time'',\\n  },\\n  yAxis: {\\n    type: ''value'',\\n    min: ''dataMin'',\\n  },\\n  grid: {\\n    left: ''2%'',\\n    right: ''2%'',\\n    top: ''2%'',\\n    bottom: 24,\\n    containLabel: true,\\n  },\\n  series,\\n};","google":{"callback":"gmapReady","key":""},"map":"none","renderer":"canvas","themeEditor":{"config":"{}","name":"default"},"visualEditor":{"code":"const data = context.panel.data;\\nlet graph = {\\n  nodes: [],\\n  links: [],\\n  categories: []\\n};\\n\\nif (data.series.length \\u003e 0) {\\n  data.series[0].fields.forEach((s) =\\u003e {\\n    console.log(``Processing field: ${s.name}``, JSON.parse(s.values));\\n    if (s.name === ''links'') graph.links = JSON.parse(s.values);\\n    else if (s.name === ''nodes'') graph.nodes = JSON.parse(s.values);\\n    else if (s.name === ''categories'') graph.categories = JSON.parse(s.values);\\n  });\\n}\\n\\ngraph.nodes.forEach(node =\\u003e {\\n  if (node.category == 0) node.category = 1;\\n  else if (node.category == 1) node.category = 0;\\n  else if (node.category == 3) node.category = 4;\\n  else if (node.category == 4) node.category = 3;\\n});\\n\\ngraph.categories.forEach(categorie =\\u003e {\\n  if (categorie.name === ''IP 1'') {\\n    categorie.name = ''設備ID-1'';\\n  } else if (categorie.name === ''IP 2'') {\\n    categorie.name = ''設備ID-2'';\\n  } else if (categorie.name === ''UDID 1'') {\\n    categorie.name = ''位址-1'';\\n  } else if (categorie.name === ''UDID 2'') {\\n    categorie.name = ''位址-2'';\\n  } else if (categorie.name === ''Account 1'') {\\n    categorie.name = ''帳號-1'';\\n  } else if (categorie.name === ''Account 2'') {\\n    categorie.name = ''帳號-2'';\\n  }\\n});\\n\\nconst categoryColors = {\\n  3: \\"#81D4FA\\", 4: \\"#AED581\\", 5: \\"#FFB74D\\",\\n  0: \\"#0288D1\\", 1: \\"#388E3C\\", 2: \\"#F57C00\\"\\n};\\n\\n// 畫布寬度\\nconst canvasWidth = 640;\\nconst colSpacing = canvasWidth / 3; // 每列的間距\\n//const colSpacing = 10;\\nconst rowSpacing = 18; // 調整節點垂直間距為 15px\\n\\nconst categoryXPositions = {};\\n\\n// 讓 6 種 category 均分 X 軸\\nfor (let i = 0; i \\u003c 6; i++) {\\n  categoryXPositions[i] = -canvasWidth / 2 + i * colSpacing + colSpacing / 2;\\n}\\n\\n// **讓節點從 ``top: 15px`` 開始**\\nlet categoryYPositions = { 0: 15, 1: 15, 2: 15, 3: 15, 4: 15, 5: 15 };\\n\\n\\n// 設定節點樣式 \\u0026 位置\\ngraph.nodes.forEach((node) =\\u003e {\\n  node.symbol = \\"circle\\"; // 設定為圓點\\n  node.symbolSize = 12; // 稍微放大圓點\\n  node.label = { show: true, position: \\"inside\\" };\\n\\n  // **設定 X、Y 軸座標**\\n  node.x = categoryXPositions[node.category]; // 分成 6 列\\n  node.y = categoryYPositions[node.category]; // 從 ``top 15px`` 開始\\n  categoryYPositions[node.category] += rowSpacing; // 下個同類別的節點往下擺\\n\\n  node.itemStyle = { color: categoryColors[node.category] || \\"#cccccc\\" };\\n});\\n\\nreturn {\\n  tooltip: {},\\n  legend: [\\n    {\\n      icon: ''circle'',\\n      data: graph.categories.map(a =\\u003e a.name)\\n    }\\n  ],\\n  animationDurationUpdate: 1500,\\n  animationEasingUpdate: \\"quinticInOut\\",\\n  series: [\\n    {\\n      name: \\"Graph\\",\\n      type: \\"graph\\",\\n      layout: \\"none\\", // 固定座標\\n      data: graph.nodes.map((node) =\\u003e ({\\n        ...node,\\n        draggable: true, // 允許拖曳\\n        itemStyle: { color: categoryColors[node.category] || \\"#cccccc\\" },\\n        symbol: ''circle'',\\n        symbolSize: [12, 12],\\n        label: {\\n          show: true,\\n          position: \\"right\\",\\n          padding: [0, 0, 0, 5],\\n          formatter: params =\\u003e params.name.substring(0, 16),\\n        },\\n      })),\\n      links: graph.links,\\n      categories: graph.categories.map((cat, index) =\\u003e ({\\n        ...cat,\\n        itemStyle: { color: categoryColors[index] || \\"#cccccc\\" }\\n      })),\\n      roam: true, // 允許拖曳，但不會影響原始佈局\\n      focusNodeAdjacency: false,\\n      lineStyle: {\\n        color: \\"source\\",\\n        curveness: 0.3\\n      }\\n    }\\n  ],\\n  graphic: graph.nodes.length === 0 ? [\\n    {\\n      type: \\"text\\",\\n      left: \\"center\\",\\n      top: \\"middle\\",\\n      style: {\\n        text: \\"查無資訊\\",\\n        fill: \\"#999\\",\\n        font: \\"bold 20px sans-serif\\"\\n      }\\n    }\\n  ] : []\\n};\\n","dataset":[],"series":[]}},"pluginVersion":"6.6.0","targets":[{"columns":[],"datasource":{"type":"yesoreyeram-infinity-datasource","uid":"aegaeyjq187b4e"},"filters":[],"format":"table","global_query_id":"","parser":"backend","refId":"A","root_selector":"","source":"url","type":"json","url":"/smartfds-adm-web/report/api/relation","url_options":{"data":"","headers":[],"method":"GET","params":[{"key":"requestIP","value":"${query_ip}"},{"key":"from_time","value":"${__from}"},{"key":"to_time","value":"${__to}"},{"key":"acctNumber","value":"${query_acct_number}"},{"key":"udid","value":"${query_udid}"},{"key":"operator_id","value":"${operator_id}"},{"key":"institute_id","value":"${institute_id}"},{"key":"top10udid","value":"${top10Udid}"},{"key":"veriid_trans_id","value":"${query_veriid_trans_id}"},{"key":"merchant_id","value":"${merchant_id}"},{"key":"auth_token","value":"${auth_token}"}]}}],"title":"UDID Relation II","transformations":[{"id":"organize","options":{}}],"type":"volkovlabs-echarts-panel"}', '2025-05-06 15:18:40', 1, '2025-06-13 15:42:41', 1, 14, 'RT99-Library');
